import { useEffect } from 'react'; import { AddContainer as AddContainerAction, AddContainerToSelectedContainer as AddContainerToSelectedContainerAction } from '../Components/Editor/Actions/AddContainer'; import { DeleteContainer as DeleteContainerAction, SelectContainer as SelectContainerAction } from '../Components/Editor/Actions/ContainerOperations'; import { AddSymbol as AddSymbolAction, DeleteSymbol as DeleteSymbolAction, SelectSymbol as SelectSymbolAction } from '../Components/Editor/Actions/SymbolOperations'; import { GetCurrentHistory } from '../Components/Editor/Editor'; import { IConfiguration } from '../Interfaces/IConfiguration'; import { IEditorState } from '../Interfaces/IEditorState'; import { IHistoryState } from '../Interfaces/IHistoryState'; import { FindContainerById } from '../utils/itertools'; import { GetCircularReplacer } from '../utils/saveload'; interface IEditorEventParams { root: Element | Document editorState: IEditorState setNewHistory: (newHistory: IHistoryState[], historyCurrentStep?: number) => void eventInitDict?: CustomEventInit } export interface IEditorEvent { name: string func: (params: IEditorEventParams) => void } export const events: IEditorEvent[] = [ { name: 'getEditorState', func: GetEditorState }, { name: 'getEditorStateAsString', func: GetEditorStateAsString }, { name: 'setHistory', func: SetHistory }, { name: 'getCurrentHistoryState', func: GetCurrentHistoryState }, { name: 'appendNewState', func: AppendNewState }, { name: 'addContainer', func: AddContainer }, { name: 'addContainerToSelectedContainer', func: AddContainerToSelectedContainer }, { name: 'appendContainer', func: AppendContainer }, { name: 'appendContainerToSelectedContainer', func: AppendContainerToSelectedContainer }, { name: 'selectContainer', func: SelectContainer }, { name: 'deleteContainer', func: DeleteContainer }, { name: 'addSymbol', func: AddSymbol }, { name: 'selectSymbol', func: SelectSymbol }, { name: 'deleteSymbol', func: DeleteSymbol } ]; export function UseCustomEvents( root: Element | Document, history: IHistoryState[], historyCurrentStep: number, configuration: IConfiguration, editorRef: React.RefObject, setNewHistory: (newHistory: IHistoryState[], historyCurrentStep?: number) => void ): void { useEffect(() => { const editorState: IEditorState = { history, historyCurrentStep, configuration }; const funcs = new Map void>(); for (const event of events) { function Func(eventInitDict?: CustomEventInit): void { return event.func({ root, editorState, setNewHistory, eventInitDict }); } 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); } }; }); } export function UseEditorListener( root: Element | Document, history: IHistoryState[], historyCurrentStep: number, configuration: IConfiguration ): void { useEffect(() => { const editorState: IEditorState = { history, historyCurrentStep, configuration }; const event = new CustomEvent('editorListener', { detail: editorState }); root.dispatchEvent(event); }); } function GetEditorState({ root, editorState }: IEditorEventParams): void { const customEvent = new CustomEvent('getEditorState', { detail: structuredClone(editorState) }); root.dispatchEvent(customEvent); } function GetEditorStateAsString({ root, editorState }: IEditorEventParams): void { const spaces = import.meta.env.DEV ? 4 : 0; const data = JSON.stringify(editorState, GetCircularReplacer(), spaces); const customEvent = new CustomEvent('getEditorStateAsString', { detail: data }); root.dispatchEvent(customEvent); } function SetHistory({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const history: IHistoryState[] = eventInitDict?.detail.history; const historyCurrentStep: number | undefined = eventInitDict?.detail.historyCurrentStep; setNewHistory(history, historyCurrentStep); const customEvent = new CustomEvent('setHistory', { detail: editorState }); root.dispatchEvent(customEvent); } function GetCurrentHistoryState({ root, editorState }: IEditorEventParams): void { const customEvent = new CustomEvent( 'getCurrentHistoryState', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } function AppendNewState({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const state: IHistoryState = eventInitDict?.detail.state; const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); history.push(state); setNewHistory(history); const customEvent = new CustomEvent( 'appendNewState', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } function AddContainer({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const { index, type, parentId } = eventInitDict?.detail; const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); const newHistory = AddContainerAction( index, type, parentId, editorState.configuration, history, editorState.historyCurrentStep ); setNewHistory(newHistory); const customEvent = new CustomEvent( 'addContainer', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } function AddContainerToSelectedContainer({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const { index, type } = eventInitDict?.detail; const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); const currentState = history[editorState.historyCurrentStep]; const newHistory = AddContainerAction( index, type, currentState.selectedContainerId, editorState.configuration, history, editorState.historyCurrentStep ); setNewHistory(newHistory); const customEvent = new CustomEvent( 'addContainerToSelectedContainer', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } function AppendContainer({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const { type, parentId } = eventInitDict?.detail; const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); const currentState = history[editorState.historyCurrentStep]; const parent = FindContainerById(currentState.containers, parentId); const newHistory = AddContainerAction( parent?.children.length ?? 0, type, parentId, editorState.configuration, history, editorState.historyCurrentStep ); setNewHistory(newHistory); const customEvent = new CustomEvent( 'appendContainerToSelectedContainer', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } function AppendContainerToSelectedContainer({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const { type } = eventInitDict?.detail; const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); const currentState = history[editorState.historyCurrentStep]; const selected = FindContainerById(currentState.containers, currentState.selectedContainerId); if (selected !== null && selected !== undefined) { setNewHistory(AddContainerToSelectedContainerAction( type, selected, editorState.configuration, history, editorState.historyCurrentStep )); } const customEvent = new CustomEvent( 'appendContainerToSelectedContainer', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } function SelectContainer({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const { containerId } = eventInitDict?.detail; const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); const newHistory = SelectContainerAction( containerId, history, editorState.historyCurrentStep ); setNewHistory(newHistory); const customEvent = new CustomEvent( 'selectContainer', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } function DeleteContainer({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const { containerId } = eventInitDict?.detail; const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); const newHistory = DeleteContainerAction( containerId, history, editorState.historyCurrentStep ); setNewHistory(newHistory); const customEvent = new CustomEvent( 'deleteContainer', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } function AddSymbol({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const { name } = eventInitDict?.detail; const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); const newHistory = AddSymbolAction( name, editorState.configuration, history, editorState.historyCurrentStep ); setNewHistory(newHistory); const customEvent = new CustomEvent( 'AddSymbol', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } function SelectSymbol({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const { symbolId } = eventInitDict?.detail; const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); const newHistory = SelectSymbolAction( symbolId, history, editorState.historyCurrentStep ); setNewHistory(newHistory); const customEvent = new CustomEvent( 'SelectSymbol', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } function DeleteSymbol({ root, editorState, setNewHistory, eventInitDict }: IEditorEventParams): void { const { symbolId } = eventInitDict?.detail; const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); const newHistory = DeleteSymbolAction( symbolId, history, editorState.historyCurrentStep ); setNewHistory(newHistory); const customEvent = new CustomEvent( 'DeleteSymbol', { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); }