Compare commits
No commits in common. "7e3ccdee991fe6bdf5fc82671d2a25663574e7e2" and "022ad381631d34652aa0c7a14a46d98c84d6398b" have entirely different histories.
7e3ccdee99
...
022ad38163
46 changed files with 181 additions and 1068 deletions
|
@ -21,7 +21,7 @@ steps:
|
||||||
path: $(pnpm_config_cache)
|
path: $(pnpm_config_cache)
|
||||||
displayName: Cache pnpm
|
displayName: Cache pnpm
|
||||||
|
|
||||||
- bash: |
|
- script: |
|
||||||
curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7
|
curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7
|
||||||
pnpm config set store-dir $(pnpm_config_cache)
|
pnpm config set store-dir $(pnpm_config_cache)
|
||||||
displayName: "Setup pnpm"
|
displayName: "Setup pnpm"
|
||||||
|
@ -31,8 +31,7 @@ steps:
|
||||||
versionSpec: '16.x'
|
versionSpec: '16.x'
|
||||||
displayName: 'Install Node.js 16.x LTS'
|
displayName: 'Install Node.js 16.x LTS'
|
||||||
|
|
||||||
- bash: |
|
- script: |
|
||||||
set -euo pipefail
|
|
||||||
node --version
|
node --version
|
||||||
node ./test-server/node-http.js &
|
node ./test-server/node-http.js &
|
||||||
jobs
|
jobs
|
||||||
|
@ -47,8 +46,7 @@ steps:
|
||||||
versionSpec: '>=18.7.0'
|
versionSpec: '>=18.7.0'
|
||||||
displayName: 'Install Node.js Latest'
|
displayName: 'Install Node.js Latest'
|
||||||
|
|
||||||
- bash: |
|
- script: |
|
||||||
set -euo pipefail
|
|
||||||
node --version
|
node --version
|
||||||
node ./test-server/node-http.js &
|
node ./test-server/node-http.js &
|
||||||
jobs
|
jobs
|
||||||
|
|
|
@ -4,19 +4,22 @@ onmessage = (e) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCircularReplacer = () => {
|
const getCircularReplacer = () => {
|
||||||
|
const seen = new WeakSet();
|
||||||
return (key, value) => {
|
return (key, value) => {
|
||||||
if (key === 'parent') {
|
if (key === 'parent') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'Symbols') {
|
if (key === 'SelectedContainer') {
|
||||||
return Array.from(value.entries());
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'linkedContainers') {
|
if (typeof value === 'object' && value !== null) {
|
||||||
return Array.from(value);
|
if (seen.has(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,10 +26,9 @@ export const App: React.FunctionComponent<IAppProps> = (props) => {
|
||||||
history: [{
|
history: [{
|
||||||
LastAction: '',
|
LastAction: '',
|
||||||
MainContainer: defaultMainContainer,
|
MainContainer: defaultMainContainer,
|
||||||
|
SelectedContainer: defaultMainContainer,
|
||||||
SelectedContainerId: defaultMainContainer.properties.id,
|
SelectedContainerId: defaultMainContainer.properties.id,
|
||||||
TypeCounters: {},
|
TypeCounters: {}
|
||||||
Symbols: new Map(),
|
|
||||||
SelectedSymbolId: ''
|
|
||||||
}],
|
}],
|
||||||
historyCurrentStep: 0
|
historyCurrentStep: 0
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { ContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { fetchConfiguration } from '../API/api';
|
import { fetchConfiguration } from '../API/api';
|
||||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||||
import { LoadState } from './Load';
|
import { LoadState } from './Load';
|
||||||
|
import { XPositionReference } from '../../Enums/XPositionReference';
|
||||||
import { DEFAULT_MAINCONTAINER_PROPS } from '../../utils/default';
|
import { DEFAULT_MAINCONTAINER_PROPS } from '../../utils/default';
|
||||||
|
|
||||||
export function NewEditor(
|
export function NewEditor(
|
||||||
|
@ -25,7 +26,6 @@ export function NewEditor(
|
||||||
|
|
||||||
// Save the configuration and the new MainContainer
|
// Save the configuration and the new MainContainer
|
||||||
// and default the selected container to it
|
// and default the selected container to it
|
||||||
// TODO: Put this in default.ts
|
|
||||||
const editorState: IEditorState = {
|
const editorState: IEditorState = {
|
||||||
configuration,
|
configuration,
|
||||||
history:
|
history:
|
||||||
|
@ -33,10 +33,9 @@ export function NewEditor(
|
||||||
{
|
{
|
||||||
LastAction: '',
|
LastAction: '',
|
||||||
MainContainer,
|
MainContainer,
|
||||||
|
SelectedContainer: MainContainer,
|
||||||
SelectedContainerId: MainContainer.properties.id,
|
SelectedContainerId: MainContainer.properties.id,
|
||||||
TypeCounters: {},
|
TypeCounters: {}
|
||||||
Symbols: new Map(),
|
|
||||||
SelectedSymbolId: ''
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
historyCurrentStep: 0
|
historyCurrentStep: 0
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import { ClockIcon, CubeIcon, LinkIcon, MapIcon } from '@heroicons/react/outline';
|
import { ClockIcon, CubeIcon, MapIcon } from '@heroicons/react/outline';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { BarIcon } from './BarIcon';
|
import { BarIcon } from './BarIcon';
|
||||||
|
|
||||||
interface IBarProps {
|
interface IBarProps {
|
||||||
isSidebarOpen: boolean
|
isSidebarOpen: boolean
|
||||||
isSymbolsOpen: boolean
|
|
||||||
isElementsSidebarOpen: boolean
|
isElementsSidebarOpen: boolean
|
||||||
isHistoryOpen: boolean
|
isHistoryOpen: boolean
|
||||||
ToggleSidebar: () => void
|
ToggleSidebar: () => void
|
||||||
ToggleSymbols: () => void
|
ToggleElementsSidebar: () => void
|
||||||
ToggleTimeline: () => void
|
ToggleTimeline: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,10 +23,10 @@ export const Bar: React.FC<IBarProps> = (props) => {
|
||||||
<CubeIcon className='heroicon'/>
|
<CubeIcon className='heroicon'/>
|
||||||
</BarIcon>
|
</BarIcon>
|
||||||
<BarIcon
|
<BarIcon
|
||||||
isActive={props.isSymbolsOpen}
|
isActive={props.isElementsSidebarOpen}
|
||||||
title='Symbols'
|
title='Map'
|
||||||
onClick={() => props.ToggleSymbols()}>
|
onClick={() => props.ToggleElementsSidebar()}>
|
||||||
<LinkIcon className='heroicon'/>
|
<MapIcon className='heroicon'/>
|
||||||
</BarIcon>
|
</BarIcon>
|
||||||
<BarIcon
|
<BarIcon
|
||||||
isActive={props.isHistoryOpen}
|
isActive={props.isHistoryOpen}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { constraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors';
|
||||||
* Apply the following modification to the overlapping rigid body container :
|
* Apply the following modification to the overlapping rigid body container :
|
||||||
* @param container Container to impose its position
|
* @param container Container to impose its position
|
||||||
*/
|
*/
|
||||||
export function ApplyAnchor(container: IContainerModel): IContainerModel {
|
export function ImposePosition(container: IContainerModel): IContainerModel {
|
||||||
if (container.parent === undefined ||
|
if (container.parent === undefined ||
|
||||||
container.parent === null) {
|
container.parent === null) {
|
||||||
return container;
|
return container;
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
|
||||||
import { APPLY_BEHAVIORS_ON_CHILDREN } from '../../../utils/default';
|
import { APPLY_BEHAVIORS_ON_CHILDREN } from '../../../utils/default';
|
||||||
import { ApplyAnchor } from './AnchorBehaviors';
|
import { ImposePosition } from './AnchorBehaviors';
|
||||||
import { ApplyRigidBody } from './RigidBodyBehaviors';
|
import { RecalculatePhysics } from './RigidBodyBehaviors';
|
||||||
import { ApplySymbol } from './SymbolBehaviors';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recalculate the position of the container and its neighbors
|
* Recalculate the position of the container and its neighbors
|
||||||
|
@ -11,24 +9,19 @@ import { ApplySymbol } from './SymbolBehaviors';
|
||||||
* @param container Container to recalculate its positions
|
* @param container Container to recalculate its positions
|
||||||
* @returns Updated container
|
* @returns Updated container
|
||||||
*/
|
*/
|
||||||
export function ApplyBehaviors(container: IContainerModel, symbols: Map<string, ISymbolModel>): IContainerModel {
|
export function ApplyBehaviors(container: IContainerModel): IContainerModel {
|
||||||
if (container.properties.isAnchor) {
|
if (container.properties.isAnchor) {
|
||||||
ApplyAnchor(container);
|
ImposePosition(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container.properties.isRigidBody) {
|
if (container.properties.isRigidBody) {
|
||||||
ApplyRigidBody(container);
|
RecalculatePhysics(container);
|
||||||
}
|
|
||||||
|
|
||||||
const symbol = symbols.get(container.properties.linkedSymbolId);
|
|
||||||
if (container.properties.linkedSymbolId !== '' && symbol !== undefined) {
|
|
||||||
ApplySymbol(container, symbol);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (APPLY_BEHAVIORS_ON_CHILDREN) {
|
if (APPLY_BEHAVIORS_ON_CHILDREN) {
|
||||||
// Apply DFS by recursion
|
// Apply DFS by recursion
|
||||||
for (const child of container.children) {
|
for (const child of container.children) {
|
||||||
ApplyBehaviors(child, symbols);
|
ApplyBehaviors(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { ISizePointer } from '../../../Interfaces/ISizePointer';
|
||||||
* @param container Container to apply its rigid body properties
|
* @param container Container to apply its rigid body properties
|
||||||
* @returns A rigid body container
|
* @returns A rigid body container
|
||||||
*/
|
*/
|
||||||
export function ApplyRigidBody(
|
export function RecalculatePhysics(
|
||||||
container: IContainerModel
|
container: IContainerModel
|
||||||
): IContainerModel {
|
): IContainerModel {
|
||||||
container = constraintBodyInsideParent(container);
|
container = constraintBodyInsideParent(container);
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
|
||||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
|
||||||
import { restoreX, transformX } from '../../../utils/svg';
|
|
||||||
|
|
||||||
export function ApplySymbol(container: IContainerModel, symbol: ISymbolModel): IContainerModel {
|
|
||||||
container.properties.x = transformX(symbol.x, symbol.width, symbol.config.XPositionReference);
|
|
||||||
container.properties.x = restoreX(container.properties.x, container.properties.width, container.properties.XPositionReference);
|
|
||||||
return container;
|
|
||||||
}
|
|
|
@ -2,20 +2,19 @@ import { Dispatch, SetStateAction } from 'react';
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||||
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { findContainerById, MakeIterator } from '../../utils/itertools';
|
import { findContainerById } from '../../utils/itertools';
|
||||||
import { getCurrentHistory, UpdateCounters } from './Editor';
|
import { getCurrentHistory } from './Editor';
|
||||||
import { AddMethod } from '../../Enums/AddMethod';
|
import { AddMethod } from '../../Enums/AddMethod';
|
||||||
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
||||||
import { GetDefaultContainerProps, DEFAULTCHILDTYPE_ALLOW_CYCLIC, DEFAULTCHILDTYPE_MAX_DEPTH } from '../../utils/default';
|
import { GetDefaultContainerProps, DEFAULTCHILDTYPE_ALLOW_CYCLIC, DEFAULTCHILDTYPE_MAX_DEPTH } from '../../utils/default';
|
||||||
import { ApplyBehaviors } from './Behaviors/Behaviors';
|
import { ApplyBehaviors } from './Behaviors/Behaviors';
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a container
|
* Select a container
|
||||||
* @param container Selected container
|
* @param container Selected container
|
||||||
*/
|
*/
|
||||||
export function SelectContainer(
|
export function SelectContainer(
|
||||||
containerId: string,
|
container: ContainerModel,
|
||||||
fullHistory: IHistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||||
|
@ -24,13 +23,19 @@ export function SelectContainer(
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
const current = history[history.length - 1];
|
const current = history[history.length - 1];
|
||||||
|
|
||||||
|
const mainContainerClone = structuredClone(current.MainContainer);
|
||||||
|
const selectedContainer = findContainerById(mainContainerClone, container.properties.id);
|
||||||
|
|
||||||
|
if (selectedContainer === undefined) {
|
||||||
|
throw new Error('[SelectContainer] Cannot find container among children of main container!');
|
||||||
|
}
|
||||||
|
|
||||||
history.push({
|
history.push({
|
||||||
LastAction: `Select ${containerId}`,
|
LastAction: `Select ${selectedContainer.properties.id}`,
|
||||||
MainContainer: structuredClone(current.MainContainer),
|
MainContainer: mainContainerClone,
|
||||||
SelectedContainerId: containerId,
|
SelectedContainer: selectedContainer,
|
||||||
TypeCounters: Object.assign({}, current.TypeCounters),
|
SelectedContainerId: selectedContainer.properties.id,
|
||||||
Symbols: structuredClone(current.Symbols),
|
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||||
SelectedSymbolId: current.SelectedSymbolId
|
|
||||||
});
|
});
|
||||||
setHistory(history);
|
setHistory(history);
|
||||||
setHistoryCurrentStep(history.length - 1);
|
setHistoryCurrentStep(history.length - 1);
|
||||||
|
@ -71,8 +76,6 @@ export function DeleteContainer(
|
||||||
if (container === null || container === undefined) {
|
if (container === null || container === undefined) {
|
||||||
throw new Error('[DeleteContainer] Container model was not found among children of the main container!');
|
throw new Error('[DeleteContainer] Container model was not found among children of the main container!');
|
||||||
}
|
}
|
||||||
const newSymbols = structuredClone(current.Symbols)
|
|
||||||
UnlinkSymbol(newSymbols, container);
|
|
||||||
|
|
||||||
const index = container.parent.children.indexOf(container);
|
const index = container.parent.children.indexOf(container);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
|
@ -91,26 +94,14 @@ export function DeleteContainer(
|
||||||
history.push({
|
history.push({
|
||||||
LastAction: `Delete ${containerId}`,
|
LastAction: `Delete ${containerId}`,
|
||||||
MainContainer: mainContainerClone,
|
MainContainer: mainContainerClone,
|
||||||
|
SelectedContainer,
|
||||||
SelectedContainerId,
|
SelectedContainerId,
|
||||||
TypeCounters: Object.assign({}, current.TypeCounters),
|
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||||
Symbols: newSymbols,
|
|
||||||
SelectedSymbolId: current.SelectedSymbolId
|
|
||||||
});
|
});
|
||||||
setHistory(history);
|
setHistory(history);
|
||||||
setHistoryCurrentStep(history.length - 1);
|
setHistoryCurrentStep(history.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function UnlinkSymbol(symbols: Map<string, ISymbolModel>, container: IContainerModel): void {
|
|
||||||
const it = MakeIterator(container);
|
|
||||||
for (const child of it) {
|
|
||||||
const symbol = symbols.get(child.properties.linkedSymbolId);
|
|
||||||
if (symbol === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
symbol.linkedContainers.delete(child.properties.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new container to a selected container
|
* Add a new container to a selected container
|
||||||
* @param type The type of container
|
* @param type The type of container
|
||||||
|
@ -123,19 +114,21 @@ function UnlinkSymbol(symbols: Map<string, ISymbolModel>, container: IContainerM
|
||||||
*/
|
*/
|
||||||
export function AddContainerToSelectedContainer(
|
export function AddContainerToSelectedContainer(
|
||||||
type: string,
|
type: string,
|
||||||
selected: IContainerModel | undefined,
|
|
||||||
configuration: IConfiguration,
|
configuration: IConfiguration,
|
||||||
fullHistory: IHistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
if (selected === null ||
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
selected === undefined) {
|
const current = history[history.length - 1];
|
||||||
|
|
||||||
|
if (current.SelectedContainer === null ||
|
||||||
|
current.SelectedContainer === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parent = selected;
|
const parent = current.SelectedContainer;
|
||||||
AddContainer(
|
AddContainer(
|
||||||
parent.children.length,
|
parent.children.length,
|
||||||
type,
|
type,
|
||||||
|
@ -173,6 +166,11 @@ export function AddContainer(
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
const current = history[history.length - 1];
|
const current = history[history.length - 1];
|
||||||
|
|
||||||
|
if (current.MainContainer === null ||
|
||||||
|
current.MainContainer === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the preset properties from the API
|
// Get the preset properties from the API
|
||||||
const containerConfig = configuration.AvailableContainers
|
const containerConfig = configuration.AvailableContainers
|
||||||
.find(option => option.Type === type);
|
.find(option => option.Type === type);
|
||||||
|
@ -222,7 +220,7 @@ export function AddContainer(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ApplyBehaviors(newContainer, current.Symbols);
|
ApplyBehaviors(newContainer);
|
||||||
|
|
||||||
// And push it the the parent children
|
// And push it the the parent children
|
||||||
if (index === parentClone.children.length) {
|
if (index === parentClone.children.length) {
|
||||||
|
@ -237,15 +235,23 @@ export function AddContainer(
|
||||||
history.push({
|
history.push({
|
||||||
LastAction: `Add ${newContainer.properties.id} in ${parentClone.properties.id}`,
|
LastAction: `Add ${newContainer.properties.id} in ${parentClone.properties.id}`,
|
||||||
MainContainer: clone,
|
MainContainer: clone,
|
||||||
|
SelectedContainer: parentClone,
|
||||||
SelectedContainerId: parentClone.properties.id,
|
SelectedContainerId: parentClone.properties.id,
|
||||||
TypeCounters: newCounters,
|
TypeCounters: newCounters
|
||||||
Symbols: structuredClone(current.Symbols),
|
|
||||||
SelectedSymbolId: current.SelectedSymbolId
|
|
||||||
});
|
});
|
||||||
setHistory(history);
|
setHistory(history);
|
||||||
setHistoryCurrentStep(history.length - 1);
|
setHistoryCurrentStep(history.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function UpdateCounters(counters: Record<string, number>, type: string): void {
|
||||||
|
if (counters[type] === null ||
|
||||||
|
counters[type] === undefined) {
|
||||||
|
counters[type] = 0;
|
||||||
|
} else {
|
||||||
|
counters[type]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function InitializeDefaultChild(
|
function InitializeDefaultChild(
|
||||||
configuration: IConfiguration,
|
configuration: IConfiguration,
|
||||||
containerConfig: IAvailableContainer,
|
containerConfig: IAvailableContainer,
|
||||||
|
|
|
@ -11,8 +11,6 @@ import { OnPropertyChange, OnPropertiesSubmit } from './PropertiesOperations';
|
||||||
import EditorEvents from '../../Events/EditorEvents';
|
import EditorEvents from '../../Events/EditorEvents';
|
||||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||||
import { MAX_HISTORY } from '../../utils/default';
|
import { MAX_HISTORY } from '../../utils/default';
|
||||||
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './SymbolOperations';
|
|
||||||
import { findContainerById } from '../../utils/itertools';
|
|
||||||
|
|
||||||
interface IEditorProps {
|
interface IEditorProps {
|
||||||
configuration: IConfiguration
|
configuration: IConfiguration
|
||||||
|
@ -20,15 +18,6 @@ interface IEditorProps {
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateCounters(counters: Record<string, number>, type: string): void {
|
|
||||||
if (counters[type] === null ||
|
|
||||||
counters[type] === undefined) {
|
|
||||||
counters[type] = 0;
|
|
||||||
} else {
|
|
||||||
counters[type]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getCurrentHistory = (history: IHistoryState[], historyCurrentStep: number): IHistoryState[] =>
|
export const getCurrentHistory = (history: IHistoryState[], historyCurrentStep: number): IHistoryState[] =>
|
||||||
history.slice(
|
history.slice(
|
||||||
Math.max(0, history.length - MAX_HISTORY), // change this to 0 for unlimited (not recommanded because of overflow)
|
Math.max(0, history.length - MAX_HISTORY), // change this to 0 for unlimited (not recommanded because of overflow)
|
||||||
|
@ -81,16 +70,13 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
|
|
||||||
const configuration = props.configuration;
|
const configuration = props.configuration;
|
||||||
const current = getCurrentHistoryState(history, historyCurrentStep);
|
const current = getCurrentHistoryState(history, historyCurrentStep);
|
||||||
const selected = findContainerById(current.MainContainer, current.SelectedContainerId);
|
|
||||||
return (
|
return (
|
||||||
<div ref={editorRef} className="Editor font-sans h-full">
|
<div ref={editorRef} className="Editor font-sans h-full">
|
||||||
<UI
|
<UI
|
||||||
SelectedContainer={selected}
|
|
||||||
current={current}
|
current={current}
|
||||||
history={history}
|
history={history}
|
||||||
historyCurrentStep={historyCurrentStep}
|
historyCurrentStep={historyCurrentStep}
|
||||||
AvailableContainers={configuration.AvailableContainers}
|
AvailableContainers={configuration.AvailableContainers}
|
||||||
AvailableSymbols={configuration.AvailableSymbols}
|
|
||||||
SelectContainer={(container) => SelectContainer(
|
SelectContainer={(container) => SelectContainer(
|
||||||
container,
|
container,
|
||||||
history,
|
history,
|
||||||
|
@ -107,7 +93,6 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
)}
|
)}
|
||||||
OnPropertyChange={(key, value, isStyle) => OnPropertyChange(
|
OnPropertyChange={(key, value, isStyle) => OnPropertyChange(
|
||||||
key, value, isStyle,
|
key, value, isStyle,
|
||||||
selected,
|
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
setHistory,
|
setHistory,
|
||||||
|
@ -115,7 +100,6 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
)}
|
)}
|
||||||
OnPropertiesSubmit={(event) => OnPropertiesSubmit(
|
OnPropertiesSubmit={(event) => OnPropertiesSubmit(
|
||||||
event,
|
event,
|
||||||
selected,
|
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
setHistory,
|
setHistory,
|
||||||
|
@ -123,7 +107,6 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
)}
|
)}
|
||||||
AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer(
|
AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer(
|
||||||
type,
|
type,
|
||||||
selected,
|
|
||||||
configuration,
|
configuration,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
|
@ -140,35 +123,6 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
setHistory,
|
setHistory,
|
||||||
setHistoryCurrentStep
|
setHistoryCurrentStep
|
||||||
)}
|
)}
|
||||||
AddSymbol={(type) => AddSymbol(
|
|
||||||
type,
|
|
||||||
configuration,
|
|
||||||
history,
|
|
||||||
historyCurrentStep,
|
|
||||||
setHistory,
|
|
||||||
setHistoryCurrentStep
|
|
||||||
)}
|
|
||||||
OnSymbolPropertyChange={(key, value) => OnSymbolPropertyChange(
|
|
||||||
key, value,
|
|
||||||
history,
|
|
||||||
historyCurrentStep,
|
|
||||||
setHistory,
|
|
||||||
setHistoryCurrentStep
|
|
||||||
)}
|
|
||||||
SelectSymbol={(symbolId) => SelectSymbol(
|
|
||||||
symbolId,
|
|
||||||
history,
|
|
||||||
historyCurrentStep,
|
|
||||||
setHistory,
|
|
||||||
setHistoryCurrentStep
|
|
||||||
)}
|
|
||||||
DeleteSymbol={(symbolId) => DeleteSymbol(
|
|
||||||
symbolId,
|
|
||||||
history,
|
|
||||||
historyCurrentStep,
|
|
||||||
setHistory,
|
|
||||||
setHistoryCurrentStep
|
|
||||||
)}
|
|
||||||
SaveEditorAsJSON={() => SaveEditorAsJSON(
|
SaveEditorAsJSON={() => SaveEditorAsJSON(
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
|
@ -180,8 +134,7 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
<SVG
|
<SVG
|
||||||
width={current.MainContainer?.properties.width}
|
width={current.MainContainer?.properties.width}
|
||||||
height={current.MainContainer?.properties.height}
|
height={current.MainContainer?.properties.height}
|
||||||
selected={selected}
|
selected={current.SelectedContainer}
|
||||||
symbols={current.Symbols}
|
|
||||||
>
|
>
|
||||||
{ current.MainContainer }
|
{ current.MainContainer }
|
||||||
</SVG>
|
</SVG>
|
||||||
|
|
|
@ -3,9 +3,8 @@ import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerMode
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { findContainerById } from '../../utils/itertools';
|
import { findContainerById } from '../../utils/itertools';
|
||||||
import { getCurrentHistory } from './Editor';
|
import { getCurrentHistory } from './Editor';
|
||||||
|
import { restoreX } from '../SVG/Elements/Container';
|
||||||
import { ApplyBehaviors } from './Behaviors/Behaviors';
|
import { ApplyBehaviors } from './Behaviors/Behaviors';
|
||||||
import { restoreX } from '../../utils/svg';
|
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handled the property change event in the properties form
|
* Handled the property change event in the properties form
|
||||||
|
@ -17,7 +16,6 @@ export function OnPropertyChange(
|
||||||
key: string,
|
key: string,
|
||||||
value: string | number | boolean,
|
value: string | number | boolean,
|
||||||
isStyle: boolean = false,
|
isStyle: boolean = false,
|
||||||
selected: IContainerModel | undefined,
|
|
||||||
fullHistory: IHistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||||
|
@ -26,66 +24,37 @@ export function OnPropertyChange(
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
const current = history[history.length - 1];
|
const current = history[history.length - 1];
|
||||||
|
|
||||||
if (selected === null ||
|
if (current.SelectedContainer === null ||
|
||||||
selected === undefined) {
|
current.SelectedContainer === undefined) {
|
||||||
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
||||||
const container: ContainerModel | undefined = findContainerById(mainContainerClone, selected.properties.id);
|
const container: ContainerModel | undefined = findContainerById(mainContainerClone, current.SelectedContainer.properties.id);
|
||||||
|
|
||||||
if (container === null || container === undefined) {
|
if (container === null || container === undefined) {
|
||||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldSymbolId = container.properties.linkedSymbolId;
|
|
||||||
|
|
||||||
if (isStyle) {
|
if (isStyle) {
|
||||||
(container.properties.style as any)[key] = value;
|
(container.properties.style as any)[key] = value;
|
||||||
} else {
|
} else {
|
||||||
(container.properties as any)[key] = value;
|
(container.properties as any)[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkSymbol(
|
ApplyBehaviors(container);
|
||||||
container.properties.id,
|
|
||||||
oldSymbolId,
|
|
||||||
container.properties.linkedSymbolId,
|
|
||||||
current.Symbols
|
|
||||||
);
|
|
||||||
|
|
||||||
ApplyBehaviors(container, current.Symbols);
|
|
||||||
|
|
||||||
history.push({
|
history.push({
|
||||||
LastAction: `Change ${key} of ${container.properties.id}`,
|
LastAction: `Change ${key} of ${container.properties.id}`,
|
||||||
MainContainer: mainContainerClone,
|
MainContainer: mainContainerClone,
|
||||||
|
SelectedContainer: container,
|
||||||
SelectedContainerId: container.properties.id,
|
SelectedContainerId: container.properties.id,
|
||||||
TypeCounters: Object.assign({}, current.TypeCounters),
|
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||||
Symbols: structuredClone(current.Symbols),
|
|
||||||
SelectedSymbolId: current.SelectedSymbolId
|
|
||||||
});
|
});
|
||||||
setHistory(history);
|
setHistory(history);
|
||||||
setHistoryCurrentStep(history.length - 1);
|
setHistoryCurrentStep(history.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function LinkSymbol(
|
|
||||||
containerId: string,
|
|
||||||
oldSymbolId: string,
|
|
||||||
newSymbolId: string,
|
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
): void {
|
|
||||||
const oldSymbol = symbols.get(oldSymbolId);
|
|
||||||
const newSymbol = symbols.get(newSymbolId);
|
|
||||||
|
|
||||||
if (newSymbol === undefined) {
|
|
||||||
if (oldSymbol !== undefined) {
|
|
||||||
oldSymbol.linkedContainers.delete(containerId);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
newSymbol.linkedContainers.add(containerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handled the property change event in the properties form
|
* Handled the property change event in the properties form
|
||||||
* @param key Property name
|
* @param key Property name
|
||||||
|
@ -94,7 +63,6 @@ function LinkSymbol(
|
||||||
*/
|
*/
|
||||||
export function OnPropertiesSubmit(
|
export function OnPropertiesSubmit(
|
||||||
event: React.SyntheticEvent<HTMLFormElement>,
|
event: React.SyntheticEvent<HTMLFormElement>,
|
||||||
selected: IContainerModel | undefined,
|
|
||||||
fullHistory: IHistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||||
|
@ -104,13 +72,13 @@ export function OnPropertiesSubmit(
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
const current = history[history.length - 1];
|
const current = history[history.length - 1];
|
||||||
|
|
||||||
if (selected === null ||
|
if (current.SelectedContainer === null ||
|
||||||
selected === undefined) {
|
current.SelectedContainer === undefined) {
|
||||||
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
||||||
const container: ContainerModel | undefined = findContainerById(mainContainerClone, selected.properties.id);
|
const container: ContainerModel | undefined = findContainerById(mainContainerClone, current.SelectedContainer.properties.id);
|
||||||
|
|
||||||
if (container === null || container === undefined) {
|
if (container === null || container === undefined) {
|
||||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||||
|
@ -142,15 +110,14 @@ export function OnPropertiesSubmit(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the behaviors
|
// Apply the behaviors
|
||||||
ApplyBehaviors(container, current.Symbols);
|
ApplyBehaviors(container);
|
||||||
|
|
||||||
history.push({
|
history.push({
|
||||||
LastAction: `Change properties of ${container.properties.id}`,
|
LastAction: `Change properties of ${container.properties.id}`,
|
||||||
MainContainer: mainContainerClone,
|
MainContainer: mainContainerClone,
|
||||||
|
SelectedContainer: container,
|
||||||
SelectedContainerId: container.properties.id,
|
SelectedContainerId: container.properties.id,
|
||||||
TypeCounters: Object.assign({}, current.TypeCounters),
|
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||||
Symbols: structuredClone(current.Symbols),
|
|
||||||
SelectedSymbolId: current.SelectedSymbolId
|
|
||||||
});
|
});
|
||||||
setHistory(history);
|
setHistory(history);
|
||||||
setHistoryCurrentStep(history.length - 1);
|
setHistoryCurrentStep(history.length - 1);
|
||||||
|
@ -220,4 +187,3 @@ const submitRadioButtons = (
|
||||||
|
|
||||||
(container.properties as any)[property] = radiobutton.value;
|
(container.properties as any)[property] = radiobutton.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,180 +0,0 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
|
||||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
import { DEFAULT_SYMBOL_HEIGHT, DEFAULT_SYMBOL_WIDTH } from '../../utils/default';
|
|
||||||
import { findContainerById } from '../../utils/itertools';
|
|
||||||
import { restoreX } from '../../utils/svg';
|
|
||||||
import { ApplyBehaviors } from './Behaviors/Behaviors';
|
|
||||||
import { getCurrentHistory, UpdateCounters } from './Editor';
|
|
||||||
|
|
||||||
export function AddSymbol(
|
|
||||||
name: string,
|
|
||||||
configuration: IConfiguration,
|
|
||||||
fullHistory: IHistoryState[],
|
|
||||||
historyCurrentStep: number,
|
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
|
||||||
): void {
|
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
|
||||||
const current = history[history.length - 1];
|
|
||||||
|
|
||||||
const symbolConfig = configuration.AvailableSymbols
|
|
||||||
.find(option => option.Name === name);
|
|
||||||
|
|
||||||
if (symbolConfig === undefined) {
|
|
||||||
throw new Error('[AddSymbol] Symbol could not be found in the config');
|
|
||||||
}
|
|
||||||
const type = `symbol-${name}`;
|
|
||||||
const newCounters = structuredClone(current.TypeCounters);
|
|
||||||
UpdateCounters(newCounters, type);
|
|
||||||
|
|
||||||
const newSymbols = structuredClone(current.Symbols);
|
|
||||||
// TODO: Put this in default.ts as GetDefaultConfig
|
|
||||||
const newSymbol: ISymbolModel = {
|
|
||||||
id: `${name}-${newCounters[type]}`,
|
|
||||||
type: name,
|
|
||||||
config: structuredClone(symbolConfig),
|
|
||||||
x: 0,
|
|
||||||
width: symbolConfig.Width ?? DEFAULT_SYMBOL_WIDTH,
|
|
||||||
height: symbolConfig.Height ?? DEFAULT_SYMBOL_HEIGHT,
|
|
||||||
linkedContainers: new Set()
|
|
||||||
};
|
|
||||||
newSymbol.x = restoreX(newSymbol.x, newSymbol.width, newSymbol.config.XPositionReference);
|
|
||||||
|
|
||||||
newSymbols.set(newSymbol.id, newSymbol);
|
|
||||||
|
|
||||||
history.push({
|
|
||||||
LastAction: `Add ${name}`,
|
|
||||||
MainContainer: structuredClone(current.MainContainer),
|
|
||||||
SelectedContainerId: current.SelectedContainerId,
|
|
||||||
TypeCounters: newCounters,
|
|
||||||
Symbols: newSymbols,
|
|
||||||
SelectedSymbolId: newSymbol.id
|
|
||||||
});
|
|
||||||
setHistory(history);
|
|
||||||
setHistoryCurrentStep(history.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SelectSymbol(
|
|
||||||
symbolId: string,
|
|
||||||
fullHistory: IHistoryState[],
|
|
||||||
historyCurrentStep: number,
|
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
|
||||||
): void {
|
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
|
||||||
const current = history[history.length - 1];
|
|
||||||
|
|
||||||
history.push({
|
|
||||||
LastAction: `Select ${symbolId}`,
|
|
||||||
MainContainer: structuredClone(current.MainContainer),
|
|
||||||
SelectedContainerId: current.SelectedContainerId,
|
|
||||||
TypeCounters: structuredClone(current.TypeCounters),
|
|
||||||
Symbols: structuredClone(current.Symbols),
|
|
||||||
SelectedSymbolId: symbolId
|
|
||||||
});
|
|
||||||
setHistory(history);
|
|
||||||
setHistoryCurrentStep(history.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DeleteSymbol(
|
|
||||||
symbolId: string,
|
|
||||||
fullHistory: IHistoryState[],
|
|
||||||
historyCurrentStep: number,
|
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
|
||||||
): void {
|
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
|
||||||
const current = history[history.length - 1];
|
|
||||||
|
|
||||||
const newSymbols = structuredClone(current.Symbols);
|
|
||||||
const symbol = newSymbols.get(symbolId);
|
|
||||||
|
|
||||||
if (symbol === undefined) {
|
|
||||||
throw new Error(`[DeleteSymbol] Could not find symbol in the current state!: ${symbolId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newMainContainer = structuredClone(current.MainContainer);
|
|
||||||
|
|
||||||
UnlinkContainers(symbol, newMainContainer);
|
|
||||||
|
|
||||||
newSymbols.delete(symbolId);
|
|
||||||
|
|
||||||
history.push({
|
|
||||||
LastAction: `Select ${symbolId}`,
|
|
||||||
MainContainer: newMainContainer,
|
|
||||||
SelectedContainerId: current.SelectedContainerId,
|
|
||||||
TypeCounters: structuredClone(current.TypeCounters),
|
|
||||||
Symbols: newSymbols,
|
|
||||||
SelectedSymbolId: symbolId
|
|
||||||
});
|
|
||||||
setHistory(history);
|
|
||||||
setHistoryCurrentStep(history.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function UnlinkContainers(symbol: ISymbolModel, newMainContainer: IContainerModel) {
|
|
||||||
symbol.linkedContainers.forEach((containerId) => {
|
|
||||||
const container = findContainerById(newMainContainer, containerId);
|
|
||||||
|
|
||||||
if (container === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
container.properties.linkedSymbolId = '';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handled the property change event in the properties form
|
|
||||||
* @param key Property name
|
|
||||||
* @param value New value of the property
|
|
||||||
* @returns void
|
|
||||||
*/
|
|
||||||
export function OnPropertyChange(
|
|
||||||
key: string,
|
|
||||||
value: string | number | boolean,
|
|
||||||
fullHistory: IHistoryState[],
|
|
||||||
historyCurrentStep: number,
|
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
|
||||||
): void {
|
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
|
||||||
const current = history[history.length - 1];
|
|
||||||
|
|
||||||
if (current.SelectedSymbolId === '') {
|
|
||||||
throw new Error('[OnSymbolPropertyChange] Property was changed before selecting a symbol');
|
|
||||||
}
|
|
||||||
|
|
||||||
const newSymbols: Map<string, ISymbolModel> = structuredClone(current.Symbols);
|
|
||||||
const symbol = newSymbols.get(current.SelectedSymbolId);
|
|
||||||
|
|
||||||
if (symbol === null || symbol === undefined) {
|
|
||||||
throw new Error('[OnSymbolPropertyChange] Symbol model was not found in state!');
|
|
||||||
}
|
|
||||||
|
|
||||||
(symbol as any)[key] = value;
|
|
||||||
|
|
||||||
const newMainContainer = structuredClone(current.MainContainer);
|
|
||||||
symbol.linkedContainers.forEach((containerId) => {
|
|
||||||
const container = findContainerById(newMainContainer, containerId);
|
|
||||||
|
|
||||||
if (container === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ApplyBehaviors(container, newSymbols);
|
|
||||||
});
|
|
||||||
|
|
||||||
history.push({
|
|
||||||
LastAction: `Change ${key} of ${symbol.id}`,
|
|
||||||
MainContainer: newMainContainer,
|
|
||||||
SelectedContainerId: current.SelectedContainerId,
|
|
||||||
TypeCounters: Object.assign({}, current.TypeCounters),
|
|
||||||
Symbols: newSymbols,
|
|
||||||
SelectedSymbolId: symbol.id
|
|
||||||
});
|
|
||||||
setHistory(history);
|
|
||||||
setHistoryCurrentStep(history.length - 1);
|
|
||||||
}
|
|
|
@ -4,19 +4,16 @@ import { fireEvent, render, screen } from '../../utils/test-utils';
|
||||||
import { ElementsSidebar } from './ElementsSidebar';
|
import { ElementsSidebar } from './ElementsSidebar';
|
||||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { XPositionReference } from '../../Enums/XPositionReference';
|
import { XPositionReference } from '../../Enums/XPositionReference';
|
||||||
import { findContainerById } from '../../utils/itertools';
|
|
||||||
|
|
||||||
describe.concurrent('Elements sidebar', () => {
|
describe.concurrent('Elements sidebar', () => {
|
||||||
it('With a MainContainer', () => {
|
it('With a MainContainer', () => {
|
||||||
render(<ElementsSidebar
|
render(<ElementsSidebar
|
||||||
symbols={new Map()}
|
|
||||||
MainContainer={{
|
MainContainer={{
|
||||||
children: [],
|
children: [],
|
||||||
parent: null,
|
parent: null,
|
||||||
properties: {
|
properties: {
|
||||||
id: 'main',
|
id: 'main',
|
||||||
parentId: null,
|
parentId: null,
|
||||||
linkedSymbolId: '',
|
|
||||||
displayedText: 'main',
|
displayedText: 'main',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -31,7 +28,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
}}
|
}}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={undefined}
|
SelectedContainer={null}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
OnPropertiesSubmit={() => {}}
|
OnPropertiesSubmit={() => {}}
|
||||||
SelectContainer={() => {}}
|
SelectContainer={() => {}}
|
||||||
|
@ -45,13 +42,12 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('With a selected MainContainer', () => {
|
it('With a selected MainContainer', () => {
|
||||||
const MainContainer: IContainerModel = {
|
const MainContainer = {
|
||||||
children: [],
|
children: [],
|
||||||
parent: null,
|
parent: null,
|
||||||
properties: {
|
properties: {
|
||||||
id: 'main',
|
id: 'main',
|
||||||
parentId: '',
|
parentId: '',
|
||||||
linkedSymbolId: '',
|
|
||||||
displayedText: 'main',
|
displayedText: 'main',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -66,7 +62,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const { container } = render(<ElementsSidebar
|
const { container } = render(<ElementsSidebar
|
||||||
symbols={new Map()}
|
|
||||||
MainContainer={MainContainer}
|
MainContainer={MainContainer}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
|
@ -107,13 +102,12 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
|
|
||||||
it('With multiple containers', () => {
|
it('With multiple containers', () => {
|
||||||
const children: IContainerModel[] = [];
|
const children: IContainerModel[] = [];
|
||||||
const MainContainer: IContainerModel = {
|
const MainContainer = {
|
||||||
children,
|
children,
|
||||||
parent: null,
|
parent: null,
|
||||||
properties: {
|
properties: {
|
||||||
id: 'main',
|
id: 'main',
|
||||||
parentId: '',
|
parentId: '',
|
||||||
linkedSymbolId: '',
|
|
||||||
displayedText: 'main',
|
displayedText: 'main',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -134,7 +128,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
properties: {
|
properties: {
|
||||||
id: 'child-1',
|
id: 'child-1',
|
||||||
parentId: 'main',
|
parentId: 'main',
|
||||||
linkedSymbolId: '',
|
|
||||||
displayedText: 'child-1',
|
displayedText: 'child-1',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -156,7 +149,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
properties: {
|
properties: {
|
||||||
id: 'child-2',
|
id: 'child-2',
|
||||||
parentId: 'main',
|
parentId: 'main',
|
||||||
linkedSymbolId: '',
|
|
||||||
displayedText: 'child-2',
|
displayedText: 'child-2',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -172,7 +164,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
render(<ElementsSidebar
|
render(<ElementsSidebar
|
||||||
symbols={new Map()}
|
|
||||||
MainContainer={MainContainer}
|
MainContainer={MainContainer}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
|
@ -199,7 +190,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
properties: {
|
properties: {
|
||||||
id: 'main',
|
id: 'main',
|
||||||
parentId: '',
|
parentId: '',
|
||||||
linkedSymbolId: '',
|
|
||||||
displayedText: 'main',
|
displayedText: 'main',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -219,7 +209,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
properties: {
|
properties: {
|
||||||
id: 'child-1',
|
id: 'child-1',
|
||||||
parentId: 'main',
|
parentId: 'main',
|
||||||
linkedSymbolId: '',
|
|
||||||
displayedText: 'child-1',
|
displayedText: 'child-1',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -234,13 +223,12 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
};
|
};
|
||||||
children.push(child1Model);
|
children.push(child1Model);
|
||||||
|
|
||||||
let SelectedContainer: IContainerModel | undefined = MainContainer;
|
let SelectedContainer = MainContainer;
|
||||||
const selectContainer = vi.fn((containerId: string) => {
|
const selectContainer = vi.fn((container: IContainerModel) => {
|
||||||
SelectedContainer = findContainerById(MainContainer, containerId);
|
SelectedContainer = container;
|
||||||
});
|
});
|
||||||
|
|
||||||
const { container, rerender } = render(<ElementsSidebar
|
const { container, rerender } = render(<ElementsSidebar
|
||||||
symbols={new Map()}
|
|
||||||
MainContainer={MainContainer}
|
MainContainer={MainContainer}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
|
@ -265,7 +253,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
fireEvent.click(child1);
|
fireEvent.click(child1);
|
||||||
|
|
||||||
rerender(<ElementsSidebar
|
rerender(<ElementsSidebar
|
||||||
symbols={new Map()}
|
|
||||||
MainContainer={MainContainer}
|
MainContainer={MainContainer}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
|
|
|
@ -7,17 +7,15 @@ import { Menu } from '../Menu/Menu';
|
||||||
import { MenuItem } from '../Menu/MenuItem';
|
import { MenuItem } from '../Menu/MenuItem';
|
||||||
import { handleDragLeave, handleDragOver, handleLeftClick, handleOnDrop, handleRightClick } from './MouseEventHandlers';
|
import { handleDragLeave, handleDragOver, handleLeftClick, handleOnDrop, handleRightClick } from './MouseEventHandlers';
|
||||||
import { IPoint } from '../../Interfaces/IPoint';
|
import { IPoint } from '../../Interfaces/IPoint';
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
|
|
||||||
interface IElementsSidebarProps {
|
interface IElementsSidebarProps {
|
||||||
MainContainer: IContainerModel
|
MainContainer: IContainerModel
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
isHistoryOpen: boolean
|
isHistoryOpen: boolean
|
||||||
SelectedContainer: IContainerModel | undefined
|
SelectedContainer: IContainerModel | null
|
||||||
OnPropertyChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
OnPropertyChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
||||||
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
||||||
SelectContainer: (containerId: string) => void
|
SelectContainer: (container: IContainerModel) => void
|
||||||
DeleteContainer: (containerid: string) => void
|
DeleteContainer: (containerid: string) => void
|
||||||
AddContainer: (index: number, type: string, parent: string) => void
|
AddContainer: (index: number, type: string, parent: string) => void
|
||||||
}
|
}
|
||||||
|
@ -106,7 +104,7 @@ export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElement
|
||||||
onDrop={(event) => handleOnDrop(event, props.MainContainer, props.AddContainer)}
|
onDrop={(event) => handleOnDrop(event, props.MainContainer, props.AddContainer)}
|
||||||
onDragOver={(event) => handleDragOver(event, props.MainContainer)}
|
onDragOver={(event) => handleDragOver(event, props.MainContainer)}
|
||||||
onDragLeave={(event) => handleDragLeave(event)}
|
onDragLeave={(event) => handleDragLeave(event)}
|
||||||
onClick={() => props.SelectContainer(container.properties.id)}
|
onClick={() => props.SelectContainer(container)}
|
||||||
>
|
>
|
||||||
{ text }
|
{ text }
|
||||||
</button>
|
</button>
|
||||||
|
@ -142,7 +140,6 @@ export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElement
|
||||||
</Menu>
|
</Menu>
|
||||||
<Properties
|
<Properties
|
||||||
properties={props.SelectedContainer?.properties}
|
properties={props.SelectedContainer?.properties}
|
||||||
symbols={props.symbols}
|
|
||||||
onChange={props.OnPropertyChange}
|
onChange={props.OnPropertyChange}
|
||||||
onSubmit={props.OnPropertiesSubmit}
|
onSubmit={props.OnPropertiesSubmit}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
import { MenuAlt2Icon, MenuAlt3Icon, MenuIcon } from '@heroicons/react/outline';
|
import { MenuAlt2Icon, MenuAlt3Icon, MenuIcon } from '@heroicons/react/outline';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { XPositionReference } from '../../Enums/XPositionReference';
|
import { XPositionReference } from '../../Enums/XPositionReference';
|
||||||
import IContainerProperties from '../../Interfaces/IContainerProperties';
|
import IProperties from '../../Interfaces/IProperties';
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
import { restoreX, transformX } from '../../utils/svg';
|
|
||||||
import { InputGroup } from '../InputGroup/InputGroup';
|
import { InputGroup } from '../InputGroup/InputGroup';
|
||||||
import { RadioGroupButtons } from '../RadioGroupButtons/RadioGroupButtons';
|
import { RadioGroupButtons } from '../RadioGroupButtons/RadioGroupButtons';
|
||||||
import { Select } from '../Select/Select';
|
import { restoreX, transformX } from '../SVG/Elements/Container';
|
||||||
|
|
||||||
interface IDynamicFormProps {
|
interface IDynamicFormProps {
|
||||||
properties: IContainerProperties
|
properties: IProperties
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCSSInputs = (
|
const getCSSInputs = (
|
||||||
properties: IContainerProperties,
|
properties: IProperties,
|
||||||
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
||||||
): JSX.Element[] => {
|
): JSX.Element[] => {
|
||||||
const groupInput: JSX.Element[] = [];
|
const groupInput: JSX.Element[] = [];
|
||||||
|
@ -70,7 +67,6 @@ const DynamicForm: React.FunctionComponent<IDynamicFormProps> = (props) => {
|
||||||
labelClassName=''
|
labelClassName=''
|
||||||
inputClassName=''
|
inputClassName=''
|
||||||
type='number'
|
type='number'
|
||||||
isDisabled={props.properties.linkedSymbolId !== ''}
|
|
||||||
value={transformX(props.properties.x, props.properties.width, props.properties.XPositionReference).toString()}
|
value={transformX(props.properties.x, props.properties.width, props.properties.XPositionReference).toString()}
|
||||||
onChange={(event) => props.onChange('x', restoreX(Number(event.target.value), props.properties.width, props.properties.XPositionReference))}
|
onChange={(event) => props.onChange('x', restoreX(Number(event.target.value), props.properties.width, props.properties.XPositionReference))}
|
||||||
/>
|
/>
|
||||||
|
@ -164,22 +160,9 @@ const DynamicForm: React.FunctionComponent<IDynamicFormProps> = (props) => {
|
||||||
]}
|
]}
|
||||||
onChange={(event) => props.onChange('XPositionReference', Number(event.target.value))}
|
onChange={(event) => props.onChange('XPositionReference', Number(event.target.value))}
|
||||||
/>
|
/>
|
||||||
<Select
|
|
||||||
inputKey='linkedSymbolId'
|
|
||||||
labelText='Align with symbol'
|
|
||||||
labelClassName=''
|
|
||||||
inputClassName=''
|
|
||||||
inputs={[...props.symbols.values()].map(symbol => ({
|
|
||||||
text: symbol.id,
|
|
||||||
value: symbol.id
|
|
||||||
}))}
|
|
||||||
value={props.properties.linkedSymbolId ?? ''}
|
|
||||||
onChange={(event) => props.onChange('linkedSymbolId', event.target.value)}
|
|
||||||
/>
|
|
||||||
{ getCSSInputs(props.properties, props.onChange) }
|
{ getCSSInputs(props.properties, props.onChange) }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default DynamicForm;
|
export default DynamicForm;
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import IContainerProperties from '../../Interfaces/IContainerProperties';
|
import IProperties from '../../Interfaces/IProperties';
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
import DynamicForm from './DynamicForm';
|
import DynamicForm from './DynamicForm';
|
||||||
import StaticForm from './StaticForm';
|
import StaticForm from './StaticForm';
|
||||||
|
|
||||||
interface IFormProps {
|
interface IFormProps {
|
||||||
properties: IContainerProperties
|
properties: IProperties
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
isDynamicInput: boolean
|
isDynamicInput: boolean
|
||||||
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
||||||
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
||||||
|
@ -16,7 +14,6 @@ export const Form: React.FunctionComponent<IFormProps> = (props) => {
|
||||||
if (props.isDynamicInput) {
|
if (props.isDynamicInput) {
|
||||||
return <DynamicForm
|
return <DynamicForm
|
||||||
properties={props.properties}
|
properties={props.properties}
|
||||||
symbols={props.symbols}
|
|
||||||
onChange={props.onChange}
|
onChange={props.onChange}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { expect, describe, it, vi } from 'vitest';
|
import { expect, describe, it, vi } from 'vitest';
|
||||||
import { XPositionReference } from '../../Enums/XPositionReference';
|
import { XPositionReference } from '../../Enums/XPositionReference';
|
||||||
import IContainerProperties from '../../Interfaces/IContainerProperties';
|
import IProperties from '../../Interfaces/IProperties';
|
||||||
import { Properties } from './Properties';
|
import { Properties } from './Properties';
|
||||||
|
|
||||||
describe.concurrent('Properties', () => {
|
describe.concurrent('Properties', () => {
|
||||||
|
@ -11,7 +11,6 @@ describe.concurrent('Properties', () => {
|
||||||
properties={undefined}
|
properties={undefined}
|
||||||
onChange={() => {}}
|
onChange={() => {}}
|
||||||
onSubmit={() => {}}
|
onSubmit={() => {}}
|
||||||
symbols={new Map()}
|
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.queryByText('id')).toBeNull();
|
expect(screen.queryByText('id')).toBeNull();
|
||||||
|
@ -21,10 +20,9 @@ describe.concurrent('Properties', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Some properties, change values with dynamic input', () => {
|
it('Some properties, change values with dynamic input', () => {
|
||||||
const prop: IContainerProperties = {
|
const prop: IProperties = {
|
||||||
id: 'stuff',
|
id: 'stuff',
|
||||||
parentId: 'parentId',
|
parentId: 'parentId',
|
||||||
linkedSymbolId: '',
|
|
||||||
displayedText: 'stuff',
|
displayedText: 'stuff',
|
||||||
x: 1,
|
x: 1,
|
||||||
y: 1,
|
y: 1,
|
||||||
|
@ -44,7 +42,6 @@ describe.concurrent('Properties', () => {
|
||||||
properties={prop}
|
properties={prop}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onSubmit={() => {}}
|
onSubmit={() => {}}
|
||||||
symbols={new Map()}
|
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.queryByText('id')).toBeDefined();
|
expect(screen.queryByText('id')).toBeDefined();
|
||||||
|
@ -79,7 +76,6 @@ describe.concurrent('Properties', () => {
|
||||||
properties={Object.assign({}, prop)}
|
properties={Object.assign({}, prop)}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onSubmit={() => {}}
|
onSubmit={() => {}}
|
||||||
symbols={new Map()}
|
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
propertyId = container.querySelector('#id');
|
propertyId = container.querySelector('#id');
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import IContainerProperties from '../../Interfaces/IContainerProperties';
|
import IProperties from '../../Interfaces/IProperties';
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
import { ToggleButton } from '../ToggleButton/ToggleButton';
|
import { ToggleButton } from '../ToggleButton/ToggleButton';
|
||||||
import { Form } from './Form';
|
import { Form } from './Form';
|
||||||
|
|
||||||
interface IPropertiesProps {
|
interface IPropertiesProps {
|
||||||
properties?: IContainerProperties
|
properties?: IProperties
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
||||||
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
||||||
}
|
}
|
||||||
|
@ -29,7 +27,6 @@ export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps)
|
||||||
/>
|
/>
|
||||||
<Form
|
<Form
|
||||||
properties={props.properties}
|
properties={props.properties}
|
||||||
symbols={props.symbols}
|
|
||||||
isDynamicInput={isDynamicInput}
|
isDynamicInput={isDynamicInput}
|
||||||
onChange={props.onChange}
|
onChange={props.onChange}
|
||||||
onSubmit={props.onSubmit}
|
onSubmit={props.onSubmit}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { MenuAlt2Icon, MenuIcon, MenuAlt3Icon } from '@heroicons/react/outline';
|
import { MenuAlt2Icon, MenuIcon, MenuAlt3Icon } from '@heroicons/react/outline';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { XPositionReference } from '../../Enums/XPositionReference';
|
import { XPositionReference } from '../../Enums/XPositionReference';
|
||||||
import IContainerProperties from '../../Interfaces/IContainerProperties';
|
import IProperties from '../../Interfaces/IProperties';
|
||||||
import { transformX } from '../../utils/svg';
|
|
||||||
import { InputGroup } from '../InputGroup/InputGroup';
|
import { InputGroup } from '../InputGroup/InputGroup';
|
||||||
import { RadioGroupButtons } from '../RadioGroupButtons/RadioGroupButtons';
|
import { RadioGroupButtons } from '../RadioGroupButtons/RadioGroupButtons';
|
||||||
|
import { transformX } from '../SVG/Elements/Container';
|
||||||
|
|
||||||
interface IStaticFormProps {
|
interface IStaticFormProps {
|
||||||
properties: IContainerProperties
|
properties: IProperties
|
||||||
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCSSInputs = (properties: IContainerProperties): JSX.Element[] => {
|
const getCSSInputs = (properties: IProperties): JSX.Element[] => {
|
||||||
const groupInput: JSX.Element[] = [];
|
const groupInput: JSX.Element[] = [];
|
||||||
for (const key in properties.style) {
|
for (const key in properties.style) {
|
||||||
groupInput.push(<InputGroup
|
groupInput.push(<InputGroup
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Interweave, Node } from 'interweave';
|
import { Interweave, Node } from 'interweave';
|
||||||
|
import { XPositionReference } from '../../../Enums/XPositionReference';
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { DIMENSION_MARGIN, SHOW_CHILDREN_DIMENSIONS, SHOW_PARENT_DIMENSION, SHOW_TEXT } from '../../../utils/default';
|
import { DIMENSION_MARGIN, SHOW_CHILDREN_DIMENSIONS, SHOW_PARENT_DIMENSION, SHOW_TEXT } from '../../../utils/default';
|
||||||
import { getDepth } from '../../../utils/itertools';
|
import { getDepth } from '../../../utils/itertools';
|
||||||
import { Dimension } from './Dimension';
|
import { Dimension } from './Dimension';
|
||||||
import IContainerProperties from '../../../Interfaces/IContainerProperties';
|
import IProperties from '../../../Interfaces/IProperties';
|
||||||
import { transformX } from '../../../utils/svg';
|
|
||||||
import { camelize } from '../../../utils/stringtools';
|
|
||||||
|
|
||||||
interface IContainerProps {
|
interface IContainerProps {
|
||||||
model: IContainerModel
|
model: IContainerModel
|
||||||
|
@ -46,7 +45,7 @@ export const Container: React.FC<IContainerProps> = (props: IContainerProps) =>
|
||||||
</rect>);
|
</rect>);
|
||||||
// Dimension props
|
// Dimension props
|
||||||
const depth = getDepth(props.model);
|
const depth = getDepth(props.model);
|
||||||
const dimensionMargin = DIMENSION_MARGIN * depth;
|
const dimensionMargin = DIMENSION_MARGIN * (depth + 1);
|
||||||
const id = `dim-${props.model.properties.id}`;
|
const id = `dim-${props.model.properties.id}`;
|
||||||
const xStart: number = 0;
|
const xStart: number = 0;
|
||||||
const xEnd = props.model.properties.width;
|
const xEnd = props.model.properties.width;
|
||||||
|
@ -133,7 +132,27 @@ function GetChildrenDimensionProps(props: IContainerProps, dimensionMargin: numb
|
||||||
return { childrenId, xChildrenStart, xChildrenEnd, yChildren, textChildren };
|
return { childrenId, xChildrenStart, xChildrenEnd, yChildren, textChildren };
|
||||||
}
|
}
|
||||||
|
|
||||||
function CreateReactCustomSVG(customSVG: string, props: IContainerProperties): React.ReactNode {
|
export function transformX(x: number, width: number, xPositionReference = XPositionReference.Left): number {
|
||||||
|
let transformedX = x;
|
||||||
|
if (xPositionReference === XPositionReference.Center) {
|
||||||
|
transformedX += width / 2;
|
||||||
|
} else if (xPositionReference === XPositionReference.Right) {
|
||||||
|
transformedX += width;
|
||||||
|
}
|
||||||
|
return transformedX;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restoreX(x: number, width: number, xPositionReference = XPositionReference.Left): number {
|
||||||
|
let transformedX = x;
|
||||||
|
if (xPositionReference === XPositionReference.Center) {
|
||||||
|
transformedX -= width / 2;
|
||||||
|
} else if (xPositionReference === XPositionReference.Right) {
|
||||||
|
transformedX -= width;
|
||||||
|
}
|
||||||
|
return transformedX;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CreateReactCustomSVG(customSVG: string, props: IProperties): React.ReactNode {
|
||||||
return <Interweave
|
return <Interweave
|
||||||
tagName='g'
|
tagName='g'
|
||||||
disableLineBreaks={true}
|
disableLineBreaks={true}
|
||||||
|
@ -143,7 +162,7 @@ function CreateReactCustomSVG(customSVG: string, props: IContainerProperties): R
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function transform(node: HTMLElement, children: Node[], props: IContainerProperties): React.ReactNode {
|
function transform(node: HTMLElement, children: Node[], props: IProperties): React.ReactNode {
|
||||||
const supportedTags = ['line', 'path', 'rect'];
|
const supportedTags = ['line', 'path', 'rect'];
|
||||||
if (supportedTags.includes(node.tagName.toLowerCase())) {
|
if (supportedTags.includes(node.tagName.toLowerCase())) {
|
||||||
const attributes: {[att: string]: string | object | null} = {};
|
const attributes: {[att: string]: string | object | null} = {};
|
||||||
|
@ -188,3 +207,7 @@ function transform(node: HTMLElement, children: Node[], props: IContainerPropert
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function camelize(str: string): any {
|
||||||
|
return str.split('-').map((word, index) => index > 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word).join('');
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
||||||
import { ContainerModel } from '../../../Interfaces/IContainerModel';
|
import { ContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { DIMENSION_MARGIN } from '../../../utils/default';
|
import { DIMENSION_MARGIN } from '../../../utils/default';
|
||||||
import { getAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools';
|
import { getAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools';
|
||||||
import { transformX } from '../../../utils/svg';
|
import { transformX } from './Container';
|
||||||
import { Dimension } from './Dimension';
|
import { Dimension } from './Dimension';
|
||||||
|
|
||||||
interface IDimensionLayerProps {
|
interface IDimensionLayerProps {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { getAbsolutePosition } from '../../../utils/itertools';
|
import { getAbsolutePosition } from '../../../utils/itertools';
|
||||||
|
|
||||||
interface ISelectorProps {
|
interface ISelectorProps {
|
||||||
selected?: IContainerModel
|
selected: IContainerModel | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Selector: React.FC<ISelectorProps> = (props) => {
|
export const Selector: React.FC<ISelectorProps> = (props) => {
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
import { Interweave } from 'interweave';
|
|
||||||
import * as React from 'react';
|
|
||||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
|
||||||
import { DIMENSION_MARGIN } from '../../../utils/default';
|
|
||||||
|
|
||||||
interface ISymbolProps {
|
|
||||||
model: ISymbolModel
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Symbol: React.FC<ISymbolProps> = (props) => {
|
|
||||||
const href = props.model.config.Image.Base64Image ?? props.model.config.Image.Url;
|
|
||||||
const hasSVG = props.model.config.Image.Svg !== undefined &&
|
|
||||||
props.model.config.Image.Svg !== null;
|
|
||||||
if (hasSVG) {
|
|
||||||
return (
|
|
||||||
<g
|
|
||||||
x={props.model.x}
|
|
||||||
y={-DIMENSION_MARGIN}
|
|
||||||
>
|
|
||||||
<Interweave
|
|
||||||
noWrap={true}
|
|
||||||
disableLineBreaks={true}
|
|
||||||
content={props.model.config.Image.Svg}
|
|
||||||
allowElements={true}
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<image
|
|
||||||
href={href}
|
|
||||||
x={props.model.x}
|
|
||||||
y={-DIMENSION_MARGIN}
|
|
||||||
height={props.model.height}
|
|
||||||
width={props.model.width}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,23 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
|
||||||
import { Symbol } from './Symbol';
|
|
||||||
|
|
||||||
interface ISymbolLayerProps {
|
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SymbolLayer: React.FC<ISymbolLayerProps> = (props) => {
|
|
||||||
const symbols: JSX.Element[] = [];
|
|
||||||
props.symbols.forEach((symbol) => {
|
|
||||||
symbols.push(
|
|
||||||
<Symbol key={`symbol-${symbol.id}`} model={symbol} />
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<g>
|
|
||||||
{
|
|
||||||
symbols
|
|
||||||
}
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -4,17 +4,15 @@ import { Container } from './Elements/Container';
|
||||||
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { Selector } from './Elements/Selector';
|
import { Selector } from './Elements/Selector';
|
||||||
import { BAR_WIDTH } from '../Bar/Bar';
|
import { BAR_WIDTH } from '../Bar/Bar';
|
||||||
|
import { DimensionLayer } from './Elements/DimensionLayer';
|
||||||
import { DepthDimensionLayer } from './Elements/DepthDimensionLayer';
|
import { DepthDimensionLayer } from './Elements/DepthDimensionLayer';
|
||||||
import { SHOW_DIMENSIONS_PER_DEPTH } from '../../utils/default';
|
import { SHOW_DIMENSIONS_PER_DEPTH } from '../../utils/default';
|
||||||
import { SymbolLayer } from './Elements/SymbolLayer';
|
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
|
|
||||||
interface ISVGProps {
|
interface ISVGProps {
|
||||||
width: number
|
width: number
|
||||||
height: number
|
height: number
|
||||||
children: ContainerModel | ContainerModel[] | null
|
children: ContainerModel | ContainerModel[] | null
|
||||||
selected?: ContainerModel
|
selected: ContainerModel | null
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Viewer {
|
interface Viewer {
|
||||||
|
@ -83,7 +81,6 @@ export const SVG: React.FC<ISVGProps> = (props: ISVGProps) => {
|
||||||
? <DepthDimensionLayer roots={props.children}/>
|
? <DepthDimensionLayer roots={props.children}/>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
<SymbolLayer symbols={props.symbols} />
|
|
||||||
<Selector selected={props.selected} /> {/* leave this at the end so it can be removed during the svg export */}
|
<Selector selected={props.selected} /> {/* leave this at the end so it can be removed during the svg export */}
|
||||||
</svg>
|
</svg>
|
||||||
</UncontrolledReactSVGPanZoom>
|
</UncontrolledReactSVGPanZoom>
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { IInputGroup } from '../../Interfaces/IInputGroup';
|
|
||||||
|
|
||||||
interface ISelectProps {
|
|
||||||
labelKey?: string
|
|
||||||
labelText: string
|
|
||||||
inputKey: string
|
|
||||||
labelClassName: string
|
|
||||||
inputClassName: string
|
|
||||||
inputs: IInputGroup[]
|
|
||||||
value?: string
|
|
||||||
onChange?: (event: React.ChangeEvent<HTMLSelectElement>) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const className = `
|
|
||||||
w-full
|
|
||||||
text-xs font-medium transition-all text-gray-800 mt-1 px-3 py-2
|
|
||||||
bg-white border-2 border-white rounded-lg placeholder-gray-800
|
|
||||||
focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500
|
|
||||||
disabled:bg-slate-300 disabled:text-gray-500 disabled:border-slate-300 disabled:shadow-none`;
|
|
||||||
|
|
||||||
export const Select: React.FC<ISelectProps> = (props) => {
|
|
||||||
const options = [(
|
|
||||||
<option key='symbol-none' value=''>None</option>
|
|
||||||
)];
|
|
||||||
|
|
||||||
props.inputs.forEach(input => {
|
|
||||||
options.push(<option
|
|
||||||
key={input.value}
|
|
||||||
value={input.value}
|
|
||||||
>
|
|
||||||
{input.text}
|
|
||||||
</option>);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<label
|
|
||||||
key={props.labelKey}
|
|
||||||
className={`mt-4 text-xs font-medium text-gray-800 ${props.labelClassName}`}
|
|
||||||
htmlFor={props.inputKey}
|
|
||||||
>
|
|
||||||
{ props.labelText }
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id={props.inputKey}
|
|
||||||
value={props.value}
|
|
||||||
onChange={props.onChange}
|
|
||||||
className={className}
|
|
||||||
>
|
|
||||||
{ options }
|
|
||||||
</select>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,56 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
import { restoreX, transformX } from '../../utils/svg';
|
|
||||||
import { InputGroup } from '../InputGroup/InputGroup';
|
|
||||||
|
|
||||||
interface IDynamicFormProps {
|
|
||||||
symbol: ISymbolModel
|
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
onChange: (key: string, value: string | number | boolean) => void
|
|
||||||
}
|
|
||||||
const DynamicForm: React.FunctionComponent<IDynamicFormProps> = (props) => {
|
|
||||||
return (
|
|
||||||
<div className='grid grid-cols-2 gap-y-4'>
|
|
||||||
<InputGroup
|
|
||||||
labelText='Name'
|
|
||||||
inputKey='id'
|
|
||||||
labelClassName=''
|
|
||||||
inputClassName=''
|
|
||||||
type='string'
|
|
||||||
value={props.symbol.id.toString()}
|
|
||||||
isDisabled={true}
|
|
||||||
/>
|
|
||||||
<InputGroup
|
|
||||||
labelText='x'
|
|
||||||
inputKey='x'
|
|
||||||
labelClassName=''
|
|
||||||
inputClassName=''
|
|
||||||
type='number'
|
|
||||||
value={transformX(props.symbol.x, props.symbol.width, props.symbol.config.XPositionReference).toString()}
|
|
||||||
onChange={(event) => props.onChange('x', restoreX(Number(event.target.value), props.symbol.width, props.symbol.config.XPositionReference))}
|
|
||||||
/>
|
|
||||||
<InputGroup
|
|
||||||
labelText='Height'
|
|
||||||
inputKey='height'
|
|
||||||
labelClassName=''
|
|
||||||
inputClassName=''
|
|
||||||
type='number'
|
|
||||||
min={0}
|
|
||||||
value={props.symbol.height.toString()}
|
|
||||||
onChange={(event) => props.onChange('height', Number(event.target.value))}
|
|
||||||
/>
|
|
||||||
<InputGroup
|
|
||||||
labelText='Width'
|
|
||||||
inputKey='width'
|
|
||||||
labelClassName=''
|
|
||||||
inputClassName=''
|
|
||||||
type='number'
|
|
||||||
min={0}
|
|
||||||
value={props.symbol.width.toString()}
|
|
||||||
onChange={(event) => props.onChange('width', Number(event.target.value))}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DynamicForm;
|
|
|
@ -1,17 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
import DynamicForm from './DynamicForm';
|
|
||||||
|
|
||||||
interface IFormProps {
|
|
||||||
symbol: ISymbolModel
|
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
onChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Form: React.FunctionComponent<IFormProps> = (props) => {
|
|
||||||
return <DynamicForm
|
|
||||||
symbol={props.symbol}
|
|
||||||
symbols={props.symbols}
|
|
||||||
onChange={props.onChange}
|
|
||||||
/>;
|
|
||||||
};
|
|
|
@ -1,27 +0,0 @@
|
||||||
import React, { useState } from 'react';
|
|
||||||
import IContainerProperties from '../../Interfaces/IContainerProperties';
|
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
import { ToggleButton } from '../ToggleButton/ToggleButton';
|
|
||||||
import { Form } from './Form';
|
|
||||||
|
|
||||||
interface ISymbolPropertiesProps {
|
|
||||||
symbol?: ISymbolModel
|
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
onChange: (key: string, value: string | number | boolean) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SymbolProperties: React.FC<ISymbolPropertiesProps> = (props: ISymbolPropertiesProps) => {
|
|
||||||
if (props.symbol === undefined) {
|
|
||||||
return <div></div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='h-3/5 p-3 bg-slate-200 overflow-y-auto'>
|
|
||||||
<Form
|
|
||||||
symbol={props.symbol}
|
|
||||||
symbols={props.symbols}
|
|
||||||
onChange={props.onChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,68 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { IAvailableSymbol } from '../../Interfaces/IAvailableSymbol';
|
|
||||||
import { truncateString } from '../../utils/stringtools';
|
|
||||||
|
|
||||||
interface ISymbolsProps {
|
|
||||||
componentOptions: IAvailableSymbol[]
|
|
||||||
isOpen: boolean
|
|
||||||
buttonOnClick: (type: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragStart(event: React.DragEvent<HTMLButtonElement>): void {
|
|
||||||
event.dataTransfer.setData('type', (event.target as HTMLButtonElement).id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Symbols: React.FC<ISymbolsProps> = (props: ISymbolsProps) => {
|
|
||||||
const listElements = props.componentOptions.map(componentOption => {
|
|
||||||
if (componentOption.Image.Url !== undefined || componentOption.Image.Base64Image !== undefined) {
|
|
||||||
const url = componentOption.Image.Base64Image ?? componentOption.Image.Url;
|
|
||||||
return (<button
|
|
||||||
className='justify-center sidebar-component-card hover:h-full'
|
|
||||||
key={componentOption.Name}
|
|
||||||
id={componentOption.Name}
|
|
||||||
title={componentOption.Name}
|
|
||||||
onClick={() => props.buttonOnClick(componentOption.Name)}
|
|
||||||
draggable={true}
|
|
||||||
onDragStart={(event) => handleDragStart(event)}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
className='transition-all h-12 w-full object-cover'
|
|
||||||
src={url}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{truncateString(componentOption.Name, 5)}
|
|
||||||
</div>
|
|
||||||
</button>);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (<button
|
|
||||||
className='group justify-center sidebar-component hover:h-full'
|
|
||||||
key={componentOption.Name}
|
|
||||||
id={componentOption.Name}
|
|
||||||
title={componentOption.Name}
|
|
||||||
onClick={() => props.buttonOnClick(componentOption.Name)}
|
|
||||||
draggable={true}
|
|
||||||
onDragStart={(event) => handleDragStart(event)}
|
|
||||||
>
|
|
||||||
|
|
||||||
{truncateString(componentOption.Name, 5)}
|
|
||||||
</button>);
|
|
||||||
});
|
|
||||||
|
|
||||||
const isOpenClasses = props.isOpen ? 'left-16' : '-left-64';
|
|
||||||
return (
|
|
||||||
<div className={`fixed z-10 bg-slate-200
|
|
||||||
text-gray-700 transition-all h-full w-64
|
|
||||||
overflow-y-auto ${isOpenClasses}`}>
|
|
||||||
<div className='bg-slate-100 sidebar-title'>
|
|
||||||
Symbols
|
|
||||||
</div>
|
|
||||||
<div className='grid grid-cols-1 md:grid-cols-3 gap-2
|
|
||||||
m-2 md:text-xs font-bold'>
|
|
||||||
{listElements}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,34 +0,0 @@
|
||||||
import { IPoint } from '../../Interfaces/IPoint';
|
|
||||||
|
|
||||||
export function handleRightClick(
|
|
||||||
event: MouseEvent,
|
|
||||||
setIsContextMenuOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
|
||||||
setOnClickSymbolId: React.Dispatch<React.SetStateAction<string>>,
|
|
||||||
setContextMenuPosition: React.Dispatch<React.SetStateAction<IPoint>>
|
|
||||||
): void {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (!(event.target instanceof HTMLButtonElement)) {
|
|
||||||
setIsContextMenuOpen(false);
|
|
||||||
setOnClickSymbolId('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const contextMenuPosition: IPoint = { x: event.pageX, y: event.pageY };
|
|
||||||
setIsContextMenuOpen(true);
|
|
||||||
setOnClickSymbolId(event.target.id);
|
|
||||||
setContextMenuPosition(contextMenuPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function handleLeftClick(
|
|
||||||
isContextMenuOpen: boolean,
|
|
||||||
setIsContextMenuOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
|
||||||
setOnClickContainerId: React.Dispatch<React.SetStateAction<string>>
|
|
||||||
): void {
|
|
||||||
if (!isContextMenuOpen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsContextMenuOpen(false);
|
|
||||||
setOnClickContainerId('');
|
|
||||||
}
|
|
|
@ -1,137 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { FixedSizeList as List } from 'react-window';
|
|
||||||
import { Menu } from '../Menu/Menu';
|
|
||||||
import { MenuItem } from '../Menu/MenuItem';
|
|
||||||
import { handleLeftClick, handleRightClick } from './MouseEventHandlers';
|
|
||||||
import { IPoint } from '../../Interfaces/IPoint';
|
|
||||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
|
||||||
import { SymbolProperties } from '../SymbolProperties/SymbolProperties';
|
|
||||||
|
|
||||||
interface ISymbolsSidebarProps {
|
|
||||||
SelectedSymbolId: string
|
|
||||||
symbols: Map<string, ISymbolModel>
|
|
||||||
isOpen: boolean
|
|
||||||
isHistoryOpen: boolean
|
|
||||||
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
|
||||||
SelectSymbol: (symbolId: string) => void
|
|
||||||
DeleteSymbol: (containerid: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SymbolsSidebar: React.FC<ISymbolsSidebarProps> = (props: ISymbolsSidebarProps): JSX.Element => {
|
|
||||||
// States
|
|
||||||
const [isContextMenuOpen, setIsContextMenuOpen] = React.useState<boolean>(false);
|
|
||||||
const [onClickSymbolId, setOnClickSymbolId] = React.useState<string>('');
|
|
||||||
const [contextMenuPosition, setContextMenuPosition] = React.useState<IPoint>({
|
|
||||||
x: 0,
|
|
||||||
y: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
const elementRef = React.useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
// Event listeners
|
|
||||||
React.useEffect(() => {
|
|
||||||
const onContextMenu = (event: MouseEvent): void => handleRightClick(
|
|
||||||
event,
|
|
||||||
setIsContextMenuOpen,
|
|
||||||
setOnClickSymbolId,
|
|
||||||
setContextMenuPosition
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLeftClick = (): void => handleLeftClick(
|
|
||||||
isContextMenuOpen,
|
|
||||||
setIsContextMenuOpen,
|
|
||||||
setOnClickSymbolId
|
|
||||||
);
|
|
||||||
|
|
||||||
elementRef.current?.addEventListener(
|
|
||||||
'contextmenu',
|
|
||||||
onContextMenu
|
|
||||||
);
|
|
||||||
|
|
||||||
window.addEventListener(
|
|
||||||
'click',
|
|
||||||
onLeftClick
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
elementRef.current?.removeEventListener(
|
|
||||||
'contextmenu',
|
|
||||||
onContextMenu
|
|
||||||
);
|
|
||||||
|
|
||||||
window.removeEventListener(
|
|
||||||
'click',
|
|
||||||
onLeftClick
|
|
||||||
);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Render
|
|
||||||
let isOpenClasses = '-right-64';
|
|
||||||
if (props.isOpen) {
|
|
||||||
isOpenClasses = props.isHistoryOpen
|
|
||||||
? 'right-64'
|
|
||||||
: 'right-0';
|
|
||||||
}
|
|
||||||
|
|
||||||
const containers = [...props.symbols.values()];
|
|
||||||
const Row = ({ index, style }: {index: number, style: React.CSSProperties}): JSX.Element => {
|
|
||||||
const container = containers[index];
|
|
||||||
const key = container.id.toString();
|
|
||||||
const text = key;
|
|
||||||
const selectedClass: string = props.SelectedSymbolId !== '' &&
|
|
||||||
props.SelectedSymbolId === container.id
|
|
||||||
? 'border-l-4 bg-slate-400/60 hover:bg-slate-400'
|
|
||||||
: 'bg-slate-300/60 hover:bg-slate-300';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className={
|
|
||||||
`w-full border-blue-500 elements-sidebar-row whitespace-pre
|
|
||||||
text-left text-sm font-medium transition-all ${selectedClass}`
|
|
||||||
}
|
|
||||||
id={key}
|
|
||||||
key={key}
|
|
||||||
style={style}
|
|
||||||
onClick={() => props.SelectSymbol(key)}
|
|
||||||
>
|
|
||||||
{ text }
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`fixed flex flex-col bg-slate-100 text-gray-800 transition-all h-full w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
|
|
||||||
<div className='bg-slate-100 font-bold sidebar-title'>
|
|
||||||
Elements
|
|
||||||
</div>
|
|
||||||
<div ref={elementRef} className='h-96 text-gray-800'>
|
|
||||||
<List
|
|
||||||
className='List divide-y divide-black'
|
|
||||||
itemCount={containers.length}
|
|
||||||
itemSize={35}
|
|
||||||
height={384}
|
|
||||||
width={256}
|
|
||||||
>
|
|
||||||
{ Row }
|
|
||||||
</List>
|
|
||||||
</div>
|
|
||||||
<Menu
|
|
||||||
className='transition-opacity rounded bg-slate-200 py-1 drop-shadow-xl'
|
|
||||||
x={contextMenuPosition.x}
|
|
||||||
y={contextMenuPosition.y}
|
|
||||||
isOpen={isContextMenuOpen}
|
|
||||||
>
|
|
||||||
<MenuItem className='contextmenu-item' text='Delete' onClick={() => {
|
|
||||||
setIsContextMenuOpen(false);
|
|
||||||
props.DeleteSymbol(onClickSymbolId);
|
|
||||||
}} />
|
|
||||||
</Menu>
|
|
||||||
<SymbolProperties
|
|
||||||
symbol={props.symbols.get(props.SelectedSymbolId)}
|
|
||||||
symbols={props.symbols}
|
|
||||||
onChange={props.OnPropertyChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -3,55 +3,38 @@ import { ElementsSidebar } from '../ElementsSidebar/ElementsSidebar';
|
||||||
import { Sidebar } from '../Sidebar/Sidebar';
|
import { Sidebar } from '../Sidebar/Sidebar';
|
||||||
import { History } from '../History/History';
|
import { History } from '../History/History';
|
||||||
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
||||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { PhotographIcon, UploadIcon } from '@heroicons/react/outline';
|
import { PhotographIcon, UploadIcon } from '@heroicons/react/outline';
|
||||||
import { FloatingButton } from '../FloatingButton/FloatingButton';
|
import { FloatingButton } from '../FloatingButton/FloatingButton';
|
||||||
import { Bar } from '../Bar/Bar';
|
import { Bar } from '../Bar/Bar';
|
||||||
import { IAvailableSymbol } from '../../Interfaces/IAvailableSymbol';
|
|
||||||
import { Symbols } from '../Symbols/Symbols';
|
|
||||||
import { SymbolsSidebar } from '../SymbolsSidebar/SymbolsSidebar';
|
|
||||||
|
|
||||||
interface IUIProps {
|
interface IUIProps {
|
||||||
SelectedContainer: IContainerModel | undefined
|
|
||||||
current: IHistoryState
|
current: IHistoryState
|
||||||
history: IHistoryState[]
|
history: IHistoryState[]
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
AvailableContainers: IAvailableContainer[]
|
AvailableContainers: IAvailableContainer[]
|
||||||
AvailableSymbols: IAvailableSymbol[]
|
SelectContainer: (container: ContainerModel) => void
|
||||||
SelectContainer: (containerId: string) => void
|
|
||||||
DeleteContainer: (containerId: string) => void
|
DeleteContainer: (containerId: string) => void
|
||||||
OnPropertyChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
OnPropertyChange: (key: string, value: string | number | boolean, isStyle?: boolean) => void
|
||||||
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
||||||
AddContainerToSelectedContainer: (type: string) => void
|
AddContainerToSelectedContainer: (type: string) => void
|
||||||
AddContainer: (index: number, type: string, parentId: string) => void
|
AddContainer: (index: number, type: string, parentId: string) => void
|
||||||
AddSymbol: (type: string) => void
|
|
||||||
OnSymbolPropertyChange: (key: string, value: string | number | boolean) => void
|
|
||||||
SelectSymbol: (symbolId: string) => void
|
|
||||||
DeleteSymbol: (symbolId: string) => void
|
|
||||||
SaveEditorAsJSON: () => void
|
SaveEditorAsJSON: () => void
|
||||||
SaveEditorAsSVG: () => void
|
SaveEditorAsSVG: () => void
|
||||||
LoadState: (move: number) => void
|
LoadState: (move: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function CloseOtherSidebars(
|
|
||||||
setIsSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
|
||||||
setIsSymbolsOpen: React.Dispatch<React.SetStateAction<boolean>>
|
|
||||||
): void {
|
|
||||||
setIsSidebarOpen(false);
|
|
||||||
setIsSymbolsOpen(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UI: React.FunctionComponent<IUIProps> = (props: IUIProps) => {
|
export const UI: React.FunctionComponent<IUIProps> = (props: IUIProps) => {
|
||||||
const [isSidebarOpen, setIsSidebarOpen] = React.useState(true);
|
const [isSidebarOpen, setIsSidebarOpen] = React.useState(true);
|
||||||
const [isSymbolsOpen, setIsSymbolsOpen] = React.useState(false);
|
const [isElementsSidebarOpen, setIsElementsSidebarOpen] = React.useState(false);
|
||||||
const [isHistoryOpen, setIsHistoryOpen] = React.useState(false);
|
const [isHistoryOpen, setIsHistoryOpen] = React.useState(false);
|
||||||
|
|
||||||
let buttonRightOffsetClasses = 'right-12';
|
let buttonRightOffsetClasses = 'right-12';
|
||||||
if (isSidebarOpen || isHistoryOpen) {
|
if (isElementsSidebarOpen || isHistoryOpen) {
|
||||||
buttonRightOffsetClasses = 'right-72';
|
buttonRightOffsetClasses = 'right-72';
|
||||||
}
|
}
|
||||||
if (isHistoryOpen && isSidebarOpen) {
|
if (isHistoryOpen && isElementsSidebarOpen) {
|
||||||
buttonRightOffsetClasses = 'right-[544px]';
|
buttonRightOffsetClasses = 'right-[544px]';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,35 +42,22 @@ export const UI: React.FunctionComponent<IUIProps> = (props: IUIProps) => {
|
||||||
<>
|
<>
|
||||||
<Bar
|
<Bar
|
||||||
isSidebarOpen={isSidebarOpen}
|
isSidebarOpen={isSidebarOpen}
|
||||||
isSymbolsOpen={isSymbolsOpen}
|
isElementsSidebarOpen={isElementsSidebarOpen}
|
||||||
isElementsSidebarOpen={isSidebarOpen}
|
|
||||||
isHistoryOpen={isHistoryOpen}
|
isHistoryOpen={isHistoryOpen}
|
||||||
ToggleSidebar={() => {
|
ToggleElementsSidebar={() => setIsElementsSidebarOpen(!isElementsSidebarOpen)}
|
||||||
CloseOtherSidebars(setIsSidebarOpen, setIsSymbolsOpen);
|
ToggleSidebar={() => setIsSidebarOpen(!isSidebarOpen)}
|
||||||
setIsSidebarOpen(!isSidebarOpen);
|
|
||||||
}}
|
|
||||||
ToggleSymbols={() => {
|
|
||||||
CloseOtherSidebars(setIsSidebarOpen, setIsSymbolsOpen);
|
|
||||||
setIsSymbolsOpen(!isSymbolsOpen);
|
|
||||||
}}
|
|
||||||
ToggleTimeline={() => setIsHistoryOpen(!isHistoryOpen)}
|
ToggleTimeline={() => setIsHistoryOpen(!isHistoryOpen)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Sidebar
|
<Sidebar
|
||||||
componentOptions={props.AvailableContainers}
|
componentOptions={props.AvailableContainers}
|
||||||
isOpen={isSidebarOpen}
|
isOpen={isSidebarOpen}
|
||||||
buttonOnClick={props.AddContainerToSelectedContainer}
|
buttonOnClick={(type: string) => props.AddContainerToSelectedContainer(type)}
|
||||||
/>
|
|
||||||
<Symbols
|
|
||||||
componentOptions={props.AvailableSymbols}
|
|
||||||
isOpen={isSymbolsOpen}
|
|
||||||
buttonOnClick={props.AddSymbol}
|
|
||||||
/>
|
/>
|
||||||
<ElementsSidebar
|
<ElementsSidebar
|
||||||
MainContainer={props.current.MainContainer}
|
MainContainer={props.current.MainContainer}
|
||||||
symbols={props.current.Symbols}
|
SelectedContainer={props.current.SelectedContainer}
|
||||||
SelectedContainer={props.SelectedContainer}
|
isOpen={isElementsSidebarOpen}
|
||||||
isOpen={isSidebarOpen}
|
|
||||||
isHistoryOpen={isHistoryOpen}
|
isHistoryOpen={isHistoryOpen}
|
||||||
OnPropertyChange={props.OnPropertyChange}
|
OnPropertyChange={props.OnPropertyChange}
|
||||||
OnPropertiesSubmit={props.OnPropertiesSubmit}
|
OnPropertiesSubmit={props.OnPropertiesSubmit}
|
||||||
|
@ -95,15 +65,6 @@ export const UI: React.FunctionComponent<IUIProps> = (props: IUIProps) => {
|
||||||
DeleteContainer={props.DeleteContainer}
|
DeleteContainer={props.DeleteContainer}
|
||||||
AddContainer={props.AddContainer}
|
AddContainer={props.AddContainer}
|
||||||
/>
|
/>
|
||||||
<SymbolsSidebar
|
|
||||||
SelectedSymbolId={props.current.SelectedSymbolId}
|
|
||||||
symbols={props.current.Symbols}
|
|
||||||
isOpen={isSymbolsOpen}
|
|
||||||
isHistoryOpen={isHistoryOpen}
|
|
||||||
OnPropertyChange={props.OnSymbolPropertyChange}
|
|
||||||
SelectSymbol={props.SelectSymbol}
|
|
||||||
DeleteSymbol={props.DeleteSymbol}
|
|
||||||
/>
|
|
||||||
<History
|
<History
|
||||||
history={props.history}
|
history={props.history}
|
||||||
historyCurrentStep={props.historyCurrentStep}
|
historyCurrentStep={props.historyCurrentStep}
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { IImage } from './IImage';
|
||||||
* Model of available symbol to configure the application */
|
* Model of available symbol to configure the application */
|
||||||
export interface IAvailableSymbol {
|
export interface IAvailableSymbol {
|
||||||
Name: string
|
Name: string
|
||||||
|
XPositionReference: XPositionReference
|
||||||
Image: IImage
|
Image: IImage
|
||||||
Width?: number
|
Width: number
|
||||||
Height?: number
|
Height: number
|
||||||
XPositionReference?: XPositionReference
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,21 @@
|
||||||
import IContainerProperties from './IContainerProperties';
|
import IProperties from './IProperties';
|
||||||
|
|
||||||
export interface IContainerModel {
|
export interface IContainerModel {
|
||||||
children: IContainerModel[]
|
children: IContainerModel[]
|
||||||
parent: IContainerModel | null
|
parent: IContainerModel | null
|
||||||
properties: IContainerProperties
|
properties: IProperties
|
||||||
userData: Record<string, string | number>
|
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 {
|
export class ContainerModel implements IContainerModel {
|
||||||
public children: IContainerModel[];
|
public children: IContainerModel[];
|
||||||
public parent: IContainerModel | null;
|
public parent: IContainerModel | null;
|
||||||
public properties: IContainerProperties;
|
public properties: IProperties;
|
||||||
public userData: Record<string, string | number>;
|
public userData: Record<string, string | number>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
parent: IContainerModel | null,
|
parent: IContainerModel | null,
|
||||||
properties: IContainerProperties,
|
properties: IProperties,
|
||||||
children: IContainerModel[] = [],
|
children: IContainerModel[] = [],
|
||||||
userData = {}) {
|
userData = {}) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
|
|
@ -1,22 +1,9 @@
|
||||||
import { IContainerModel } from './IContainerModel';
|
import { IContainerModel } from './IContainerModel';
|
||||||
import { ISymbolModel } from './ISymbolModel';
|
|
||||||
|
|
||||||
export interface IHistoryState {
|
export interface IHistoryState {
|
||||||
/** Last editor action */
|
|
||||||
LastAction: string
|
LastAction: string
|
||||||
|
|
||||||
/** Reference to the main container */
|
|
||||||
MainContainer: IContainerModel
|
MainContainer: IContainerModel
|
||||||
|
SelectedContainer: IContainerModel | null
|
||||||
/** Id of the selected container */
|
|
||||||
SelectedContainerId: string
|
SelectedContainerId: string
|
||||||
|
|
||||||
/** Counter of type of container. Used for ids. */
|
|
||||||
TypeCounters: Record<string, number>
|
TypeCounters: Record<string, number>
|
||||||
|
|
||||||
/** List of symbols */
|
|
||||||
Symbols: Map<string, ISymbolModel>
|
|
||||||
|
|
||||||
/** Selected symbols id */
|
|
||||||
SelectedSymbolId: string
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,7 @@
|
||||||
/**
|
/** Model of an image with multiple source */
|
||||||
* 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 {
|
export interface IImage {
|
||||||
/** Name of the image */
|
|
||||||
Name: string
|
Name: string
|
||||||
|
Url: string
|
||||||
/** (optional) Url of the image */
|
Base64Image: string
|
||||||
Url?: string
|
Svg: string
|
||||||
|
|
||||||
/** (optional) base64 data of the image */
|
|
||||||
Base64Image?: string
|
|
||||||
|
|
||||||
/** (optional) SVG string */
|
|
||||||
Svg?: string
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,13 @@ import { XPositionReference } from '../Enums/XPositionReference';
|
||||||
/**
|
/**
|
||||||
* Properties of a container
|
* Properties of a container
|
||||||
*/
|
*/
|
||||||
export default interface IContainerProperties {
|
export default interface IProperties {
|
||||||
/** id of the container */
|
/** id of the container */
|
||||||
id: string
|
id: string
|
||||||
|
|
||||||
// TODO: replace null by empty string
|
|
||||||
/** id of the parent container (null when there is no parent) */
|
/** id of the parent container (null when there is no parent) */
|
||||||
parentId: string | null
|
parentId: string | null
|
||||||
|
|
||||||
/** id of the linked symbol ('' when there is no parent) */
|
|
||||||
linkedSymbolId: string
|
|
||||||
|
|
||||||
/** Text displayed in the container */
|
/** Text displayed in the container */
|
||||||
displayedText: string
|
displayedText: string
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { IAvailableSymbol } from './IAvailableSymbol';
|
|
||||||
|
|
||||||
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>
|
|
||||||
}
|
|
|
@ -11,10 +11,6 @@
|
||||||
@apply transition-all px-2 py-6 text-sm rounded-lg bg-slate-300/60 hover:bg-slate-300
|
@apply transition-all px-2 py-6 text-sm rounded-lg bg-slate-300/60 hover:bg-slate-300
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-component-card {
|
|
||||||
@apply transition-all overflow-hidden text-sm rounded-lg bg-slate-300/60 hover:bg-slate-300
|
|
||||||
}
|
|
||||||
|
|
||||||
.elements-sidebar-row {
|
.elements-sidebar-row {
|
||||||
@apply pl-6 pr-6 pt-2 pb-2 w-full
|
@apply pl-6 pr-6 pt-2 pb-2 w-full
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { XPositionReference } from '../Enums/XPositionReference';
|
||||||
import { IAvailableContainer } from '../Interfaces/IAvailableContainer';
|
import { IAvailableContainer } from '../Interfaces/IAvailableContainer';
|
||||||
import { IConfiguration } from '../Interfaces/IConfiguration';
|
import { IConfiguration } from '../Interfaces/IConfiguration';
|
||||||
import { IContainerModel } from '../Interfaces/IContainerModel';
|
import { IContainerModel } from '../Interfaces/IContainerModel';
|
||||||
import IContainerProperties from '../Interfaces/IContainerProperties';
|
import IProperties from '../Interfaces/IProperties';
|
||||||
|
|
||||||
/// CONTAINER DEFAULTS ///
|
/// CONTAINER DEFAULTS ///
|
||||||
|
|
||||||
|
@ -18,11 +18,6 @@ export const SHOW_DIMENSIONS_PER_DEPTH = true;
|
||||||
export const DIMENSION_MARGIN = 50;
|
export const DIMENSION_MARGIN = 50;
|
||||||
export const NOTCHES_LENGTH = 4;
|
export const NOTCHES_LENGTH = 4;
|
||||||
|
|
||||||
/// SYMBOL DEFAULTS ///
|
|
||||||
|
|
||||||
export const DEFAULT_SYMBOL_WIDTH = 32;
|
|
||||||
export const DEFAULT_SYMBOL_HEIGHT = 32;
|
|
||||||
|
|
||||||
/// EDITOR DEFAULTS ///
|
/// EDITOR DEFAULTS ///
|
||||||
|
|
||||||
export const ENABLE_SHORTCUTS = true;
|
export const ENABLE_SHORTCUTS = true;
|
||||||
|
@ -53,10 +48,9 @@ export const DEFAULT_CONFIG: IConfiguration = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_MAINCONTAINER_PROPS: IContainerProperties = {
|
export const DEFAULT_MAINCONTAINER_PROPS: IProperties = {
|
||||||
id: 'main',
|
id: 'main',
|
||||||
parentId: 'null',
|
parentId: 'null',
|
||||||
linkedSymbolId: '',
|
|
||||||
displayedText: 'main',
|
displayedText: 'main',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -79,10 +73,9 @@ export const GetDefaultContainerProps = (
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
containerConfig: IAvailableContainer
|
containerConfig: IAvailableContainer
|
||||||
): IContainerProperties => ({
|
): IProperties => ({
|
||||||
id: `${type}-${typeCount}`,
|
id: `${type}-${typeCount}`,
|
||||||
parentId: parent.properties.id,
|
parentId: parent.properties.id,
|
||||||
linkedSymbolId: '',
|
|
||||||
displayedText: `${type}-${typeCount}`,
|
displayedText: `${type}-${typeCount}`,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
|
|
@ -18,11 +18,6 @@ export function Revive(editorState: IEditorState): void {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.Symbols = new Map(state.Symbols);
|
|
||||||
for (const symbol of state.Symbols.values()) {
|
|
||||||
symbol.linkedContainers = new Set(symbol.linkedContainers);
|
|
||||||
}
|
|
||||||
|
|
||||||
const it = MakeIterator(state.MainContainer);
|
const it = MakeIterator(state.MainContainer);
|
||||||
for (const container of it) {
|
for (const container of it) {
|
||||||
const parentId = container.properties.parentId;
|
const parentId = container.properties.parentId;
|
||||||
|
@ -36,21 +31,24 @@ export function Revive(editorState: IEditorState): void {
|
||||||
}
|
}
|
||||||
container.parent = parent;
|
container.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selected = findContainerById(state.MainContainer, state.SelectedContainerId);
|
||||||
|
if (selected === undefined) {
|
||||||
|
state.SelectedContainer = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
state.SelectedContainer = selected;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getCircularReplacer = (): (key: any, value: object | Map<string, any> | null) => object | null | undefined => {
|
export const getCircularReplacer = (): (key: any, value: object | null) => object | null | undefined => {
|
||||||
return (key: any, value: object | null) => {
|
return (key: any, value: object | null) => {
|
||||||
if (key === 'parent') {
|
if (key === 'parent') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'Symbols') {
|
if (key === 'SelectedContainer') {
|
||||||
return Array.from((value as Map<string, any>).entries());
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'linkedContainers') {
|
|
||||||
return Array.from(value as Set<string>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -4,7 +4,3 @@ export function truncateString(str: string, num: number): string {
|
||||||
}
|
}
|
||||||
return `${str.slice(0, num)}...`;
|
return `${str.slice(0, num)}...`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function camelize(str: string): any {
|
|
||||||
return str.split('-').map((word, index) => index > 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word).join('');
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { XPositionReference } from '../Enums/XPositionReference';
|
|
||||||
|
|
||||||
export function transformX(x: number, width: number, xPositionReference = XPositionReference.Left): number {
|
|
||||||
let transformedX = x;
|
|
||||||
if (xPositionReference === XPositionReference.Center) {
|
|
||||||
transformedX += width / 2;
|
|
||||||
} else if (xPositionReference === XPositionReference.Right) {
|
|
||||||
transformedX += width;
|
|
||||||
}
|
|
||||||
return transformedX;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function restoreX(x: number, width: number, xPositionReference = XPositionReference.Left): number {
|
|
||||||
let transformedX = x;
|
|
||||||
if (xPositionReference === XPositionReference.Center) {
|
|
||||||
transformedX -= width / 2;
|
|
||||||
} else if (xPositionReference === XPositionReference.Right) {
|
|
||||||
transformedX -= width;
|
|
||||||
}
|
|
||||||
return transformedX;
|
|
||||||
}
|
|
|
@ -112,8 +112,7 @@ const GetSVGLayoutConfiguration = () => {
|
||||||
],
|
],
|
||||||
AvailableSymbols: [
|
AvailableSymbols: [
|
||||||
{
|
{
|
||||||
Width: 32,
|
Height: 0,
|
||||||
Height: 32,
|
|
||||||
Image: {
|
Image: {
|
||||||
Base64Image: null,
|
Base64Image: null,
|
||||||
Name: null,
|
Name: null,
|
||||||
|
@ -121,11 +120,11 @@ const GetSVGLayoutConfiguration = () => {
|
||||||
Url: 'https://www.manutan.fr/img/S/GRP/ST/AIG3930272.jpg'
|
Url: 'https://www.manutan.fr/img/S/GRP/ST/AIG3930272.jpg'
|
||||||
},
|
},
|
||||||
Name: 'Poteau structure',
|
Name: 'Poteau structure',
|
||||||
|
Width: 0,
|
||||||
XPositionReference: 1
|
XPositionReference: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Width: 32,
|
Height: 0,
|
||||||
Height: 32,
|
|
||||||
Image: {
|
Image: {
|
||||||
Base64Image: null,
|
Base64Image: null,
|
||||||
Name: null,
|
Name: null,
|
||||||
|
@ -133,6 +132,7 @@ const GetSVGLayoutConfiguration = () => {
|
||||||
Url: 'https://e7.pngegg.com/pngimages/647/127/png-clipart-svg-working-group-information-world-wide-web-internet-structure.png'
|
Url: 'https://e7.pngegg.com/pngimages/647/127/png-clipart-svg-working-group-information-world-wide-web-internet-structure.png'
|
||||||
},
|
},
|
||||||
Name: 'Joint de structure',
|
Name: 'Joint de structure',
|
||||||
|
Width: 0,
|
||||||
XPositionReference: 0
|
XPositionReference: 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue