From 23ed3ed1ad3e52a643a700bc16fb4bc643487dd0 Mon Sep 17 00:00:00 2001 From: Eric Nguyen Date: Wed, 21 Sep 2022 09:24:14 +0000 Subject: [PATCH] Merged PR 189: Simplify usage of SmartComponent - Added new Events : - AddContainer, AddContainerToSelectedContainer, AppendContainer, AppendContainerToSelectedContainer, SelectContainer, DeleteContainer - AddSymbol, SelectSymbol, DeleteSymbol - Changed the component to an iframe (you only need to copy the whole dist now) - Added callbacks to every methods in the component - Create event listener on demand: no need to initialize the event listener! - Update d.ts - Added Fastboot and enable it by default on production build --- README.md | 21 +- index.html | 4 +- public/Interfaces.d.ts | 65 --- .../smartcomponent/svg-layout-designer.html | 4 +- public/smartcomponent/svg-layout-designer.ts | 97 +++- src/Components/Editor/Editor.tsx | 4 +- src/Components/Loader/Loader.scss | 59 +++ src/Components/Loader/Loader.tsx | 14 + src/Components/MainMenu/MainMenu.tsx | 24 +- src/Events/EditorEvents.ts | 263 +++++++++- src/dts/generate_dts.py | 101 ++++ src/dts/svgld.d.ts | 469 ++++++++++++++++++ src/dts/tsconfig.dts.json | 15 + src/utils/default.ts | 15 +- 14 files changed, 1047 insertions(+), 108 deletions(-) delete mode 100644 public/Interfaces.d.ts create mode 100644 src/Components/Loader/Loader.scss create mode 100644 src/Components/Loader/Loader.tsx create mode 100644 src/dts/generate_dts.py create mode 100644 src/dts/svgld.d.ts create mode 100644 src/dts/tsconfig.dts.json diff --git a/README.md b/README.md index 1a08ffd..989d50c 100644 --- a/README.md +++ b/README.md @@ -103,4 +103,23 @@ Inside `.vscode/settings.json`, set the following : } ``` -Change the `url` to the dev server url. Set the `runtimeExecutable` to your favorite chromium browser. \ No newline at end of file +Change the `url` to the dev server url. Set the `runtimeExecutable` to your favorite chromium browser. + + +# Generate definition files for SmartModeler + +Pre-requisite: `typescript`, `python3` + +Go to the `src/dts` directory and run the following command + +``` +tsc --project tsconfig.dts.json +``` + +Then, run `python3` (or `py` on Windows) on `generate_dts.py`: + +``` +python3 generate_dts.py SVGLD svgld.d.ts +``` + +A definition will be generated as `svgld.d.ts` with the namespace `SVGLD`. \ No newline at end of file diff --git a/index.html b/index.html index d9f5a08..7342bd5 100644 --- a/index.html +++ b/index.html @@ -3,12 +3,12 @@ - + Vite + React + TS
- + diff --git a/public/Interfaces.d.ts b/public/Interfaces.d.ts deleted file mode 100644 index 7179edd..0000000 --- a/public/Interfaces.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -declare interface IHistoryState { - LastAction: string - MainContainer: IContainerModel - SelectedContainer: IContainerModel | null - SelectedContainerId: string - TypeCounters: Record -} - -declare interface IAvailableContainer { - Type: string - Width: number - Height: number - XPositionReference?: XPositionReference - Style: React.CSSProperties -} - -declare interface IEditorState { - history: IHistoryState[] - historyCurrentStep: number - configuration: IConfiguration -} - -declare interface IConfiguration { - AvailableContainers: IAvailableContainer[] - AvailableSymbols: IAvailableSymbol[] - MainContainer: IAvailableContainer -} - -declare interface IContainerModel { - children: IContainerModel[] - parent: IContainerModel | null - properties: IProperties - userData: Record -} - -declare interface IProperties extends React.CSSProperties { - id: string - parentId: string | null - x: number - y: number - - XPositionReference?: XPositionReference -} - -declare enum XPositionReference { - Left, - Center, - Right -} - -declare interface IAvailableSymbol { - Name: string - XPositionReference: XPositionReference - Image: IImage - Width: number - Height: number -} - -declare interface IImage { - Name: string - Url: string - Base64Image: string - Svg: string -} - diff --git a/public/smartcomponent/svg-layout-designer.html b/public/smartcomponent/svg-layout-designer.html index bc310ed..210b312 100644 --- a/public/smartcomponent/svg-layout-designer.html +++ b/public/smartcomponent/svg-layout-designer.html @@ -1,2 +1,2 @@ -
-
+ \ No newline at end of file diff --git a/public/smartcomponent/svg-layout-designer.ts b/public/smartcomponent/svg-layout-designer.ts index 4927000..d6c166b 100644 --- a/public/smartcomponent/svg-layout-designer.ts +++ b/public/smartcomponent/svg-layout-designer.ts @@ -1,40 +1,99 @@ namespace SmartBusiness.Web.Components { + import IHistoryState = SVGLD.IHistoryState; + import IEditorState = SVGLD.IEditorState; + export class SVGLayoutDesigner extends Components.ComponentBase { public constructor(componentInfo: KnockoutComponentTypes.ComponentInfo, params: any) { super(componentInfo, params); - setTimeout(() => (window as any).SVGLayoutDesigner.Render(this.$component[0])); - this.InitEventsListener(); } public GetEditorComponent() { - return this.$component[0].querySelector('.Editor'); + 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; } - public GetCurrentHistoryState() { - this.GetEditorComponent().dispatchEvent(new CustomEvent('getCurrentHistoryState')); + public GetRootComponent() { + return this.$component[0] + .querySelector('iframe') + .contentDocument; } - public GetEditorState() { - this.GetEditorComponent().dispatchEvent(new CustomEvent('getEditorState')); + public GetCurrentHistoryState(callback: (state: IHistoryState) => void) { + const component = this.GetEditorComponent(); + const eventType = 'getCurrentHistoryState'; + component.dispatchEvent(new CustomEvent(eventType)); + this.AddEventListener(callback, eventType); } - public SetEditorState(editorState: IEditorState) { - this.GetEditorComponent().dispatchEvent(new CustomEvent('SetEditorState', { detail: editorState })); + public GetEditorState(callback: (state: IEditorState) => void) { + const component = this.GetEditorComponent(); + const eventType = 'getEditorState'; + component.dispatchEvent(new CustomEvent(eventType)); + this.AddEventListener(callback, eventType); } - public AppendNewHistoryState(historyState: IHistoryState) { - this.GetEditorComponent().dispatchEvent(new CustomEvent('appendNewState', { detail: historyState })); + public AppendNewHistoryState(historyState: SVGLD.IHistoryState, callback?: (state: IEditorState) => void) { + const eventType = 'appendNewState'; + this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail: historyState })); + this.AddEventListener(callback, eventType); } - public OHistoryState: KnockoutObservable; + public AddContainer(index: number, type: string, parentId: string, callback?: (state: IEditorState) => void) { + const detail = { + index, + type, + parentId + } + const eventType = 'addContainer'; + this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail })); + this.AddEventListener(callback, eventType); + } - private InitEventsListener() { - this.$component[0].addEventListener('getCurrentHistoryState', (e: CustomEvent) => { - this.OHistoryState(e.detail); - console.log(this.OHistoryState()); - }); - this.$component[0].addEventListener('getEditorState', (e) => console.log((e as any).detail)); + public AddContainerToSelectedContainer(index: number, type: string, callback?: (state: IEditorState) => void) { + const detail = { + index, + type + } + const eventType = 'addContainerToSelectedContainer'; + this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail })); + this.AddEventListener(callback, eventType); + } + + public AppendContainer(type: string, parentId: string, callback?: (state: IEditorState) => void) { + const detail = { + type, + parentId + } + const eventType = 'appendContainer'; + this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail })); + this.AddEventListener(callback, eventType); + } + + public AppendContainerToSelectedContainer(type: string, callback?: (state: IEditorState) => void) { + const detail = { + type + } + const eventType = 'appendContainerToSelectedContainer'; + this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail })); + this.AddEventListener(callback, eventType); + } + + private AddEventListener(callback: (...args: any[]) => void, eventType: string) { + const root = this.GetRootComponent(); + const listener = (e: CustomEvent) => { + e.target.removeEventListener(e.type, listener); + callback && callback(e.detail); + }; + root.addEventListener(eventType, listener); } } @@ -46,4 +105,4 @@ }, template: { element: 'svg-layout-designer' } }); -} \ No newline at end of file +} diff --git a/src/Components/Editor/Editor.tsx b/src/Components/Editor/Editor.tsx index 239f0ce..cec0510 100644 --- a/src/Components/Editor/Editor.tsx +++ b/src/Components/Editor/Editor.tsx @@ -112,7 +112,7 @@ function UseShortcuts( }); } -function UseWindowEvents( +function UseCustomEvents( root: Element | Document, history: IHistoryState[], historyCurrentStep: number, @@ -178,7 +178,7 @@ export function Editor(props: IEditorProps): JSX.Element { // Events UseShortcuts(history, historyCurrentStep, setHistoryCurrentStep); - UseWindowEvents( + UseCustomEvents( props.root, history, historyCurrentStep, diff --git a/src/Components/Loader/Loader.scss b/src/Components/Loader/Loader.scss new file mode 100644 index 0000000..959fd3c --- /dev/null +++ b/src/Components/Loader/Loader.scss @@ -0,0 +1,59 @@ +$foreground: #3b82f6; + +.loader, +.loader:before, +.loader:after { + background:$foreground; + -webkit-animation:load1 1s infinite ease-in-out; + animation:load1 1s infinite ease-in-out; + width:1em; + height:4em; +} + +.loader { + color:$foreground; + text-indent:-9999em; + margin:88px auto; + position:relative; + font-size:11px; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); + -webkit-animation-delay:-0.16s; + animation-delay:-0.16s; + + &:before, + &:after { + position:absolute; + top:0; + content:''; + } + + &:before { + left:-1.5em; + -webkit-animation-delay:-0.32s; + animation-delay:-0.32s; + } + + &:after { + left:1.5em; + } + +} + +@mixin load1-frames { + 0%, + 80%, + 100% { + box-shadow:0 0; + height:4em; + } + + 40% { + box-shadow:0 -2em; + height:5em; + } +} + +@-webkit-keyframes load1 {@include load1-frames;} +@keyframes load1 {@include load1-frames;} \ No newline at end of file diff --git a/src/Components/Loader/Loader.tsx b/src/Components/Loader/Loader.tsx new file mode 100644 index 0000000..21ff32b --- /dev/null +++ b/src/Components/Loader/Loader.tsx @@ -0,0 +1,14 @@ +import './Loader.scss'; + +import * as React from 'react'; + +export interface ILoaderProps { +} + +export function Loader(props: ILoaderProps): JSX.Element { + return ( +
+ Loading... +
+ ); +} diff --git a/src/Components/MainMenu/MainMenu.tsx b/src/Components/MainMenu/MainMenu.tsx index 7744ee1..2d83537 100644 --- a/src/Components/MainMenu/MainMenu.tsx +++ b/src/Components/MainMenu/MainMenu.tsx @@ -1,4 +1,6 @@ import * as React from 'react'; +import { FAST_BOOT } from '../../utils/default'; +import { Loader } from '../Loader/Loader'; interface IMainMenuProps { newEditor: () => void @@ -8,10 +10,21 @@ interface IMainMenuProps { enum WindowState { Main, Load, + Loading, } export function MainMenu(props: IMainMenuProps): JSX.Element { const [windowState, setWindowState] = React.useState(WindowState.Main); + + if (FAST_BOOT) { + props.newEditor(); + return ( +
+ +
+ ); + } + switch (windowState) { case WindowState.Load: return ( @@ -46,10 +59,19 @@ export function MainMenu(props: IMainMenuProps): JSX.Element { ); + case WindowState.Loading: + return ( +
+ +
+ ); default: return (
- +
); diff --git a/src/Events/EditorEvents.ts b/src/Events/EditorEvents.ts index 1b3db5b..2e5bc87 100644 --- a/src/Events/EditorEvents.ts +++ b/src/Events/EditorEvents.ts @@ -1,17 +1,15 @@ -import { Dispatch, SetStateAction } 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 { ReviveState } from '../utils/saveload'; -function InitEditor(configuration: IConfiguration): void { - // faire comme la callback de fetch -} - function GetEditorState(root: Element | Document, editorState: IEditorState): void { - const customEvent = new CustomEvent('getEditorState', { detail: editorState }); + const customEvent = new CustomEvent('getEditorState', { detail: structuredClone(editorState) }); root.dispatchEvent(customEvent); } @@ -19,7 +17,7 @@ function GetCurrentHistoryState(root: Element | Document, editorState: IEditorState): void { const customEvent = new CustomEvent( 'getCurrentHistoryState', - { detail: editorState.history[editorState.historyCurrentStep] }); + { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); root.dispatchEvent(customEvent); } @@ -33,6 +31,244 @@ function AppendNewState(root: Element | Document, history.push(state); setNewHistory(history); + + const customEvent = new CustomEvent( + 'appendNewState', + { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); + root.dispatchEvent(customEvent); +} + +function AddContainer(root: Element | Document, + editorState: IEditorState, + setNewHistory: (newHistory: IHistoryState[]) => void, + eventInitDict?: CustomEventInit): 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: Element | Document, + editorState: IEditorState, + setNewHistory: (newHistory: IHistoryState[]) => void, + eventInitDict?: CustomEventInit): 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: Element | Document, + editorState: IEditorState, + setNewHistory: (newHistory: IHistoryState[]) => void, + eventInitDict?: CustomEventInit): void { + const { + type, + parentId + } = eventInitDict?.detail; + + const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); + const currentState = history[editorState.historyCurrentStep]; + + const parent = FindContainerById(currentState.mainContainer, 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: Element | Document, + editorState: IEditorState, + setNewHistory: (newHistory: IHistoryState[]) => void, + eventInitDict?: CustomEventInit): void { + const { + type + } = eventInitDict?.detail; + + const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); + const currentState = history[editorState.historyCurrentStep]; + + const selected = FindContainerById(currentState.mainContainer, currentState.selectedContainerId); + + const newHistory = AddContainerToSelectedContainerAction( + type, + selected, + editorState.configuration, + history, + editorState.historyCurrentStep + ); + + if (newHistory === null) { + return; + } + + setNewHistory(newHistory); + + const customEvent = new CustomEvent( + 'appendContainerToSelectedContainer', + { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); + root.dispatchEvent(customEvent); +} + +function SelectContainer(root: Element | Document, + editorState: IEditorState, + setNewHistory: (newHistory: IHistoryState[]) => void, + eventInitDict?: CustomEventInit): 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: Element | Document, + editorState: IEditorState, + setNewHistory: (newHistory: IHistoryState[]) => void, + eventInitDict?: CustomEventInit): 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: Element | Document, + editorState: IEditorState, + setNewHistory: (newHistory: IHistoryState[]) => void, + eventInitDict?: CustomEventInit): 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: Element | Document, + editorState: IEditorState, + setNewHistory: (newHistory: IHistoryState[]) => void, + eventInitDict?: CustomEventInit): 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: Element | Document, + editorState: IEditorState, + setNewHistory: (newHistory: IHistoryState[]) => void, + eventInitDict?: CustomEventInit): 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); } export interface IEditorEvent { @@ -48,5 +284,14 @@ export interface IEditorEvent { export const events: IEditorEvent[] = [ { name: 'getEditorState', func: GetEditorState }, { name: 'getCurrentHistoryState', func: GetCurrentHistoryState }, - { name: 'appendNewState', func: AppendNewState } + { 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 } ]; diff --git a/src/dts/generate_dts.py b/src/dts/generate_dts.py new file mode 100644 index 0000000..8ad0e60 --- /dev/null +++ b/src/dts/generate_dts.py @@ -0,0 +1,101 @@ +''' +Generate a definition file with a global namespace +from a typescript module definition file +''' + +import os +import pathlib +import re +import argparse + +parser = parser = argparse.ArgumentParser(description='Generate a definition file with a global namespace from a typescript module definition file') +parser.add_argument('namespace', + help='Namespace used in the global script. (example: Three.js use THREE as its namespace)') +parser.add_argument('output_filename', + help='Output d.ts definition file. (example: three.d.ts)') + +args = parser.parse_args() + +output_filename = args.output_filename +namespace = args.namespace + + +import_pattern = re.compile( + r"import(?:[\"'\s]*([\w*{}\n\r\t, ]+)from\s*)?[\"'\s].*([@\w_-]+)[\"'\s].*;" + ) +export_pattern = re.compile( + r"export ([*] from [\"'\s].*[\"'\s]|{.*}(?: from [\"'\s].*[\"'\s])?);" + ) + +filter_directories = ["./Enums", "./Interfaces"] + +def main(): + ''' + Entry point function + ''' + with open(output_filename, 'w') as output_file: + output_file.write(('declare namespace {} {{\n'.format(namespace))) + for root, subdirs, files in os.walk('./'): + if root not in filter_directories: + continue + + print('--\nroot = ' + root) + + for subdir in subdirs: + print('\t- subdirectory ' + subdir) + + for filename in files: + if filename == 'output.d.ts': + continue + + if filename == 'my-directory-list.txt': + os.remove(os.path.join(root, filename)) + continue + + suffixes = pathlib.Path(filename).suffixes + if (suffixes != ['.d', '.ts']): + continue + + file_path = os.path.join(root, filename) + file_path = file_path.replace('\\', '/') + + print('\t- file %s (full path: %s)' % (filename, file_path)) + with open(file_path, 'r') as cur_file: + f_content = cur_file.read() + + # removes imports + # see https://gist.github.com/manekinekko/7e58a17bc62a9be47172 + f_content = import_pattern.sub('', f_content) + + # Replace 'export { my_class as synonym };' + # => 'class synonym extend my_class {}' + f_content = re.sub( + r"export ({ ([A-Z].*) as ([A-Z].*) });", + "export class \\g<3> extends \\g<2> {}", + f_content, + 0, + re.MULTILINE + ) + + # Replace 'export declare class' => 'export class' + f_content = re.sub(r"export (declare) class", 'export class', f_content, 0, re.MULTILINE) + f_content = re.sub(r"export (declare) enum", 'export enum', f_content, 0, re.MULTILINE) + f_content = re.sub(r"export (declare) function", 'export function', f_content, 0, re.MULTILINE) + f_content = re.sub(r"export (declare) type", 'export type', f_content, 0, re.MULTILINE) + + # Replace 'export { my_func as synonym }' => 'export function synonym = my_func' + + # Replace 'export default class' => 'export class' + f_content = re.sub(r"(export) default", "\\1", f_content, 0, re.MULTILINE) + + # Replace other exports : 'export { .* } from '.*';' and 'export [*] from '.*'; + f_content = export_pattern.sub('', f_content) + + # Specific to your module + f_content = re.sub('export as namespace {};'.format(namespace), '', f_content) + + output_file.write(f_content) + output_file.write('\n') + output_file.write(('}\n')) + +main() \ No newline at end of file diff --git a/src/dts/svgld.d.ts b/src/dts/svgld.d.ts new file mode 100644 index 0000000..6b51b11 --- /dev/null +++ b/src/dts/svgld.d.ts @@ -0,0 +1,469 @@ +declare namespace SVGLD { +/** + * Add method when creating a container + * - Append will append to the last children in list + * - Insert will always place it at the begining + * - Replace will remove the selected container and insert a new one + * (default: Append) + */ +export enum AddMethod { + Append = 0, + Insert = 1, + Replace = 2 +} + +export enum MessageType { + Normal = 0, + Success = 1, + Warning = 2, + Error = 3 +} + +/** + * Describe the type of the property. + * Used for the assignation in the OnPropertyChange function + * See ContainerOperations.ts's OnPropertyChange + */ +export enum PropertyType { + /** + * Simple property: is not inside any object: id, x, width... (default) + */ + Simple = 0, + /** + * Style property: is inside the style object: stroke, fillOpacity... + */ + Style = 1, + /** + * Margin property: is inside the margin property: left, bottom, top, right... + */ + Margin = 2 +} + +export enum XPositionReference { + Left = 0, + Center = 1, + Right = 2 +} + + + +export interface IAction { + Id: string; + CustomLogo: IImage; + Label: string; + Description: string; + Action: string; + AddingBehavior: AddMethod; +} + + + + + + +/** Model of available container used in application configuration */ +export interface IAvailableContainer { + /** type */ + Type: string; + /** displayed text */ + DisplayedText?: string; + /** category */ + Category?: string; + /** horizontal offset */ + X?: number; + /** vertical offset */ + Y?: number; + /** width */ + Width?: number; + /** height */ + Height?: number; + /** + * Minimum width (min=1) + * Allows the container to set isRigidBody to false when it gets squeezed + * by an anchor + */ + MinWidth?: number; + /** + * Maximum width + */ + MaxWidth?: number; + /** margin */ + Margin?: IMargin; + /** true if anchor, false otherwise */ + IsAnchor?: boolean; + /** true if flex, false otherwise */ + IsFlex?: boolean; + /** Method used on container add */ + AddMethod?: AddMethod; + /** Horizontal alignment, also determines the visual location of x {Left = 0, Center, Right } */ + XPositionReference?: XPositionReference; + /** + * (optional) + * Replace a by a customized "SVG". It is not really an svg but it at least allows + * to draw some patterns that can be bind to the properties of the container + * Use {prop} to bind a property. Use {{ styleProp }} to use an object. + * Example : + * ``` + * ` + * + * + * + * ` + * ``` + */ + CustomSVG?: string; + /** + * (optional) + * Disabled when Pattern is used. + * + * Replace a by a customized "SVG". It is not really an svg but it at least allows + * to draw some patterns that can be bind to the properties of the container + * Use {prop} to bind a property. Use {{ styleProp }} to use an object. + * Example : + * ``` + * ` + * + * + * + * ` + * ``` + */ + DefaultChildType?: string; + /** + * Allow to use a Pattern to create the list of children + * Cannot be used with DefaultChildType, + * DefaultChildType will be disabled for this container and the children + */ + Pattern?: string; + /** if true, show the dimension of the container */ + ShowSelfDimensions?: boolean; + /** if true show the overall dimensions of its children */ + ShowChildrenDimensions?: boolean; + /** + * if true, allows a parent dimension borrower to uses its x coordinate for as a reference point for a dimension + */ + MarkPositionToDimensionBorrower?: boolean; + /** + * if true, show a dimension from the edge of the container to end + * and insert dimensions marks at lift up children (see liftDimensionToBorrower) + */ + IsDimensionBorrower?: boolean; + /** + * if true, hide the entry in the sidebar (default: false) + */ + IsHidden?: boolean; + /** + * Disable a list of available container to be added inside + */ + Blacklist?: string[]; + /** + * Cannot be used with blacklist. Whitelist will be prioritized. + * To disable the whitelist, Whitelist must be undefined. + * Only allow a set of available container to be added inside + */ + Whitelist?: string[]; + /** + * (optional) + * Style of the + */ + Style?: React.CSSProperties; + /** + * List of possible actions shown on right-click + */ + Actions?: IAction[]; + /** + * (optional) + * User data that can be used for data storage or custom SVG + */ + UserData?: object; +} + + + +/** + * Model of available symbol to configure the application */ +export interface IAvailableSymbol { + Name: string; + Image: IImage; + Width?: number; + Height?: number; + XPositionReference?: XPositionReference; +} + +export interface ICategory { + Type: string; + DisplayedText?: string; +} + + + + + +/** Model of configuration for the application to configure it */ +export interface IConfiguration { + AvailableContainers: IAvailableContainer[]; + AvailableSymbols: IAvailableSymbol[]; + Categories: ICategory[]; + Patterns: IPattern[]; + MainContainer: IAvailableContainer; +} + + +export interface IContainerModel { + children: IContainerModel[]; + parent: IContainerModel | null; + properties: IContainerProperties; + userData: Record; +} +/** + * Macro for creating the interface + * Do not add methods since they will be lost during serialization + */ +export class ContainerModel implements IContainerModel { + children: IContainerModel[]; + parent: IContainerModel | null; + properties: IContainerProperties; + userData: Record; + constructor(parent: IContainerModel | null, properties: IContainerProperties, children?: IContainerModel[], userData?: {}); +} + + + + +/** + * Properties of a container + */ +export interface IContainerProperties { + /** id of the container */ + id: string; + /** type matching the configuration on construction */ + type: string; + /** id of the parent container (null when there is no parent) */ + parentId: string; + /** id of the linked symbol ('' when there is no parent) */ + linkedSymbolId: string; + /** Text displayed in the container */ + displayedText: string; + /** horizontal offset */ + x: number; + /** vertical offset */ + y: number; + /** margin */ + margin: IMargin; + /** + * Minimum width (min=1) + * Allows the container to set isRigidBody to false when it gets squeezed + * by an anchor + */ + minWidth: number; + /** + * Maximum width + */ + maxWidth: number; + /** width */ + width: number; + /** height */ + height: number; + /** true if anchor, false otherwise */ + isAnchor: boolean; + /** true if flex, false otherwise */ + isFlex: boolean; + /** Horizontal alignment, also determines the visual location of x {Left = 0, Center, Right } */ + xPositionReference: XPositionReference; + /** if true, show the dimension of the container */ + showSelfDimensions: boolean; + /** if true show the overall dimensions of its children */ + showChildrenDimensions: boolean; + /** + * if true, allows a parent dimension borrower to borrow its x coordinate + * as a reference point for a dimension + */ + markPositionToDimensionBorrower: boolean; + /** + * if true, show a dimension from the edge of the container to end + * and insert dimensions marks at lift up children (see liftDimensionToBorrower) + */ + isDimensionBorrower: boolean; + /** + * Warnings of a container + */ + warning: string; + /** + * (optional) + * Replace a by a customized "SVG". It is not really an svg but it at least allows + * to draw some patterns that can be bind to the properties of the container + * Use {prop} to bind a property. Use {{ styleProp }} to use an object. + * Example : + * ``` + * ` + * + * + * + * ` + * ``` + */ + customSVG?: string; + /** + * (optional) + * Style of the + */ + style?: React.CSSProperties; + /** + * (optional) + * User data that can be used for data storage or custom SVG + */ + userData?: object; +} + + + +export interface IEditorState { + history: IHistoryState[]; + historyCurrentStep: number; + configuration: IConfiguration; +} + + +export interface IGetFeedbackRequest { + /** Current application state */ + ApplicationState: IHistoryState; +} + + +export interface IGetFeedbackResponse { + messages: IMessage[]; +} + + + +export interface IHistoryState { + /** Last editor action */ + lastAction: string; + /** Reference to the main container */ + mainContainer: IContainerModel; + /** Id of the selected container */ + selectedContainerId: string; + /** Counter of type of container. Used for ids. */ + typeCounters: Record; + /** List of symbols */ + symbols: Map; + /** Selected symbols id */ + selectedSymbolId: string; +} + +/** + * Model of an image with multiple source + * It must at least have one source. + * + * If Url/Base64Image and Svg are set, + * Url/Base64Image will be shown in the menu while SVG will be drawn + */ +export interface IImage { + /** Name of the image */ + Name: string; + /** (optional) Url of the image */ + Url?: string; + /** (optional) base64 data of the image */ + Base64Image?: string; + /** (optional) SVG string */ + Svg?: string; +} + + +export interface IInputGroup { + text: React.ReactNode; + value: string; +} + +export interface IMargin { + left?: number; + bottom?: number; + top?: number; + right?: number; +} + + +export interface IMessage { + text: string; + type: MessageType; +} + + +export interface IPattern { + /** + * Unique id for the pattern + */ + id: string; + /** + * Text to display in the sidebar + */ + text: string; + /** + * IAvailableContainer id used to wrap the children. + */ + wrapper: string; + /** + * List of ids of Pattern or IAvailableContainer + * If a IAvailableContainer and a Pattern have the same id, + * IAvailableContainer will be prioritized + */ + children: string[]; +} +export type ContainerOrPattern = IAvailableContainer | IPattern; +export function GetPattern(id: string, configs: Map, patterns: Map): ContainerOrPattern | undefined; +export function IsPattern(id: string, configs: Map, patterns: Map): boolean; + +export interface IPoint { + x: number; + y: number; +} + + + +export interface ISetContainerListRequest { + /** Name of the action declared in the API */ + Action: string; + /** Selected container */ + Container: IContainerModel; + /** The previous sibling container */ + PreviousContainer: IContainerModel | undefined; + /** The next sibling container */ + NextContainer: IContainerModel | undefined; + /** Current application state */ + ApplicationState: IHistoryState; +} + + +export interface ISetContainerListResponse { + Containers: IAvailableContainer[]; +} + +/** + * A SizePointer is a pointer in a 1 dimensional array of width/space + * x being the address where the pointer is pointing + * width being the overall (un)allocated space affected to the address + */ +export interface ISizePointer { + x: number; + width: number; +} + + +export interface ISymbolModel { + /** Identifier */ + id: string; + /** Type */ + type: string; + /** Configuration of the symbol */ + config: IAvailableSymbol; + /** Horizontal offset */ + x: number; + /** Width */ + width: number; + /** Height */ + height: number; + /** List of linked container id */ + linkedContainers: Set; +} + +} diff --git a/src/dts/tsconfig.dts.json b/src/dts/tsconfig.dts.json new file mode 100644 index 0000000..3d2caa3 --- /dev/null +++ b/src/dts/tsconfig.dts.json @@ -0,0 +1,15 @@ +{ + // Change this to match your project + "include": ["../Enums/*", "../Interfaces/*"], + "compilerOptions": { + "target": "ES2021", + "module": "es2020", + "allowJs": false, + "declaration": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "emitDeclarationOnly": true, + "outDir": "dist", + "declarationMap": false + } +} \ No newline at end of file diff --git a/src/utils/default.ts b/src/utils/default.ts index 7b99101..45047bf 100644 --- a/src/utils/default.ts +++ b/src/utils/default.ts @@ -7,6 +7,14 @@ import { IContainerProperties } from '../Interfaces/IContainerProperties'; import { IEditorState } from '../Interfaces/IEditorState'; import { ISymbolModel } from '../Interfaces/ISymbolModel'; +/// EDITOR DEFAULTS /// + +export const FAST_BOOT = import.meta.env.PROD; +export const ENABLE_SHORTCUTS = true; +export const MAX_HISTORY = 200; +export const APPLY_BEHAVIORS_ON_CHILDREN = true; +export const MAX_FRAMERATE = 60; + /// CONTAINER DEFAULTS /// /** Enable the swap behavior */ @@ -41,13 +49,6 @@ export const NOTCHES_LENGTH = 10; export const DEFAULT_SYMBOL_WIDTH = 32; export const DEFAULT_SYMBOL_HEIGHT = 32; -/// EDITOR DEFAULTS /// - -export const ENABLE_SHORTCUTS = true; -export const MAX_HISTORY = 200; -export const APPLY_BEHAVIORS_ON_CHILDREN = true; -export const MAX_FRAMERATE = 60; - /** * Returns the default editor state given the configuration */