diff --git a/public/smartcomponent/svg-layout-designer.ts b/public/smartcomponent/svg-layout-designer.ts index cf7930c..7dfa633 100644 --- a/public/smartcomponent/svg-layout-designer.ts +++ b/public/smartcomponent/svg-layout-designer.ts @@ -7,9 +7,70 @@ export class SVGLayoutDesigner extends Components.ComponentBase { + public App: AppController; + public Editor: EditorController; + public constructor(componentInfo: KnockoutComponentTypes.ComponentInfo, params: any) { super(componentInfo, params); - this._hooks = {}; + this.App = new AppController(this, this.$component); + this.Editor = new EditorController(this, this.$component); + } + + /** + * Return the root HTML component of the SmartComponent + * In the iframe, it would be the document. + */ + public GetRootComponent() { + return this.$component[0] + .querySelector('iframe') + .contentDocument; + } + + /** + * Add the a new event listener that will be delete on call, + * optionnally call a callback + * @param callback Callback function to call in the event listener + * @param eventType Event type for the listener to listen to + */ + public AddEventListener(callback: ((...args: any[]) => void) | undefined, eventType: string) { + const root = this.GetRootComponent(); + const listener = (e: CustomEvent) => { + e.target.removeEventListener(e.type, listener); + callback && callback(e.detail); + }; + root.addEventListener(eventType, listener); + } + + + /// Macros /// + + /** + * Reset to the first state and clear all history + */ + public Reset(): void { + this.Editor.GetEditorState((state) => { + this.Editor.SetHistory([state.history[0]]); + }); + } + + /** + * Clear all previous history but the last state + */ + public ClearHistory(): void { + this.Editor.GetEditorState((state) => { + this.Editor.SetHistory([state.history[state.history.length - 1]]); + }); + } + } + + + class AppController { + app: SVGLayoutDesigner; + $component: JQuery; + + constructor(app: SVGLayoutDesigner, $component: JQuery) { + this.app = app; + this.$component = $component; } /** @@ -28,6 +89,46 @@ return component; } + /// 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.app.AddEventListener(callback, eventType); + const component = this.GetAppComponent(); + component.dispatchEvent(new CustomEvent(eventType, { detail: newEditor })) + } + + /** + * Hide the main menu to go to the application. + * SetEditor must be called first or the application will crash. + * @param isLoaded + * @param callback + */ + public SetLoaded(isLoaded: boolean, callback?: (state: IEditorState) => void) { + const eventType = 'setLoaded'; + this.app.AddEventListener(callback, eventType); + const component = this.GetAppComponent(); + component.dispatchEvent(new CustomEvent(eventType, { detail: isLoaded })) + } + } + + + class EditorController { + app: SVGLayoutDesigner; + $component: JQuery; + private _hooks: Record void>; + + constructor(app: SVGLayoutDesigner, $component: JQuery) { + this.app = app; + this.$component = $component; + this._hooks = {}; + } /** * Return the HTML component handling the editor @@ -45,33 +146,6 @@ return component; } - /** - * Return the root HTML component of the SmartComponent - * In the iframe, it would be the document. - */ - public GetRootComponent() { - return this.$component[0] - .querySelector('iframe') - .contentDocument; - } - - - /// 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 /// /** @@ -80,7 +154,7 @@ */ public GetCurrentHistoryState(callback: (state: IHistoryState) => void) { const eventType = 'getCurrentHistoryState'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const component = this.GetEditorComponent(); component.dispatchEvent(new CustomEvent(eventType)); } @@ -91,7 +165,7 @@ */ public GetEditorState(callback: (state: IEditorState) => void) { const eventType = 'getEditorState'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const component = this.GetEditorComponent(); component.dispatchEvent(new CustomEvent(eventType)); } @@ -103,7 +177,7 @@ */ public SetHistory(history: IHistoryState[], callback?: (state: IEditorState) => void) { const eventType = 'setHistory'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const component = this.GetEditorComponent(); component.dispatchEvent(new CustomEvent(eventType, { detail: history })); } @@ -116,7 +190,7 @@ */ public ReviveEditorState(editorState: IEditorState, callback: (state: IEditorState) => void) { const eventType = 'reviveEditorState'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const component = this.GetEditorComponent(); component.dispatchEvent(new CustomEvent(eventType, { detail: editorState })); } @@ -129,7 +203,7 @@ */ public ReviveHistory(history: IHistoryState[], callback: (state: IHistoryState[]) => void) { const eventType = 'reviveHistory'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const component = this.GetEditorComponent(); component.dispatchEvent(new CustomEvent(eventType, { detail: history })); } @@ -141,7 +215,7 @@ */ public AppendNewHistoryState(historyState: IHistoryState, callback?: (state: IEditorState) => void) { const eventType = 'appendNewState'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail: historyState })); } @@ -154,7 +228,7 @@ */ public AddContainer(index: number, type: string, parentId: string, callback?: (state: IEditorState) => void) { const eventType = 'addContainer'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const detail = { index, type, @@ -171,7 +245,7 @@ */ public AddContainerToSelectedContainer(index: number, type: string, callback?: (state: IEditorState) => void) { const eventType = 'addContainerToSelectedContainer'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const detail = { index, type @@ -187,7 +261,7 @@ */ public AppendContainer(type: string, parentId: string, callback?: (state: IEditorState) => void) { const eventType = 'appendContainer'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const detail = { type, parentId @@ -203,7 +277,7 @@ */ public AppendContainerToSelectedContainer(type: string, callback?: (state: IEditorState) => void) { const eventType = 'appendContainerToSelectedContainer'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const detail = { type } @@ -217,7 +291,7 @@ */ public SelectContainer(containerId: string, callback?: (state: IEditorState) => void) { const eventType = 'selectContainer'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const detail = { containerId } @@ -231,7 +305,7 @@ */ public DeleteContainer(containerId: string, callback?: (state: IEditorState) => void) { const eventType = 'deleteContainer'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const detail = { containerId } @@ -246,7 +320,7 @@ */ public AddSymbol(name: string, callback?: (state: IEditorState) => void) { const eventType = 'addSymbol'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const detail = { name } @@ -260,7 +334,7 @@ */ public SelectSymbol(symbolId: string, callback?: (state: IEditorState) => void) { const eventType = 'selectSymbol'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const detail = { symbolId } @@ -274,33 +348,18 @@ */ public DeleteSymbol(symbolId: string, callback?: (state: IEditorState) => void) { const eventType = 'deleteSymbol'; - this.AddEventListener(callback, eventType); + this.app.AddEventListener(callback, eventType); const detail = { symbolId } this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail })); } - /** - * Add the a new event listener that will be delete on call, - * optionnally call a callback - * @param callback Callback function to call in the event listener - * @param eventType Event type for the listener to listen to - */ - private AddEventListener(callback: ((...args: any[]) => void) | undefined, eventType: string) { - const root = this.GetRootComponent(); - const listener = (e: CustomEvent) => { - e.target.removeEventListener(e.type, listener); - callback && callback(e.detail); - }; - root.addEventListener(eventType, listener); - } /// Hooks /// private static EDITOR_LISTENER_TYPE = 'editorListener'; - private _hooks: Record void>; /** * Add a hook to the editor state change. @@ -308,7 +367,7 @@ * @param callback Callback to add that listen to the event */ public AddEditorListenerHook(hookId: string, callback: (state: IEditorState) => void): void { - const root = this.GetRootComponent(); + const root = this.app.GetRootComponent(); const customEvent = (e: CustomEvent) => { callback(e.detail); } @@ -319,7 +378,7 @@ } this._hooks[hookId] = customEvent; - root.addEventListener(SVGLayoutDesigner.EDITOR_LISTENER_TYPE, customEvent); + root.addEventListener(EditorController.EDITOR_LISTENER_TYPE, customEvent); } /** @@ -327,33 +386,11 @@ * @param callback Callback to remove that listen to the event */ public RemoveEditorListenerHook(hookId): void { - const root = this.GetRootComponent(); - root.removeEventListener(SVGLayoutDesigner.EDITOR_LISTENER_TYPE, this._hooks[hookId]); + const root = this.app.GetRootComponent(); + root.removeEventListener(EditorController.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 72ae3bd..3d0ad1f 100644 --- a/src/Components/App/App.tsx +++ b/src/Components/App/App.tsx @@ -44,7 +44,8 @@ function UseHTTPGETStatePreloading( function UseCustomEvents( root: Element | Document, appRef: React.RefObject, - setEditor: (newState: IEditorState) => void + setEditor: (newState: IEditorState) => void, + setLoaded: (loaded: boolean) => void ): void { useEffect(() => { const funcs = new Map void>(); @@ -53,6 +54,7 @@ function UseCustomEvents( return event.func( root, setEditor, + setLoaded, eventInitDict ); } @@ -96,7 +98,8 @@ export function App(props: IAppProps): JSX.Element { UseCustomEvents( props.root, appRef, - setEditorState + setEditorState, + setLoaded ); UseHTTPGETStatePreloading(isLoaded, setEditorState, setLoaded); diff --git a/src/Events/AppEvents.ts b/src/Events/AppEvents.ts index bf72f89..e85eab9 100644 --- a/src/Events/AppEvents.ts +++ b/src/Events/AppEvents.ts @@ -5,17 +5,20 @@ export interface IAppEvent { func: ( root: Element | Document, setEditor: (newState: IEditorState) => void, + setLoaded: (loaded: boolean) => void, eventInitDict?: CustomEventInit ) => void } export const events: IAppEvent[] = [ - { name: 'setEditor', func: SetEditor } + { name: 'setEditor', func: SetEditor }, + { name: 'setLoaded', func: SetLoaded } ]; function SetEditor( root: Element | Document, setEditor: (newState: IEditorState) => void, + setLoaded: (loaded: boolean) => void, eventInitDict?: CustomEventInit ): void { const editor: IEditorState = eventInitDict?.detail; @@ -23,3 +26,13 @@ function SetEditor( const customEvent = new CustomEvent('setEditor', { detail: editor }); root.dispatchEvent(customEvent); } + +function SetLoaded( + root: Element | Document, + setEditor: (newState: IEditorState) => void, + setLoaded: (loaded: boolean) => void, + eventInitDict?: CustomEventInit +): void { + const isLoaded: boolean = eventInitDict?.detail; + setLoaded(isLoaded); +}