/* ItemsList.tsx - ESP3D WebUI component file Copyright (c) 2021 Luc LEBOSSE. All rights reserved. This code is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with This code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ import { Fragment, FunctionalComponent, TargetedMouseEvent, JSX } from "preact" import { useState, useEffect } from "preact/hooks" import { ButtonImg } from "../../Controls" import { T } from "../../Translations" import { iconsFeather } from "../../Images" import { iconsTarget } from "../../../targets" import { generateUID, generateDependIds, checkDependencies, } from "../../Helpers" import { Field } from "../../Controls" import { formatItem } from "../../../tabs/interface/importHelper" import { useUiContextFn, useSettingsContext } from "../../../contexts" import { Plus, ArrowUp, ArrowDown, Trash2, Minimize2, Flag, } from "preact-feather" import defaultPanel from "./def_panel.json" import defaultMacro from "./def_macro.json" import defaultPolling from "./def_polling.json" interface FieldItem { id: string type?: string label?: string initial: any value: any options?: any[] name?: string haserror?: boolean hasmodified?: boolean [key: string]: any } interface ItemData { id: string value: FieldItem[] editionMode?: boolean newItem?: boolean [key: string]: any } type ValidationError = string | null | true // true or null means valid, string is the error message type ValidationFunction = (item: FieldItem) => ValidationError | Promise interface ItemControlProps { itemData: ItemData index: number completeList: ItemData[] idList: string depend?: any setValue: (value: ItemData[] | null, update?: boolean) => void validationfn: ValidationFunction fixed?: boolean nodelete?: boolean editable?: boolean sorted?: boolean } interface ItemsListProps { id: string label?: string validationfn: ValidationFunction validation?: any value: ItemData[] type?: string setValue: (value: ItemData[] | null, update?: boolean) => void inline?: boolean fixed?: boolean sorted?: boolean depend?: any nodelete?: boolean editable?: boolean [key: string]: any } /* * Local const * */ const ItemControl: FunctionalComponent = ({ itemData, index, completeList, idList, depend, setValue, validationfn, fixed, nodelete, editable, sorted, }) => { const iconsList: Record = { ...iconsTarget, ...iconsFeather } const { id, value, editionMode, ...rest } = itemData const indexIcon = value.findIndex((element) => element.id == `${id }-icon`) const indexName = value.findIndex((element) => element.id == `${id }-name`) const icon = value ? value[indexIcon != -1 ? indexIcon : 0].value : null const name = value ? value[indexName != -1 ? indexName : 0].value : null const controlIcon = iconsList[icon] ? iconsList[icon] : "" const onEdit = (state: boolean) => { completeList[index].editionMode = state setValue([...completeList]) } const downItem = (e: TargetedMouseEvent) => { e.currentTarget.blur() useUiContextFn.haptic() const item = completeList[index] completeList.splice(index, 1) completeList.splice(index + 1, 0, item) setValue(completeList) } const upItem = (e: TargetedMouseEvent) => { e.currentTarget.blur() useUiContextFn.haptic() const item = completeList[index] completeList.splice(index, 1) completeList.splice(index - 1, 0, item) setValue(completeList) } const removeItem = (e: TargetedMouseEvent) => { useUiContextFn.haptic() e.currentTarget.blur() completeList.splice(index, 1) setValue(completeList) } useEffect(() => { //to update state when import- but why ? if (setValue) setValue(null, true) }, [completeList]) let colorStyle: string | undefined if ( JSON.stringify(value).includes('"hasmodified":true') || JSON.stringify(itemData).includes('"newitem":true') ) colorStyle = "box-shadow: 0 0 0 .2rem rgba(255, 183, 0, .4);margin-right:0.5rem!important" if (JSON.stringify(value).includes('"haserror":true')) colorStyle = "box-shadow: 0 0 0 .2rem rgba(255, 0, 0, .4);margin-right:0.5rem!important" const val = value.findIndex((e) => { return e.name == "key" }) const labelBtn = val != -1 ? T(name) + (value[val].value.length != 0 ? ` [${ value[val].value }]` : "") : T(name) return ( {!editionMode && (
{((index > 0 && completeList.length > 1) || (index == 0 && completeList.length > 1)) && sorted && (
{index > 0 && completeList.length > 1 && ( } onClick={upItem} /> )} {completeList.length != 1 && index < completeList.length - 1 && ( } onClick={downItem} /> )}
)}
{(!fixed || editable) && ( ) => { useUiContextFn.haptic() e.currentTarget.blur() onEdit(true) }} /> )} {fixed && !editable && ( )}
{!(fixed || nodelete) && ( } onClick={removeItem} /> )}
)} {editionMode && (
} onClick={(e: TargetedMouseEvent) => { useUiContextFn.haptic() e.currentTarget.blur() onEdit(false) }} class="float-right" />
{index > 0 && completeList.length > 1 && sorted && ( } onClick={upItem} /> )} {completeList.length != 1 && sorted && index < completeList.length - 1 && ( } onClick={downItem} /> )} {!nodelete && ( } onClick={removeItem} /> )}
{value && value.map((item) => { const { id, type, label, initial, options, ...rest } = item const [validation, setvalidation] = useState( validationfn(item) ) //Do translation if necessary const Options = options ? [...options].reduce((acc: any[], curr: any) => { acc.push({ label: T(curr.label), value: curr.value, depend: curr.depend, }) return acc }, []) : null if (idList == "keymap" && item.name == "name") { return } return ( { if (!update) item.value = val setvalidation(validationfn(item)) setValue(completeList, update) }} validation={validation} /> ) })}
)}
) } const ItemsList: FunctionalComponent = ({ id, label, validationfn, validation, value, type, setValue, inline, fixed, sorted, depend, nodelete, editable, }) => { const { interfaceSettings, connectionSettings } = useSettingsContext() const dependIds = generateDependIds( depend, interfaceSettings.current.settings ) console.log(id) const addItem = (e: TargetedMouseEvent) => { useUiContextFn.haptic() e.currentTarget.blur() const newItem: any = JSON.parse( JSON.stringify( id == "macros" ? defaultMacro : id == "pollingcmds" ? defaultPolling : defaultPanel ) ) newItem.id = generateUID() newItem.name += ` ${ newItem.id}` const formatedNewItem = formatItem(newItem, -1, id) formatedNewItem.editionMode = true formatedNewItem.newItem = true value.unshift(formatedNewItem) setValue(value) } useEffect(() => { //to update state when import- but why ? if (setValue) setValue(null, true) }, [value]) useEffect(() => { let visible = checkDependencies(depend, interfaceSettings.current.settings, connectionSettings.current) if (document.getElementById(id)) document.getElementById(id)!.style.display = visible ? "block" : "none" if (document.getElementById(`group-${ id}`)) document.getElementById(`group-${ id}`)!.style.display = visible ? "block" : "none" }, [...dependIds]) return (
{!fixed && ( } onClick={addItem} /> )} {fixed && }
{value && value.map((element, index, completeList) => { return ( ) })}
) } export default ItemsList