Merge branch 'dev'
This commit is contained in:
commit
d48d45f5ca
35 changed files with 565 additions and 486 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -2,3 +2,4 @@
|
||||||
*.pdf filter=lfs diff=lfs merge=lfs -text
|
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||||
*.dwg filter=lfs diff=lfs merge=lfs -text
|
*.dwg filter=lfs diff=lfs merge=lfs -text
|
||||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pptx filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace SVGLDLibs.Models
|
namespace SVGLDLibs.Models
|
||||||
{
|
{
|
||||||
|
@ -149,6 +150,6 @@ namespace SVGLDLibs.Models
|
||||||
* User data that can be used for data storage or custom SVG
|
* User data that can be used for data storage or custom SVG
|
||||||
*/
|
*/
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public object userData;
|
public Dictionary<string, string> userData;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,8 +6,8 @@ namespace SVGLDLibs.Models
|
||||||
public class PointModel
|
public class PointModel
|
||||||
{
|
{
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string X { get; set; }
|
public double x { get; set; }
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string Y { get; set; }
|
public double y { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,17 +1,24 @@
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace SVGLDLibs.Models
|
namespace SVGLDLibs.Models
|
||||||
{
|
{
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class SymbolModel : AvailableSymbolModel
|
public class SymbolModel
|
||||||
{
|
{
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string Id { get; set; }
|
public string id { get; set; }
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public PointModel Point { get; set; }
|
public string type { get; set; }
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public bool IsLinkedToContainer { get; set; }
|
public AvailableSymbolModel config { get; set; }
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string LinkedContainerId { get; set; }
|
public double x { get; set; }
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public double width { get; set; }
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public double height { get; set; }
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public List<string> linkedContainers { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -45,7 +45,7 @@ public class SVGLDController : ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost(Name = nameof(SVGLDLibs.Models.Configuration))]
|
[HttpPost(Name = nameof(SVGLDLibs.Models.Configuration))]
|
||||||
public bool ConfigurationResponseModel(Configuration model)
|
public bool Configuration(Configuration model)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
BIN
docs/#Project/Pages/SVGLD_Cotes.pdf
(Stored with Git LFS)
Normal file
BIN
docs/#Project/Pages/SVGLD_Cotes.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
docs/#Project/Pages/SVGLD_Cotes.pptx
(Stored with Git LFS)
Normal file
BIN
docs/#Project/Pages/SVGLD_Cotes.pptx
(Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -5,10 +5,6 @@ onmessage = (e) => {
|
||||||
|
|
||||||
const getCircularReplacer = () => {
|
const getCircularReplacer = () => {
|
||||||
return (key, value) => {
|
return (key, value) => {
|
||||||
if (key === 'parent') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'containers') {
|
if (key === 'containers') {
|
||||||
return [...value.entries()]
|
return [...value.entries()]
|
||||||
.map(([Key, Value]) => ({ Key, Value }));
|
.map(([Key, Value]) => ({ Key, Value }));
|
||||||
|
|
|
@ -98,7 +98,6 @@ describe.concurrent('Models test suite', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const mainContainer = new ContainerModel(
|
const mainContainer = new ContainerModel(
|
||||||
null,
|
|
||||||
DEFAULT_MAINCONTAINER_PROPS
|
DEFAULT_MAINCONTAINER_PROPS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -191,12 +190,7 @@ describe.concurrent('Models test suite', () => {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
CustomSVG: 'string',
|
CustomSVG: 'string',
|
||||||
Style: {},
|
Style: {}
|
||||||
UserData: {
|
|
||||||
additionalProp1: 'string',
|
|
||||||
additionalProp2: 'string',
|
|
||||||
additionalProp3: 'string'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it('AvailableContainerModel', async() => {
|
it('AvailableContainerModel', async() => {
|
||||||
|
@ -239,7 +233,7 @@ describe.concurrent('Models test suite', () => {
|
||||||
wrapper: 'string'
|
wrapper: 'string'
|
||||||
};
|
};
|
||||||
|
|
||||||
it('ConfigurationResponseModel', async() => {
|
it('Configuration', async() => {
|
||||||
const model: IConfiguration = {
|
const model: IConfiguration = {
|
||||||
AvailableContainers: [
|
AvailableContainers: [
|
||||||
availableContainerModel
|
availableContainerModel
|
||||||
|
@ -251,10 +245,15 @@ describe.concurrent('Models test suite', () => {
|
||||||
category
|
category
|
||||||
],
|
],
|
||||||
MainContainer: availableContainerModel,
|
MainContainer: availableContainerModel,
|
||||||
Patterns: [pattern]
|
Patterns: [pattern],
|
||||||
|
APIConfiguration: {
|
||||||
|
apiFetchUrl: '',
|
||||||
|
apiGetFeedbackUrl: '',
|
||||||
|
apiSetContainerListUrl: ''
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await Post2API('ConfigurationResponseModel', JSON.stringify(model));
|
const res = await Post2API('Configuration', JSON.stringify(model));
|
||||||
expect(res).toBe(true);
|
expect(res).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -267,7 +266,6 @@ describe.concurrent('Models test suite', () => {
|
||||||
it('ContainerModel', async() => {
|
it('ContainerModel', async() => {
|
||||||
const model: IContainerModel = {
|
const model: IContainerModel = {
|
||||||
children: [],
|
children: [],
|
||||||
parent: null,
|
|
||||||
properties: containerProperties,
|
properties: containerProperties,
|
||||||
userData: {}
|
userData: {}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
|
import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { events as EVENTS } from '../../Events/AppEvents';
|
import { UseCustomEvents } from '../../Events/AppEvents';
|
||||||
import { MainMenu } from '../MainMenu/MainMenu';
|
import { MainMenu } from '../MainMenu/MainMenu';
|
||||||
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { Editor } from '../Editor/Editor';
|
import { Editor } from '../Editor/Editor';
|
||||||
|
@ -41,44 +41,11 @@ function UseHTTPGETStatePreloading(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function UseCustomEvents(
|
|
||||||
root: Element | Document,
|
|
||||||
appRef: React.RefObject<HTMLDivElement>,
|
|
||||||
setEditor: (newState: IEditorState) => void,
|
|
||||||
setLoaded: (loaded: boolean) => void
|
|
||||||
): void {
|
|
||||||
useEffect(() => {
|
|
||||||
const funcs = new Map<string, () => void>();
|
|
||||||
for (const event of EVENTS) {
|
|
||||||
function Func(eventInitDict?: CustomEventInit): void {
|
|
||||||
return event.func(
|
|
||||||
root,
|
|
||||||
setEditor,
|
|
||||||
setLoaded,
|
|
||||||
eventInitDict
|
|
||||||
);
|
|
||||||
}
|
|
||||||
appRef.current?.addEventListener(event.name, Func);
|
|
||||||
funcs.set(event.name, Func);
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
for (const event of EVENTS) {
|
|
||||||
const func = funcs.get(event.name);
|
|
||||||
if (func === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
appRef.current?.removeEventListener(event.name, func);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function App(props: IAppProps): JSX.Element {
|
export function App(props: IAppProps): JSX.Element {
|
||||||
const [isLoaded, setLoaded] = useState<boolean>(false);
|
const [isLoaded, setLoaded] = useState<boolean>(false);
|
||||||
const appRef = useRef<HTMLDivElement>(null);
|
const appRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const defaultMainContainer = new ContainerModel(
|
const defaultMainContainer = new ContainerModel(
|
||||||
null,
|
|
||||||
DEFAULT_MAINCONTAINER_PROPS
|
DEFAULT_MAINCONTAINER_PROPS
|
||||||
);
|
);
|
||||||
const containers = new Map<string, IContainerModel>();
|
const containers = new Map<string, IContainerModel>();
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { GetAbsolutePosition } from '../../utils/itertools';
|
||||||
import { RemoveMargin } from '../../utils/svg';
|
import { RemoveMargin } from '../../utils/svg';
|
||||||
|
|
||||||
interface ISelectorProps {
|
interface ISelectorProps {
|
||||||
|
containers: Map<string, IContainerModel>
|
||||||
selected?: IContainerModel
|
selected?: IContainerModel
|
||||||
scale?: number
|
scale?: number
|
||||||
}
|
}
|
||||||
|
@ -14,7 +15,7 @@ export function RenderSelector(ctx: CanvasRenderingContext2D, frameCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const scale = (props.scale ?? 1);
|
const scale = (props.scale ?? 1);
|
||||||
let [x, y] = GetAbsolutePosition(props.selected);
|
let [x, y] = GetAbsolutePosition(props.containers, props.selected);
|
||||||
let [width, height] = [
|
let [width, height] = [
|
||||||
props.selected.properties.width,
|
props.selected.properties.width,
|
||||||
props.selected.properties.height
|
props.selected.properties.height
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { IHistoryState } from '../../../Interfaces/IHistoryState';
|
||||||
import { IPattern, GetPattern, ContainerOrPattern } from '../../../Interfaces/IPattern';
|
import { IPattern, GetPattern, ContainerOrPattern } from '../../../Interfaces/IPattern';
|
||||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
||||||
import { Orientation } from '../../../Enums/Orientation';
|
import { Orientation } from '../../../Enums/Orientation';
|
||||||
import { GetDefaultContainerProps, DEFAULTCHILDTYPE_MAX_DEPTH, DEFAULTCHILDTYPE_ALLOW_CYCLIC } from '../../../utils/default';
|
import { GetDefaultContainerProps } from '../../../utils/default';
|
||||||
import { FindContainerById } from '../../../utils/itertools';
|
import { FindContainerById } from '../../../utils/itertools';
|
||||||
import { ApplyMargin } from '../../../utils/svg';
|
import { ApplyMargin } from '../../../utils/svg';
|
||||||
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
|
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
|
||||||
|
@ -27,21 +27,15 @@ import { SortChildren } from './ContainerOperations';
|
||||||
*/
|
*/
|
||||||
export function AddContainerToSelectedContainer(
|
export function AddContainerToSelectedContainer(
|
||||||
type: string,
|
type: string,
|
||||||
selected: IContainerModel | undefined,
|
selected: IContainerModel,
|
||||||
configuration: IConfiguration,
|
configuration: IConfiguration,
|
||||||
fullHistory: IHistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
): IHistoryState[] | null {
|
): IHistoryState[] {
|
||||||
if (selected === null ||
|
|
||||||
selected === undefined) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = selected;
|
|
||||||
return AddContainer(
|
return AddContainer(
|
||||||
parent.children.length,
|
selected.children.length,
|
||||||
type,
|
type,
|
||||||
parent.properties.id,
|
selected.properties.id,
|
||||||
configuration,
|
configuration,
|
||||||
fullHistory,
|
fullHistory,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
|
@ -163,7 +157,6 @@ function AddNewContainerToParent(
|
||||||
|
|
||||||
// Create the container
|
// Create the container
|
||||||
const newContainer = new ContainerModel(
|
const newContainer = new ContainerModel(
|
||||||
parentClone,
|
|
||||||
defaultProperties,
|
defaultProperties,
|
||||||
[],
|
[],
|
||||||
{
|
{
|
||||||
|
@ -308,96 +301,176 @@ function InitializeChildrenWithPattern(
|
||||||
const configs: Map<string, IAvailableContainer> = new Map(configuration.AvailableContainers.map(config => [config.Type, config]));
|
const configs: Map<string, IAvailableContainer> = new Map(configuration.AvailableContainers.map(config => [config.Type, config]));
|
||||||
const patterns: Map<string, IPattern> = new Map(configuration.Patterns.map(pattern => [pattern.id, pattern]));
|
const patterns: Map<string, IPattern> = new Map(configuration.Patterns.map(pattern => [pattern.id, pattern]));
|
||||||
const containerOrPattern = GetPattern(patternId, configs, patterns);
|
const containerOrPattern = GetPattern(patternId, configs, patterns);
|
||||||
const pattern = containerOrPattern as IPattern;
|
|
||||||
|
|
||||||
if (pattern.children === undefined) {
|
if (containerOrPattern === undefined) {
|
||||||
const container = containerOrPattern as IAvailableContainer;
|
console.warn(`[InitializeChildrenWithPattern] PatternId ${patternId} was neither found as Pattern nor as IAvailableContainer`);
|
||||||
// Add Container
|
return;
|
||||||
AddNewContainerToParent(
|
|
||||||
container,
|
|
||||||
configuration,
|
|
||||||
containers,
|
|
||||||
newContainer,
|
|
||||||
0, 0,
|
|
||||||
newCounters,
|
|
||||||
symbols
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BFS over patterns
|
// BFS over patterns
|
||||||
|
BuildPatterns(containerOrPattern, newContainer, configuration, containers, newCounters, symbols, configs, patterns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the BFS algorithm to build containers from given patterns
|
||||||
|
* from the top to the bottom
|
||||||
|
*
|
||||||
|
* @param pattern
|
||||||
|
* @param newContainer
|
||||||
|
* @param configuration
|
||||||
|
* @param containers
|
||||||
|
* @param newCounters
|
||||||
|
* @param symbols
|
||||||
|
* @param configs
|
||||||
|
* @param patterns
|
||||||
|
*/
|
||||||
|
function BuildPatterns(
|
||||||
|
rootPattern: ContainerOrPattern,
|
||||||
|
newContainer: ContainerModel,
|
||||||
|
configuration: IConfiguration,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
|
newCounters: Record<string, number>,
|
||||||
|
symbols: Map<string, ISymbolModel>,
|
||||||
|
configs: Map<string, IAvailableContainer>,
|
||||||
|
patterns: Map<string, IPattern>
|
||||||
|
): void {
|
||||||
const rootNode: Node = {
|
const rootNode: Node = {
|
||||||
containerOrPattern: pattern,
|
containerOrPattern: rootPattern,
|
||||||
parent: newContainer
|
parent: newContainer
|
||||||
};
|
};
|
||||||
|
|
||||||
const queue: Node[] = [rootNode];
|
const queue: Node[] = [rootNode];
|
||||||
while (queue.length > 0) {
|
while (queue.length > 0) {
|
||||||
let levelSize = queue.length;
|
let levelSize = queue.length;
|
||||||
|
const maxLevelSize = levelSize - 1;
|
||||||
while (levelSize-- !== 0) {
|
while (levelSize-- !== 0) {
|
||||||
const node = queue.shift() as Node;
|
const node = queue.shift() as Node;
|
||||||
|
|
||||||
const pattern = node.containerOrPattern as IPattern;
|
const newParent = AddContainerInLevel(node, maxLevelSize, levelSize, configuration, containers, newCounters, symbols, configs);
|
||||||
|
|
||||||
if (pattern.children === undefined) {
|
if (newParent === undefined) {
|
||||||
// Add Container
|
// node.pattern is not a IPattern, there is no children to iterate
|
||||||
const containerConfig = node.containerOrPattern as IAvailableContainer;
|
|
||||||
AddNewContainerToParent(
|
|
||||||
containerConfig,
|
|
||||||
configuration,
|
|
||||||
containers,
|
|
||||||
node.parent,
|
|
||||||
0, 0,
|
|
||||||
newCounters,
|
|
||||||
symbols
|
|
||||||
);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let parent = node.parent;
|
for (let i = 0; i <= newParent.pattern.children.length - 1; i++) {
|
||||||
if (pattern.wrapper !== undefined) {
|
const nextNode = GetNextNode(newParent.parent, newParent.pattern, i, configs, patterns);
|
||||||
// Add Container
|
|
||||||
const container = configs.get(pattern.wrapper);
|
|
||||||
if (container === undefined) {
|
|
||||||
console.warn(`[InitializeChildrenFromPattern] IAvailableContainer from pattern was not found in the configuration: ${pattern.wrapper}.
|
|
||||||
Process will ignore the container.`);
|
|
||||||
} else {
|
|
||||||
const newChildContainer = AddNewContainerToParent(
|
|
||||||
container,
|
|
||||||
configuration,
|
|
||||||
containers,
|
|
||||||
parent,
|
|
||||||
0, 0,
|
|
||||||
newCounters,
|
|
||||||
symbols,
|
|
||||||
undefined,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
// iterate
|
if (nextNode === undefined) {
|
||||||
parent = newChildContainer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = pattern.children.length - 1; i >= 0; i--) {
|
|
||||||
const childId: string = pattern.children[i];
|
|
||||||
|
|
||||||
const child = GetPattern(childId, configs, patterns);
|
|
||||||
|
|
||||||
if (child === undefined) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextNode: Node = {
|
|
||||||
containerOrPattern: child,
|
|
||||||
parent
|
|
||||||
};
|
|
||||||
|
|
||||||
queue.push(nextNode);
|
queue.push(nextNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new container in the parent if node.pattern is a Pattern.
|
||||||
|
* Then, return the next parent to iterate with a pattern/container.
|
||||||
|
* Otherwise, if node.pattern is a IAvailableContainer,
|
||||||
|
* create the container from node.pattern and return undefined.
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
* @param maxLevelSize
|
||||||
|
* @param levelSize
|
||||||
|
* @param configuration
|
||||||
|
* @param containers
|
||||||
|
* @param newCounters
|
||||||
|
* @param symbols
|
||||||
|
* @param configs
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function AddContainerInLevel(
|
||||||
|
node: Node,
|
||||||
|
maxLevelSize: number,
|
||||||
|
levelSize: number,
|
||||||
|
configuration: IConfiguration,
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
|
newCounters: Record<string, number>,
|
||||||
|
symbols: Map<string, ISymbolModel>,
|
||||||
|
configs: Map<string, IAvailableContainer>
|
||||||
|
): { parent: IContainerModel, pattern: IPattern } | undefined {
|
||||||
|
if (!('children' in node.containerOrPattern)) {
|
||||||
|
// Add Container from pattern
|
||||||
|
const containerConfig: IAvailableContainer = node.containerOrPattern;
|
||||||
|
const index = maxLevelSize - levelSize;
|
||||||
|
AddNewContainerToParent(
|
||||||
|
containerConfig,
|
||||||
|
configuration,
|
||||||
|
containers,
|
||||||
|
node.parent,
|
||||||
|
index, 0,
|
||||||
|
newCounters,
|
||||||
|
symbols
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pattern: IPattern = node.containerOrPattern;
|
||||||
|
const parent = node.parent;
|
||||||
|
|
||||||
|
if (pattern.wrapper === undefined) {
|
||||||
|
return { parent, pattern };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Container from wrapper
|
||||||
|
// and set the new parent as the child of this parent
|
||||||
|
const container = configs.get(pattern.wrapper);
|
||||||
|
if (container === undefined) {
|
||||||
|
console.warn(`[InitializeChildrenFromPattern] IAvailableContainer from pattern was not found in the configuration: ${pattern.wrapper}.
|
||||||
|
Process will ignore the container.`);
|
||||||
|
return { parent, pattern };
|
||||||
|
}
|
||||||
|
|
||||||
|
const newChildContainer = AddNewContainerToParent(
|
||||||
|
container,
|
||||||
|
configuration,
|
||||||
|
containers,
|
||||||
|
parent,
|
||||||
|
0, 0,
|
||||||
|
newCounters,
|
||||||
|
symbols,
|
||||||
|
undefined,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
// change the parent to be the child of the wrapper
|
||||||
|
return { parent: newChildContainer, pattern };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the next node from the given pattern from the configs
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
* @param pattern
|
||||||
|
* @param i
|
||||||
|
* @param configs
|
||||||
|
* @param patterns
|
||||||
|
* @returns {Node} The next node
|
||||||
|
*/
|
||||||
|
function GetNextNode(
|
||||||
|
parent: IContainerModel,
|
||||||
|
pattern: IPattern,
|
||||||
|
i: number,
|
||||||
|
configs: Map<string, IAvailableContainer>,
|
||||||
|
patterns: Map<string, IPattern>
|
||||||
|
): Node | undefined {
|
||||||
|
const childId: string = pattern.children[i];
|
||||||
|
|
||||||
|
const child = GetPattern(childId, configs, patterns);
|
||||||
|
|
||||||
|
if (child === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
containerOrPattern: child,
|
||||||
|
parent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface Node {
|
interface Node {
|
||||||
containerOrPattern: ContainerOrPattern
|
containerOrPattern: ContainerOrPattern
|
||||||
parent: IContainerModel
|
parent: IContainerModel
|
||||||
|
|
|
@ -57,9 +57,10 @@ export function DeleteContainer(
|
||||||
throw new Error(`[DeleteContainer] Tried to delete a container that is not present in the main container: ${containerId}`);
|
throw new Error(`[DeleteContainer] Tried to delete a container that is not present in the main container: ${containerId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parent = FindContainerById(containers, container.properties.parentId);
|
||||||
if (container === mainContainerClone ||
|
if (container === mainContainerClone ||
|
||||||
container.parent === undefined ||
|
parent === undefined ||
|
||||||
container.parent === null) {
|
parent === null) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: 'Oops...',
|
title: 'Oops...',
|
||||||
text: 'Deleting the main container is not allowed!',
|
text: 'Deleting the main container is not allowed!',
|
||||||
|
@ -75,10 +76,10 @@ export function DeleteContainer(
|
||||||
const newSymbols = structuredClone(current.symbols);
|
const newSymbols = structuredClone(current.symbols);
|
||||||
UnlinkContainerFromSymbols(containers, newSymbols, container);
|
UnlinkContainerFromSymbols(containers, newSymbols, container);
|
||||||
|
|
||||||
const index = container.parent.children.indexOf(container.properties.id);
|
const index = parent.children.indexOf(container.properties.id);
|
||||||
const success = containers.delete(container.properties.id);
|
const success = containers.delete(container.properties.id);
|
||||||
if (index > -1 && success) {
|
if (index > -1 && success) {
|
||||||
container.parent.children.splice(index, 1);
|
parent.children.splice(index, 1);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('[DeleteContainer] Could not find container among parent\'s children');
|
throw new Error('[DeleteContainer] Could not find container among parent\'s children');
|
||||||
}
|
}
|
||||||
|
@ -90,7 +91,7 @@ export function DeleteContainer(
|
||||||
const selectedContainerId = GetSelectedContainerOnDelete(
|
const selectedContainerId = GetSelectedContainerOnDelete(
|
||||||
containers,
|
containers,
|
||||||
current.selectedContainerId,
|
current.selectedContainerId,
|
||||||
container.parent,
|
parent,
|
||||||
index
|
index
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -202,12 +203,8 @@ export function OnPropertyChange(
|
||||||
*/
|
*/
|
||||||
export function SortChildren(
|
export function SortChildren(
|
||||||
containers: Map<string, IContainerModel>,
|
containers: Map<string, IContainerModel>,
|
||||||
parent: IContainerModel | null | undefined
|
parent: IContainerModel
|
||||||
): void {
|
): void {
|
||||||
if (parent === null || parent === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||||
const children = parent.children;
|
const children = parent.children;
|
||||||
|
|
||||||
|
@ -292,14 +289,17 @@ function SetContainer(
|
||||||
symbols
|
symbols
|
||||||
);
|
);
|
||||||
|
|
||||||
// sort the children list by their position
|
|
||||||
SortChildren(containers, container.parent);
|
|
||||||
|
|
||||||
// Apply special behaviors: rigid, flex, symbol, anchor
|
// Apply special behaviors: rigid, flex, symbol, anchor
|
||||||
ApplyBehaviors(containers, container, symbols);
|
ApplyBehaviors(containers, container, symbols);
|
||||||
|
|
||||||
// Apply special behaviors on siblings
|
// Apply special behaviors on siblings
|
||||||
ApplyBehaviorsOnSiblingsChildren(containers, container, symbols);
|
ApplyBehaviorsOnSiblingsChildren(containers, container, symbols);
|
||||||
|
|
||||||
|
// sort the children list by their position
|
||||||
|
const parent = FindContainerById(containers, container.properties.parentId);
|
||||||
|
if (parent !== null && parent !== undefined) {
|
||||||
|
SortChildren(containers, parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -327,7 +327,7 @@ function AssignProperty(container: ContainerModel, key: string, value: string |
|
||||||
function SetMargin(): void {
|
function SetMargin(): void {
|
||||||
// We need to detect change in order to apply transformation to the width and height
|
// We need to detect change in order to apply transformation to the width and height
|
||||||
// Knowing the current margin is not enough as we dont keep the original width and height
|
// Knowing the current margin is not enough as we dont keep the original width and height
|
||||||
const oldMarginValue: number = (container.properties.margin as any)[key];
|
const oldMarginValue: number = (container.properties.margin as any)[key] ?? 0;
|
||||||
const diff = Number(value) - oldMarginValue;
|
const diff = Number(value) - oldMarginValue;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'left':
|
case 'left':
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Swal from 'sweetalert2';
|
import Swal from 'sweetalert2';
|
||||||
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { AddMethod } from '../../../Enums/AddMethod';
|
import { AddMethod } from '../../../Enums/AddMethod';
|
||||||
import { IAction } from '../../../Interfaces/IAction';
|
import { IAction } from '../../../Interfaces/IAction';
|
||||||
import { IConfiguration } from '../../../Interfaces/IConfiguration';
|
import { IConfiguration } from '../../../Interfaces/IConfiguration';
|
||||||
|
@ -6,12 +7,122 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { IHistoryState } from '../../../Interfaces/IHistoryState';
|
import { IHistoryState } from '../../../Interfaces/IHistoryState';
|
||||||
import { ISetContainerListRequest } from '../../../Interfaces/ISetContainerListRequest';
|
import { ISetContainerListRequest } from '../../../Interfaces/ISetContainerListRequest';
|
||||||
import { ISetContainerListResponse } from '../../../Interfaces/ISetContainerListResponse';
|
import { ISetContainerListResponse } from '../../../Interfaces/ISetContainerListResponse';
|
||||||
|
import { DISABLE_API } from '../../../utils/default';
|
||||||
import { FindContainerById } from '../../../utils/itertools';
|
import { FindContainerById } from '../../../utils/itertools';
|
||||||
import { SetContainerList } from '../../API/api';
|
import { SetContainerList } from '../../API/api';
|
||||||
|
import { IMenuAction } from '../../Menu/Menu';
|
||||||
|
import { GetCurrentHistoryState } from '../Editor';
|
||||||
import { AddContainers } from './AddContainer';
|
import { AddContainers } from './AddContainer';
|
||||||
import { DeleteContainer } from './ContainerOperations';
|
import { DeleteContainer } from './ContainerOperations';
|
||||||
|
import { DeleteSymbol } from './SymbolOperations';
|
||||||
|
|
||||||
export function GetAction(
|
export function InitActions(
|
||||||
|
menuActions: Map<string, IMenuAction[]>,
|
||||||
|
configuration: IConfiguration,
|
||||||
|
history: IHistoryState[],
|
||||||
|
historyCurrentStep: number,
|
||||||
|
setNewHistory: (newHistory: IHistoryState[]) => void,
|
||||||
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
|
): void {
|
||||||
|
menuActions.set(
|
||||||
|
'',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: 'Undo',
|
||||||
|
title: 'Undo last action',
|
||||||
|
shortcut: '<kbd>Ctrl</kbd>+<kbd>Z</kbd>',
|
||||||
|
action: () => {
|
||||||
|
if (historyCurrentStep <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setHistoryCurrentStep(historyCurrentStep - 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Redo',
|
||||||
|
title: 'Redo last action',
|
||||||
|
shortcut: '<kbd>Ctrl</kbd>+<kbd>Y</kbd>',
|
||||||
|
action: () => {
|
||||||
|
if (historyCurrentStep >= history.length - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setHistoryCurrentStep(historyCurrentStep + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
menuActions.set(
|
||||||
|
'elements-sidebar-row',
|
||||||
|
[{
|
||||||
|
text: 'Delete',
|
||||||
|
title: 'Delete the container',
|
||||||
|
shortcut: '<kbd>Suppr</kbd>',
|
||||||
|
action: (target: HTMLElement) => {
|
||||||
|
const id = target.id;
|
||||||
|
const newHistory = DeleteContainer(
|
||||||
|
id,
|
||||||
|
history,
|
||||||
|
historyCurrentStep
|
||||||
|
);
|
||||||
|
setNewHistory(newHistory);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
|
||||||
|
menuActions.set(
|
||||||
|
'symbols-sidebar-row',
|
||||||
|
[{
|
||||||
|
text: 'Delete',
|
||||||
|
title: 'Delete the container',
|
||||||
|
shortcut: '<kbd>Suppr</kbd>',
|
||||||
|
action: (target: HTMLElement) => {
|
||||||
|
const id = target.id;
|
||||||
|
const newHistory = DeleteSymbol(
|
||||||
|
id,
|
||||||
|
history,
|
||||||
|
historyCurrentStep
|
||||||
|
);
|
||||||
|
setNewHistory(newHistory);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
|
||||||
|
// API Actions
|
||||||
|
if (DISABLE_API) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const availableContainer of configuration.AvailableContainers) {
|
||||||
|
if (availableContainer.Actions === undefined || availableContainer.Actions === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const action of availableContainer.Actions) {
|
||||||
|
if (menuActions.get(availableContainer.Type) === undefined) {
|
||||||
|
menuActions.set(availableContainer.Type, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentState = GetCurrentHistoryState(history, historyCurrentStep);
|
||||||
|
const newAction: IMenuAction = {
|
||||||
|
text: action.Label,
|
||||||
|
title: action.Description,
|
||||||
|
action: GetAction(
|
||||||
|
action,
|
||||||
|
currentState,
|
||||||
|
configuration,
|
||||||
|
history,
|
||||||
|
historyCurrentStep,
|
||||||
|
setNewHistory
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
menuActions.get(availableContainer.Type)?.push(newAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetAction(
|
||||||
action: IAction,
|
action: IAction,
|
||||||
currentState: IHistoryState,
|
currentState: IHistoryState,
|
||||||
configuration: IConfiguration,
|
configuration: IConfiguration,
|
||||||
|
@ -62,15 +173,16 @@ export function GetAction(
|
||||||
function GetPreviousAndNextSiblings(containers: Map<string, IContainerModel>, container: IContainerModel): { prev: IContainerModel | undefined, next: IContainerModel | undefined } {
|
function GetPreviousAndNextSiblings(containers: Map<string, IContainerModel>, container: IContainerModel): { prev: IContainerModel | undefined, next: IContainerModel | undefined } {
|
||||||
let prev;
|
let prev;
|
||||||
let next;
|
let next;
|
||||||
if (container.parent !== undefined &&
|
const parent = FindContainerById(containers, container.properties.parentId);
|
||||||
container.parent !== null &&
|
if (parent !== undefined &&
|
||||||
container.parent.children.length > 1) {
|
parent !== null &&
|
||||||
const index = container.parent.children.indexOf(container.properties.id);
|
parent.children.length > 1) {
|
||||||
|
const index = parent.children.indexOf(container.properties.id);
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
prev = FindContainerById(containers, container.parent.children[index - 1]);
|
prev = FindContainerById(containers, parent.children[index - 1]);
|
||||||
}
|
}
|
||||||
if (index < container.parent.children.length - 1) {
|
if (index < parent.children.length - 1) {
|
||||||
next = FindContainerById(containers, container.parent.children[index + 1]);
|
next = FindContainerById(containers, parent.children[index + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { prev, next };
|
return { prev, next };
|
||||||
|
@ -86,6 +198,9 @@ function HandleSetContainerList(
|
||||||
setNewHistory: (newHistory: IHistoryState[]) => void
|
setNewHistory: (newHistory: IHistoryState[]) => void
|
||||||
): void {
|
): void {
|
||||||
const addingBehavior = response.AddingBehavior ?? action.AddingBehavior;
|
const addingBehavior = response.AddingBehavior ?? action.AddingBehavior;
|
||||||
|
const current = GetCurrentHistoryState(history, historyCurrentStep);
|
||||||
|
const containers = current.containers;
|
||||||
|
const parent = FindContainerById(containers, selectedContainer.properties.parentId);
|
||||||
switch (addingBehavior) {
|
switch (addingBehavior) {
|
||||||
case AddMethod.Append:
|
case AddMethod.Append:
|
||||||
setNewHistory(
|
setNewHistory(
|
||||||
|
@ -103,6 +218,7 @@ function HandleSetContainerList(
|
||||||
case AddMethod.Replace:
|
case AddMethod.Replace:
|
||||||
setNewHistory(
|
setNewHistory(
|
||||||
HandleReplace(
|
HandleReplace(
|
||||||
|
containers,
|
||||||
selectedContainer,
|
selectedContainer,
|
||||||
response,
|
response,
|
||||||
configuration,
|
configuration,
|
||||||
|
@ -112,7 +228,7 @@ function HandleSetContainerList(
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AddMethod.ReplaceParent:
|
case AddMethod.ReplaceParent:
|
||||||
if (selectedContainer.parent === undefined || selectedContainer.parent === null) {
|
if (parent === undefined || parent === null) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
text: 'The selected container has not parent to replace',
|
text: 'The selected container has not parent to replace',
|
||||||
|
@ -122,7 +238,8 @@ function HandleSetContainerList(
|
||||||
}
|
}
|
||||||
setNewHistory(
|
setNewHistory(
|
||||||
HandleReplace(
|
HandleReplace(
|
||||||
selectedContainer.parent,
|
containers,
|
||||||
|
parent,
|
||||||
response,
|
response,
|
||||||
configuration,
|
configuration,
|
||||||
history,
|
history,
|
||||||
|
@ -134,17 +251,19 @@ function HandleSetContainerList(
|
||||||
}
|
}
|
||||||
|
|
||||||
function HandleReplace(
|
function HandleReplace(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
selectedContainer: IContainerModel,
|
selectedContainer: IContainerModel,
|
||||||
response: ISetContainerListResponse,
|
response: ISetContainerListResponse,
|
||||||
configuration: IConfiguration,
|
configuration: IConfiguration,
|
||||||
history: IHistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
): IHistoryState[] {
|
): IHistoryState[] {
|
||||||
if (selectedContainer.parent === undefined || selectedContainer.parent === null) {
|
const parent = FindContainerById(containers, selectedContainer.properties.parentId);
|
||||||
|
if (parent === undefined || parent === null) {
|
||||||
throw new Error('[ReplaceContainer] Cannot replace a container that does not exists');
|
throw new Error('[ReplaceContainer] Cannot replace a container that does not exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = selectedContainer.parent.children.indexOf(selectedContainer.properties.id);
|
const index = parent.children.indexOf(selectedContainer.properties.id);
|
||||||
|
|
||||||
const newHistoryAfterDelete = DeleteContainer(
|
const newHistoryAfterDelete = DeleteContainer(
|
||||||
selectedContainer.properties.id,
|
selectedContainer.properties.id,
|
||||||
|
|
|
@ -18,12 +18,11 @@ export function ApplyBehaviors(containers: Map<string, IContainerModel>, contain
|
||||||
try {
|
try {
|
||||||
const symbol = symbols.get(container.properties.linkedSymbolId);
|
const symbol = symbols.get(container.properties.linkedSymbolId);
|
||||||
if (container.properties.linkedSymbolId !== '' && symbol !== undefined) {
|
if (container.properties.linkedSymbolId !== '' && symbol !== undefined) {
|
||||||
ApplySymbol(container, symbol);
|
ApplySymbol(containers, container, symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container.parent !== undefined && container.parent !== null) {
|
const parent = FindContainerById(containers, container.properties.parentId);
|
||||||
const parent = container.parent;
|
if (parent !== undefined && parent !== null) {
|
||||||
|
|
||||||
if (container.properties.isAnchor) {
|
if (container.properties.isAnchor) {
|
||||||
ApplyAnchor(containers, container, parent);
|
ApplyAnchor(containers, container, parent);
|
||||||
}
|
}
|
||||||
|
@ -69,11 +68,12 @@ export function ApplyBehaviorsOnSiblingsChildren(
|
||||||
containers: Map<string, IContainerModel>,
|
containers: Map<string, IContainerModel>,
|
||||||
newContainer: IContainerModel,
|
newContainer: IContainerModel,
|
||||||
symbols: Map<string, ISymbolModel>): void {
|
symbols: Map<string, ISymbolModel>): void {
|
||||||
if (newContainer.parent === null || newContainer.parent === undefined) {
|
const parent = FindContainerById(containers, newContainer.properties.parentId);
|
||||||
|
if (parent === null || parent === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
newContainer.parent.children
|
parent.children
|
||||||
.forEach((containerId: string) => {
|
.forEach((containerId: string) => {
|
||||||
const container = FindContainerById(containers, containerId);
|
const container = FindContainerById(containers, containerId);
|
||||||
|
|
||||||
|
@ -81,8 +81,9 @@ export function ApplyBehaviorsOnSiblingsChildren(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container.parent !== null) {
|
const containerParent = FindContainerById(containers, container.properties.parentId);
|
||||||
UpdateWarning(containers, container, container.parent);
|
if (containerParent !== null && containerParent !== undefined) {
|
||||||
|
UpdateWarning(containers, container, containerParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container === newContainer) {
|
if (container === newContainer) {
|
||||||
|
@ -102,11 +103,12 @@ export function ApplyBehaviorsOnSiblingsChildren(
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function ApplyBehaviorsOnSiblings(containers: Map<string, IContainerModel>, newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
|
export function ApplyBehaviorsOnSiblings(containers: Map<string, IContainerModel>, newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
|
||||||
if (newContainer.parent === null || newContainer.parent === undefined) {
|
const parent = FindContainerById(containers, newContainer.properties.parentId);
|
||||||
|
if (parent === null || parent === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
newContainer.parent.children
|
parent.children
|
||||||
.forEach((containerId: string) => {
|
.forEach((containerId: string) => {
|
||||||
const container = FindContainerById(containers, containerId);
|
const container = FindContainerById(containers, containerId);
|
||||||
|
|
||||||
|
@ -116,8 +118,9 @@ export function ApplyBehaviorsOnSiblings(containers: Map<string, IContainerModel
|
||||||
|
|
||||||
ApplyBehaviors(containers, container, symbols);
|
ApplyBehaviors(containers, container, symbols);
|
||||||
|
|
||||||
if (container.parent != null) {
|
const containerParent = FindContainerById(containers, container.properties.parentId);
|
||||||
UpdateWarning(containers, container, container.parent);
|
if (containerParent !== null && containerParent !== undefined) {
|
||||||
|
UpdateWarning(containers, container, containerParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container === newContainer) {
|
if (container === newContainer) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { ISizePointer } from '../../../Interfaces/ISizePointer';
|
import { ISizePointer } from '../../../Interfaces/ISizePointer';
|
||||||
import { Orientation } from '../../../Enums/Orientation';
|
import { Orientation } from '../../../Enums/Orientation';
|
||||||
import { ENABLE_HARD_RIGID } from '../../../utils/default';
|
import { ENABLE_HARD_RIGID } from '../../../utils/default';
|
||||||
import { MakeChildrenIterator } from '../../../utils/itertools';
|
import { FindContainerById, MakeChildrenIterator } from '../../../utils/itertools';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Transform the container into a rigid body"
|
* "Transform the container into a rigid body"
|
||||||
|
@ -122,17 +122,18 @@ export function ConstraintBodyInsideUnallocatedWidth(
|
||||||
containers: Map<string, IContainerModel>,
|
containers: Map<string, IContainerModel>,
|
||||||
container: IContainerModel
|
container: IContainerModel
|
||||||
): IContainerModel {
|
): IContainerModel {
|
||||||
if (container.parent === null || container.parent === undefined) {
|
const parent = FindContainerById(containers, container.properties.parentId);
|
||||||
|
if (parent === null || parent === undefined) {
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the available spaces of the parent
|
// Get the available spaces of the parent
|
||||||
const isHorizontal =
|
const isHorizontal =
|
||||||
container.parent.properties.orientation === Orientation.Horizontal;
|
parent.properties.orientation === Orientation.Horizontal;
|
||||||
const children: IContainerModel[] = [...MakeChildrenIterator(containers, container.parent.children)];
|
const children: IContainerModel[] = [...MakeChildrenIterator(containers, parent.children)];
|
||||||
const availableWidths = GetAvailableWidths(
|
const availableWidths = GetAvailableWidths(
|
||||||
0,
|
0,
|
||||||
container.parent.properties.width,
|
parent.properties.width,
|
||||||
children,
|
children,
|
||||||
container,
|
container,
|
||||||
isHorizontal
|
isHorizontal
|
||||||
|
@ -172,7 +173,7 @@ export function ConstraintBodyInsideUnallocatedWidth(
|
||||||
container,
|
container,
|
||||||
0,
|
0,
|
||||||
availableWidthFound.x,
|
availableWidthFound.x,
|
||||||
container.parent.properties.width,
|
parent.properties.width,
|
||||||
availableWidthFound.width
|
availableWidthFound.width
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -203,7 +204,7 @@ export function ConstraintBodyInsideUnallocatedWidth(
|
||||||
availableWidthFound.x,
|
availableWidthFound.x,
|
||||||
0,
|
0,
|
||||||
availableWidthFound.width,
|
availableWidthFound.width,
|
||||||
container.parent.properties.height
|
parent.properties.height
|
||||||
);
|
);
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
||||||
import { ApplyParentTransform } from '../../../utils/itertools';
|
import { ApplyParentTransform, FindContainerById } from '../../../utils/itertools';
|
||||||
import { RestoreX, TransformX } from '../../../utils/svg';
|
import { RestoreX, TransformX } from '../../../utils/svg';
|
||||||
|
|
||||||
export function ApplySymbol(container: IContainerModel, symbol: ISymbolModel): IContainerModel {
|
export function ApplySymbol(containers: Map<string, IContainerModel>, container: IContainerModel, symbol: ISymbolModel): IContainerModel {
|
||||||
container.properties.x = TransformX(symbol.x, symbol.width, symbol.config.PositionReference);
|
container.properties.x = TransformX(symbol.x, symbol.width, symbol.config.PositionReference);
|
||||||
container.properties.x = RestoreX(container.properties.x, container.properties.width, container.properties.positionReference);
|
container.properties.x = RestoreX(container.properties.x, container.properties.width, container.properties.positionReference);
|
||||||
const [x] = ApplyParentTransform(container.parent, container.properties.x, 0);
|
const parent = FindContainerById(containers, container.properties.parentId);
|
||||||
|
let x = 0;
|
||||||
|
if (parent !== undefined && parent !== null) {
|
||||||
|
([x] = ApplyParentTransform(containers, parent, container.properties.x, 0));
|
||||||
|
}
|
||||||
container.properties.x = x;
|
container.properties.x = x;
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,12 @@ import { UI } from '../UI/UI';
|
||||||
import { SelectContainer, DeleteContainer, OnPropertyChange } from './Actions/ContainerOperations';
|
import { SelectContainer, DeleteContainer, OnPropertyChange } from './Actions/ContainerOperations';
|
||||||
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save';
|
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save';
|
||||||
import { OnKey } from './Actions/Shortcuts';
|
import { OnKey } from './Actions/Shortcuts';
|
||||||
import { events as EVENTS } from '../../Events/EditorEvents';
|
import { UseCustomEvents, UseEditorListener } from '../../Events/EditorEvents';
|
||||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
import { MAX_HISTORY } from '../../utils/default';
|
||||||
import { DISABLE_API, MAX_HISTORY } from '../../utils/default';
|
|
||||||
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations';
|
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations';
|
||||||
import { FindContainerById } from '../../utils/itertools';
|
import { FindContainerById } from '../../utils/itertools';
|
||||||
import { IMenuAction, Menu } from '../Menu/Menu';
|
import { Menu } from '../Menu/Menu';
|
||||||
import { GetAction } from './Actions/ContextMenuActions';
|
import { InitActions } from './Actions/ContextMenuActions';
|
||||||
import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer';
|
import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer';
|
||||||
|
|
||||||
interface IEditorProps {
|
interface IEditorProps {
|
||||||
|
@ -22,112 +21,6 @@ interface IEditorProps {
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
}
|
}
|
||||||
|
|
||||||
function InitActions(
|
|
||||||
menuActions: Map<string, IMenuAction[]>,
|
|
||||||
configuration: IConfiguration,
|
|
||||||
history: IHistoryState[],
|
|
||||||
historyCurrentStep: number,
|
|
||||||
setNewHistory: (newHistory: IHistoryState[]) => void,
|
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
|
||||||
): void {
|
|
||||||
menuActions.set(
|
|
||||||
'',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: 'Undo',
|
|
||||||
title: 'Undo last action',
|
|
||||||
shortcut: '<kbd>Ctrl</kbd>+<kbd>Z</kbd>',
|
|
||||||
action: () => {
|
|
||||||
if (historyCurrentStep <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setHistoryCurrentStep(historyCurrentStep - 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Redo',
|
|
||||||
title: 'Redo last action',
|
|
||||||
shortcut: '<kbd>Ctrl</kbd>+<kbd>Y</kbd>',
|
|
||||||
action: () => {
|
|
||||||
if (historyCurrentStep >= history.length - 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setHistoryCurrentStep(historyCurrentStep + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
menuActions.set(
|
|
||||||
'elements-sidebar-row',
|
|
||||||
[{
|
|
||||||
text: 'Delete',
|
|
||||||
title: 'Delete the container',
|
|
||||||
shortcut: '<kbd>Suppr</kbd>',
|
|
||||||
action: (target: HTMLElement) => {
|
|
||||||
const id = target.id;
|
|
||||||
const newHistory = DeleteContainer(
|
|
||||||
id,
|
|
||||||
history,
|
|
||||||
historyCurrentStep
|
|
||||||
);
|
|
||||||
setNewHistory(newHistory);
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
|
|
||||||
menuActions.set(
|
|
||||||
'symbols-sidebar-row',
|
|
||||||
[{
|
|
||||||
text: 'Delete',
|
|
||||||
title: 'Delete the container',
|
|
||||||
shortcut: '<kbd>Suppr</kbd>',
|
|
||||||
action: (target: HTMLElement) => {
|
|
||||||
const id = target.id;
|
|
||||||
const newHistory = DeleteSymbol(
|
|
||||||
id,
|
|
||||||
history,
|
|
||||||
historyCurrentStep
|
|
||||||
);
|
|
||||||
setNewHistory(newHistory);
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
|
|
||||||
// API Actions
|
|
||||||
if (DISABLE_API) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const availableContainer of configuration.AvailableContainers) {
|
|
||||||
if (availableContainer.Actions === undefined || availableContainer.Actions === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const action of availableContainer.Actions) {
|
|
||||||
if (menuActions.get(availableContainer.Type) === undefined) {
|
|
||||||
menuActions.set(availableContainer.Type, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentState = GetCurrentHistoryState(history, historyCurrentStep);
|
|
||||||
const newAction: IMenuAction = {
|
|
||||||
text: action.Label,
|
|
||||||
title: action.Description,
|
|
||||||
action: GetAction(
|
|
||||||
action,
|
|
||||||
currentState,
|
|
||||||
configuration,
|
|
||||||
history,
|
|
||||||
historyCurrentStep,
|
|
||||||
setNewHistory
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
menuActions.get(availableContainer.Type)?.push(newAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function UseShortcuts(
|
function UseShortcuts(
|
||||||
history: IHistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
|
@ -152,63 +45,6 @@ function UseShortcuts(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function UseCustomEvents(
|
|
||||||
root: Element | Document,
|
|
||||||
history: IHistoryState[],
|
|
||||||
historyCurrentStep: number,
|
|
||||||
configuration: IConfiguration,
|
|
||||||
editorRef: React.RefObject<HTMLDivElement>,
|
|
||||||
setNewHistory: (newHistory: IHistoryState[], historyCurrentStep?: number) => void
|
|
||||||
): void {
|
|
||||||
useEffect(() => {
|
|
||||||
const editorState: IEditorState = {
|
|
||||||
history,
|
|
||||||
historyCurrentStep,
|
|
||||||
configuration
|
|
||||||
};
|
|
||||||
|
|
||||||
const funcs = new Map<string, () => void>();
|
|
||||||
for (const event of EVENTS) {
|
|
||||||
function Func(eventInitDict?: CustomEventInit): void {
|
|
||||||
return event.func(
|
|
||||||
root,
|
|
||||||
editorState,
|
|
||||||
setNewHistory,
|
|
||||||
eventInitDict
|
|
||||||
);
|
|
||||||
}
|
|
||||||
editorRef.current?.addEventListener(event.name, Func);
|
|
||||||
funcs.set(event.name, Func);
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
for (const event of EVENTS) {
|
|
||||||
const func = funcs.get(event.name);
|
|
||||||
if (func === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
editorRef.current?.removeEventListener(event.name, func);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function UseEditorListener(
|
|
||||||
root: Element | Document,
|
|
||||||
history: IHistoryState[],
|
|
||||||
historyCurrentStep: number,
|
|
||||||
configuration: IConfiguration
|
|
||||||
): void {
|
|
||||||
useEffect(() => {
|
|
||||||
const editorState: IEditorState = {
|
|
||||||
history,
|
|
||||||
historyCurrentStep,
|
|
||||||
configuration
|
|
||||||
};
|
|
||||||
const event = new CustomEvent('editorListener', { detail: editorState });
|
|
||||||
root.dispatchEvent(event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a macro function to use both setHistory
|
* Return a macro function to use both setHistory
|
||||||
* and setHistoryCurrentStep at the same time
|
* and setHistoryCurrentStep at the same time
|
||||||
|
@ -279,14 +115,11 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div ref={editorRef} className="Editor font-sans h-full">
|
<div ref={editorRef} className="Editor font-sans h-full">
|
||||||
<UI
|
<UI
|
||||||
selectedContainer={selected}
|
editorState={{
|
||||||
current={current}
|
configuration: props.configuration,
|
||||||
history={history}
|
history,
|
||||||
historyCurrentStep={historyCurrentStep}
|
historyCurrentStep
|
||||||
availableContainers={configuration.AvailableContainers}
|
}}
|
||||||
availableSymbols={configuration.AvailableSymbols}
|
|
||||||
categories={configuration.Categories}
|
|
||||||
apiConfiguration={configuration.APIConfiguration}
|
|
||||||
selectContainer={(container) => setNewHistory(
|
selectContainer={(container) => setNewHistory(
|
||||||
SelectContainer(
|
SelectContainer(
|
||||||
container,
|
container,
|
||||||
|
@ -307,16 +140,17 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
))}
|
))}
|
||||||
addContainer={(type) => {
|
addContainer={(type) => {
|
||||||
const newHistory = AddContainerToSelectedContainer(
|
if (selected === null || selected === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setNewHistory(AddContainerToSelectedContainer(
|
||||||
type,
|
type,
|
||||||
selected,
|
selected,
|
||||||
configuration,
|
configuration,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
);
|
));
|
||||||
if (newHistory !== null) {
|
|
||||||
setNewHistory(newHistory);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
addContainerAt={(index, type, parent) => setNewHistory(
|
addContainerAt={(index, type, parent) => setNewHistory(
|
||||||
AddContainer(
|
AddContainer(
|
||||||
|
|
|
@ -87,8 +87,9 @@ function HandleOnDrop(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetContainer.parent === null ||
|
const parent = FindContainerById(containers, targetContainer.properties.parentId);
|
||||||
targetContainer.parent === undefined) {
|
if (parent === null ||
|
||||||
|
parent === undefined) {
|
||||||
throw new Error('[handleDrop] Tried to drop into a child container without a parent!');
|
throw new Error('[handleDrop] Tried to drop into a child container without a parent!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,11 +98,11 @@ function HandleOnDrop(
|
||||||
|
|
||||||
// locate the hitboxes
|
// locate the hitboxes
|
||||||
if (y < 12) {
|
if (y < 12) {
|
||||||
const index = targetContainer.parent.children.indexOf(targetContainer.properties.id);
|
const index = parent.children.indexOf(targetContainer.properties.id);
|
||||||
addContainer(
|
addContainer(
|
||||||
index,
|
index,
|
||||||
type,
|
type,
|
||||||
targetContainer.parent.properties.id
|
parent.properties.id
|
||||||
);
|
);
|
||||||
} else if (y < 24) {
|
} else if (y < 24) {
|
||||||
addContainer(
|
addContainer(
|
||||||
|
@ -109,11 +110,11 @@ function HandleOnDrop(
|
||||||
type,
|
type,
|
||||||
targetContainer.properties.id);
|
targetContainer.properties.id);
|
||||||
} else {
|
} else {
|
||||||
const index = targetContainer.parent.children.indexOf(targetContainer.properties.id);
|
const index = parent.children.indexOf(targetContainer.properties.id);
|
||||||
addContainer(
|
addContainer(
|
||||||
index + 1,
|
index + 1,
|
||||||
type,
|
type,
|
||||||
targetContainer.parent.properties.id
|
parent.properties.id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,7 @@ function AddClassSpecificActions(
|
||||||
shortcut={action.shortcut}
|
shortcut={action.shortcut}
|
||||||
onClick={() => action.action(target)} />);
|
onClick={() => action.action(target)} />);
|
||||||
});
|
});
|
||||||
children.push(<hr className='border-slate-400' />);
|
children.push(<hr key={`contextmenu-hr-${count}`} className='border-slate-400' />);
|
||||||
};
|
};
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ function GetDimensionsNodes(
|
||||||
max = -Infinity;
|
max = -Infinity;
|
||||||
}
|
}
|
||||||
|
|
||||||
const absoluteX = GetAbsolutePosition(container)[0];
|
const absoluteX = GetAbsolutePosition(containers, container)[0];
|
||||||
const x = TransformX(absoluteX, container.properties.width, container.properties.positionReference);
|
const x = TransformX(absoluteX, container.properties.width, container.properties.positionReference);
|
||||||
lastY = container.properties.y + container.properties.height;
|
lastY = container.properties.y + container.properties.height;
|
||||||
if (x < min) {
|
if (x < min) {
|
||||||
|
@ -74,7 +74,7 @@ function AddNewDimension(currentDepth: number, min: number, max: number, lastY:
|
||||||
const id = `dim-depth-${currentDepth}`;
|
const id = `dim-depth-${currentDepth}`;
|
||||||
const xStart = min;
|
const xStart = min;
|
||||||
const xEnd = max;
|
const xEnd = max;
|
||||||
const y = (lastY + (DIMENSION_MARGIN * (currentDepth + 1))) / scale;
|
const y = lastY + (DIMENSION_MARGIN * (currentDepth + 1)) / scale;
|
||||||
const strokeWidth = 1;
|
const strokeWidth = 1;
|
||||||
const width = xEnd - xStart;
|
const width = xEnd - xStart;
|
||||||
const text = width
|
const text = width
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { GetAbsolutePosition } from '../../../../utils/itertools';
|
||||||
import { RemoveMargin } from '../../../../utils/svg';
|
import { RemoveMargin } from '../../../../utils/svg';
|
||||||
|
|
||||||
interface ISelectorProps {
|
interface ISelectorProps {
|
||||||
|
containers: Map<string, IContainerModel>
|
||||||
selected?: IContainerModel
|
selected?: IContainerModel
|
||||||
scale?: number
|
scale?: number
|
||||||
}
|
}
|
||||||
|
@ -19,7 +20,7 @@ export function Selector(props: ISelectorProps): JSX.Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
const scale = (props.scale ?? 1);
|
const scale = (props.scale ?? 1);
|
||||||
let [x, y] = GetAbsolutePosition(props.selected);
|
let [x, y] = GetAbsolutePosition(props.containers, props.selected);
|
||||||
let [width, height] = [
|
let [width, height] = [
|
||||||
props.selected.properties.width,
|
props.selected.properties.width,
|
||||||
props.selected.properties.height
|
props.selected.properties.height
|
||||||
|
|
|
@ -98,7 +98,7 @@ export function SVG(props: ISVGProps): JSX.Element {
|
||||||
: null}
|
: null}
|
||||||
<DimensionLayer containers={props.containers} scale={scale} root={props.children} />
|
<DimensionLayer containers={props.containers} scale={scale} root={props.children} />
|
||||||
<SymbolLayer scale={scale} symbols={props.symbols} />
|
<SymbolLayer scale={scale} symbols={props.symbols} />
|
||||||
<Selector scale={scale} selected={props.selected} /> {/* leave this at the end so it can be removed during the svg export */}
|
<Selector containers={props.containers} scale={scale} selected={props.selected} /> {/* leave this at the end so it can be removed during the svg export */}
|
||||||
</svg>
|
</svg>
|
||||||
</ReactSVGPanZoom>
|
</ReactSVGPanZoom>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ElementsList } from '../ElementsList/ElementsList';
|
import { ElementsList } from '../ElementsList/ElementsList';
|
||||||
import { History } from '../History/History';
|
import { History } from '../History/History';
|
||||||
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
|
||||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
|
||||||
import { Bar } from '../Bar/Bar';
|
import { Bar } from '../Bar/Bar';
|
||||||
import { IAvailableSymbol } from '../../Interfaces/IAvailableSymbol';
|
|
||||||
import { Symbols } from '../Symbols/Symbols';
|
import { Symbols } from '../Symbols/Symbols';
|
||||||
import { SymbolsSidebar } from '../SymbolsList/SymbolsList';
|
import { SymbolsSidebar } from '../SymbolsList/SymbolsList';
|
||||||
import { PropertyType } from '../../Enums/PropertyType';
|
import { PropertyType } from '../../Enums/PropertyType';
|
||||||
import { Messages } from '../Messages/Messages';
|
import { Messages } from '../Messages/Messages';
|
||||||
import { ICategory } from '../../Interfaces/ICategory';
|
|
||||||
import { Sidebar } from '../Sidebar/Sidebar';
|
import { Sidebar } from '../Sidebar/Sidebar';
|
||||||
import { Components } from '../Components/Components';
|
import { Components } from '../Components/Components';
|
||||||
import { Viewer } from '../Viewer/Viewer';
|
import { Viewer } from '../Viewer/Viewer';
|
||||||
|
@ -19,17 +14,11 @@ import { IMessage } from '../../Interfaces/IMessage';
|
||||||
import { DISABLE_API } from '../../utils/default';
|
import { DISABLE_API } from '../../utils/default';
|
||||||
import { UseWorker, UseAsync } from './UseWorker';
|
import { UseWorker, UseAsync } from './UseWorker';
|
||||||
import { FindContainerById } from '../../utils/itertools';
|
import { FindContainerById } from '../../utils/itertools';
|
||||||
import { IAPIConfiguration } from '../../Interfaces/IAPIConfiguration';
|
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||||
|
import { GetCurrentHistoryState } from '../Editor/Editor';
|
||||||
|
|
||||||
export interface IUIProps {
|
export interface IUIProps {
|
||||||
selectedContainer: IContainerModel | undefined
|
editorState: IEditorState
|
||||||
current: IHistoryState
|
|
||||||
history: IHistoryState[]
|
|
||||||
historyCurrentStep: number
|
|
||||||
availableContainers: IAvailableContainer[]
|
|
||||||
availableSymbols: IAvailableSymbol[]
|
|
||||||
categories: ICategory[]
|
|
||||||
apiConfiguration: IAPIConfiguration | undefined
|
|
||||||
selectContainer: (containerId: string) => void
|
selectContainer: (containerId: string) => void
|
||||||
deleteContainer: (containerId: string) => void
|
deleteContainer: (containerId: string) => void
|
||||||
onPropertyChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void
|
onPropertyChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void
|
||||||
|
@ -67,21 +56,23 @@ function UseSetOrToggleSidebar(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UI(props: IUIProps): JSX.Element {
|
export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
const [selectedSidebar, setSelectedSidebar] = React.useState<SidebarType>(SidebarType.Components);
|
const [selectedSidebar, setSelectedSidebar] = React.useState<SidebarType>(SidebarType.Components);
|
||||||
const [messages, setMessages] = React.useState<IMessage[]>([]);
|
const [messages, setMessages] = React.useState<IMessage[]>([]);
|
||||||
|
const current = GetCurrentHistoryState(editorState.history, editorState.historyCurrentStep);
|
||||||
|
const configuration = editorState.configuration;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||||
if (window.Worker && !DISABLE_API) {
|
if (window.Worker && !DISABLE_API) {
|
||||||
UseWorker(
|
UseWorker(
|
||||||
props.current,
|
current,
|
||||||
props.apiConfiguration?.apiGetFeedbackUrl,
|
configuration.APIConfiguration?.apiGetFeedbackUrl,
|
||||||
setMessages
|
setMessages
|
||||||
);
|
);
|
||||||
} else if (!DISABLE_API) {
|
} else if (!DISABLE_API) {
|
||||||
UseAsync(
|
UseAsync(
|
||||||
props.current,
|
current,
|
||||||
props.apiConfiguration?.apiGetFeedbackUrl,
|
configuration.APIConfiguration?.apiGetFeedbackUrl,
|
||||||
setMessages
|
setMessages
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -94,61 +85,63 @@ export function UI(props: IUIProps): JSX.Element {
|
||||||
let leftChildren: JSX.Element = (<></>);
|
let leftChildren: JSX.Element = (<></>);
|
||||||
let rightChildren: JSX.Element = (<></>);
|
let rightChildren: JSX.Element = (<></>);
|
||||||
|
|
||||||
const mainContainer = FindContainerById(props.current.containers, props.current.mainContainer)
|
const mainContainer = FindContainerById(current.containers, current.mainContainer);
|
||||||
|
|
||||||
if (mainContainer === undefined) {
|
if (mainContainer === undefined) {
|
||||||
throw new Error('Tried to initialized UI but there is no main container!');
|
throw new Error('Tried to initialized UI but there is no main container!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedContainer = FindContainerById(current.containers, current.selectedContainerId);
|
||||||
|
|
||||||
switch (selectedSidebar) {
|
switch (selectedSidebar) {
|
||||||
case SidebarType.Components:
|
case SidebarType.Components:
|
||||||
leftSidebarTitle = 'Components';
|
leftSidebarTitle = 'Components';
|
||||||
leftChildren = <Components
|
leftChildren = <Components
|
||||||
selectedContainer={props.selectedContainer}
|
selectedContainer={selectedContainer}
|
||||||
componentOptions={props.availableContainers}
|
componentOptions={configuration.AvailableContainers}
|
||||||
categories={props.categories}
|
categories={configuration.Categories}
|
||||||
buttonOnClick={props.addContainer}
|
buttonOnClick={methods.addContainer}
|
||||||
/>;
|
/>;
|
||||||
rightSidebarTitle = 'Elements';
|
rightSidebarTitle = 'Elements';
|
||||||
rightChildren = <ElementsList
|
rightChildren = <ElementsList
|
||||||
containers={props.current.containers}
|
containers={current.containers}
|
||||||
mainContainer={mainContainer}
|
mainContainer={mainContainer}
|
||||||
symbols={props.current.symbols}
|
symbols={current.symbols}
|
||||||
selectedContainer={props.selectedContainer}
|
selectedContainer={selectedContainer}
|
||||||
onPropertyChange={props.onPropertyChange}
|
onPropertyChange={methods.onPropertyChange}
|
||||||
selectContainer={props.selectContainer}
|
selectContainer={methods.selectContainer}
|
||||||
addContainer={props.addContainerAt}
|
addContainer={methods.addContainerAt}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SidebarType.Symbols:
|
case SidebarType.Symbols:
|
||||||
leftSidebarTitle = 'Symbols';
|
leftSidebarTitle = 'Symbols';
|
||||||
leftChildren = <Symbols
|
leftChildren = <Symbols
|
||||||
componentOptions={props.availableSymbols}
|
componentOptions={configuration.AvailableSymbols}
|
||||||
buttonOnClick={props.addSymbol}
|
buttonOnClick={methods.addSymbol}
|
||||||
/>;
|
/>;
|
||||||
rightSidebarTitle = 'Symbols';
|
rightSidebarTitle = 'Symbols';
|
||||||
rightChildren = <SymbolsSidebar
|
rightChildren = <SymbolsSidebar
|
||||||
selectedSymbolId={props.current.selectedSymbolId}
|
selectedSymbolId={current.selectedSymbolId}
|
||||||
symbols={props.current.symbols}
|
symbols={current.symbols}
|
||||||
onPropertyChange={props.onSymbolPropertyChange}
|
onPropertyChange={methods.onSymbolPropertyChange}
|
||||||
selectSymbol={props.selectSymbol}
|
selectSymbol={methods.selectSymbol}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SidebarType.History:
|
case SidebarType.History:
|
||||||
leftSidebarTitle = 'Timeline';
|
leftSidebarTitle = 'Timeline';
|
||||||
leftChildren = <History
|
leftChildren = <History
|
||||||
history={props.history}
|
history={editorState.history}
|
||||||
historyCurrentStep={props.historyCurrentStep}
|
historyCurrentStep={editorState.historyCurrentStep}
|
||||||
jumpTo={props.loadState}
|
jumpTo={methods.loadState}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SidebarType.Messages:
|
case SidebarType.Messages:
|
||||||
leftSidebarTitle = 'Messages';
|
leftSidebarTitle = 'Messages';
|
||||||
leftChildren = <Messages
|
leftChildren = <Messages
|
||||||
historyState={props.current}
|
historyState={current}
|
||||||
messages={messages}
|
messages={messages}
|
||||||
clearMessage={() => setMessages([])}
|
clearMessage={() => setMessages([])}
|
||||||
/>;
|
/>;
|
||||||
|
@ -157,8 +150,8 @@ export function UI(props: IUIProps): JSX.Element {
|
||||||
case SidebarType.Settings:
|
case SidebarType.Settings:
|
||||||
leftSidebarTitle = 'Settings';
|
leftSidebarTitle = 'Settings';
|
||||||
leftChildren = <Settings
|
leftChildren = <Settings
|
||||||
saveEditorAsJSON={props.saveEditorAsJSON}
|
saveEditorAsJSON={methods.saveEditorAsJSON}
|
||||||
saveEditorAsSVG={props.saveEditorAsSVG}
|
saveEditorAsSVG={methods.saveEditorAsSVG}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +159,7 @@ export function UI(props: IUIProps): JSX.Element {
|
||||||
const isLeftSidebarOpen = selectedSidebar !== SidebarType.None;
|
const isLeftSidebarOpen = selectedSidebar !== SidebarType.None;
|
||||||
const isRightSidebarOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.Symbols;
|
const isRightSidebarOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.Symbols;
|
||||||
|
|
||||||
let isLeftSidebarOpenClasses = new Set<string>([
|
const isLeftSidebarOpenClasses = new Set<string>([
|
||||||
'left-sidebar',
|
'left-sidebar',
|
||||||
'left-16',
|
'left-16',
|
||||||
'-bottom-full',
|
'-bottom-full',
|
||||||
|
@ -222,9 +215,9 @@ export function UI(props: IUIProps): JSX.Element {
|
||||||
<Viewer
|
<Viewer
|
||||||
isLeftSidebarOpen={isLeftSidebarOpen}
|
isLeftSidebarOpen={isLeftSidebarOpen}
|
||||||
isRightSidebarOpen={isRightSidebarOpen}
|
isRightSidebarOpen={isRightSidebarOpen}
|
||||||
current={props.current}
|
current={current}
|
||||||
selectedContainer={props.selectedContainer}
|
selectedContainer={selectedContainer}
|
||||||
selectContainer={props.selectContainer}
|
selectContainer={methods.selectContainer}
|
||||||
/>
|
/>
|
||||||
<Sidebar
|
<Sidebar
|
||||||
className={`right-sidebar ${isRightSidebarOpenClasses}`}
|
className={`right-sidebar ${isRightSidebarOpenClasses}`}
|
||||||
|
|
|
@ -147,6 +147,7 @@ export function Viewer({
|
||||||
|
|
||||||
// Draw selector
|
// Draw selector
|
||||||
RenderSelector(ctx, frameCount, {
|
RenderSelector(ctx, frameCount, {
|
||||||
|
containers: current.containers,
|
||||||
scale,
|
scale,
|
||||||
selected: selectedContainer
|
selected: selectedContainer
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
import { IConfiguration } from '../Interfaces/IConfiguration';
|
import { IConfiguration } from '../Interfaces/IConfiguration';
|
||||||
import { IEditorState } from '../Interfaces/IEditorState';
|
import { IEditorState } from '../Interfaces/IEditorState';
|
||||||
import { IHistoryState } from '../Interfaces/IHistoryState';
|
import { IHistoryState } from '../Interfaces/IHistoryState';
|
||||||
|
@ -22,6 +23,38 @@ export const events: IAppEvent[] = [
|
||||||
{ name: 'getDefaultEditorState', func: GetDefaultEditorState }
|
{ name: 'getDefaultEditorState', func: GetDefaultEditorState }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export function UseCustomEvents(
|
||||||
|
root: Element | Document,
|
||||||
|
appRef: React.RefObject<HTMLDivElement>,
|
||||||
|
setEditor: (newState: IEditorState) => void,
|
||||||
|
setLoaded: (loaded: boolean) => void
|
||||||
|
): void {
|
||||||
|
useEffect(() => {
|
||||||
|
const funcs = new Map<string, () => void>();
|
||||||
|
for (const event of events) {
|
||||||
|
function Func(eventInitDict?: CustomEventInit): void {
|
||||||
|
return event.func(
|
||||||
|
root,
|
||||||
|
setEditor,
|
||||||
|
setLoaded,
|
||||||
|
eventInitDict
|
||||||
|
);
|
||||||
|
}
|
||||||
|
appRef.current?.addEventListener(event.name, Func);
|
||||||
|
funcs.set(event.name, Func);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
for (const event of events) {
|
||||||
|
const func = funcs.get(event.name);
|
||||||
|
if (func === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
appRef.current?.removeEventListener(event.name, func);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function SetEditor(
|
function SetEditor(
|
||||||
root: Element | Document,
|
root: Element | Document,
|
||||||
setEditor: (newState: IEditorState) => void,
|
setEditor: (newState: IEditorState) => void,
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
import { AddContainer as AddContainerAction, AddContainerToSelectedContainer as AddContainerToSelectedContainerAction } from '../Components/Editor/Actions/AddContainer';
|
import { AddContainer as AddContainerAction, AddContainerToSelectedContainer as AddContainerToSelectedContainerAction } from '../Components/Editor/Actions/AddContainer';
|
||||||
import { DeleteContainer as DeleteContainerAction, SelectContainer as SelectContainerAction } from '../Components/Editor/Actions/ContainerOperations';
|
import { DeleteContainer as DeleteContainerAction, SelectContainer as SelectContainerAction } from '../Components/Editor/Actions/ContainerOperations';
|
||||||
import { AddSymbol as AddSymbolAction, DeleteSymbol as DeleteSymbolAction, SelectSymbol as SelectSymbolAction } from '../Components/Editor/Actions/SymbolOperations';
|
import { AddSymbol as AddSymbolAction, DeleteSymbol as DeleteSymbolAction, SelectSymbol as SelectSymbolAction } from '../Components/Editor/Actions/SymbolOperations';
|
||||||
import { GetCurrentHistory } from '../Components/Editor/Editor';
|
import { GetCurrentHistory } from '../Components/Editor/Editor';
|
||||||
|
import { IConfiguration } from '../Interfaces/IConfiguration';
|
||||||
import { IEditorState } from '../Interfaces/IEditorState';
|
import { IEditorState } from '../Interfaces/IEditorState';
|
||||||
import { IHistoryState } from '../Interfaces/IHistoryState';
|
import { IHistoryState } from '../Interfaces/IHistoryState';
|
||||||
import { FindContainerById } from '../utils/itertools';
|
import { FindContainerById } from '../utils/itertools';
|
||||||
import { GetCircularReplacer, ReviveHistory as ReviveHistoryAction } from '../utils/saveload';
|
import { GetCircularReplacer } from '../utils/saveload';
|
||||||
|
|
||||||
export interface IEditorEvent {
|
export interface IEditorEvent {
|
||||||
name: string
|
name: string
|
||||||
|
@ -34,6 +36,63 @@ export const events: IEditorEvent[] = [
|
||||||
{ name: 'deleteSymbol', func: DeleteSymbol }
|
{ name: 'deleteSymbol', func: DeleteSymbol }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export function UseCustomEvents(
|
||||||
|
root: Element | Document,
|
||||||
|
history: IHistoryState[],
|
||||||
|
historyCurrentStep: number,
|
||||||
|
configuration: IConfiguration,
|
||||||
|
editorRef: React.RefObject<HTMLDivElement>,
|
||||||
|
setNewHistory: (newHistory: IHistoryState[], historyCurrentStep?: number) => void
|
||||||
|
): void {
|
||||||
|
useEffect(() => {
|
||||||
|
const editorState: IEditorState = {
|
||||||
|
history,
|
||||||
|
historyCurrentStep,
|
||||||
|
configuration
|
||||||
|
};
|
||||||
|
|
||||||
|
const funcs = new Map<string, () => void>();
|
||||||
|
for (const event of events) {
|
||||||
|
function Func(eventInitDict?: CustomEventInit): void {
|
||||||
|
return event.func(
|
||||||
|
root,
|
||||||
|
editorState,
|
||||||
|
setNewHistory,
|
||||||
|
eventInitDict
|
||||||
|
);
|
||||||
|
}
|
||||||
|
editorRef.current?.addEventListener(event.name, Func);
|
||||||
|
funcs.set(event.name, Func);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
for (const event of events) {
|
||||||
|
const func = funcs.get(event.name);
|
||||||
|
if (func === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
editorRef.current?.removeEventListener(event.name, func);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UseEditorListener(
|
||||||
|
root: Element | Document,
|
||||||
|
history: IHistoryState[],
|
||||||
|
historyCurrentStep: number,
|
||||||
|
configuration: IConfiguration
|
||||||
|
): void {
|
||||||
|
useEffect(() => {
|
||||||
|
const editorState: IEditorState = {
|
||||||
|
history,
|
||||||
|
historyCurrentStep,
|
||||||
|
configuration
|
||||||
|
};
|
||||||
|
const event = new CustomEvent('editorListener', { detail: editorState });
|
||||||
|
root.dispatchEvent(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function GetEditorState(root: Element | Document,
|
function GetEditorState(root: Element | Document,
|
||||||
editorState: IEditorState): void {
|
editorState: IEditorState): void {
|
||||||
const customEvent = new CustomEvent<IEditorState>('getEditorState', { detail: structuredClone(editorState) });
|
const customEvent = new CustomEvent<IEditorState>('getEditorState', { detail: structuredClone(editorState) });
|
||||||
|
@ -181,20 +240,16 @@ function AppendContainerToSelectedContainer(root: Element | Document,
|
||||||
|
|
||||||
const selected = FindContainerById(currentState.containers, currentState.selectedContainerId);
|
const selected = FindContainerById(currentState.containers, currentState.selectedContainerId);
|
||||||
|
|
||||||
const newHistory = AddContainerToSelectedContainerAction(
|
if (selected !== null && selected !== undefined) {
|
||||||
type,
|
setNewHistory(AddContainerToSelectedContainerAction(
|
||||||
selected,
|
type,
|
||||||
editorState.configuration,
|
selected,
|
||||||
history,
|
editorState.configuration,
|
||||||
editorState.historyCurrentStep
|
history,
|
||||||
);
|
editorState.historyCurrentStep
|
||||||
|
));
|
||||||
if (newHistory === null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setNewHistory(newHistory);
|
|
||||||
|
|
||||||
const customEvent = new CustomEvent<IHistoryState>(
|
const customEvent = new CustomEvent<IHistoryState>(
|
||||||
'appendContainerToSelectedContainer',
|
'appendContainerToSelectedContainer',
|
||||||
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) });
|
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) });
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { IAction } from './IAction';
|
||||||
import { IMargin } from './IMargin';
|
import { IMargin } from './IMargin';
|
||||||
import { Orientation } from '../Enums/Orientation';
|
import { Orientation } from '../Enums/Orientation';
|
||||||
import { Position } from '../Enums/Position';
|
import { Position } from '../Enums/Position';
|
||||||
|
import { IKeyValue } from './IKeyValue';
|
||||||
|
|
||||||
/** Model of available container used in application configuration */
|
/** Model of available container used in application configuration */
|
||||||
export interface IAvailableContainer {
|
export interface IAvailableContainer {
|
||||||
|
@ -161,5 +162,5 @@ export interface IAvailableContainer {
|
||||||
* (optional)
|
* (optional)
|
||||||
* User data that can be used for data storage or custom SVG
|
* User data that can be used for data storage or custom SVG
|
||||||
*/
|
*/
|
||||||
UserData?: object
|
UserData?: IKeyValue[]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,6 @@ import { IContainerProperties } from './IContainerProperties';
|
||||||
|
|
||||||
export interface IContainerModel {
|
export interface IContainerModel {
|
||||||
children: string[]
|
children: string[]
|
||||||
// TODO: Remove parent now that accessing the parent by id is faster.
|
|
||||||
// TODO: Use GetContainerById(container.properties.parentId) as the better alternative.
|
|
||||||
parent: IContainerModel | null
|
|
||||||
properties: IContainerProperties
|
properties: IContainerProperties
|
||||||
userData: Record<string, string | number>
|
userData: Record<string, string | number>
|
||||||
}
|
}
|
||||||
|
@ -15,18 +12,13 @@ export interface IContainerModel {
|
||||||
*/
|
*/
|
||||||
export class ContainerModel implements IContainerModel {
|
export class ContainerModel implements IContainerModel {
|
||||||
public children: string[];
|
public children: string[];
|
||||||
// TODO: Remove parent now that accessing the parent by id is faster.
|
|
||||||
// TODO: Use GetContainerById(container.properties.parentId) as the better alternative.
|
|
||||||
public parent: IContainerModel | null;
|
|
||||||
public properties: IContainerProperties;
|
public properties: IContainerProperties;
|
||||||
public userData: Record<string, string | number>;
|
public userData: Record<string, string | number>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
parent: IContainerModel | null,
|
|
||||||
properties: IContainerProperties,
|
properties: IContainerProperties,
|
||||||
children: string[] = [],
|
children: string[] = [],
|
||||||
userData = {}) {
|
userData = {}) {
|
||||||
this.parent = parent;
|
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.children = children;
|
this.children = children;
|
||||||
this.userData = userData;
|
this.userData = userData;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { PositionReference } from '../Enums/PositionReference';
|
||||||
import { IMargin } from './IMargin';
|
import { IMargin } from './IMargin';
|
||||||
import { Orientation } from '../Enums/Orientation';
|
import { Orientation } from '../Enums/Orientation';
|
||||||
import { Position } from '../Enums/Position';
|
import { Position } from '../Enums/Position';
|
||||||
|
import { IKeyValue } from './IKeyValue';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Properties of a container
|
* Properties of a container
|
||||||
|
@ -122,5 +123,5 @@ export interface IContainerProperties {
|
||||||
* (optional)
|
* (optional)
|
||||||
* User data that can be used for data storage or custom SVG
|
* User data that can be used for data storage or custom SVG
|
||||||
*/
|
*/
|
||||||
userData?: object
|
userData?: IKeyValue[]
|
||||||
}
|
}
|
||||||
|
|
5
src/Interfaces/IKeyValue.ts
Normal file
5
src/Interfaces/IKeyValue.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
|
export interface IKeyValue {
|
||||||
|
Key: string
|
||||||
|
Value: string
|
||||||
|
}
|
|
@ -121,7 +121,6 @@ export function GetDefaultEditorState(configuration: IConfiguration): IEditorSta
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const mainContainer = new ContainerModel(
|
const mainContainer = new ContainerModel(
|
||||||
null,
|
|
||||||
mainContainerConfig
|
mainContainerConfig
|
||||||
);
|
);
|
||||||
const containers = new Map<string, IContainerModel>();
|
const containers = new Map<string, IContainerModel>();
|
||||||
|
@ -235,8 +234,6 @@ export function GetDefaultContainerProps(type: string,
|
||||||
height: number,
|
height: number,
|
||||||
containerConfig: IAvailableContainer): IContainerProperties {
|
containerConfig: IAvailableContainer): IContainerProperties {
|
||||||
const orientation = containerConfig.Orientation ?? Orientation.Horizontal;
|
const orientation = containerConfig.Orientation ?? Orientation.Horizontal;
|
||||||
const defaultIsFlex = (orientation === Orientation.Vertical && containerConfig.Height === undefined) ||
|
|
||||||
(orientation === Orientation.Horizontal && containerConfig.Width === undefined);
|
|
||||||
return ({
|
return ({
|
||||||
id: `${type}-${typeCount}`,
|
id: `${type}-${typeCount}`,
|
||||||
type,
|
type,
|
||||||
|
@ -250,7 +247,7 @@ export function GetDefaultContainerProps(type: string,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
isAnchor: containerConfig.IsAnchor ?? false,
|
isAnchor: containerConfig.IsAnchor ?? false,
|
||||||
isFlex: containerConfig.IsFlex ?? defaultIsFlex,
|
isFlex: containerConfig.IsFlex ?? false,
|
||||||
positionReference: containerConfig.PositionReference ?? PositionReference.TopLeft,
|
positionReference: containerConfig.PositionReference ?? PositionReference.TopLeft,
|
||||||
minWidth: containerConfig.MinWidth ?? 1,
|
minWidth: containerConfig.MinWidth ?? 1,
|
||||||
maxWidth: containerConfig.MaxWidth ?? Number.MAX_SAFE_INTEGER,
|
maxWidth: containerConfig.MaxWidth ?? Number.MAX_SAFE_INTEGER,
|
||||||
|
|
|
@ -123,16 +123,16 @@ export function * MakeRecursionDFSIterator(
|
||||||
/**
|
/**
|
||||||
* Returns the depth of the container
|
* Returns the depth of the container
|
||||||
* @returns The depth of the container
|
* @returns The depth of the container
|
||||||
* @deprecated Please avoid using this function inside an iterationl,
|
* @deprecated Please avoid using this function inside an iteration,
|
||||||
* use recursive DFS or iterative BFS to get the depth
|
* use recursive DFS or iterative BFS to get the depth
|
||||||
*/
|
*/
|
||||||
export function GetDepth(parent: IContainerModel): number {
|
export function GetDepth(containers: Map<string, IContainerModel>, parent: IContainerModel): number {
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
|
|
||||||
let current: IContainerModel | null = parent;
|
let current: IContainerModel | null = parent;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
depth++;
|
depth++;
|
||||||
current = current.parent;
|
current = FindContainerById(containers, current.properties.parentId) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return depth;
|
return depth;
|
||||||
|
@ -142,22 +142,31 @@ export function GetDepth(parent: IContainerModel): number {
|
||||||
* Returns the absolute position by iterating to the parent
|
* Returns the absolute position by iterating to the parent
|
||||||
* @returns The absolute position of the container
|
* @returns The absolute position of the container
|
||||||
*/
|
*/
|
||||||
export function GetAbsolutePosition(container: IContainerModel): [number, number] {
|
export function GetAbsolutePosition(containers: Map<string, IContainerModel>, container: IContainerModel): [number, number] {
|
||||||
const x = container.properties.x;
|
const x = container.properties.x;
|
||||||
const y = container.properties.y;
|
const y = container.properties.y;
|
||||||
return CancelParentTransform(container.parent, x, y);
|
const parent = FindContainerById(containers, container.properties.parentId) ?? null;
|
||||||
|
return CancelParentTransform(containers, parent, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GetContainerLinkedList(container: IContainerModel, stop?: IContainerModel): IContainerModel[] {
|
export function GetContainerLinkedList(
|
||||||
const it = MakeContainerLinkedListIterator(container, stop);
|
containers: Map<string, IContainerModel>,
|
||||||
|
container: IContainerModel,
|
||||||
|
stop?: IContainerModel
|
||||||
|
): IContainerModel[] {
|
||||||
|
const it = MakeContainerLinkedListIterator(containers, container, stop);
|
||||||
return [...it];
|
return [...it];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function * MakeContainerLinkedListIterator(container: IContainerModel, stop?: IContainerModel): Generator<IContainerModel, void, unknown> {
|
export function * MakeContainerLinkedListIterator(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
|
container: IContainerModel,
|
||||||
|
stop?: IContainerModel
|
||||||
|
): Generator<IContainerModel, void, unknown> {
|
||||||
let current: IContainerModel | null = container;
|
let current: IContainerModel | null = container;
|
||||||
while (current !== stop && current != null) {
|
while (current !== stop && current != null) {
|
||||||
yield current;
|
yield current;
|
||||||
current = current.parent;
|
current = FindContainerById(containers, current.properties.parentId) ?? null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +178,7 @@ export function * MakeContainerLinkedListIterator(container: IContainerModel, st
|
||||||
* @returns x and y such that the transformations of the parent are cancelled
|
* @returns x and y such that the transformations of the parent are cancelled
|
||||||
*/
|
*/
|
||||||
export function CancelParentTransform(
|
export function CancelParentTransform(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
parent: IContainerModel | null,
|
parent: IContainerModel | null,
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
|
@ -178,7 +188,7 @@ export function CancelParentTransform(
|
||||||
return [x, y];
|
return [x, y];
|
||||||
}
|
}
|
||||||
|
|
||||||
const it = MakeContainerLinkedListIterator(parent, stop);
|
const it = MakeContainerLinkedListIterator(containers, parent, stop);
|
||||||
for (const current of it) {
|
for (const current of it) {
|
||||||
x += current.properties.x;
|
x += current.properties.x;
|
||||||
y += current.properties.y;
|
y += current.properties.y;
|
||||||
|
@ -195,6 +205,7 @@ export function CancelParentTransform(
|
||||||
* @returns x and y such that the transformations of the parent are applied
|
* @returns x and y such that the transformations of the parent are applied
|
||||||
*/
|
*/
|
||||||
export function ApplyParentTransform(
|
export function ApplyParentTransform(
|
||||||
|
containers: Map<string, IContainerModel>,
|
||||||
parent: IContainerModel | null,
|
parent: IContainerModel | null,
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
|
@ -204,7 +215,7 @@ export function ApplyParentTransform(
|
||||||
return [x, y];
|
return [x, y];
|
||||||
}
|
}
|
||||||
|
|
||||||
const it = MakeContainerLinkedListIterator(parent, stop);
|
const it = MakeContainerLinkedListIterator(containers, parent, stop);
|
||||||
for (const current of it) {
|
for (const current of it) {
|
||||||
x -= current.properties.x;
|
x -= current.properties.x;
|
||||||
y -= current.properties.y;
|
y -= current.properties.y;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
import { FindContainerById, MakeDFSIterator } from './itertools';
|
|
||||||
import { IEditorState } from '../Interfaces/IEditorState';
|
import { IEditorState } from '../Interfaces/IEditorState';
|
||||||
import { IHistoryState } from '../Interfaces/IHistoryState';
|
import { IHistoryState } from '../Interfaces/IHistoryState';
|
||||||
import { IContainerModel } from '../Interfaces/IContainerModel';
|
import { IContainerModel } from '../Interfaces/IContainerModel';
|
||||||
|
import { ISymbolModel } from '../Interfaces/ISymbolModel';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revive the Editor state
|
* Revive the Editor state
|
||||||
|
@ -30,43 +30,18 @@ export function ReviveState(state: IHistoryState): void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.symbols = new Map(state.symbols);
|
const symbols: Array<{ Key: string, Value: ISymbolModel }> = (state.symbols) as any;
|
||||||
|
state.symbols = new Map(symbols.map(({ Key, Value }) => [Key, Value]));
|
||||||
for (const symbol of state.symbols.values()) {
|
for (const symbol of state.symbols.values()) {
|
||||||
symbol.linkedContainers = new Set(symbol.linkedContainers);
|
symbol.linkedContainers = new Set(symbol.linkedContainers);
|
||||||
}
|
}
|
||||||
|
|
||||||
const containers: Array<{ Key: string, Value: IContainerModel }> = (state.containers) as any;
|
const containers: Array<{ Key: string, Value: IContainerModel }> = (state.containers) as any;
|
||||||
state.containers = new Map(containers.map(({ Key, Value }: {Key: string, Value: IContainerModel}) => [Key, Value]));
|
state.containers = new Map(containers.map(({ Key, Value }) => [Key, Value]));
|
||||||
|
|
||||||
const root = FindContainerById(state.containers, state.mainContainer);
|
|
||||||
|
|
||||||
if (root === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove parent and remove this bloc of code
|
|
||||||
// TODO: See IContainerModel.ts for more detail
|
|
||||||
const it = MakeDFSIterator(root, state.containers);
|
|
||||||
for (const container of it) {
|
|
||||||
const parentId = container.properties.parentId;
|
|
||||||
if (parentId === null) {
|
|
||||||
container.parent = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const parent = FindContainerById(state.containers, parentId);
|
|
||||||
if (parent === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
container.parent = parent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GetCircularReplacer(): (key: any, value: object | Map<string, any> | null) => object | null | undefined {
|
export function GetCircularReplacer(): (key: any, value: object | Map<string, any> | null) => object | null | undefined {
|
||||||
return (key: any, value: object | null) => {
|
return (key: any, value: object | null) => {
|
||||||
if (key === 'parent') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'containers') {
|
if (key === 'containers') {
|
||||||
return [...(value as Map<string, any>).entries()]
|
return [...(value as Map<string, any>).entries()]
|
||||||
.map(([Key, Value]: [string, any]) => ({ Key, Value }));
|
.map(([Key, Value]: [string, any]) => ({ Key, Value }));
|
||||||
|
|
|
@ -113,6 +113,8 @@ export function RemoveMargin(
|
||||||
top?: number,
|
top?: number,
|
||||||
right?: number
|
right?: number
|
||||||
): { x: number, y: number, width: number, height: number } {
|
): { x: number, y: number, width: number, height: number } {
|
||||||
|
left = left ?? 0;
|
||||||
|
right = right ?? 0;
|
||||||
bottom = bottom ?? 0;
|
bottom = bottom ?? 0;
|
||||||
top = top ?? 0;
|
top = top ?? 0;
|
||||||
x = RemoveXMargin(x, left);
|
x = RemoveXMargin(x, left);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue