281 lines
12 KiB
TypeScript
281 lines
12 KiB
TypeScript
import React from 'react';
|
||
import { useField, Form, FormikProps, Formik } from 'formik';
|
||
import {
|
||
Select, Textarea, VStack, Checkbox, Button,
|
||
} from '@chakra-ui/react'
|
||
import 'suneditor/dist/css/suneditor.min.css'; // Import Sun Editor's CSS File
|
||
import Description from "./description";
|
||
import Settings from "./settings";
|
||
|
||
//Значения, собираемые для отправки на бэк
|
||
interface Values {
|
||
title: string;
|
||
type: string;
|
||
children: string;
|
||
description: string;
|
||
}
|
||
//Поля объектов, используемых для отображения созданных пользователем инструментов
|
||
interface ElementsOfObject {
|
||
text: string;
|
||
id: number;
|
||
isFocus: boolean;
|
||
color?: string;
|
||
}
|
||
|
||
const types = [
|
||
{desc:"текст", value:"text"},
|
||
{desc:"селект", value:"select"},
|
||
{desc:"чекбокс", value:"checkbox"},
|
||
{desc:"файл", value:"file"},
|
||
{desc:"кнопка", value:"button"},
|
||
{desc:"ничего", value:"none"}
|
||
]
|
||
|
||
const TextField = (props: any) => {
|
||
|
||
const [field, meta, helpers] = useField(props);
|
||
|
||
return (
|
||
<>
|
||
<Textarea resize="none" width="80%" {...field} {...props} />
|
||
{meta.touched && meta.error ? (
|
||
<div className="error">{meta.error}</div>
|
||
) : null}
|
||
</>
|
||
);
|
||
};
|
||
|
||
const getFreeNumber = (array:Array<ElementsOfObject>):number => {
|
||
// Для первого элемента в списке
|
||
if (array.length === 0) {
|
||
return(0)
|
||
} else {
|
||
//Для всех последующих элементов
|
||
|
||
//Создаём массив, состоящий из id всех существующих модалок
|
||
let indexes:any = []
|
||
for (let i = 0; i < array.length; i++) {
|
||
indexes.push(array[i].id)
|
||
}
|
||
//Сортируем в порядке возрастания
|
||
indexes.sort(function compare(a:any, b:any):any {
|
||
if (a < b) { return -1;}
|
||
if (a > b) { return 1;}
|
||
return 0;
|
||
}
|
||
)
|
||
let max = indexes[indexes.length - 1]
|
||
|
||
//Создаём массив - маску от 0 до самого высокого значения id
|
||
let mask:any = []
|
||
for (let i = 0; i <= max; i++) {
|
||
mask.push(i)
|
||
}
|
||
|
||
//Ищем разницу между существующими id окон и маской. Список пропущенных значений есть список доступных имён.
|
||
let difference = indexes
|
||
.filter((num:any):any => !mask.includes(num))
|
||
.concat(mask.filter((num:any):any => !indexes.includes(num)));
|
||
|
||
// difference - массив нехватающих в списке номеров.
|
||
// Если все окна у нас по порядку, без пропусков - нужно добавить новый номер
|
||
if (difference.length === 0) {
|
||
return(max + 1)
|
||
} else {
|
||
//Иначе добавить нехватающий
|
||
return(difference[0])
|
||
}
|
||
}
|
||
}
|
||
const getIndexById = (id:number, array:Array<ElementsOfObject>):number => {
|
||
let index
|
||
for (let i = 0; i <= array.length; i++) {
|
||
if (array[i] !== undefined) {
|
||
if (array[i].id === id) {
|
||
index = i
|
||
break
|
||
}
|
||
}
|
||
}
|
||
if (typeof index === "number") {
|
||
return index
|
||
} else {
|
||
console.log("Я не нашёл нужный индекс, вывел 0")
|
||
return 0
|
||
}
|
||
|
||
}
|
||
|
||
export default () => {
|
||
|
||
|
||
const [type, setType] = React.useState<number>(4)
|
||
const [stockroom, setStockroom] = React.useState<Array<ElementsOfObject>>([])
|
||
const [focus, setFocus] = React.useState<number | undefined>() //Хранит id объекта
|
||
|
||
//При пересоздании массива для изменения фокуса объекта отменяются фокусы у всех элементов массива
|
||
|
||
const typeHC = (value:number): void => {
|
||
setType(value)
|
||
setStockroom([])
|
||
}
|
||
const changeFocus = (id: number): void => {
|
||
//Не менять фокус если снова выбрано то же окно
|
||
if (focus !== id) {
|
||
//Хранилище с отменённым фокусом у объектов (по задумке у одного элемента)
|
||
let newArr = stockroom.map((e:ElementsOfObject) => {
|
||
e.isFocus = false
|
||
return e
|
||
})
|
||
|
||
//Получаем индексы фокусированных объектов. Новый и, если есть, старый
|
||
let index = getIndexById(id, stockroom)
|
||
|
||
//Устанавливаем новый фокус и пересоздаём массив
|
||
setFocus(id)
|
||
|
||
newArr[index].isFocus = true
|
||
setStockroom([...newArr])
|
||
}
|
||
}
|
||
const changeBgColor = (color: string): void => {
|
||
if (focus !== undefined) {
|
||
let index = getIndexById(focus, stockroom)
|
||
|
||
let list = stockroom
|
||
list[index].color = color
|
||
setStockroom([...list])
|
||
}
|
||
}
|
||
const changeText = (text: string): void => {
|
||
if (focus !== undefined) {
|
||
let index = getIndexById(focus, stockroom)
|
||
let list = stockroom
|
||
list[index].text = text
|
||
setStockroom([...list])
|
||
}
|
||
}
|
||
|
||
const createObject = (obj:ElementsOfObject) => {
|
||
|
||
//Получаем и присваиваем первый свободный айдишник (по возрастанию)
|
||
const free = getFreeNumber(stockroom)
|
||
obj.id = free
|
||
|
||
//Хранилище с отменённым фокусом у объектов (по задумке у одного элемента)
|
||
let newArr = stockroom.map((e:ElementsOfObject) => {
|
||
e.isFocus = false
|
||
return e
|
||
})
|
||
|
||
//Мы должны вставить новый объект следующим после того, на котором фокус. Это достигается позиционированием в массив
|
||
if (focus === undefined){ //фокуса нет - добавляем в конец массива
|
||
newArr.push(obj)
|
||
setStockroom([...newArr])
|
||
//Говорим стейту с фокусом, что фокус изменился
|
||
setFocus(newArr.length - 1)
|
||
} else { //фокус есть - добавляем после объекта с фокусом
|
||
let index = getIndexById(focus, stockroom)
|
||
newArr.splice(index + 1, 0, obj)
|
||
|
||
//Говорим стейту с фокусом, что фокус изменился
|
||
setFocus(index + 1)
|
||
setStockroom([...newArr])
|
||
}
|
||
}
|
||
const deleteObject = (id: number): void => {
|
||
let index = getIndexById(id, stockroom)
|
||
//Проверка, что объект с таким id существует
|
||
if (stockroom[index] !== undefined) {
|
||
//Если удалён был фокусный объект - фокус теперь неизвестен
|
||
if (stockroom[index].isFocus) {
|
||
setFocus(undefined)
|
||
}
|
||
let newArr = stockroom
|
||
newArr.splice(index, 1)
|
||
setStockroom([...newArr])
|
||
}
|
||
}
|
||
|
||
return(
|
||
<>
|
||
<Formik
|
||
initialValues={{
|
||
children: '',
|
||
title: '',
|
||
type: '',
|
||
description:'описание',
|
||
}}
|
||
|
||
onSubmit={(values, actions) => {
|
||
console.log(JSON.stringify(values))
|
||
}}
|
||
>
|
||
{(props: FormikProps<Values>) => (
|
||
|
||
<Form>
|
||
<VStack>
|
||
<TextField placeholder="Заголовок" name="title" type="text" />
|
||
<Description name="description"/>
|
||
{
|
||
type === 0 ? //Тип вопроса - текст?
|
||
<TextField name="text" placeholder="текст" type="text" />
|
||
:
|
||
type === 3 ? //Тип вопроса - файл?
|
||
<input type="file"/>
|
||
:
|
||
stockroom.length === 0 ? //В поле для юзерских данных есть что-то?
|
||
null //Ничего не внесено
|
||
:
|
||
type === 1 ? //Тип вопроса - селект?
|
||
<Select
|
||
>
|
||
{
|
||
stockroom.map((e: any, i: number) => {
|
||
return <option key={i}>{e.text}</option>
|
||
})
|
||
}
|
||
</Select>
|
||
:
|
||
type === 2 ? //Тип вопроса - чекбокс?
|
||
stockroom.map((e:any, i:number) => {
|
||
return <Checkbox key={i}>{e.text}</Checkbox>
|
||
})
|
||
:
|
||
type === 4 ? //Тип вопроса - кнопка?
|
||
stockroom.map((e:any, i:number) => {
|
||
return <Button
|
||
backgroundColor={e.color}
|
||
key={i}
|
||
onClick={(event: any) => {
|
||
changeFocus(e.id)
|
||
event.target.blur()
|
||
}}
|
||
>{e.text}</Button>
|
||
})
|
||
:
|
||
null //Тип вопроса - ничего
|
||
}
|
||
</VStack>
|
||
|
||
<Settings
|
||
types={types}
|
||
stockroom={stockroom}
|
||
typeHC={typeHC}
|
||
type={type}
|
||
focus={focus}
|
||
changeFocus={changeFocus}
|
||
changeBgColor={changeBgColor}
|
||
changeText={changeText}
|
||
getIndexById={getIndexById}
|
||
createObject={createObject}
|
||
deleteObject={deleteObject}
|
||
/>
|
||
</Form>
|
||
|
||
)}
|
||
</Formik>
|
||
</>
|
||
)
|
||
}
|