Refactor App and Editor events

This commit is contained in:
Eric NGUYEN 2022-10-17 19:17:46 +02:00
parent 28b0965626
commit d05d0fb196
5 changed files with 209 additions and 203 deletions

View file

@ -1,5 +1,5 @@
import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
import { events as EVENTS } from '../../Events/AppEvents';
import { events as EVENTS, UseCustomEvents } from '../../Events/AppEvents';
import { MainMenu } from '../MainMenu/MainMenu';
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
import { Editor } from '../Editor/Editor';
@ -41,38 +41,6 @@ function UseHTTPGETStatePreloading(
});
};
function UseCustomEvents(
root: Element | Document,
appRef: React.RefObject<HTMLDivElement>,
setEditor: (newState: IEditorState) => void,
setLoaded: (loaded: boolean) => void
): void {
useEffect(() => {
const funcs = new Map<string, () => void>();
for (const event of EVENTS) {
function Func(eventInitDict?: CustomEventInit): void {
return event.func(
root,
setEditor,
setLoaded,
eventInitDict
);
}
appRef.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;
}
appRef.current?.removeEventListener(event.name, func);
}
};
});
}
export function App(props: IAppProps): JSX.Element {
const [isLoaded, setLoaded] = useState<boolean>(false);
const appRef = useRef<HTMLDivElement>(null);

View file

@ -1,4 +1,5 @@
import Swal from 'sweetalert2';
import { Dispatch, SetStateAction } from 'react';
import { AddMethod } from '../../../Enums/AddMethod';
import { IAction } from '../../../Interfaces/IAction';
import { IConfiguration } from '../../../Interfaces/IConfiguration';
@ -6,13 +7,122 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { IHistoryState } from '../../../Interfaces/IHistoryState';
import { ISetContainerListRequest } from '../../../Interfaces/ISetContainerListRequest';
import { ISetContainerListResponse } from '../../../Interfaces/ISetContainerListResponse';
import { DISABLE_API } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools';
import { SetContainerList } from '../../API/api';
import { IMenuAction } from '../../Menu/Menu';
import { GetCurrentHistoryState } from '../Editor';
import { AddContainers } from './AddContainer';
import { DeleteContainer } from './ContainerOperations';
import { DeleteSymbol } from './SymbolOperations';
export function GetAction(
export function InitActions(
menuActions: Map<string, IMenuAction[]>,
configuration: IConfiguration,
history: IHistoryState[],
historyCurrentStep: number,
setNewHistory: (newHistory: IHistoryState[]) => void,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
menuActions.set(
'',
[
{
text: 'Undo',
title: 'Undo last action',
shortcut: '<kbd>Ctrl</kbd>+<kbd>Z</kbd>',
action: () => {
if (historyCurrentStep <= 0) {
return;
}
setHistoryCurrentStep(historyCurrentStep - 1);
}
},
{
text: 'Redo',
title: 'Redo last action',
shortcut: '<kbd>Ctrl</kbd>+<kbd>Y</kbd>',
action: () => {
if (historyCurrentStep >= history.length - 1) {
return;
}
setHistoryCurrentStep(historyCurrentStep + 1);
}
}
]
);
menuActions.set(
'elements-sidebar-row',
[{
text: 'Delete',
title: 'Delete the container',
shortcut: '<kbd>Suppr</kbd>',
action: (target: HTMLElement) => {
const id = target.id;
const newHistory = DeleteContainer(
id,
history,
historyCurrentStep
);
setNewHistory(newHistory);
}
}]
);
menuActions.set(
'symbols-sidebar-row',
[{
text: 'Delete',
title: 'Delete the container',
shortcut: '<kbd>Suppr</kbd>',
action: (target: HTMLElement) => {
const id = target.id;
const newHistory = DeleteSymbol(
id,
history,
historyCurrentStep
);
setNewHistory(newHistory);
}
}]
);
// API Actions
if (DISABLE_API) {
return;
}
for (const availableContainer of configuration.AvailableContainers) {
if (availableContainer.Actions === undefined || availableContainer.Actions === null) {
continue;
}
for (const action of availableContainer.Actions) {
if (menuActions.get(availableContainer.Type) === undefined) {
menuActions.set(availableContainer.Type, []);
}
const currentState = GetCurrentHistoryState(history, historyCurrentStep);
const newAction: IMenuAction = {
text: action.Label,
title: action.Description,
action: GetAction(
action,
currentState,
configuration,
history,
historyCurrentStep,
setNewHistory
)
};
menuActions.get(availableContainer.Type)?.push(newAction);
}
}
}
function GetAction(
action: IAction,
currentState: IHistoryState,
configuration: IConfiguration,

View file

@ -6,13 +6,12 @@ import { UI } from '../UI/UI';
import { SelectContainer, DeleteContainer, OnPropertyChange } from './Actions/ContainerOperations';
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save';
import { OnKey } from './Actions/Shortcuts';
import { events as EVENTS } from '../../Events/EditorEvents';
import { IEditorState } from '../../Interfaces/IEditorState';
import { DISABLE_API, MAX_HISTORY } from '../../utils/default';
import { UseCustomEvents, UseEditorListener } from '../../Events/EditorEvents';
import { MAX_HISTORY } from '../../utils/default';
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations';
import { FindContainerById } from '../../utils/itertools';
import { IMenuAction, Menu } from '../Menu/Menu';
import { GetAction } from './Actions/ContextMenuActions';
import { Menu } from '../Menu/Menu';
import { InitActions } from './Actions/ContextMenuActions';
import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer';
interface IEditorProps {
@ -22,112 +21,6 @@ interface IEditorProps {
historyCurrentStep: number
}
function InitActions(
menuActions: Map<string, IMenuAction[]>,
configuration: IConfiguration,
history: IHistoryState[],
historyCurrentStep: number,
setNewHistory: (newHistory: IHistoryState[]) => void,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
menuActions.set(
'',
[
{
text: 'Undo',
title: 'Undo last action',
shortcut: '<kbd>Ctrl</kbd>+<kbd>Z</kbd>',
action: () => {
if (historyCurrentStep <= 0) {
return;
}
setHistoryCurrentStep(historyCurrentStep - 1);
}
},
{
text: 'Redo',
title: 'Redo last action',
shortcut: '<kbd>Ctrl</kbd>+<kbd>Y</kbd>',
action: () => {
if (historyCurrentStep >= history.length - 1) {
return;
}
setHistoryCurrentStep(historyCurrentStep + 1);
}
}
]
);
menuActions.set(
'elements-sidebar-row',
[{
text: 'Delete',
title: 'Delete the container',
shortcut: '<kbd>Suppr</kbd>',
action: (target: HTMLElement) => {
const id = target.id;
const newHistory = DeleteContainer(
id,
history,
historyCurrentStep
);
setNewHistory(newHistory);
}
}]
);
menuActions.set(
'symbols-sidebar-row',
[{
text: 'Delete',
title: 'Delete the container',
shortcut: '<kbd>Suppr</kbd>',
action: (target: HTMLElement) => {
const id = target.id;
const newHistory = DeleteSymbol(
id,
history,
historyCurrentStep
);
setNewHistory(newHistory);
}
}]
);
// API Actions
if (DISABLE_API) {
return;
}
for (const availableContainer of configuration.AvailableContainers) {
if (availableContainer.Actions === undefined || availableContainer.Actions === null) {
continue;
}
for (const action of availableContainer.Actions) {
if (menuActions.get(availableContainer.Type) === undefined) {
menuActions.set(availableContainer.Type, []);
}
const currentState = GetCurrentHistoryState(history, historyCurrentStep);
const newAction: IMenuAction = {
text: action.Label,
title: action.Description,
action: GetAction(
action,
currentState,
configuration,
history,
historyCurrentStep,
setNewHistory
)
};
menuActions.get(availableContainer.Type)?.push(newAction);
}
}
}
function UseShortcuts(
history: IHistoryState[],
historyCurrentStep: number,
@ -152,63 +45,6 @@ function UseShortcuts(
});
}
function UseCustomEvents(
root: Element | Document,
history: IHistoryState[],
historyCurrentStep: number,
configuration: IConfiguration,
editorRef: React.RefObject<HTMLDivElement>,
setNewHistory: (newHistory: IHistoryState[], historyCurrentStep?: number) => void
): void {
useEffect(() => {
const editorState: IEditorState = {
history,
historyCurrentStep,
configuration
};
const funcs = new Map<string, () => 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);
}
};
});
}
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);
});
}
/**
* Return a macro function to use both setHistory
* and setHistoryCurrentStep at the same time

View file

@ -1,3 +1,4 @@
import { useEffect } from 'react';
import { IConfiguration } from '../Interfaces/IConfiguration';
import { IEditorState } from '../Interfaces/IEditorState';
import { IHistoryState } from '../Interfaces/IHistoryState';
@ -22,6 +23,38 @@ export const events: IAppEvent[] = [
{ name: 'getDefaultEditorState', func: GetDefaultEditorState }
];
export function UseCustomEvents(
root: Element | Document,
appRef: React.RefObject<HTMLDivElement>,
setEditor: (newState: IEditorState) => void,
setLoaded: (loaded: boolean) => void
): void {
useEffect(() => {
const funcs = new Map<string, () => void>();
for (const event of events) {
function Func(eventInitDict?: CustomEventInit): void {
return event.func(
root,
setEditor,
setLoaded,
eventInitDict
);
}
appRef.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;
}
appRef.current?.removeEventListener(event.name, func);
}
};
});
}
function SetEditor(
root: Element | Document,
setEditor: (newState: IEditorState) => void,

View file

@ -1,11 +1,13 @@
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, ReviveHistory as ReviveHistoryAction } from '../utils/saveload';
import { GetCircularReplacer } from '../utils/saveload';
export interface IEditorEvent {
name: string
@ -34,6 +36,63 @@ export const events: IEditorEvent[] = [
{ name: 'deleteSymbol', func: DeleteSymbol }
];
export function UseCustomEvents(
root: Element | Document,
history: IHistoryState[],
historyCurrentStep: number,
configuration: IConfiguration,
editorRef: React.RefObject<HTMLDivElement>,
setNewHistory: (newHistory: IHistoryState[], historyCurrentStep?: number) => void
): void {
useEffect(() => {
const editorState: IEditorState = {
history,
historyCurrentStep,
configuration
};
const funcs = new Map<string, () => 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: Element | Document,
editorState: IEditorState): void {
const customEvent = new CustomEvent<IEditorState>('getEditorState', { detail: structuredClone(editorState) });