Merged PR 190: Update master
This commit is contained in:
commit
3a0c4a5714
24 changed files with 1182 additions and 149 deletions
19
README.md
19
README.md
|
@ -104,3 +104,22 @@ Inside `.vscode/settings.json`, set the following :
|
||||||
```
|
```
|
||||||
|
|
||||||
Change the `url` to the dev server url. Set the `runtimeExecutable` to your favorite chromium browser.
|
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`.
|
|
@ -9,6 +9,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="./src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
65
public/Interfaces.d.ts
vendored
65
public/Interfaces.d.ts
vendored
|
@ -1,65 +0,0 @@
|
||||||
declare interface IHistoryState {
|
|
||||||
LastAction: string
|
|
||||||
MainContainer: IContainerModel
|
|
||||||
SelectedContainer: IContainerModel | null
|
|
||||||
SelectedContainerId: string
|
|
||||||
TypeCounters: Record<string, number>
|
|
||||||
}
|
|
||||||
|
|
||||||
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<string, string | number>
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<div id="root">
|
<iframe src="./Components/svg-layout-designer/index.html" style="border:none; width:100%; height:100%">
|
||||||
</div>
|
</iframe>
|
|
@ -1,40 +1,99 @@
|
||||||
namespace SmartBusiness.Web.Components {
|
namespace SmartBusiness.Web.Components {
|
||||||
|
import IHistoryState = SVGLD.IHistoryState;
|
||||||
|
import IEditorState = SVGLD.IEditorState;
|
||||||
|
|
||||||
export class SVGLayoutDesigner extends Components.ComponentBase {
|
export class SVGLayoutDesigner extends Components.ComponentBase {
|
||||||
|
|
||||||
public constructor(componentInfo: KnockoutComponentTypes.ComponentInfo, params: any) {
|
public constructor(componentInfo: KnockoutComponentTypes.ComponentInfo, params: any) {
|
||||||
super(componentInfo, params);
|
super(componentInfo, params);
|
||||||
setTimeout(() => (window as any).SVGLayoutDesigner.Render(this.$component[0]));
|
|
||||||
this.InitEventsListener();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetEditorComponent() {
|
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')
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetCurrentHistoryState() {
|
return component;
|
||||||
this.GetEditorComponent().dispatchEvent(new CustomEvent('getCurrentHistoryState'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetEditorState() {
|
public GetRootComponent() {
|
||||||
this.GetEditorComponent().dispatchEvent(new CustomEvent('getEditorState'));
|
return this.$component[0]
|
||||||
|
.querySelector('iframe')
|
||||||
|
.contentDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SetEditorState(editorState: IEditorState) {
|
public GetCurrentHistoryState(callback: (state: IHistoryState) => void) {
|
||||||
this.GetEditorComponent().dispatchEvent(new CustomEvent('SetEditorState', { detail: editorState }));
|
const component = this.GetEditorComponent();
|
||||||
|
const eventType = 'getCurrentHistoryState';
|
||||||
|
component.dispatchEvent(new CustomEvent(eventType));
|
||||||
|
this.AddEventListener(callback, eventType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppendNewHistoryState(historyState: IHistoryState) {
|
public GetEditorState(callback: (state: IEditorState) => void) {
|
||||||
this.GetEditorComponent().dispatchEvent(new CustomEvent('appendNewState', { detail: historyState }));
|
const component = this.GetEditorComponent();
|
||||||
|
const eventType = 'getEditorState';
|
||||||
|
component.dispatchEvent(new CustomEvent(eventType));
|
||||||
|
this.AddEventListener(callback, eventType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OHistoryState: KnockoutObservable<any>;
|
public AppendNewHistoryState(historyState: SVGLD.IHistoryState, callback?: (state: IEditorState) => void) {
|
||||||
|
const eventType = 'appendNewState';
|
||||||
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail: historyState }));
|
||||||
|
this.AddEventListener(callback, eventType);
|
||||||
|
}
|
||||||
|
|
||||||
private InitEventsListener() {
|
public AddContainer(index: number, type: string, parentId: string, callback?: (state: IEditorState) => void) {
|
||||||
this.$component[0].addEventListener('getCurrentHistoryState', (e: CustomEvent) => {
|
const detail = {
|
||||||
this.OHistoryState(e.detail);
|
index,
|
||||||
console.log(this.OHistoryState());
|
type,
|
||||||
});
|
parentId
|
||||||
this.$component[0].addEventListener('getEditorState', (e) => console.log((e as any).detail));
|
}
|
||||||
|
const eventType = 'addContainer';
|
||||||
|
this.GetEditorComponent().dispatchEvent(new CustomEvent(eventType, { detail }));
|
||||||
|
this.AddEventListener(callback, eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
69
public/workers/message_packet_worker.js
Normal file
69
public/workers/message_packet_worker.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/**
|
||||||
|
* Web worker handling message API.
|
||||||
|
* When sending quickly multiple request,
|
||||||
|
* a single packet of message merging all request' responses is returned
|
||||||
|
*/
|
||||||
|
|
||||||
|
const DELAY_BEFORE_SEND = 200;
|
||||||
|
const queue = [];
|
||||||
|
let messagePacket = [];
|
||||||
|
onmessage = async(e) => {
|
||||||
|
let packetLength
|
||||||
|
|
||||||
|
const url = e.data.url;
|
||||||
|
const state = e.data.state;
|
||||||
|
const request = {
|
||||||
|
ApplicationState: state
|
||||||
|
};
|
||||||
|
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
||||||
|
queue.push(request);
|
||||||
|
fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: new Headers({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}),
|
||||||
|
body: dataParsed
|
||||||
|
})
|
||||||
|
.then((response) =>
|
||||||
|
response.json()
|
||||||
|
)
|
||||||
|
.then(async(json) => {
|
||||||
|
messagePacket.push.apply(messagePacket, json.messages);
|
||||||
|
queue.pop();
|
||||||
|
|
||||||
|
// The sleep allow the message packet to be filled by
|
||||||
|
// others requests before being sent as a single batch
|
||||||
|
// Reducing the wait time will reduce latency but increase error rate
|
||||||
|
let doLoop = true;
|
||||||
|
do {
|
||||||
|
packetLength = messagePacket.length;
|
||||||
|
await sleep(DELAY_BEFORE_SEND);
|
||||||
|
const newPacketLength = messagePacket.length;
|
||||||
|
doLoop = newPacketLength !== packetLength;
|
||||||
|
packetLength = newPacketLength;
|
||||||
|
} while (doLoop);
|
||||||
|
|
||||||
|
if (queue.length <= 0 && messagePacket.length > 0) {
|
||||||
|
console.debug(`[GetFeedback] Packet size before sent: ${messagePacket.length}`)
|
||||||
|
postMessage(messagePacket)
|
||||||
|
messagePacket.splice(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetCircularReplacerKeepDataStructure()
|
||||||
|
{
|
||||||
|
return (key, value) => {
|
||||||
|
if (key === 'parent') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
const DELAY_BEFORE_SEND = 200;
|
/**
|
||||||
const queue = [];
|
* Web worker handling message API.
|
||||||
let messagePacket = [];
|
* Return a message of the request.
|
||||||
onmessage = async(e) => {
|
*/
|
||||||
let packetLength
|
|
||||||
|
|
||||||
|
onmessage = async(e) => {
|
||||||
const url = e.data.url;
|
const url = e.data.url;
|
||||||
const state = e.data.state;
|
const state = e.data.state;
|
||||||
const request = {
|
const request = {
|
||||||
ApplicationState: state
|
ApplicationState: state
|
||||||
};
|
};
|
||||||
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
||||||
queue.push(request);
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
|
@ -23,33 +22,10 @@ onmessage = async(e) => {
|
||||||
response.json()
|
response.json()
|
||||||
)
|
)
|
||||||
.then(async(json) => {
|
.then(async(json) => {
|
||||||
messagePacket.push.apply(messagePacket, json.messages);
|
postMessage(json.messages)
|
||||||
queue.pop();
|
|
||||||
|
|
||||||
// The sleep allow the message packet to be filled by
|
|
||||||
// others requests before being sent as a single batch
|
|
||||||
// Reducing the wait time will reduce latency but increase error rate
|
|
||||||
let doLoop = true;
|
|
||||||
do {
|
|
||||||
packetLength = messagePacket.length;
|
|
||||||
await sleep(DELAY_BEFORE_SEND);
|
|
||||||
const newPacketLength = messagePacket.length;
|
|
||||||
doLoop = newPacketLength !== packetLength;
|
|
||||||
packetLength = newPacketLength;
|
|
||||||
} while (doLoop);
|
|
||||||
|
|
||||||
if (queue.length <= 0 && messagePacket.length > 0) {
|
|
||||||
console.debug(`[GetFeedback] Packet size before sent: ${messagePacket.length}`)
|
|
||||||
postMessage(messagePacket)
|
|
||||||
messagePacket.splice(0);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function sleep(ms) {
|
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
function GetCircularReplacerKeepDataStructure()
|
function GetCircularReplacerKeepDataStructure()
|
||||||
{
|
{
|
||||||
return (key, value) => {
|
return (key, value) => {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ICategory } from '../../Interfaces/ICategory';
|
||||||
import { TruncateString } from '../../utils/stringtools';
|
import { TruncateString } from '../../utils/stringtools';
|
||||||
|
|
||||||
interface ICategoryProps {
|
interface ICategoryProps {
|
||||||
children: JSX.Element | JSX.Element[]
|
children: JSX.Element[]
|
||||||
category: ICategory
|
category: ICategory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ export function Category(props: ICategoryProps): JSX.Element {
|
||||||
title={categoryDisplayedText}
|
title={categoryDisplayedText}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className='flex flex-row group cursor-pointer h-16'
|
className={'flex flex-row group h-16 cursor-pointer'}
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
>
|
>
|
||||||
<span className={`transition-all flex-1 h-full justify-center sidebar-component-left ${isOpen ? 'rounded-b-none bg-slate-400/80 group-hover:bg-blue-600' : ''}`}>
|
<span className={`transition-all flex-1 h-full justify-center sidebar-component-left ${isOpen ? 'rounded-b-none bg-slate-400/80 group-hover:bg-blue-600' : ''}`}>
|
||||||
|
|
|
@ -112,7 +112,7 @@ function UseShortcuts(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function UseWindowEvents(
|
function UseCustomEvents(
|
||||||
root: Element | Document,
|
root: Element | Document,
|
||||||
history: IHistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
|
@ -178,7 +178,7 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
UseShortcuts(history, historyCurrentStep, setHistoryCurrentStep);
|
UseShortcuts(history, historyCurrentStep, setHistoryCurrentStep);
|
||||||
UseWindowEvents(
|
UseCustomEvents(
|
||||||
props.root,
|
props.root,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
|
|
59
src/Components/Loader/Loader.scss
Normal file
59
src/Components/Loader/Loader.scss
Normal file
|
@ -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;}
|
14
src/Components/Loader/Loader.tsx
Normal file
14
src/Components/Loader/Loader.tsx
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import './Loader.scss';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export interface ILoaderProps {
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Loader(props: ILoaderProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className='loader'>
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { FAST_BOOT } from '../../utils/default';
|
||||||
|
import { Loader } from '../Loader/Loader';
|
||||||
|
|
||||||
interface IMainMenuProps {
|
interface IMainMenuProps {
|
||||||
newEditor: () => void
|
newEditor: () => void
|
||||||
|
@ -8,10 +10,21 @@ interface IMainMenuProps {
|
||||||
enum WindowState {
|
enum WindowState {
|
||||||
Main,
|
Main,
|
||||||
Load,
|
Load,
|
||||||
|
Loading,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MainMenu(props: IMainMenuProps): JSX.Element {
|
export function MainMenu(props: IMainMenuProps): JSX.Element {
|
||||||
const [windowState, setWindowState] = React.useState(WindowState.Main);
|
const [windowState, setWindowState] = React.useState(WindowState.Main);
|
||||||
|
|
||||||
|
if (FAST_BOOT) {
|
||||||
|
props.newEditor();
|
||||||
|
return (
|
||||||
|
<div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'>
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
switch (windowState) {
|
switch (windowState) {
|
||||||
case WindowState.Load:
|
case WindowState.Load:
|
||||||
return (
|
return (
|
||||||
|
@ -46,10 +59,19 @@ export function MainMenu(props: IMainMenuProps): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
case WindowState.Loading:
|
||||||
|
return (
|
||||||
|
<div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'>
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<div className='absolute bg-blue-50 p-12 rounded-lg drop-shadow-lg grid grid-cols-1 md:grid-cols-2 gap-8 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'>
|
<div className='absolute bg-blue-50 p-12 rounded-lg drop-shadow-lg grid grid-cols-1 md:grid-cols-2 gap-8 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'>
|
||||||
<button type="button" className='mainmenu-btn' onClick={props.newEditor}>Start from scratch</button>
|
<button type="button" className='mainmenu-btn' onClick={() => {
|
||||||
|
setWindowState(WindowState.Loading);
|
||||||
|
props.newEditor();
|
||||||
|
}}>Start from scratch</button>
|
||||||
<button type="button" className='mainmenu-btn' onClick={() => setWindowState(WindowState.Load)}>Load a configuration file</button>
|
<button type="button" className='mainmenu-btn' onClick={() => setWindowState(WindowState.Load)}>Load a configuration file</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,7 +30,7 @@ function UseWorker(
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
myWorker.onmessage = (event) => {
|
myWorker.onmessage = (event) => {
|
||||||
setMessages(messages.concat(event.data as IMessage[]));
|
setMessages(event.data as IMessage[]);
|
||||||
};
|
};
|
||||||
}, [messages, setMessages]);
|
}, [messages, setMessages]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,10 @@ describe.concurrent('Sidebar', () => {
|
||||||
it('Start default', () => {
|
it('Start default', () => {
|
||||||
render(
|
render(
|
||||||
<Sidebar
|
<Sidebar
|
||||||
|
selectedContainer={undefined}
|
||||||
componentOptions={[]}
|
componentOptions={[]}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
buttonOnClick={() => {}}
|
buttonOnClick={() => { } } categories={[]}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const stuff = screen.queryByText(/stuff/i);
|
const stuff = screen.queryByText(/stuff/i);
|
||||||
|
@ -22,8 +23,7 @@ describe.concurrent('Sidebar', () => {
|
||||||
render(<Sidebar
|
render(<Sidebar
|
||||||
componentOptions={[]}
|
componentOptions={[]}
|
||||||
isOpen={false}
|
isOpen={false}
|
||||||
buttonOnClick={() => {}}
|
buttonOnClick={() => { } } selectedContainer={undefined} categories={[]} />);
|
||||||
/>);
|
|
||||||
|
|
||||||
const stuff = screen.queryByText(/stuff/i);
|
const stuff = screen.queryByText(/stuff/i);
|
||||||
expect(screen.getByText(/Components/i).classList.contains('-left-64')).toBeDefined();
|
expect(screen.getByText(/Components/i).classList.contains('-left-64')).toBeDefined();
|
||||||
|
@ -45,8 +45,7 @@ describe.concurrent('Sidebar', () => {
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
buttonOnClick={handleButtonClick}
|
buttonOnClick={handleButtonClick} selectedContainer={undefined} categories={[]} />);
|
||||||
/>);
|
|
||||||
const stuff = screen.getByText(/stuff/i);
|
const stuff = screen.getByText(/stuff/i);
|
||||||
|
|
||||||
expect(stuff).toBeDefined();
|
expect(stuff).toBeDefined();
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
import { EyeIcon, EyeOffIcon } from '@heroicons/react/outline';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
||||||
import { ICategory } from '../../Interfaces/ICategory';
|
import { ICategory } from '../../Interfaces/ICategory';
|
||||||
|
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { TruncateString } from '../../utils/stringtools';
|
import { TruncateString } from '../../utils/stringtools';
|
||||||
import { Category } from '../Category/Category';
|
import { Category } from '../Category/Category';
|
||||||
|
|
||||||
interface ISidebarProps {
|
interface ISidebarProps {
|
||||||
|
selectedContainer: IContainerModel | undefined
|
||||||
componentOptions: IAvailableContainer[]
|
componentOptions: IAvailableContainer[]
|
||||||
categories: ICategory[]
|
categories: ICategory[]
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
|
@ -21,6 +24,8 @@ interface SidebarCategory {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Sidebar(props: ISidebarProps): JSX.Element {
|
export function Sidebar(props: ISidebarProps): JSX.Element {
|
||||||
|
const [hideDisabled, setHideDisabled] = React.useState<boolean>(false);
|
||||||
|
|
||||||
const rootElements: Array<JSX.Element | undefined> = [];
|
const rootElements: Array<JSX.Element | undefined> = [];
|
||||||
const categories = new Map<string, SidebarCategory>(props.categories.map(category => [
|
const categories = new Map<string, SidebarCategory>(props.categories.map(category => [
|
||||||
category.Type,
|
category.Type,
|
||||||
|
@ -41,12 +46,25 @@ export function Sidebar(props: ISidebarProps): JSX.Element {
|
||||||
</Category>);
|
</Category>);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const selectedContainer = props.selectedContainer;
|
||||||
|
const config = props.componentOptions.find(option => option.Type === selectedContainer?.properties.type);
|
||||||
// build the components
|
// build the components
|
||||||
props.componentOptions.forEach(componentOption => {
|
props.componentOptions.forEach(componentOption => {
|
||||||
if (componentOption.IsHidden === true) {
|
if (componentOption.IsHidden === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let disabled = false;
|
||||||
|
if (config?.Whitelist !== undefined) {
|
||||||
|
disabled = config.Whitelist?.find(type => type === componentOption.Type) === undefined;
|
||||||
|
} else if (config?.Blacklist !== undefined) {
|
||||||
|
disabled = config.Blacklist?.find(type => type === componentOption.Type) !== undefined ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disabled && hideDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const componentButton = (<button
|
const componentButton = (<button
|
||||||
key={componentOption.Type}
|
key={componentOption.Type}
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -56,9 +74,11 @@ export function Sidebar(props: ISidebarProps): JSX.Element {
|
||||||
onClick={() => props.buttonOnClick(componentOption.Type)}
|
onClick={() => props.buttonOnClick(componentOption.Type)}
|
||||||
draggable={true}
|
draggable={true}
|
||||||
onDragStart={(event) => HandleDragStart(event)}
|
onDragStart={(event) => HandleDragStart(event)}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{TruncateString(componentOption.DisplayedText ?? componentOption.Type, 25)}
|
{TruncateString(componentOption.DisplayedText ?? componentOption.Type, 25)}
|
||||||
</button>);
|
</button>);
|
||||||
|
|
||||||
if (componentOption.Category === null || componentOption.Category === undefined) {
|
if (componentOption.Category === null || componentOption.Category === undefined) {
|
||||||
rootElements.push(componentButton);
|
rootElements.push(componentButton);
|
||||||
return;
|
return;
|
||||||
|
@ -69,6 +89,7 @@ export function Sidebar(props: ISidebarProps): JSX.Element {
|
||||||
console.error(`[Category] Category does not exists in configuration.Categories: ${componentOption.Category}`);
|
console.error(`[Category] Category does not exists in configuration.Categories: ${componentOption.Category}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
category.children.push(componentButton);
|
category.children.push(componentButton);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -77,10 +98,22 @@ export function Sidebar(props: ISidebarProps): JSX.Element {
|
||||||
<div className={`fixed z-10 bg-slate-200
|
<div className={`fixed z-10 bg-slate-200
|
||||||
text-gray-700 transition-all h-full w-64
|
text-gray-700 transition-all h-full w-64
|
||||||
overflow-y-auto ${isOpenClasses}`}>
|
overflow-y-auto ${isOpenClasses}`}>
|
||||||
<div className='bg-slate-100 sidebar-title'>
|
<div className='bg-slate-100 sidebar-title flex place-content-between'>
|
||||||
Components
|
Components
|
||||||
|
<button
|
||||||
|
onClick={() => { setHideDisabled(!hideDisabled); }}
|
||||||
|
className='h-6'
|
||||||
|
aria-label='Hide disabled component'
|
||||||
|
title='Hide disabled component'
|
||||||
|
>
|
||||||
|
{
|
||||||
|
hideDisabled
|
||||||
|
? <EyeOffIcon className='heroicon'></EyeOffIcon>
|
||||||
|
: <EyeIcon className='heroicon'></EyeIcon>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className='grid grid-cols-1 md:grid-cols-1 gap-2
|
<div className='transition-all grid grid-cols-1 md:grid-cols-1 gap-2
|
||||||
m-2 md:text-xs font-bold'>
|
m-2 md:text-xs font-bold'>
|
||||||
{rootElements}
|
{rootElements}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -84,6 +84,7 @@ export function UI(props: IUIProps): JSX.Element {
|
||||||
} }/>
|
} }/>
|
||||||
|
|
||||||
<Sidebar
|
<Sidebar
|
||||||
|
selectedContainer={props.selectedContainer}
|
||||||
componentOptions={props.availableContainers}
|
componentOptions={props.availableContainers}
|
||||||
categories={props.categories}
|
categories={props.categories}
|
||||||
isOpen={isSidebarOpen}
|
isOpen={isSidebarOpen}
|
||||||
|
|
|
@ -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 { GetCurrentHistory } from '../Components/Editor/Editor';
|
||||||
import { IConfiguration } from '../Interfaces/IConfiguration';
|
|
||||||
import { IEditorState } from '../Interfaces/IEditorState';
|
import { IEditorState } from '../Interfaces/IEditorState';
|
||||||
import { IHistoryState } from '../Interfaces/IHistoryState';
|
import { IHistoryState } from '../Interfaces/IHistoryState';
|
||||||
|
import { FindContainerById } from '../utils/itertools';
|
||||||
import { ReviveState } from '../utils/saveload';
|
import { ReviveState } from '../utils/saveload';
|
||||||
|
|
||||||
function InitEditor(configuration: IConfiguration): void {
|
|
||||||
// faire comme la callback de fetch
|
|
||||||
}
|
|
||||||
|
|
||||||
function GetEditorState(root: Element | Document,
|
function GetEditorState(root: Element | Document,
|
||||||
editorState: IEditorState): void {
|
editorState: IEditorState): void {
|
||||||
const customEvent = new CustomEvent<IEditorState>('getEditorState', { detail: editorState });
|
const customEvent = new CustomEvent<IEditorState>('getEditorState', { detail: structuredClone(editorState) });
|
||||||
root.dispatchEvent(customEvent);
|
root.dispatchEvent(customEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +17,7 @@ function GetCurrentHistoryState(root: Element | Document,
|
||||||
editorState: IEditorState): void {
|
editorState: IEditorState): void {
|
||||||
const customEvent = new CustomEvent<IHistoryState>(
|
const customEvent = new CustomEvent<IHistoryState>(
|
||||||
'getCurrentHistoryState',
|
'getCurrentHistoryState',
|
||||||
{ detail: editorState.history[editorState.historyCurrentStep] });
|
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) });
|
||||||
root.dispatchEvent(customEvent);
|
root.dispatchEvent(customEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +31,244 @@ function AppendNewState(root: Element | Document,
|
||||||
|
|
||||||
history.push(state);
|
history.push(state);
|
||||||
setNewHistory(history);
|
setNewHistory(history);
|
||||||
|
|
||||||
|
const customEvent = new CustomEvent<IHistoryState>(
|
||||||
|
'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<IHistoryState>(
|
||||||
|
'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<IHistoryState>(
|
||||||
|
'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<IHistoryState>(
|
||||||
|
'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<IHistoryState>(
|
||||||
|
'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<IHistoryState>(
|
||||||
|
'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<IHistoryState>(
|
||||||
|
'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<IHistoryState>(
|
||||||
|
'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<IHistoryState>(
|
||||||
|
'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<IHistoryState>(
|
||||||
|
'DeleteSymbol',
|
||||||
|
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) });
|
||||||
|
root.dispatchEvent(customEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEditorEvent {
|
export interface IEditorEvent {
|
||||||
|
@ -48,5 +284,14 @@ export interface IEditorEvent {
|
||||||
export const events: IEditorEvent[] = [
|
export const events: IEditorEvent[] = [
|
||||||
{ name: 'getEditorState', func: GetEditorState },
|
{ name: 'getEditorState', func: GetEditorState },
|
||||||
{ name: 'getCurrentHistoryState', func: GetCurrentHistoryState },
|
{ 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 }
|
||||||
];
|
];
|
||||||
|
|
|
@ -118,6 +118,18 @@ export interface IAvailableContainer {
|
||||||
*/
|
*/
|
||||||
IsHidden?: boolean
|
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)
|
* (optional)
|
||||||
* Style of the <rect>
|
* Style of the <rect>
|
||||||
|
|
101
src/dts/generate_dts.py
Normal file
101
src/dts/generate_dts.py
Normal file
|
@ -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()
|
469
src/dts/svgld.d.ts
vendored
Normal file
469
src/dts/svgld.d.ts
vendored
Normal file
|
@ -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 <rect> 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 :
|
||||||
|
* ```
|
||||||
|
* `<rect width="{width}" height="{height}" style="{style}"></rect>
|
||||||
|
* <rect width="{width}" height="{height}" stroke="black" fill-opacity="0"></rect>
|
||||||
|
* <line x1="0" y1="0" x2="{width}" y2="{height}" stroke="black" style='{{ "transform":"scaleY(0.5)"}}'></line>
|
||||||
|
* <line x1="{width}" y1="0" x2="0" y2="{height}" stroke="black" style='{userData.styleLine}'></line>
|
||||||
|
* `
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
CustomSVG?: string;
|
||||||
|
/**
|
||||||
|
* (optional)
|
||||||
|
* Disabled when Pattern is used.
|
||||||
|
*
|
||||||
|
* Replace a <rect> 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 :
|
||||||
|
* ```
|
||||||
|
* `<rect width="{width}" height="{height}" style="{style}"></rect>
|
||||||
|
* <rect width="{width}" height="{height}" stroke="black" fill-opacity="0"></rect>
|
||||||
|
* <line x1="0" y1="0" x2="{width}" y2="{height}" stroke="black" style='{{ "transform":"scaleY(0.5)"}}'></line>
|
||||||
|
* <line x1="{width}" y1="0" x2="0" y2="{height}" stroke="black" style='{userData.styleLine}'></line>
|
||||||
|
* `
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
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 <rect>
|
||||||
|
*/
|
||||||
|
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<string, string | number>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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<string, string | number>;
|
||||||
|
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 <rect> 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 :
|
||||||
|
* ```
|
||||||
|
* `<rect width="{width}" height="{height}" style="{style}"></rect>
|
||||||
|
* <rect width="{width}" height="{height}" stroke="black" fill-opacity="0"></rect>
|
||||||
|
* <line x1="0" y1="0" x2="{width}" y2="{height}" stroke="black" style='{{ "transform":"scaleY(0.5)"}}'></line>
|
||||||
|
* <line x1="{width}" y1="0" x2="0" y2="{height}" stroke="black" style='{userData.styleLine}'></line>
|
||||||
|
* `
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
customSVG?: string;
|
||||||
|
/**
|
||||||
|
* (optional)
|
||||||
|
* Style of the <rect>
|
||||||
|
*/
|
||||||
|
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<string, number>;
|
||||||
|
/** List of symbols */
|
||||||
|
symbols: Map<string, ISymbolModel>;
|
||||||
|
/** 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<string, IAvailableContainer>, patterns: Map<string, IPattern>): ContainerOrPattern | undefined;
|
||||||
|
export function IsPattern(id: string, configs: Map<string, IAvailableContainer>, patterns: Map<string, IPattern>): 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<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
src/dts/tsconfig.dts.json
Normal file
15
src/dts/tsconfig.dts.json
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-component {
|
.sidebar-component {
|
||||||
@apply transition-all px-2 py-6 text-sm rounded-lg bg-slate-300/80 hover:bg-blue-500 hover:text-slate-50
|
@apply transition-all px-2 py-6 text-sm rounded-lg
|
||||||
|
bg-slate-300/80 hover:bg-blue-500 hover:text-slate-50
|
||||||
|
disabled:bg-slate-400 disabled:text-slate-500
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-component-left {
|
.sidebar-component-left {
|
||||||
|
|
|
@ -7,6 +7,14 @@ import { IContainerProperties } from '../Interfaces/IContainerProperties';
|
||||||
import { IEditorState } from '../Interfaces/IEditorState';
|
import { IEditorState } from '../Interfaces/IEditorState';
|
||||||
import { ISymbolModel } from '../Interfaces/ISymbolModel';
|
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 ///
|
/// CONTAINER DEFAULTS ///
|
||||||
|
|
||||||
/** Enable the swap behavior */
|
/** Enable the swap behavior */
|
||||||
|
@ -41,13 +49,6 @@ export const NOTCHES_LENGTH = 10;
|
||||||
export const DEFAULT_SYMBOL_WIDTH = 32;
|
export const DEFAULT_SYMBOL_WIDTH = 32;
|
||||||
export const DEFAULT_SYMBOL_HEIGHT = 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
|
* Returns the default editor state given the configuration
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -65,6 +65,7 @@ const GetSVGLayoutConfiguration = () => {
|
||||||
{
|
{
|
||||||
Type: 'Chassis',
|
Type: 'Chassis',
|
||||||
DisplayedText: 'Chassis?',
|
DisplayedText: 'Chassis?',
|
||||||
|
Whitelist: ["Trou"],
|
||||||
MaxWidth: 500,
|
MaxWidth: 500,
|
||||||
MinWidth: 200,
|
MinWidth: 200,
|
||||||
Width: 200,
|
Width: 200,
|
||||||
|
@ -81,6 +82,7 @@ const GetSVGLayoutConfiguration = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: 'Trou',
|
Type: 'Trou',
|
||||||
|
Blacklist: ["Chassis"],
|
||||||
DefaultX: 0,
|
DefaultX: 0,
|
||||||
DefaultY: 0,
|
DefaultY: 0,
|
||||||
Margin: {
|
Margin: {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue