import React, { Dispatch, SetStateAction, useEffect, useRef } from 'react'; import './Editor.scss'; import { IConfiguration } from '../../Interfaces/IConfiguration'; import { SVG } from '../SVG/SVG'; import { IHistoryState } from '../../Interfaces/IHistoryState'; import { UI } from '../UI/UI'; import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, AddContainer, OnPropertyChange } from './Actions/ContainerOperations'; import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save'; import { onKeyDown } from './Actions/Shortcuts'; import EditorEvents from '../../Events/EditorEvents'; import { IEditorState } from '../../Interfaces/IEditorState'; import { MAX_HISTORY } from '../../utils/default'; import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations'; import { findContainerById } from '../../utils/itertools'; interface IEditorProps { configuration: IConfiguration history: IHistoryState[] historyCurrentStep: number } export function UpdateCounters(counters: Record, type: string): void { if (counters[type] === null || counters[type] === undefined) { counters[type] = 0; } else { counters[type]++; } } export const getCurrentHistory = (history: IHistoryState[], historyCurrentStep: number): IHistoryState[] => history.slice( Math.max(0, history.length - MAX_HISTORY), // change this to 0 for unlimited (not recommanded because of overflow) historyCurrentStep + 1 ); export const getCurrentHistoryState = (history: IHistoryState[], historyCurrentStep: number): IHistoryState => history[historyCurrentStep]; function useShortcuts( history: IHistoryState[], historyCurrentStep: number, setHistoryCurrentStep: Dispatch> ): void { useEffect(() => { const onKeyUp = (event: KeyboardEvent): void => onKeyDown( event, history, historyCurrentStep, setHistoryCurrentStep ); window.addEventListener('keyup', onKeyUp); return () => { window.removeEventListener('keyup', onKeyUp); }; }); } function useWindowEvents( history: IHistoryState[], historyCurrentStep: number, configuration: IConfiguration, editorRef: React.RefObject ): void { useEffect(() => { const events = EditorEvents; const editorState: IEditorState = { history, historyCurrentStep, configuration }; const funcs = new Map void>(); for (const event of events) { const func = (): void => event.func(editorState); editorRef.current?.addEventListener(event.name, func); funcs.set(event.name, func); } return () => { for (const event of events) { const func = funcs.get(event.name); if (func === undefined) { continue; } editorRef.current?.removeEventListener(event.name, func); } }; }); } const Editor: React.FunctionComponent = (props) => { const [history, setHistory] = React.useState(structuredClone(props.history)); const [historyCurrentStep, setHistoryCurrentStep] = React.useState(props.historyCurrentStep); const editorRef = useRef(null); useShortcuts(history, historyCurrentStep, setHistoryCurrentStep); useWindowEvents(history, historyCurrentStep, props.configuration, editorRef); const configuration = props.configuration; const current = getCurrentHistoryState(history, historyCurrentStep); const selected = findContainerById(current.MainContainer, current.SelectedContainerId); return (
SelectContainer( container, history, historyCurrentStep, setHistory, setHistoryCurrentStep )} DeleteContainer={(containerId: string) => DeleteContainer( containerId, history, historyCurrentStep, setHistory, setHistoryCurrentStep )} OnPropertyChange={(key, value, isStyle) => OnPropertyChange( key, value, isStyle, selected, history, historyCurrentStep, setHistory, setHistoryCurrentStep )} AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer( type, selected, configuration, history, historyCurrentStep, setHistory, setHistoryCurrentStep )} AddContainer={(index, type, parentId) => AddContainer( index, type, parentId, configuration, history, historyCurrentStep, setHistory, setHistoryCurrentStep )} AddSymbol={(type) => AddSymbol( type, configuration, history, historyCurrentStep, setHistory, setHistoryCurrentStep )} OnSymbolPropertyChange={(key, value) => OnSymbolPropertyChange( key, value, history, historyCurrentStep, setHistory, setHistoryCurrentStep )} SelectSymbol={(symbolId) => SelectSymbol( symbolId, history, historyCurrentStep, setHistory, setHistoryCurrentStep )} DeleteSymbol={(symbolId) => DeleteSymbol( symbolId, history, historyCurrentStep, setHistory, setHistoryCurrentStep )} SaveEditorAsJSON={() => SaveEditorAsJSON( history, historyCurrentStep, configuration )} SaveEditorAsSVG={() => SaveEditorAsSVG()} LoadState={(move) => setHistoryCurrentStep(move)} /> { current.MainContainer }
); }; export default Editor;