Added option to disable any API call Fix http.js and node-http.js Fix MainContainer not using IAvailableContainer from MainContainer Fix generate_dts.py script More docs Fix MainContainer default style. Extend config will now be taken into account. But Main container can still have its own type.
402 lines
15 KiB
TypeScript
402 lines
15 KiB
TypeScript
namespace SmartBusiness.Web.Components {
|
|
/**
|
|
* Types macros
|
|
*/
|
|
type IHistoryState = SVGLD.IHistoryState;
|
|
type IEditorState = SVGLD.IEditorState;
|
|
|
|
export class SVGLayoutDesigner extends Components.ComponentBase {
|
|
|
|
private _hooks: Record<string, (e: CustomEvent) => void>;
|
|
public App: AppController;
|
|
public Editor: EditorController;
|
|
|
|
public constructor(componentInfo: KnockoutComponentTypes.ComponentInfo, params: any) {
|
|
super(componentInfo, params);
|
|
this.App = new AppController(this, this.$component);
|
|
this.Editor = new EditorController(this, this.$component);
|
|
this._hooks = {};
|
|
}
|
|
|
|
/**
|
|
* 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(eventType: string, callback: ((...args: any[]) => void) | undefined) {
|
|
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';
|
|
|
|
/**
|
|
* Add a hook to the editor state change.
|
|
* After every time an action is perform on the editor, the callback will be called
|
|
* @param callback Callback to add that listen to the event
|
|
*/
|
|
public AddEditorListenerHook(hookId: string, callback: (state: IEditorState) => void): void {
|
|
const root = this.GetRootComponent();
|
|
const customEvent = (e: CustomEvent) => {
|
|
callback(e.detail);
|
|
}
|
|
|
|
if (this._hooks[hookId] !== undefined) {
|
|
console.error(`HookId is already occupied. Please use a different HookId: ${hookId}`);
|
|
return;
|
|
}
|
|
|
|
this._hooks[hookId] = customEvent;
|
|
root.addEventListener(SVGLayoutDesigner.EDITOR_LISTENER_TYPE, customEvent);
|
|
}
|
|
|
|
/**
|
|
* Remove a hook to the editor state change.
|
|
* @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]);
|
|
delete this._hooks[hookId];
|
|
}
|
|
|
|
|
|
/// 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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/// 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(eventType, callback);
|
|
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(eventType, callback);
|
|
const component = this.GetAppComponent();
|
|
component.dispatchEvent(new CustomEvent(eventType, { detail: isLoaded }))
|
|
}
|
|
}
|
|
|
|
|
|
class EditorController {
|
|
app: SVGLayoutDesigner;
|
|
$component: JQuery;
|
|
|
|
constructor(app: SVGLayoutDesigner, $component: JQuery) {
|
|
this.app = app;
|
|
this.$component = $component;
|
|
}
|
|
|
|
/**
|
|
* Return the HTML component handling the editor
|
|
*/
|
|
public GetEditorComponent() {
|
|
const component = this.$component[0]
|
|
.querySelector('iframe')
|
|
.contentDocument
|
|
.querySelector('.Editor');
|
|
|
|
if (component === undefined) {
|
|
throw new Error('[SVGLD] Cannot hook the event because the editor is not yet open')
|
|
}
|
|
|
|
return component;
|
|
}
|
|
|
|
/// Editor Events ///
|
|
|
|
/**
|
|
* Return in a callback the current state in the history of the editor
|
|
* @param callback
|
|
*/
|
|
public GetCurrentHistoryState(callback: (state: IHistoryState) => void) {
|
|
const eventType = 'getCurrentHistoryState';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const component = this.GetEditorComponent();
|
|
component.dispatchEvent(new CustomEvent(eventType));
|
|
}
|
|
|
|
/**
|
|
* Return in a callback the current history of the editor
|
|
* @param callback
|
|
*/
|
|
public GetEditorState(callback: (state: IEditorState) => void) {
|
|
const eventType = 'getEditorState';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const component = this.GetEditorComponent();
|
|
component.dispatchEvent(new CustomEvent(eventType));
|
|
}
|
|
|
|
/**
|
|
* Set the current history of the editor
|
|
* @param history Whole history of the editor
|
|
* @param callback (optional)
|
|
*/
|
|
public SetHistory(history: IHistoryState[], callback?: (state: IEditorState) => void) {
|
|
const eventType = 'setHistory';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const component = this.GetEditorComponent();
|
|
component.dispatchEvent(new CustomEvent(eventType, { detail: history }));
|
|
}
|
|
|
|
/**
|
|
* Revive the references in the editor state by mutation
|
|
* Useful after using JSON.stringify with a replacer
|
|
* @param editorState Editor state to revive
|
|
* @param callback Callback with the revived state
|
|
*/
|
|
public ReviveEditorState(editorState: IEditorState, callback: (state: IEditorState) => void) {
|
|
const eventType = 'reviveEditorState';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const component = this.GetEditorComponent();
|
|
component.dispatchEvent(new CustomEvent(eventType, { detail: editorState }));
|
|
}
|
|
|
|
/**
|
|
* Revive the references in the history by mutation
|
|
* Useful after using JSON.stringify with a replacer
|
|
* @param history History to revive
|
|
* @param callback Callback with the revived state
|
|
*/
|
|
public ReviveHistory(history: IHistoryState[], callback: (state: IHistoryState[]) => void) {
|
|
const eventType = 'reviveHistory';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const component = this.GetEditorComponent();
|
|
component.dispatchEvent(new CustomEvent(eventType, { detail: history }));
|
|
}
|
|
|
|
/**
|
|
* Add a new state to the editor
|
|
* @param historyState New history state to append
|
|
* @param callback
|
|
*/
|
|
public AppendNewHistoryState(historyState: IHistoryState, callback?: (state: IEditorState) => void) {
|
|
const eventType = 'appendNewState';
|
|
this.app.AddEventListener(eventType, callback);
|
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail: historyState }));
|
|
}
|
|
|
|
/**
|
|
* Create a new container at the given index position in a given parent container
|
|
* @param index Position to insert the container
|
|
* @param type Container type to create
|
|
* @param parentId Parent container of the new container
|
|
* @param callback
|
|
*/
|
|
public AddContainer(index: number, type: string, parentId: string, callback?: (state: IEditorState) => void) {
|
|
const eventType = 'addContainer';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const detail = {
|
|
index,
|
|
type,
|
|
parentId
|
|
}
|
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail }));
|
|
}
|
|
|
|
/**
|
|
* Create a new container at the given index position in the current selected container
|
|
* @param index Position to insert the container
|
|
* @param type Container type to create
|
|
* @param callback
|
|
*/
|
|
public AddContainerToSelectedContainer(index: number, type: string, callback?: (state: IEditorState) => void) {
|
|
const eventType = 'addContainerToSelectedContainer';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const detail = {
|
|
index,
|
|
type
|
|
}
|
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail }));
|
|
}
|
|
|
|
/**
|
|
* Append a new container in a given parent container
|
|
* @param type Container type to create
|
|
* @param parentId Parent container of the new container
|
|
* @param callback
|
|
*/
|
|
public AppendContainer(type: string, parentId: string, callback?: (state: IEditorState) => void) {
|
|
const eventType = 'appendContainer';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const detail = {
|
|
type,
|
|
parentId
|
|
}
|
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail }));
|
|
}
|
|
|
|
|
|
/**
|
|
* Append a new container in the current selected container
|
|
* @param type Container type to create
|
|
* @param callback
|
|
*/
|
|
public AppendContainerToSelectedContainer(type: string, callback?: (state: IEditorState) => void) {
|
|
const eventType = 'appendContainerToSelectedContainer';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const detail = {
|
|
type
|
|
}
|
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail }));
|
|
}
|
|
|
|
/**
|
|
* Select a container by id
|
|
* @param containerId Container's id to select
|
|
* @param callback
|
|
*/
|
|
public SelectContainer(containerId: string, callback?: (state: IEditorState) => void) {
|
|
const eventType = 'selectContainer';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const detail = {
|
|
containerId
|
|
}
|
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail }));
|
|
}
|
|
|
|
/**
|
|
* Delete a container by id
|
|
* @param containerId Container's id to delete
|
|
* @param callback
|
|
*/
|
|
public DeleteContainer(containerId: string, callback?: (state: IEditorState) => void) {
|
|
const eventType = 'deleteContainer';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const detail = {
|
|
containerId
|
|
}
|
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail }));
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a new symbol
|
|
* @param name Name of the symbol present in the config
|
|
* @param callback
|
|
*/
|
|
public AddSymbol(name: string, callback?: (state: IEditorState) => void) {
|
|
const eventType = 'addSymbol';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const detail = {
|
|
name
|
|
}
|
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail }));
|
|
}
|
|
|
|
/**
|
|
* Select a symbol by id
|
|
* @param symbolId Symbol's id to select
|
|
* @param callback
|
|
*/
|
|
public SelectSymbol(symbolId: string, callback?: (state: IEditorState) => void) {
|
|
const eventType = 'selectSymbol';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const detail = {
|
|
symbolId
|
|
}
|
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail }));
|
|
}
|
|
|
|
/**
|
|
* Delete a symbol by id
|
|
* @param symbolId Symbol's id to delete
|
|
* @param callback
|
|
*/
|
|
public DeleteSymbol(symbolId: string, callback?: (state: IEditorState) => void) {
|
|
const eventType = 'deleteSymbol';
|
|
this.app.AddEventListener(eventType, callback);
|
|
const detail = {
|
|
symbolId
|
|
}
|
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail }));
|
|
}
|
|
}
|
|
|
|
ko.components.register('svg-layout-designer', {
|
|
viewModel: {
|
|
createViewModel: function (params, componentInfo) {
|
|
return new SmartBusiness.Web.Components.SVGLayoutDesigner(componentInfo, params);
|
|
}
|
|
},
|
|
template: { element: 'svg-layout-designer' }
|
|
});
|
|
}
|
|
|