This commit is contained in:
neko
2022-06-07 21:31:23 +03:00
parent 4f91ec75cb
commit 286dbb31bb
7 changed files with 161 additions and 3 deletions

View File

@@ -15,7 +15,6 @@ type SuperInputTextPropsType = Omit<DefaultInputPropsType, 'type'> & { // и +
const SuperInputText: React.FC<SuperInputTextPropsType> = (
{
// type, // достаём и игнорируем чтоб нельзя было задать другой тип инпута
onChange, onChangeText,
onKeyPress, onEnter,
error,

View File

@@ -2,7 +2,7 @@ import React from 'react'
const Error404 = () => {
return (
<div id={'page-404'}>
<div id={'hw5-page-404'}>
<div>404</div>
<div>Page not found!</div>
<div>/.̫ .\</div>

View File

@@ -1,10 +1,11 @@
import React from 'react'
import HW6 from '../../hw06/HW6'
function Junior() {
return (
<div id={'hw5-page-junior'}>
junior page
{/*<HW6/>*/}
<HW6/>
{/*<HW7/>*/}
{/*<HW8/>*/}
{/*<HW9/>*/}

View File

@@ -0,0 +1,44 @@
import React, {useState} from 'react'
import SuperEditableSpan from './common/c4-SuperEditableSpan/SuperEditableSpan'
import {restoreState, saveState} from './localStorage/localStorage'
import s2 from '../../s1-main/App.module.css'
import SuperButton from '../hw04/common/c2-SuperButton/SuperButton'
const HW6 = () => {
const [value, setValue] = useState<string>('')
const save = () => {
saveState<string>('hw6-editable-span-value', value)
}
const restore = () => { // делают студенты
setValue(restoreState<string>('hw6-editable-span-value', ''))
}
return (
<div id={'hw6'} className={s2.hw}>
<hr/>
{/*можно убрать этот тег*/}
<div className={s2.hwTitle}>homeworks 6</div>
{/*should work (должно работать)*/}
<div>
<SuperEditableSpan
id={'hw6-spanable-input'}
value={value}
onChangeText={setValue}
spanProps={{children: value ? undefined : 'enter text...', id: 'hw6-editable-span'}}
/>
</div>
<SuperButton id={'hw6-save'} onClick={save}>save to ls</SuperButton>
<SuperButton id={'hw6-restore'} onClick={restore}>get from ls</SuperButton>
<hr/>
{/*можно убрать этот тег*/}
<hr/>
{/*можно убрать этот тег*/}
</div>
)
}
export default HW6

View File

@@ -0,0 +1,9 @@
.span {
display: inline-block;
height: 31px;
padding-top: 10px;
cursor: pointer;
color: #77aaff;
}

View File

@@ -0,0 +1,78 @@
import React, {DetailedHTMLProps, InputHTMLAttributes, HTMLAttributes, useState} from 'react'
import s from './SuperEditableSpan.module.css'
import SuperInputText from '../../../hw04/common/c1-SuperInputText/SuperInputText'
// тип пропсов обычного инпута
type DefaultInputPropsType = DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
// тип пропсов обычного спана
type DefaultSpanPropsType = DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
// здесь мы говорим что у нашего инпута будут такие же пропсы как у обычного инпута, кроме type
// (чтоб не писать value: string, onChange: ...; они уже все описаны в DefaultInputPropsType)
type SuperEditableSpanType = Omit<DefaultInputPropsType, 'type'> & { // и + ещё пропсы которых нет в стандартном инпуте
onChangeText?: (value: string) => void
onEnter?: () => void
error?: string
spanProps?: DefaultSpanPropsType // пропсы для спана
}
const SuperEditableSpan: React.FC<SuperEditableSpanType> = (
{
autoFocus,
onBlur,
onEnter,
spanProps,
...restProps// все остальные пропсы попадут в объект restProps
}
) => {
const [editMode, setEditMode] = useState<boolean>(false)
const {children, onDoubleClick, className, ...restSpanProps} = spanProps || {}
const onEnterCallback = () => {
setEditMode(false) // выключить editMode при нажатии Enter // делают студенты
onEnter?.()
}
const onBlurCallback = (e: React.FocusEvent<HTMLInputElement>) => {
setEditMode(false) // выключить editMode при нажатии за пределами инпута // делают студенты
onBlur?.(e)
}
const onDoubleClickCallBack = (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
setEditMode(true) // включить editMode при двойном клике // делают студенты
onDoubleClick?.(e)
}
const spanClassName = `${s.span} ${className ? className : ''}`
return (
<>
{editMode
? (
<SuperInputText
autoFocus={autoFocus || true}
onBlur={onBlurCallback}
onEnter={onEnterCallback}
{...restProps} // отдаём инпуту остальные пропсы если они есть (value например там внутри)
/>
) : (
<span
onDoubleClick={onDoubleClickCallBack}
className={spanClassName}
{...restSpanProps}
>
{/*если нет захардкодженного текста для спана, то значение инпута*/}
{children || restProps.value}
</span>
)
}
</>
)
}
export default SuperEditableSpan

View File

@@ -0,0 +1,27 @@
// вот вам функция для сохранения объектов в память браузера
// (данные в этом хранилище сохраняться даже при перезагрузке компа):
export function saveState<T>(key: string, state: T) {
const stateAsString = JSON.stringify(state)
localStorage.setItem(key, stateAsString)
}
// и вот вам функция для получения сохранённого объекта в памяти браузера:
export function restoreState<T>(key: string, defaultState: T) {
let state = defaultState
const stateAsString = localStorage.getItem(key)
if (stateAsString !== null) state = JSON.parse(stateAsString) as T
return state
}
// ---------------------------------------------------------------------------------------------------------------
// пример использования:
type StateType = {
x: string
y: number
}
// сохраняем объект типа StateType в ячейке 'test'
saveState<StateType>('test', {x: 'A', y: 1})
// получем в переменную state объект из ячейки 'test' или дэфолтный объект если ячейка пуста
const state: StateType = restoreState<StateType>('test', {x: '', y: 0})