From 2ea43890f0191e9bbab8c489041978789e6474fb Mon Sep 17 00:00:00 2001 From: Eric NGUYEN Date: Fri, 23 Sep 2022 14:55:57 +0200 Subject: [PATCH] Implement setEditor + Add some macros --- pnpm-lock.yaml | 6 +- public/smartcomponent/svg-layout-designer.ts | 67 ++++++++++++++++++-- src/Components/App/App.tsx | 50 ++++++++++++++- src/Events/AppEvents.ts | 25 ++++++++ src/Events/EditorEvents.ts | 7 +- 5 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 src/Events/AppEvents.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 205c22e..cdebf40 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,7 +75,7 @@ devDependencies: jsdom: 20.0.0 postcss: 8.4.16 sass: 1.54.3 - tailwindcss: 3.1.8 + tailwindcss: 3.1.8_postcss@8.4.16 typescript: 4.7.4 vite: 3.0.4_sass@1.54.3 vitest: 0.20.3_hymhw3vkyr5yfvzfskw3x5v26q @@ -3222,10 +3222,12 @@ packages: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: true - /tailwindcss/3.1.8: + /tailwindcss/3.1.8_postcss@8.4.16: resolution: {integrity: sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g==} engines: {node: '>=12.13.0'} hasBin: true + peerDependencies: + postcss: ^8.0.9 dependencies: arg: 5.0.2 chokidar: 3.5.3 diff --git a/public/smartcomponent/svg-layout-designer.ts b/public/smartcomponent/svg-layout-designer.ts index 44c2108..cf7930c 100644 --- a/public/smartcomponent/svg-layout-designer.ts +++ b/public/smartcomponent/svg-layout-designer.ts @@ -12,6 +12,23 @@ this._hooks = {}; } + /** + * Return the HTML component handling the editor + */ + public GetAppComponent() { + const component = this.$component[0] + .querySelector('iframe') + .contentDocument + .querySelector('.App'); + + if (component === undefined) { + throw new Error('[SVGLD] Cannot hook the event because the editor is not yet open') + } + + return component; + } + + /** * Return the HTML component handling the editor */ @@ -39,7 +56,23 @@ } - /// Custom Events /// + /// App Events /// + + /** + * Not to be confused with setHistory, + * change the default configuration for the new containers, symbols etc. + * @param newEditor New editor configuration to set + * @param callback + */ + public SetEditor(newEditor: IEditorState, callback?: (state: IEditorState) => void) { + const eventType = 'setEditor'; + this.AddEventListener(callback, eventType); + const component = this.GetAppComponent(); + component.dispatchEvent(new CustomEvent(eventType, { detail: newEditor })) + } + + + /// Editor Events /// /** * Return in a callback the current state in the history of the editor @@ -53,7 +86,7 @@ } /** - * Return in a callback the current state of the editor + * Return in a callback the current history of the editor * @param callback */ public GetEditorState(callback: (state: IEditorState) => void) { @@ -68,8 +101,8 @@ * @param history Whole history of the editor * @param callback (optional) */ - public SetEditorState(history: IHistoryState[], callback?: (state: IEditorState) => void) { - const eventType = 'setEditorState'; + public SetHistory(history: IHistoryState[], callback?: (state: IEditorState) => void) { + const eventType = 'setHistory'; this.AddEventListener(callback, eventType); const component = this.GetEditorComponent(); component.dispatchEvent(new CustomEvent(eventType, { detail: history })); @@ -263,6 +296,9 @@ root.addEventListener(eventType, listener); } + + /// Hooks /// + private static EDITOR_LISTENER_TYPE = 'editorListener'; private _hooks: Record void>; @@ -295,6 +331,29 @@ root.removeEventListener(SVGLayoutDesigner.EDITOR_LISTENER_TYPE, this._hooks[hookId]); delete this._hooks[hookId]; } + + + /// Macros /// + + /** + * Reset to the first state and clear all history + */ + public Reset(): void { + this.GetEditorState((state) => { + this.SetHistory([state.history[0]]); + }); + } + + /** + * Clear all previous history but the last state + */ + public ClearHistory(): void { + this.GetEditorState((state) => { + this.SetHistory([state.history[state.history.length - 1]]); + }); + } + + } ko.components.register('svg-layout-designer', { diff --git a/src/Components/App/App.tsx b/src/Components/App/App.tsx index 3b1771b..72ae3bd 100644 --- a/src/Components/App/App.tsx +++ b/src/Components/App/App.tsx @@ -1,4 +1,5 @@ -import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react'; +import { events as EVENTS } from '../../Events/AppEvents'; import { MainMenu } from '../MainMenu/MainMenu'; import { ContainerModel } from '../../Interfaces/IContainerModel'; import { Editor } from '../Editor/Editor'; @@ -40,8 +41,39 @@ function UseHTTPGETStatePreloading( }); }; +function UseCustomEvents( + root: Element | Document, + appRef: React.RefObject, + setEditor: (newState: IEditorState) => void +): void { + useEffect(() => { + const funcs = new Map void>(); + for (const event of EVENTS) { + function Func(eventInitDict?: CustomEventInit): void { + return event.func( + root, + setEditor, + 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(false); + const appRef = useRef(null); const defaultMainContainer = new ContainerModel( null, @@ -61,11 +93,20 @@ export function App(props: IAppProps): JSX.Element { historyCurrentStep: 0 }); + UseCustomEvents( + props.root, + appRef, + setEditorState + ); + UseHTTPGETStatePreloading(isLoaded, setEditorState, setLoaded); if (isLoaded) { return ( -
+
+
NewEditor( setEditorState, setLoaded diff --git a/src/Events/AppEvents.ts b/src/Events/AppEvents.ts new file mode 100644 index 0000000..bf72f89 --- /dev/null +++ b/src/Events/AppEvents.ts @@ -0,0 +1,25 @@ +import { IEditorState } from '../Interfaces/IEditorState'; + +export interface IAppEvent { + name: string + func: ( + root: Element | Document, + setEditor: (newState: IEditorState) => void, + eventInitDict?: CustomEventInit + ) => void +} + +export const events: IAppEvent[] = [ + { name: 'setEditor', func: SetEditor } +]; + +function SetEditor( + root: Element | Document, + setEditor: (newState: IEditorState) => void, + eventInitDict?: CustomEventInit +): void { + const editor: IEditorState = eventInitDict?.detail; + setEditor(editor); + const customEvent = new CustomEvent('setEditor', { detail: editor }); + root.dispatchEvent(customEvent); +} diff --git a/src/Events/EditorEvents.ts b/src/Events/EditorEvents.ts index 38e14a0..b199c6a 100644 --- a/src/Events/EditorEvents.ts +++ b/src/Events/EditorEvents.ts @@ -19,7 +19,7 @@ export interface IEditorEvent { export const events: IEditorEvent[] = [ { name: 'getEditorState', func: GetEditorState }, - { name: 'setEditorState', func: SetEditorState }, + { name: 'setHistory', func: SetHistory }, { name: 'reviveEditorState', func: ReviveEditorState }, { name: 'reviveHistory', func: ReviveHistory }, { name: 'getCurrentHistoryState', func: GetCurrentHistoryState }, @@ -41,15 +41,14 @@ function GetEditorState(root: Element | Document, root.dispatchEvent(customEvent); } -// TODO: In the near future, implement a real SetEditorState which also change IConfiguration -function SetEditorState(root: Element | Document, +function SetHistory(root: Element | Document, editorState: IEditorState, setNewHistory: (newHistory: IHistoryState[]) => void, eventInitDict?: CustomEventInit): void { const history: IHistoryState[] = eventInitDict?.detail; ReviveHistoryAction(history); setNewHistory(history); - const customEvent = new CustomEvent('setEditorState', { detail: editorState }); + const customEvent = new CustomEvent('setHistory', { detail: editorState }); root.dispatchEvent(customEvent); }