Merged PR 212: Optimize FindChildrenById from O(n) to O(1)
Optimize FindChildrenById from O(n) to O(1): - Deprecate FindContainerByIdDFS - Container: Replace Children to string[] - Add HashMap to IHistoryState that contains all containers To access a container by id now cost O(1) without any additional cost + Implement CICD for SVGLibs
This commit is contained in:
parent
466ef2b08b
commit
c256a76e01
45 changed files with 775 additions and 450 deletions
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { API_FETCH_URL } from '../../../public/svgld-settings';
|
||||
import { AddMethod } from '../../Enums/AddMethod';
|
||||
import { Orientation } from '../../Enums/Orientation';
|
||||
import { Position } from '../../Enums/Position';
|
||||
|
@ -10,7 +11,6 @@ import { IAvailableSymbol } from '../../Interfaces/IAvailableSymbol';
|
|||
import { ICategory } from '../../Interfaces/ICategory';
|
||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||
import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
import { IPattern } from '../../Interfaces/IPattern';
|
||||
import { DEFAULT_MAINCONTAINER_PROPS, GetDefaultContainerProps } from '../../utils/default';
|
||||
|
@ -21,9 +21,9 @@ const CHARP_WEB_API_RESOURCE_URL = 'SVGLD';
|
|||
const CSHARP_WEB_API_URL = CSHARP_WEB_API_BASE_URL + CHARP_WEB_API_RESOURCE_URL + '/';
|
||||
|
||||
// TODO: Migrate this test to SVGLDWebAPI rather than using test-server/
|
||||
describe.concurrent('Test server test', () => {
|
||||
describe.concurrent('Test server test', async() => {
|
||||
it('Load environment', () => {
|
||||
const url = import.meta.env.VITE_API_FETCH_URL;
|
||||
const url = API_FETCH_URL;
|
||||
expect(url).toBe('http://localhost:5000');
|
||||
});
|
||||
|
||||
|
@ -103,9 +103,11 @@ describe.concurrent('Models test suite', () => {
|
|||
DEFAULT_MAINCONTAINER_PROPS
|
||||
);
|
||||
|
||||
const containers = new Map<string, IContainerModel>();
|
||||
const historyState: IHistoryState = {
|
||||
lastAction: 'string',
|
||||
mainContainer,
|
||||
mainContainer: mainContainer.properties.id,
|
||||
containers,
|
||||
selectedContainerId: '3',
|
||||
typeCounters: {
|
||||
main: 1
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { API_FETCH_URL, API_SET_CONTAINER_LIST_URL } from '../../../public/svgld-settings';
|
||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||
import { ISetContainerListRequest } from '../../Interfaces/ISetContainerListRequest';
|
||||
import { ISetContainerListResponse } from '../../Interfaces/ISetContainerListResponse';
|
||||
|
@ -8,7 +9,7 @@ import { GetCircularReplacerKeepDataStructure } from '../../utils/saveload';
|
|||
* @returns {Configation} The model of the configuration for the application
|
||||
*/
|
||||
export async function FetchConfiguration(): Promise<IConfiguration> {
|
||||
const url = import.meta.env.VITE_API_FETCH_URL;
|
||||
const url = API_FETCH_URL;
|
||||
// The test library cannot use the Fetch API
|
||||
// @ts-expect-error
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
|
@ -34,7 +35,7 @@ export async function FetchConfiguration(): Promise<IConfiguration> {
|
|||
}
|
||||
|
||||
export async function SetContainerList(request: ISetContainerListRequest): Promise<ISetContainerListResponse> {
|
||||
const url = import.meta.env.VITE_API_SET_CONTAINER_LIST_URL;
|
||||
const url = API_SET_CONTAINER_LIST_URL;
|
||||
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
||||
// The test library cannot use the Fetch API
|
||||
// @ts-expect-error
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
|
||||
import { events as EVENTS } from '../../Events/AppEvents';
|
||||
import { MainMenu } from '../MainMenu/MainMenu';
|
||||
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { Editor } from '../Editor/Editor';
|
||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||
import { LoadState } from './Actions/Load';
|
||||
|
@ -81,12 +81,15 @@ export function App(props: IAppProps): JSX.Element {
|
|||
null,
|
||||
DEFAULT_MAINCONTAINER_PROPS
|
||||
);
|
||||
const containers = new Map<string, IContainerModel>();
|
||||
containers.set(defaultMainContainer.properties.id, defaultMainContainer);
|
||||
|
||||
const [editorState, setEditorState] = useState<IEditorState>({
|
||||
configuration: DEFAULT_CONFIG,
|
||||
history: [{
|
||||
lastAction: '',
|
||||
mainContainer: defaultMainContainer,
|
||||
mainContainer: defaultMainContainer.properties.id,
|
||||
containers,
|
||||
selectedContainerId: defaultMainContainer.properties.id,
|
||||
typeCounters: {},
|
||||
symbols: new Map(),
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Orientation } from '../../Enums/Orientation';
|
|||
import { Position } from '../../Enums/Position';
|
||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { SHOW_SELF_DIMENSIONS, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS } from '../../utils/default';
|
||||
import { MakeRecursionDFSIterator, Pairwise } from '../../utils/itertools';
|
||||
import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../utils/itertools';
|
||||
import { TransformX, TransformY } from '../../utils/svg';
|
||||
import { RenderDimension } from './Dimension';
|
||||
|
||||
|
@ -10,6 +10,7 @@ const MODULE_STROKE_WIDTH = 1;
|
|||
|
||||
export function AddDimensions(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
dimMapped: number[],
|
||||
currentTransform: [number, number],
|
||||
|
@ -24,7 +25,8 @@ export function AddDimensions(
|
|||
container.properties.showSelfDimensions,
|
||||
AddHorizontalSelfDimension,
|
||||
AddVerticalSelfDimension,
|
||||
[container,
|
||||
[
|
||||
container,
|
||||
currentTransform,
|
||||
scale]
|
||||
);
|
||||
|
@ -37,7 +39,9 @@ export function AddDimensions(
|
|||
container.properties.showDimensionWithMarks,
|
||||
AddHorizontalBorrowerDimension,
|
||||
AddVerticalBorrowerDimension,
|
||||
[container,
|
||||
[
|
||||
containers,
|
||||
container,
|
||||
depth,
|
||||
currentTransform,
|
||||
scale]
|
||||
|
@ -51,7 +55,9 @@ export function AddDimensions(
|
|||
container.properties.showChildrenDimensions,
|
||||
AddHorizontalChildrenDimension,
|
||||
AddVerticalChildrenDimension,
|
||||
[container,
|
||||
[
|
||||
containers,
|
||||
container,
|
||||
currentTransform,
|
||||
scale]
|
||||
);
|
||||
|
@ -94,19 +100,30 @@ function ActionByPosition(
|
|||
function AddHorizontalChildrenDimension(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
yDim: number,
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
currentTransform: [number, number],
|
||||
scale: number
|
||||
): void {
|
||||
const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`;
|
||||
|
||||
const lastChild = container.children[container.children.length - 1];
|
||||
const lastChildId = container.children[container.children.length - 1];
|
||||
const lastChild = FindContainerById(containers, lastChildId);
|
||||
|
||||
if (lastChild === undefined) {
|
||||
return;
|
||||
}
|
||||
let xChildrenStart = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
||||
let xChildrenEnd = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
||||
|
||||
// Find the min and max
|
||||
for (let i = container.children.length - 2; i >= 0; i--) {
|
||||
const child = container.children[i];
|
||||
const childId = container.children[i];
|
||||
const child = FindContainerById(containers, childId);
|
||||
|
||||
if (child === undefined) {
|
||||
continue;
|
||||
}
|
||||
const left = TransformX(child.properties.x, child.properties.width, child.properties.positionReference);
|
||||
if (left < xChildrenStart) {
|
||||
xChildrenStart = left;
|
||||
|
@ -142,19 +159,32 @@ function AddHorizontalChildrenDimension(
|
|||
function AddVerticalChildrenDimension(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
xDim: number,
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
currentTransform: [number, number],
|
||||
scale: number
|
||||
): void {
|
||||
const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`;
|
||||
|
||||
const lastChild = container.children[container.children.length - 1];
|
||||
const lastChildId = container.children[container.children.length - 1];
|
||||
const lastChild = FindContainerById(containers, lastChildId);
|
||||
|
||||
if (lastChild === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let yChildrenStart = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
||||
let yChildrenEnd = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
||||
|
||||
// Find the min and max
|
||||
for (let i = container.children.length - 2; i >= 0; i--) {
|
||||
const child = container.children[i];
|
||||
const childId = container.children[i];
|
||||
const child = FindContainerById(containers, childId);
|
||||
|
||||
if (child === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const top = TransformY(child.properties.y, child.properties.height, child.properties.positionReference);
|
||||
if (top < yChildrenStart) {
|
||||
yChildrenStart = top;
|
||||
|
@ -191,12 +221,13 @@ function AddVerticalChildrenDimension(
|
|||
function AddHorizontalBorrowerDimension(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
yDim: number,
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
depth: number,
|
||||
currentTransform: [number, number],
|
||||
scale: number
|
||||
): void {
|
||||
const it = MakeRecursionDFSIterator(container, depth, currentTransform);
|
||||
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
|
||||
const marks = []; // list of vertical lines for the dimension
|
||||
for (const {
|
||||
container: childContainer, currentTransform: childCurrentTransform
|
||||
|
@ -243,12 +274,13 @@ function AddHorizontalBorrowerDimension(
|
|||
function AddVerticalBorrowerDimension(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
xDim: number,
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
depth: number,
|
||||
currentTransform: [number, number],
|
||||
scale: number
|
||||
): void {
|
||||
const it = MakeRecursionDFSIterator(container, depth, currentTransform);
|
||||
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
|
||||
const marks = []; // list of vertical lines for the dimension
|
||||
for (const {
|
||||
container: childContainer, currentTransform: childCurrentTransform
|
||||
|
|
|
@ -69,12 +69,11 @@ export function AddContainers(
|
|||
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
|
||||
const current = history[history.length - 1];
|
||||
|
||||
// Deep clone the main container for the history
|
||||
const clone: IContainerModel = structuredClone(current.mainContainer);
|
||||
const containers = structuredClone(current.containers);
|
||||
|
||||
// Find the parent in the clone
|
||||
const parentClone: IContainerModel | undefined = FindContainerById(
|
||||
clone, parentId
|
||||
containers, parentId
|
||||
);
|
||||
|
||||
if (parentClone === null || parentClone === undefined) {
|
||||
|
@ -90,14 +89,15 @@ export function AddContainers(
|
|||
// Iterate over the containers
|
||||
availableContainers.forEach((availableContainer, typeIndex) => {
|
||||
// Get the preset properties from the API
|
||||
AddNewContainerToParent(availableContainer, configuration, parentClone, index, typeIndex, newCounters, current.symbols, containerIds);
|
||||
AddNewContainerToParent(availableContainer, configuration, containers, parentClone, index, typeIndex, newCounters, current.symbols, containerIds);
|
||||
});
|
||||
|
||||
// Update the state
|
||||
history.push({
|
||||
lastAction: `Add [${containerIds.join(', ')}] in ${parentClone.properties.id}`,
|
||||
mainContainer: clone,
|
||||
mainContainer: current.mainContainer,
|
||||
selectedContainerId: parentClone.properties.id,
|
||||
containers,
|
||||
typeCounters: newCounters,
|
||||
symbols: structuredClone(current.symbols),
|
||||
selectedSymbolId: current.selectedSymbolId
|
||||
|
@ -109,6 +109,7 @@ export function AddContainers(
|
|||
function AddNewContainerToParent(
|
||||
availableContainer: IAvailableContainer,
|
||||
configuration: IConfiguration,
|
||||
containers: Map<string, IContainerModel>,
|
||||
parentClone: IContainerModel,
|
||||
index: number,
|
||||
typeIndex: number,
|
||||
|
@ -143,7 +144,7 @@ function AddNewContainerToParent(
|
|||
({ x, y, width, height } = ApplyMargin(x, y, width, height, left, bottom, top, right));
|
||||
|
||||
// Apply an add method (append or insert/replace)
|
||||
({ x, y } = ApplyAddMethod(index + typeIndex, containerConfig, parentClone, x, y));
|
||||
({ x, y } = ApplyAddMethod(containers, index + typeIndex, containerConfig, parentClone, x, y));
|
||||
|
||||
// Set the counter of the object type in order to assign an unique id
|
||||
UpdateCounters(newCounters, type);
|
||||
|
@ -170,22 +171,25 @@ function AddNewContainerToParent(
|
|||
}
|
||||
);
|
||||
|
||||
// Register the container in the hashmap
|
||||
containers.set(newContainer.properties.id, newContainer);
|
||||
|
||||
// Add it to the parent
|
||||
if (index === parentClone.children.length) {
|
||||
parentClone.children.push(newContainer);
|
||||
parentClone.children.push(newContainer.properties.id);
|
||||
} else {
|
||||
parentClone.children.splice(index, 0, newContainer);
|
||||
parentClone.children.splice(index, 0, newContainer.properties.id);
|
||||
}
|
||||
|
||||
// Sort the parent children by x
|
||||
SortChildren(parentClone);
|
||||
SortChildren(containers, parentClone);
|
||||
|
||||
/// Handle behaviors here ///
|
||||
// Apply the behaviors (flex, rigid, anchor)
|
||||
ApplyBehaviors(newContainer, symbols);
|
||||
ApplyBehaviors(containers, newContainer, symbols);
|
||||
|
||||
// Then, apply the behaviors on its siblings (mostly for flex)
|
||||
ApplyBehaviorsOnSiblingsChildren(newContainer, symbols);
|
||||
ApplyBehaviorsOnSiblingsChildren(containers, newContainer, symbols);
|
||||
|
||||
// Initialize default children of the container
|
||||
if (initChilds) {
|
||||
|
@ -194,6 +198,7 @@ function AddNewContainerToParent(
|
|||
newContainer,
|
||||
configuration,
|
||||
containerConfig,
|
||||
containers,
|
||||
newCounters,
|
||||
symbols
|
||||
);
|
||||
|
@ -201,6 +206,7 @@ function AddNewContainerToParent(
|
|||
InitializeChildrenWithPattern(
|
||||
newContainer,
|
||||
configuration,
|
||||
containers,
|
||||
containerConfig,
|
||||
newCounters,
|
||||
symbols
|
||||
|
@ -258,6 +264,7 @@ function InitializeDefaultChild(
|
|||
newContainer: ContainerModel,
|
||||
configuration: IConfiguration,
|
||||
containerConfig: IAvailableContainer,
|
||||
containers: Map<string, IContainerModel>,
|
||||
newCounters: Record<string, number>,
|
||||
symbols: Map<string, ISymbolModel>
|
||||
): void {
|
||||
|
@ -276,6 +283,7 @@ function InitializeDefaultChild(
|
|||
AddNewContainerToParent(
|
||||
currentConfig,
|
||||
configuration,
|
||||
containers,
|
||||
parent,
|
||||
0, 0,
|
||||
newCounters,
|
||||
|
@ -286,6 +294,7 @@ function InitializeDefaultChild(
|
|||
function InitializeChildrenWithPattern(
|
||||
newContainer: ContainerModel,
|
||||
configuration: IConfiguration,
|
||||
containers: Map<string, IContainerModel>,
|
||||
containerConfig: IAvailableContainer,
|
||||
newCounters: Record<string, number>,
|
||||
symbols: Map<string, ISymbolModel>
|
||||
|
@ -307,6 +316,7 @@ function InitializeChildrenWithPattern(
|
|||
AddNewContainerToParent(
|
||||
container,
|
||||
configuration,
|
||||
containers,
|
||||
newContainer,
|
||||
0, 0,
|
||||
newCounters,
|
||||
|
@ -334,6 +344,7 @@ function InitializeChildrenWithPattern(
|
|||
AddNewContainerToParent(
|
||||
containerConfig,
|
||||
configuration,
|
||||
containers,
|
||||
node.parent,
|
||||
0, 0,
|
||||
newCounters,
|
||||
|
@ -350,9 +361,10 @@ function InitializeChildrenWithPattern(
|
|||
console.warn(`[InitializeChildrenFromPattern] IAvailableContainer from pattern was not found in the configuration: ${pattern.wrapper}.
|
||||
Process will ignore the container.`);
|
||||
} else {
|
||||
parent = AddNewContainerToParent(
|
||||
const newChildContainer = AddNewContainerToParent(
|
||||
container,
|
||||
configuration,
|
||||
containers,
|
||||
parent,
|
||||
0, 0,
|
||||
newCounters,
|
||||
|
@ -360,6 +372,9 @@ function InitializeChildrenWithPattern(
|
|||
undefined,
|
||||
false
|
||||
);
|
||||
|
||||
// iterate
|
||||
parent = newChildContainer;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -398,6 +413,7 @@ interface Node {
|
|||
* @returns New offset
|
||||
*/
|
||||
function ApplyAddMethod(
|
||||
containers: Map<string, IContainerModel>,
|
||||
index: number,
|
||||
containerConfig: IAvailableContainer,
|
||||
parent: IContainerModel,
|
||||
|
@ -410,8 +426,8 @@ function ApplyAddMethod(
|
|||
containerConfig.AddMethod === AddMethod.Append
|
||||
)) {
|
||||
// Append method (default)
|
||||
const lastChild: IContainerModel | undefined = parent.children
|
||||
.at(index - 1);
|
||||
const lastChildId: string = parent.children[index - 1];
|
||||
const lastChild = FindContainerById(containers, lastChildId);
|
||||
|
||||
if (lastChild !== undefined) {
|
||||
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||
|
|
|
@ -24,7 +24,8 @@ export function SelectContainer(
|
|||
|
||||
history.push({
|
||||
lastAction: `Select ${containerId}`,
|
||||
mainContainer: structuredClone(current.mainContainer),
|
||||
mainContainer: current.mainContainer,
|
||||
containers: structuredClone(current.containers),
|
||||
selectedContainerId: containerId,
|
||||
typeCounters: Object.assign({}, current.typeCounters),
|
||||
symbols: structuredClone(current.symbols),
|
||||
|
@ -48,8 +49,9 @@ export function DeleteContainer(
|
|||
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
|
||||
const current = history[history.length - 1];
|
||||
|
||||
const mainContainerClone: IContainerModel = structuredClone(current.mainContainer);
|
||||
const container = FindContainerById(mainContainerClone, containerId);
|
||||
const containers = structuredClone(current.containers);
|
||||
const mainContainerClone: IContainerModel | undefined = FindContainerById(containers, current.mainContainer);
|
||||
const container = FindContainerById(containers, containerId);
|
||||
|
||||
if (container === undefined) {
|
||||
throw new Error(`[DeleteContainer] Tried to delete a container that is not present in the main container: ${containerId}`);
|
||||
|
@ -71,21 +73,22 @@ export function DeleteContainer(
|
|||
}
|
||||
|
||||
const newSymbols = structuredClone(current.symbols);
|
||||
UnlinkContainerFromSymbols(newSymbols, container);
|
||||
UnlinkContainerFromSymbols(containers, newSymbols, container);
|
||||
|
||||
const index = container.parent.children.indexOf(container);
|
||||
if (index > -1) {
|
||||
const index = container.parent.children.indexOf(container.properties.id);
|
||||
const success = containers.delete(container.properties.id);
|
||||
if (index > -1 && success) {
|
||||
container.parent.children.splice(index, 1);
|
||||
} else {
|
||||
throw new Error('[DeleteContainer] Could not find container among parent\'s children');
|
||||
}
|
||||
|
||||
ApplyBehaviorsOnSiblings(container, current.symbols);
|
||||
ApplyBehaviorsOnSiblings(containers, container, current.symbols);
|
||||
|
||||
// Select the previous container
|
||||
// or select the one above
|
||||
const selectedContainerId = GetSelectedContainerOnDelete(
|
||||
mainContainerClone,
|
||||
containers,
|
||||
current.selectedContainerId,
|
||||
container.parent,
|
||||
index
|
||||
|
@ -93,7 +96,8 @@ export function DeleteContainer(
|
|||
|
||||
history.push({
|
||||
lastAction: `Delete ${containerId}`,
|
||||
mainContainer: mainContainerClone,
|
||||
mainContainer: current.mainContainer,
|
||||
containers,
|
||||
selectedContainerId,
|
||||
typeCounters: Object.assign({}, current.typeCounters),
|
||||
symbols: newSymbols,
|
||||
|
@ -115,16 +119,15 @@ export function DeleteContainer(
|
|||
* @returns {IContainerModel} Next selected container
|
||||
*/
|
||||
function GetSelectedContainerOnDelete(
|
||||
mainContainerClone: IContainerModel,
|
||||
containers: Map<string, IContainerModel>,
|
||||
selectedContainerId: string,
|
||||
parent: IContainerModel,
|
||||
index: number
|
||||
): string {
|
||||
const newSelectedContainer = FindContainerById(mainContainerClone, selectedContainerId) ??
|
||||
const newSelectedContainerId = FindContainerById(containers, selectedContainerId)?.properties.id ??
|
||||
parent.children.at(index) ??
|
||||
parent.children.at(index - 1) ??
|
||||
parent;
|
||||
const newSelectedContainerId = newSelectedContainer.properties.id;
|
||||
parent.properties.id;
|
||||
return newSelectedContainerId;
|
||||
}
|
||||
|
||||
|
@ -134,8 +137,12 @@ function GetSelectedContainerOnDelete(
|
|||
* @param symbols Symbols to update
|
||||
* @param container Container to unlink
|
||||
*/
|
||||
function UnlinkContainerFromSymbols(symbols: Map<string, ISymbolModel>, container: IContainerModel): void {
|
||||
const it = MakeDFSIterator(container);
|
||||
function UnlinkContainerFromSymbols(
|
||||
containers: Map<string, IContainerModel>,
|
||||
symbols: Map<string, ISymbolModel>,
|
||||
container: IContainerModel
|
||||
): void {
|
||||
const it = MakeDFSIterator(container, containers);
|
||||
for (const child of it) {
|
||||
const symbol = symbols.get(child.properties.linkedSymbolId);
|
||||
if (symbol === undefined) {
|
||||
|
@ -167,18 +174,19 @@ export function OnPropertyChange(
|
|||
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
||||
}
|
||||
|
||||
const mainContainerClone: IContainerModel = structuredClone(current.mainContainer);
|
||||
const container: ContainerModel | undefined = FindContainerById(mainContainerClone, selected.properties.id);
|
||||
const containers = structuredClone(current.containers);
|
||||
const container: ContainerModel | undefined = FindContainerById(containers, selected.properties.id);
|
||||
|
||||
if (container === null || container === undefined) {
|
||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||
}
|
||||
|
||||
SetContainer(container, key, value, type, current.symbols);
|
||||
SetContainer(containers, container, key, value, type, current.symbols);
|
||||
|
||||
history.push({
|
||||
lastAction: `Change ${key} of ${container.properties.id}`,
|
||||
mainContainer: mainContainerClone,
|
||||
mainContainer: current.mainContainer,
|
||||
containers,
|
||||
selectedContainerId: container.properties.id,
|
||||
typeCounters: Object.assign({}, current.typeCounters),
|
||||
symbols: structuredClone(current.symbols),
|
||||
|
@ -189,20 +197,30 @@ export function OnPropertyChange(
|
|||
|
||||
/**
|
||||
* Sort the parent children by x
|
||||
* @param parentClone The clone used for the sort
|
||||
* @param parent The clone used for the sort
|
||||
* @returns void
|
||||
*/
|
||||
export function SortChildren(parentClone: IContainerModel | null | undefined): void {
|
||||
if (parentClone === null || parentClone === undefined) {
|
||||
export function SortChildren(
|
||||
containers: Map<string, IContainerModel>,
|
||||
parent: IContainerModel | null | undefined
|
||||
): void {
|
||||
if (parent === null || parent === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isHorizontal = parentClone.properties.orientation === Orientation.Horizontal;
|
||||
const children = parentClone.children;
|
||||
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||
const children = parent.children;
|
||||
|
||||
if (!isHorizontal) {
|
||||
parentClone.children.sort(
|
||||
(a, b) => {
|
||||
parent.children.sort(
|
||||
(aId, bId) => {
|
||||
const a = FindContainerById(containers, aId);
|
||||
const b = FindContainerById(containers, bId);
|
||||
|
||||
if (a === undefined || b === undefined) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const yA = TransformY(a.properties.y, a.properties.height, a.properties.positionReference);
|
||||
const yB = TransformY(b.properties.y, b.properties.height, b.properties.positionReference);
|
||||
if (yA < yB) {
|
||||
|
@ -212,16 +230,23 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
|
|||
return 1;
|
||||
}
|
||||
// xA = xB
|
||||
const indexA = children.indexOf(a);
|
||||
const indexB = children.indexOf(b);
|
||||
const indexA = children.indexOf(aId);
|
||||
const indexB = children.indexOf(bId);
|
||||
return indexA - indexB;
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
parentClone.children.sort(
|
||||
(a, b) => {
|
||||
parent.children.sort(
|
||||
(aId, bId) => {
|
||||
const a = FindContainerById(containers, aId);
|
||||
const b = FindContainerById(containers, bId);
|
||||
|
||||
if (a === undefined || b === undefined) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const xA = TransformX(a.properties.x, a.properties.width, a.properties.positionReference);
|
||||
const xB = TransformX(b.properties.x, b.properties.width, b.properties.positionReference);
|
||||
if (xA < xB) {
|
||||
|
@ -231,8 +256,8 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
|
|||
return 1;
|
||||
}
|
||||
// xA = xB
|
||||
const indexA = children.indexOf(a);
|
||||
const indexB = children.indexOf(b);
|
||||
const indexA = children.indexOf(aId);
|
||||
const indexB = children.indexOf(bId);
|
||||
return indexA - indexB;
|
||||
}
|
||||
);
|
||||
|
@ -247,6 +272,7 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
|
|||
* @param symbols Current list of symbols
|
||||
*/
|
||||
function SetContainer(
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: ContainerModel,
|
||||
key: string, value: string | number | boolean | number[],
|
||||
type: PropertyType,
|
||||
|
@ -267,13 +293,13 @@ function SetContainer(
|
|||
);
|
||||
|
||||
// sort the children list by their position
|
||||
SortChildren(container.parent);
|
||||
SortChildren(containers, container.parent);
|
||||
|
||||
// Apply special behaviors: rigid, flex, symbol, anchor
|
||||
ApplyBehaviors(container, symbols);
|
||||
ApplyBehaviors(containers, container, symbols);
|
||||
|
||||
// Apply special behaviors on siblings
|
||||
ApplyBehaviorsOnSiblingsChildren(container, symbols);
|
||||
ApplyBehaviorsOnSiblingsChildren(containers, container, symbols);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,7 +21,7 @@ export function GetAction(
|
|||
): (target: HTMLElement) => void {
|
||||
return (target: HTMLElement) => {
|
||||
const id = target.id;
|
||||
const container = FindContainerById(currentState.mainContainer, id);
|
||||
const container = FindContainerById(currentState.containers, id);
|
||||
|
||||
if (container === undefined) {
|
||||
Swal.fire({
|
||||
|
@ -33,7 +33,7 @@ export function GetAction(
|
|||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
const { prev, next } = GetPreviousAndNextSiblings(container);
|
||||
const { prev, next } = GetPreviousAndNextSiblings(currentState.containers, container);
|
||||
|
||||
const request: ISetContainerListRequest = {
|
||||
Container: container,
|
||||
|
@ -59,18 +59,18 @@ export function GetAction(
|
|||
};
|
||||
}
|
||||
|
||||
function GetPreviousAndNextSiblings(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 next;
|
||||
if (container.parent !== undefined &&
|
||||
container.parent !== null &&
|
||||
container.parent.children.length > 1) {
|
||||
const index = container.parent.children.indexOf(container);
|
||||
const index = container.parent.children.indexOf(container.properties.id);
|
||||
if (index > 0) {
|
||||
prev = container.parent.children[index - 1];
|
||||
prev = FindContainerById(containers, container.parent.children[index - 1]);
|
||||
}
|
||||
if (index < container.parent.children.length - 1) {
|
||||
next = container.parent.children[index + 1];
|
||||
next = FindContainerById(containers, container.parent.children[index + 1]);
|
||||
}
|
||||
}
|
||||
return { prev, next };
|
||||
|
@ -144,7 +144,7 @@ function HandleReplace(
|
|||
throw new Error('[ReplaceContainer] Cannot replace a container that does not exists');
|
||||
}
|
||||
|
||||
const index = selectedContainer.parent.children.indexOf(selectedContainer);
|
||||
const index = selectedContainer.parent.children.indexOf(selectedContainer.properties.id);
|
||||
|
||||
const newHistoryAfterDelete = DeleteContainer(
|
||||
selectedContainer.properties.id,
|
||||
|
|
|
@ -36,6 +36,7 @@ export function AddSymbol(
|
|||
history.push({
|
||||
lastAction: `Add ${name}`,
|
||||
mainContainer: structuredClone(current.mainContainer),
|
||||
containers: structuredClone(current.containers),
|
||||
selectedContainerId: current.selectedContainerId,
|
||||
typeCounters: newCounters,
|
||||
symbols: newSymbols,
|
||||
|
@ -55,6 +56,7 @@ export function SelectSymbol(
|
|||
history.push({
|
||||
lastAction: `Select ${symbolId}`,
|
||||
mainContainer: structuredClone(current.mainContainer),
|
||||
containers: structuredClone(current.containers),
|
||||
selectedContainerId: current.selectedContainerId,
|
||||
typeCounters: structuredClone(current.typeCounters),
|
||||
symbols: structuredClone(current.symbols),
|
||||
|
@ -78,15 +80,15 @@ export function DeleteSymbol(
|
|||
throw new Error(`[DeleteSymbol] Could not find symbol in the current state!: ${symbolId}`);
|
||||
}
|
||||
|
||||
const newMainContainer = structuredClone(current.mainContainer);
|
||||
|
||||
UnlinkSymbolFromContainers(symbol, newMainContainer);
|
||||
const containers = structuredClone(current.containers);
|
||||
UnlinkSymbolFromContainers(containers, symbol);
|
||||
|
||||
newSymbols.delete(symbolId);
|
||||
|
||||
history.push({
|
||||
lastAction: `Select ${symbolId}`,
|
||||
mainContainer: newMainContainer,
|
||||
mainContainer: current.mainContainer,
|
||||
containers,
|
||||
selectedContainerId: current.selectedContainerId,
|
||||
typeCounters: structuredClone(current.typeCounters),
|
||||
symbols: newSymbols,
|
||||
|
@ -100,9 +102,9 @@ export function DeleteSymbol(
|
|||
* @param symbol Symbol to remove
|
||||
* @param root Container and its children to remove a symbol from
|
||||
*/
|
||||
function UnlinkSymbolFromContainers(symbol: ISymbolModel, root: IContainerModel): void {
|
||||
function UnlinkSymbolFromContainers(containers: Map<string, IContainerModel>, symbol: ISymbolModel): void {
|
||||
symbol.linkedContainers.forEach((containerId) => {
|
||||
const container = FindContainerById(root, containerId);
|
||||
const container = FindContainerById(containers, containerId);
|
||||
|
||||
if (container === undefined) {
|
||||
return;
|
||||
|
@ -140,22 +142,23 @@ export function OnPropertyChange(
|
|||
|
||||
(symbol as any)[key] = value;
|
||||
|
||||
const newMainContainer = structuredClone(current.mainContainer);
|
||||
const containers = structuredClone(current.containers);
|
||||
symbol.linkedContainers.forEach((containerId) => {
|
||||
const container = FindContainerById(newMainContainer, containerId);
|
||||
const container = FindContainerById(containers, containerId);
|
||||
|
||||
if (container === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyBehaviors(container, newSymbols);
|
||||
ApplyBehaviors(containers, container, newSymbols);
|
||||
|
||||
ApplyBehaviorsOnSiblingsChildren(container, newSymbols);
|
||||
ApplyBehaviorsOnSiblingsChildren(containers, container, newSymbols);
|
||||
});
|
||||
|
||||
history.push({
|
||||
lastAction: `Change ${key} of ${symbol.id}`,
|
||||
mainContainer: newMainContainer,
|
||||
mainContainer: current.mainContainer,
|
||||
containers,
|
||||
selectedContainerId: current.selectedContainerId,
|
||||
typeCounters: Object.assign({}, current.typeCounters),
|
||||
symbols: newSymbols,
|
||||
|
|
|
@ -16,16 +16,28 @@
|
|||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { ConstraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors';
|
||||
import { FindContainerById } from '../../../utils/itertools';
|
||||
|
||||
/**
|
||||
* Impose the container position to its siblings
|
||||
* Apply the following modification to the overlapping rigid body container :
|
||||
* @param container Container to impose its position
|
||||
*/
|
||||
export function ApplyAnchor(container: IContainerModel, parent: IContainerModel): IContainerModel {
|
||||
const rigidBodies = parent.children.filter(
|
||||
child => !child.properties.isAnchor
|
||||
);
|
||||
export function ApplyAnchor(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): IContainerModel {
|
||||
const rigidBodies: IContainerModel[] = [];
|
||||
parent.children.forEach(
|
||||
childId => {
|
||||
const child = FindContainerById(containers, childId);
|
||||
|
||||
if (child === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (child.properties.isAnchor) {
|
||||
return;
|
||||
}
|
||||
rigidBodies.push(child);
|
||||
});
|
||||
|
||||
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||
const overlappingContainers = isHorizontal
|
||||
|
@ -33,7 +45,7 @@ export function ApplyAnchor(container: IContainerModel, parent: IContainerModel)
|
|||
: GetVerticallyOverlappingContainers(container, rigidBodies);
|
||||
|
||||
for (const overlappingContainer of overlappingContainers) {
|
||||
ConstraintBodyInsideUnallocatedWidth(overlappingContainer);
|
||||
ConstraintBodyInsideUnallocatedWidth(containers, overlappingContainer);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
||||
import { APPLY_BEHAVIORS_ON_CHILDREN, ENABLE_RIGID, ENABLE_SWAP } from '../../../utils/default';
|
||||
import { FindContainerById, MakeChildrenIterator } from '../../../utils/itertools';
|
||||
import { ApplyAnchor, GetOverlappingContainers } from './AnchorBehaviors';
|
||||
import { Flex } from './FlexBehaviors';
|
||||
import { ApplyRigidBody } from './RigidBodyBehaviors';
|
||||
|
@ -13,7 +14,7 @@ import { ApplySymbol } from './SymbolBehaviors';
|
|||
* @param container Container to recalculate its positions
|
||||
* @returns Updated container
|
||||
*/
|
||||
export function ApplyBehaviors(container: IContainerModel, symbols: Map<string, ISymbolModel>): IContainerModel {
|
||||
export function ApplyBehaviors(containers: Map<string, IContainerModel>, container: IContainerModel, symbols: Map<string, ISymbolModel>): IContainerModel {
|
||||
try {
|
||||
const symbol = symbols.get(container.properties.linkedSymbolId);
|
||||
if (container.properties.linkedSymbolId !== '' && symbol !== undefined) {
|
||||
|
@ -24,24 +25,24 @@ export function ApplyBehaviors(container: IContainerModel, symbols: Map<string,
|
|||
const parent = container.parent;
|
||||
|
||||
if (container.properties.isAnchor) {
|
||||
ApplyAnchor(container, parent);
|
||||
ApplyAnchor(containers, container, parent);
|
||||
}
|
||||
|
||||
if (ENABLE_SWAP) {
|
||||
ApplySwap(container, parent);
|
||||
ApplySwap(containers, container, parent);
|
||||
}
|
||||
|
||||
Flex(container, parent);
|
||||
Flex(containers, container, parent);
|
||||
|
||||
if (ENABLE_RIGID) {
|
||||
ApplyRigidBody(container, parent);
|
||||
ApplyRigidBody(containers, container, parent);
|
||||
}
|
||||
}
|
||||
|
||||
if (APPLY_BEHAVIORS_ON_CHILDREN) {
|
||||
// Apply DFS by recursion
|
||||
for (const child of container.children) {
|
||||
ApplyBehaviors(child, symbols);
|
||||
for (const child of MakeChildrenIterator(containers, container.children)) {
|
||||
ApplyBehaviors(containers, child, symbols);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -64,23 +65,32 @@ export function ApplyBehaviors(container: IContainerModel, symbols: Map<string,
|
|||
* @param symbols
|
||||
* @returns
|
||||
*/
|
||||
export function ApplyBehaviorsOnSiblingsChildren(newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
|
||||
export function ApplyBehaviorsOnSiblingsChildren(
|
||||
containers: Map<string, IContainerModel>,
|
||||
newContainer: IContainerModel,
|
||||
symbols: Map<string, ISymbolModel>): void {
|
||||
if (newContainer.parent === null || newContainer.parent === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
newContainer.parent.children
|
||||
.forEach((container: IContainerModel) => {
|
||||
if (container.parent != null) {
|
||||
UpdateWarning(container, container.parent);
|
||||
.forEach((containerId: string) => {
|
||||
const container = FindContainerById(containers, containerId);
|
||||
|
||||
if (container === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.parent !== null) {
|
||||
UpdateWarning(containers, container, container.parent);
|
||||
}
|
||||
|
||||
if (container === newContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const child of container.children) {
|
||||
ApplyBehaviors(child, symbols);
|
||||
for (const child of MakeChildrenIterator(containers, container.children)) {
|
||||
ApplyBehaviors(containers, child, symbols);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -91,30 +101,47 @@ export function ApplyBehaviorsOnSiblingsChildren(newContainer: IContainerModel,
|
|||
* @param symbols
|
||||
* @returns
|
||||
*/
|
||||
export function ApplyBehaviorsOnSiblings(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) {
|
||||
return;
|
||||
}
|
||||
|
||||
newContainer.parent.children
|
||||
.forEach((container: IContainerModel) => {
|
||||
ApplyBehaviors(container, symbols);
|
||||
.forEach((containerId: string) => {
|
||||
const container = FindContainerById(containers, containerId);
|
||||
|
||||
if (container === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyBehaviors(containers, container, symbols);
|
||||
|
||||
if (container.parent != null) {
|
||||
UpdateWarning(container, container.parent);
|
||||
UpdateWarning(containers, container, container.parent);
|
||||
}
|
||||
|
||||
if (container === newContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const child of container.children) {
|
||||
ApplyBehaviors(child, symbols);
|
||||
for (const child of MakeChildrenIterator(containers, container.children)) {
|
||||
ApplyBehaviors(containers, child, symbols);
|
||||
}
|
||||
});
|
||||
}
|
||||
function UpdateWarning(container: IContainerModel, parent: IContainerModel): void {
|
||||
const overlappingContainers = GetOverlappingContainers(container, parent.children);
|
||||
function UpdateWarning(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): void {
|
||||
const targetContainers: IContainerModel[] = [];
|
||||
|
||||
parent.children.forEach((child) => {
|
||||
const targetContainer = FindContainerById(containers, child);
|
||||
|
||||
if (targetContainer === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
targetContainers.push(targetContainer);
|
||||
});
|
||||
const overlappingContainers = GetOverlappingContainers(container, targetContainers);
|
||||
if (overlappingContainers.length > 0) {
|
||||
container.properties.warning = `There are overlapping containers: ${overlappingContainers.map(c => c.properties.id).join(' ')}`;
|
||||
} else {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
|||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { Simplex } from '../../../utils/simplex';
|
||||
import { ApplyWidthMargin, ApplyXMargin } from '../../../utils/svg';
|
||||
import { MakeChildrenIterator } from '../../../utils/itertools';
|
||||
|
||||
interface IFlexibleGroup {
|
||||
group: IContainerModel[]
|
||||
|
@ -13,13 +14,13 @@ interface IFlexibleGroup {
|
|||
* Flex the container and its siblings (mutate)
|
||||
* @returns Flexed container
|
||||
*/
|
||||
export function Flex(container: IContainerModel, parent: IContainerModel): void {
|
||||
export function Flex(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): void {
|
||||
const isVertical = parent.properties.orientation === Orientation.Vertical;
|
||||
|
||||
if (isVertical) {
|
||||
const wantedWidth = Math.min(container.properties.maxWidth, parent.properties.width);
|
||||
container.properties.width = ApplyWidthMargin(wantedWidth, container.properties.margin.left, container.properties.margin.right);
|
||||
const flexibleGroups = GetVerticalFlexibleGroups(parent);
|
||||
const flexibleGroups = GetVerticalFlexibleGroups(containers, parent);
|
||||
for (const flexibleGroup of flexibleGroups) {
|
||||
FlexGroupVertically(flexibleGroup);
|
||||
}
|
||||
|
@ -28,7 +29,7 @@ export function Flex(container: IContainerModel, parent: IContainerModel): void
|
|||
|
||||
const wantedHeight = Math.min(container.properties.maxHeight, parent.properties.height);
|
||||
container.properties.height = ApplyWidthMargin(wantedHeight, container.properties.margin.top, container.properties.margin.bottom);
|
||||
const flexibleGroups = GetHorizontalFlexibleGroups(parent);
|
||||
const flexibleGroups = GetHorizontalFlexibleGroups(containers, parent);
|
||||
for (const flexibleGroup of flexibleGroups) {
|
||||
FlexGroupHorizontally(flexibleGroup);
|
||||
}
|
||||
|
@ -39,12 +40,12 @@ export function Flex(container: IContainerModel, parent: IContainerModel): void
|
|||
* @param parent Parent in which the flexible children will be set in groups
|
||||
* @returns a list of groups of flexible containers
|
||||
*/
|
||||
export function GetHorizontalFlexibleGroups(parent: IContainerModel): IFlexibleGroup[] {
|
||||
export function GetHorizontalFlexibleGroups(containers: Map<string, IContainerModel>, parent: IContainerModel): IFlexibleGroup[] {
|
||||
const flexibleGroups: IFlexibleGroup[] = [];
|
||||
let group: IContainerModel[] = [];
|
||||
let offset = 0;
|
||||
let size = 0;
|
||||
for (const child of parent.children) {
|
||||
for (const child of MakeChildrenIterator(containers, parent.children)) {
|
||||
if (child.properties.isAnchor) {
|
||||
size = child.properties.x - offset;
|
||||
const flexibleGroup: IFlexibleGroup = {
|
||||
|
@ -77,12 +78,15 @@ export function GetHorizontalFlexibleGroups(parent: IContainerModel): IFlexibleG
|
|||
* @param parent Parent in which the flexible children will be set in groups
|
||||
* @returns a list of groups of flexible containers
|
||||
*/
|
||||
export function GetVerticalFlexibleGroups(parent: IContainerModel): IFlexibleGroup[] {
|
||||
export function GetVerticalFlexibleGroups(
|
||||
containers: Map<string, IContainerModel>,
|
||||
parent: IContainerModel
|
||||
): IFlexibleGroup[] {
|
||||
const flexibleGroups: IFlexibleGroup[] = [];
|
||||
let group: IContainerModel[] = [];
|
||||
let offset = 0;
|
||||
let size = 0;
|
||||
for (const child of parent.children) {
|
||||
for (const child of MakeChildrenIterator(containers, parent.children)) {
|
||||
if (child.properties.isAnchor) {
|
||||
size = child.properties.y - offset;
|
||||
const flexibleGroup: IFlexibleGroup = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { ReversePairwise } from '../../../utils/itertools';
|
||||
import { MakeChildrenIterator, ReversePairwise } from '../../../utils/itertools';
|
||||
import { Flex } from './FlexBehaviors';
|
||||
|
||||
/**
|
||||
|
@ -8,12 +8,17 @@ import { Flex } from './FlexBehaviors';
|
|||
* @param container
|
||||
* @returns
|
||||
*/
|
||||
export function ApplyPush(container: IContainerModel, parent: IContainerModel): IContainerModel {
|
||||
export function ApplyPush(
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
parent: IContainerModel
|
||||
): IContainerModel {
|
||||
if (parent.children.length <= 1) {
|
||||
return container;
|
||||
}
|
||||
|
||||
const children = parent.children;
|
||||
const children: IContainerModel[] = [...MakeChildrenIterator(containers, parent.children)];
|
||||
|
||||
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||
|
||||
if (isHorizontal) {
|
||||
|
@ -22,7 +27,7 @@ export function ApplyPush(container: IContainerModel, parent: IContainerModel):
|
|||
PushContainersVertically(container, children);
|
||||
}
|
||||
|
||||
Flex(container, parent);
|
||||
Flex(containers, container, parent);
|
||||
return container;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
|||
import { ISizePointer } from '../../../Interfaces/ISizePointer';
|
||||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { ENABLE_HARD_RIGID } from '../../../utils/default';
|
||||
import { MakeChildrenIterator } from '../../../utils/itertools';
|
||||
|
||||
/**
|
||||
* "Transform the container into a rigid body"
|
||||
|
@ -21,13 +22,14 @@ import { ENABLE_HARD_RIGID } from '../../../utils/default';
|
|||
* @returns A rigid body container
|
||||
*/
|
||||
export function ApplyRigidBody(
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
parent: IContainerModel
|
||||
): IContainerModel {
|
||||
container = ConstraintBodyInsideParent(container, parent);
|
||||
|
||||
if (ENABLE_HARD_RIGID) {
|
||||
container = ConstraintBodyInsideUnallocatedWidth(container);
|
||||
container = ConstraintBodyInsideUnallocatedWidth(containers, container);
|
||||
}
|
||||
|
||||
return container;
|
||||
|
@ -117,6 +119,7 @@ function ConstraintBodyInsideSpace(
|
|||
* @returns Updated container
|
||||
*/
|
||||
export function ConstraintBodyInsideUnallocatedWidth(
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel
|
||||
): IContainerModel {
|
||||
if (container.parent === null || container.parent === undefined) {
|
||||
|
@ -126,10 +129,11 @@ export function ConstraintBodyInsideUnallocatedWidth(
|
|||
// Get the available spaces of the parent
|
||||
const isHorizontal =
|
||||
container.parent.properties.orientation === Orientation.Horizontal;
|
||||
const children: IContainerModel[] = [...MakeChildrenIterator(containers, container.parent.children)];
|
||||
const availableWidths = GetAvailableWidths(
|
||||
0,
|
||||
container.parent.properties.width,
|
||||
container.parent.children,
|
||||
children,
|
||||
container,
|
||||
isHorizontal
|
||||
);
|
||||
|
|
|
@ -5,9 +5,13 @@
|
|||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { GetHorizontallyOverlappingContainers, GetVerticallyOverlappingContainers } from './AnchorBehaviors';
|
||||
import { MakeChildrenIterator } from '../../../utils/itertools';
|
||||
|
||||
export function ApplySwap(container: IContainerModel, parent: IContainerModel): void {
|
||||
const children = parent.children;
|
||||
export function ApplySwap(
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
parent: IContainerModel): void {
|
||||
const children = [...MakeChildrenIterator(containers, parent.children)];
|
||||
|
||||
const isVertical = parent.properties.orientation === Orientation.Vertical;
|
||||
if (isVertical) {
|
||||
|
|
|
@ -274,7 +274,7 @@ export function Editor(props: IEditorProps): JSX.Element {
|
|||
// Render
|
||||
const configuration = props.configuration;
|
||||
const current = GetCurrentHistoryState(history, historyCurrentStep);
|
||||
const selected = FindContainerById(current.mainContainer, current.selectedContainerId);
|
||||
const selected = FindContainerById(current.containers, current.selectedContainerId);
|
||||
|
||||
return (
|
||||
<div ref={editorRef} className="Editor font-sans h-full">
|
||||
|
|
|
@ -9,6 +9,7 @@ import { PropertyType } from '../../Enums/PropertyType';
|
|||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
interface IElementsListProps {
|
||||
containers: Map<string, IContainerModel>
|
||||
mainContainer: IContainerModel
|
||||
symbols: Map<string, ISymbolModel>
|
||||
selectedContainer: IContainerModel | undefined
|
||||
|
@ -59,6 +60,7 @@ function HandleDragOver(
|
|||
|
||||
function HandleOnDrop(
|
||||
event: React.DragEvent,
|
||||
containers: Map<string, IContainerModel>,
|
||||
mainContainer: IContainerModel,
|
||||
addContainer: (index: number, type: string, parent: string) => void
|
||||
): void {
|
||||
|
@ -68,7 +70,7 @@ function HandleOnDrop(
|
|||
RemoveBorderClasses(target);
|
||||
|
||||
const targetContainer: IContainerModel | undefined = FindContainerById(
|
||||
mainContainer,
|
||||
containers,
|
||||
target.id
|
||||
);
|
||||
|
||||
|
@ -95,7 +97,7 @@ function HandleOnDrop(
|
|||
|
||||
// locate the hitboxes
|
||||
if (y < 12) {
|
||||
const index = targetContainer.parent.children.indexOf(targetContainer);
|
||||
const index = targetContainer.parent.children.indexOf(targetContainer.properties.id);
|
||||
addContainer(
|
||||
index,
|
||||
type,
|
||||
|
@ -107,7 +109,7 @@ function HandleOnDrop(
|
|||
type,
|
||||
targetContainer.properties.id);
|
||||
} else {
|
||||
const index = targetContainer.parent.children.indexOf(targetContainer);
|
||||
const index = targetContainer.parent.children.indexOf(targetContainer.properties.id);
|
||||
addContainer(
|
||||
index + 1,
|
||||
type,
|
||||
|
@ -119,10 +121,10 @@ function HandleOnDrop(
|
|||
export function ElementsList(props: IElementsListProps): JSX.Element {
|
||||
// States
|
||||
const divRef = React.useRef<HTMLDivElement>(null);
|
||||
const [width, height] = useSize(divRef);
|
||||
const [, height] = useSize(divRef);
|
||||
|
||||
// Render
|
||||
const it = MakeRecursionDFSIterator(props.mainContainer, 0, [0, 0], true);
|
||||
const it = MakeRecursionDFSIterator(props.mainContainer, props.containers, 0, [0, 0], true);
|
||||
const containers = [...it];
|
||||
function Row({
|
||||
index, style
|
||||
|
@ -154,7 +156,7 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
|
|||
style={style}
|
||||
title={container.properties.warning}
|
||||
onClick={() => props.selectContainer(container.properties.id)}
|
||||
onDrop={(event) => HandleOnDrop(event, props.mainContainer, props.addContainer)}
|
||||
onDrop={(event) => HandleOnDrop(event, props.containers, props.mainContainer, props.addContainer)}
|
||||
onDragOver={(event) => HandleDragOver(event, props.mainContainer)}
|
||||
onDragLeave={(event) => HandleDragLeave(event)}
|
||||
>
|
||||
|
|
|
@ -1,183 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import { Properties } from '../ContainerProperties/ContainerProperties';
|
||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
|
||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import { PropertyType } from '../../Enums/PropertyType';
|
||||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
interface IElementsProps {
|
||||
mainContainer: IContainerModel
|
||||
symbols: Map<string, ISymbolModel>
|
||||
selectedContainer: IContainerModel | undefined
|
||||
onPropertyChange: (
|
||||
key: string,
|
||||
value: string | number | boolean | number[],
|
||||
type?: PropertyType
|
||||
) => void
|
||||
selectContainer: (containerId: string) => void
|
||||
addContainer: (index: number, type: string, parent: string) => void
|
||||
}
|
||||
|
||||
function RemoveBorderClasses(target: HTMLButtonElement, exception: string = ''): void {
|
||||
const bordersClasses = ['border-t-8', 'border-8', 'border-b-8'].filter(className => className !== exception);
|
||||
target.classList.remove(...bordersClasses);
|
||||
}
|
||||
|
||||
function HandleDragLeave(event: React.DragEvent): void {
|
||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
||||
RemoveBorderClasses(target);
|
||||
}
|
||||
|
||||
function HandleDragOver(
|
||||
event: React.DragEvent,
|
||||
mainContainer: IContainerModel
|
||||
): void {
|
||||
event.preventDefault();
|
||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
||||
const rect = target.getBoundingClientRect();
|
||||
const y = event.clientY - rect.top; // y position within the element.
|
||||
|
||||
if (target.id === mainContainer.properties.id) {
|
||||
target.classList.add('border-8');
|
||||
return;
|
||||
}
|
||||
|
||||
if (y < 12) {
|
||||
RemoveBorderClasses(target, 'border-t-8');
|
||||
target.classList.add('border-t-8');
|
||||
} else if (y < 24) {
|
||||
RemoveBorderClasses(target, 'border-8');
|
||||
target.classList.add('border-8');
|
||||
} else {
|
||||
RemoveBorderClasses(target, 'border-b-8');
|
||||
target.classList.add('border-b-8');
|
||||
}
|
||||
}
|
||||
|
||||
function HandleOnDrop(
|
||||
event: React.DragEvent,
|
||||
mainContainer: IContainerModel,
|
||||
addContainer: (index: number, type: string, parent: string) => void
|
||||
): void {
|
||||
event.preventDefault();
|
||||
const type = event.dataTransfer.getData('type');
|
||||
const target: HTMLButtonElement = event.target as HTMLButtonElement;
|
||||
RemoveBorderClasses(target);
|
||||
|
||||
const targetContainer: IContainerModel | undefined = FindContainerById(
|
||||
mainContainer,
|
||||
target.id
|
||||
);
|
||||
|
||||
if (targetContainer === undefined) {
|
||||
throw new Error('[handleOnDrop] Tried to drop onto a unknown container!');
|
||||
}
|
||||
|
||||
if (targetContainer === mainContainer) {
|
||||
// if the container is the root, only add type as child
|
||||
addContainer(
|
||||
targetContainer.children.length,
|
||||
type,
|
||||
targetContainer.properties.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetContainer.parent === null ||
|
||||
targetContainer.parent === undefined) {
|
||||
throw new Error('[handleDrop] Tried to drop into a child container without a parent!');
|
||||
}
|
||||
|
||||
const rect = target.getBoundingClientRect();
|
||||
const y = event.clientY - rect.top; // y position within the element.
|
||||
|
||||
// locate the hitboxes
|
||||
if (y < 12) {
|
||||
const index = targetContainer.parent.children.indexOf(targetContainer);
|
||||
addContainer(
|
||||
index,
|
||||
type,
|
||||
targetContainer.parent.properties.id
|
||||
);
|
||||
} else if (y < 24) {
|
||||
addContainer(
|
||||
targetContainer.children.length,
|
||||
type,
|
||||
targetContainer.properties.id);
|
||||
} else {
|
||||
const index = targetContainer.parent.children.indexOf(targetContainer);
|
||||
addContainer(
|
||||
index + 1,
|
||||
type,
|
||||
targetContainer.parent.properties.id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function Elements(props: IElementsProps): JSX.Element {
|
||||
// Render
|
||||
const it = MakeRecursionDFSIterator(props.mainContainer, 0, [0, 0], true);
|
||||
const containers = [...it];
|
||||
function Row({
|
||||
index, style
|
||||
}: {
|
||||
index: number
|
||||
style: React.CSSProperties
|
||||
}): JSX.Element {
|
||||
const { container, depth } = containers[index];
|
||||
const key = container.properties.id.toString();
|
||||
const tabs = '|\t'.repeat(depth);
|
||||
const text = container.properties.displayedText === key
|
||||
? `${key}`
|
||||
: `${container.properties.displayedText}`;
|
||||
|
||||
const isSelected = props.selectedContainer !== undefined &&
|
||||
props.selectedContainer !== null &&
|
||||
props.selectedContainer.properties.id === container.properties.id;
|
||||
|
||||
const selectedClass: string = isSelected
|
||||
? 'border-l-4 bg-blue-500 shadow-lg shadow-blue-500/60 hover:bg-blue-600 hover:shadow-blue-500 text-slate-50'
|
||||
: 'bg-slate-300/60 hover:bg-slate-400 hover:shadow-slate-400';
|
||||
|
||||
return (
|
||||
<button type="button"
|
||||
className={`transition-all w-full border-blue-500 hover:shadow-lg elements-sidebar-row whitespace-pre
|
||||
text-left text-sm font-medium inline-flex ${container.properties.type} ${selectedClass}`}
|
||||
id={key}
|
||||
key={key}
|
||||
style={style}
|
||||
title={container.properties.warning}
|
||||
onClick={() => props.selectContainer(container.properties.id)}
|
||||
onDrop={(event) => HandleOnDrop(event, props.mainContainer, props.addContainer)}
|
||||
onDragOver={(event) => HandleDragOver(event, props.mainContainer)}
|
||||
onDragLeave={(event) => HandleDragLeave(event)}
|
||||
>
|
||||
{tabs}
|
||||
{text}
|
||||
{container.properties.warning.length > 0 &&
|
||||
<ExclamationTriangleIcon className='w-8'/>
|
||||
}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<List
|
||||
className="List divide-y divide-black overflow-y-auto"
|
||||
itemCount={containers.length}
|
||||
itemSize={35}
|
||||
height={192}
|
||||
width={'100%'}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
<Properties
|
||||
properties={props.selectedContainer?.properties}
|
||||
symbols={props.symbols}
|
||||
onChange={props.onPropertyChange}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { TrashIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import { API_GET_FEEDBACK_URL } from '../../../public/svgld-settings';
|
||||
import { MessageType } from '../../Enums/MessageType';
|
||||
import { IGetFeedbackRequest } from '../../Interfaces/IGetFeedbackRequest';
|
||||
import { IGetFeedbackResponse } from '../../Interfaces/IGetFeedbackResponse';
|
||||
|
@ -25,7 +25,7 @@ function UseWorker(
|
|||
// use webworker for the stringify to avoid freezing
|
||||
myWorker.postMessage({
|
||||
state,
|
||||
url: import.meta.env.VITE_API_GET_FEEDBACK_URL
|
||||
url: API_GET_FEEDBACK_URL
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
@ -49,7 +49,7 @@ function UseAsync(
|
|||
ApplicationState: state
|
||||
};
|
||||
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
||||
fetch(import.meta.env.VITE_API_GET_FEEDBACK_URL, {
|
||||
fetch(API_GET_FEEDBACK_URL, {
|
||||
method: 'POST',
|
||||
headers: new Headers({
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
|
|
@ -4,8 +4,10 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
|||
import { IContainerProperties } from '../../../Interfaces/IContainerProperties';
|
||||
import { Camelize } from '../../../utils/stringtools';
|
||||
import { SHOW_TEXT } from '../../../utils/default';
|
||||
import { FindContainerById } from '../../../utils/itertools';
|
||||
|
||||
interface IContainerProps {
|
||||
containers: Map<string, IContainerModel>
|
||||
model: IContainerModel
|
||||
depth: number
|
||||
scale: number
|
||||
|
@ -18,13 +20,22 @@ interface IContainerProps {
|
|||
*/
|
||||
export function Container(props: IContainerProps): JSX.Element {
|
||||
const containersElements = props.model.children.map(
|
||||
child => <Container
|
||||
key={`container-${child.properties.id}`}
|
||||
model={child}
|
||||
depth={props.depth + 1}
|
||||
scale={props.scale}
|
||||
selectContainer={props.selectContainer}
|
||||
/>);
|
||||
childId => {
|
||||
const child = FindContainerById(props.containers, childId);
|
||||
|
||||
if (child === undefined) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return <Container
|
||||
key={`container-${child.properties.id}`}
|
||||
containers={props.containers}
|
||||
model={child}
|
||||
depth={props.depth + 1}
|
||||
scale={props.scale}
|
||||
selectContainer={props.selectContainer}
|
||||
/>;
|
||||
});
|
||||
|
||||
const width: number = props.model.properties.width;
|
||||
const height: number = props.model.properties.height;
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import { ContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { DIMENSION_MARGIN } from '../../../utils/default';
|
||||
import { GetAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools';
|
||||
import { TransformX } from '../../../utils/svg';
|
||||
import { Dimension } from './Dimension';
|
||||
|
||||
interface IDimensionLayerProps {
|
||||
containers: Map<string, IContainerModel>
|
||||
roots: ContainerModel | ContainerModel[] | null
|
||||
scale?: number
|
||||
}
|
||||
|
||||
function GetDimensionsNodes(root: ContainerModel, scale: number): React.ReactNode[] {
|
||||
const it = MakeBFSIterator(root);
|
||||
function GetDimensionsNodes(
|
||||
containers: Map<string, IContainerModel>,
|
||||
root: ContainerModel,
|
||||
scale: number
|
||||
): React.ReactNode[] {
|
||||
const it = MakeBFSIterator(root, containers);
|
||||
const dimensions: React.ReactNode[] = [];
|
||||
let currentDepth = 0;
|
||||
let min = Infinity;
|
||||
|
@ -53,10 +58,10 @@ export function DepthDimensionLayer(props: IDimensionLayerProps): JSX.Element {
|
|||
const scale = props.scale ?? 1;
|
||||
if (Array.isArray(props.roots)) {
|
||||
props.roots.forEach(child => {
|
||||
dimensions.concat(GetDimensionsNodes(child, scale));
|
||||
dimensions.concat(GetDimensionsNodes(props.containers, child, scale));
|
||||
});
|
||||
} else if (props.roots !== null) {
|
||||
dimensions = GetDimensionsNodes(props.roots, scale);
|
||||
dimensions = GetDimensionsNodes(props.containers, props.roots, scale);
|
||||
}
|
||||
return (
|
||||
<g>
|
||||
|
|
|
@ -3,11 +3,12 @@ import { Orientation } from '../../../Enums/Orientation';
|
|||
import { Position } from '../../../Enums/Position';
|
||||
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { DIMENSION_MARGIN, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, SHOW_SELF_DIMENSIONS } from '../../../utils/default';
|
||||
import { MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools';
|
||||
import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools';
|
||||
import { TransformX, TransformY } from '../../../utils/svg';
|
||||
import { Dimension } from './Dimension';
|
||||
|
||||
interface IDimensionLayerProps {
|
||||
containers: Map<string, IContainerModel>
|
||||
root: ContainerModel
|
||||
scale: number
|
||||
}
|
||||
|
@ -51,8 +52,8 @@ function ActionByPosition(
|
|||
* @param param0 Object with the root container and the scale of the svg
|
||||
* @returns A list of dimensions
|
||||
*/
|
||||
function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
||||
const it = MakeRecursionDFSIterator(root, 0, [0, 0]);
|
||||
function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
||||
const it = MakeRecursionDFSIterator(root, containers, 0, [0, 0]);
|
||||
const dimensions: React.ReactNode[] = [];
|
||||
const topDim = root.properties.y;
|
||||
const leftDim = root.properties.x;
|
||||
|
@ -75,7 +76,8 @@ function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
|||
container.properties.showSelfDimensions,
|
||||
AddHorizontalSelfDimension,
|
||||
AddVerticalSelfDimension,
|
||||
[container,
|
||||
[
|
||||
container,
|
||||
currentTransform,
|
||||
dimensions,
|
||||
scale]
|
||||
|
@ -88,7 +90,9 @@ function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
|||
container.properties.showDimensionWithMarks,
|
||||
AddHorizontalBorrowerDimension,
|
||||
AddVerticalBorrowerDimension,
|
||||
[container,
|
||||
[
|
||||
containers,
|
||||
container,
|
||||
depth,
|
||||
currentTransform,
|
||||
dimensions,
|
||||
|
@ -102,7 +106,9 @@ function Dimensions({ root, scale }: IDimensionLayerProps): React.ReactNode[] {
|
|||
container.properties.showChildrenDimensions,
|
||||
AddHorizontalChildrenDimension,
|
||||
AddVerticalChildrenDimension,
|
||||
[container,
|
||||
[
|
||||
containers,
|
||||
container,
|
||||
currentTransform,
|
||||
dimensions,
|
||||
scale]
|
||||
|
@ -130,6 +136,7 @@ export function DimensionLayer(props: IDimensionLayerProps): JSX.Element {
|
|||
|
||||
function AddHorizontalChildrenDimension(
|
||||
yDim: number,
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
|
@ -137,13 +144,25 @@ function AddHorizontalChildrenDimension(
|
|||
): void {
|
||||
const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`;
|
||||
|
||||
const lastChild = container.children[container.children.length - 1];
|
||||
const lastChildId = container.children[container.children.length - 1];
|
||||
const lastChild = FindContainerById(containers, lastChildId);
|
||||
|
||||
if (lastChild === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let xChildrenStart = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
||||
let xChildrenEnd = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference);
|
||||
|
||||
// Find the min and max
|
||||
for (let i = container.children.length - 2; i >= 0; i--) {
|
||||
const child = container.children[i];
|
||||
const childId = container.children[i];
|
||||
const child = FindContainerById(containers, childId);
|
||||
|
||||
if (child === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const left = TransformX(child.properties.x, child.properties.width, child.properties.positionReference);
|
||||
if (left < xChildrenStart) {
|
||||
xChildrenStart = left;
|
||||
|
@ -178,6 +197,7 @@ function AddHorizontalChildrenDimension(
|
|||
|
||||
function AddVerticalChildrenDimension(
|
||||
xDim: number,
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
|
@ -185,13 +205,25 @@ function AddVerticalChildrenDimension(
|
|||
): void {
|
||||
const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`;
|
||||
|
||||
const lastChild = container.children[container.children.length - 1];
|
||||
const lastChildId = container.children[container.children.length - 1];
|
||||
const lastChild = FindContainerById(containers, lastChildId);
|
||||
|
||||
if (lastChild === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let yChildrenStart = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
||||
let yChildrenEnd = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference);
|
||||
|
||||
// Find the min and max
|
||||
for (let i = container.children.length - 2; i >= 0; i--) {
|
||||
const child = container.children[i];
|
||||
const childId = container.children[i];
|
||||
const child = FindContainerById(containers, childId);
|
||||
|
||||
if (child === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const top = TransformY(child.properties.y, child.properties.height, child.properties.positionReference);
|
||||
if (top < yChildrenStart) {
|
||||
yChildrenStart = top;
|
||||
|
@ -227,13 +259,14 @@ function AddVerticalChildrenDimension(
|
|||
|
||||
function AddHorizontalBorrowerDimension(
|
||||
yDim: number,
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
depth: number,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number
|
||||
): void {
|
||||
const it = MakeRecursionDFSIterator(container, depth, currentTransform);
|
||||
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
|
||||
const marks = []; // list of vertical lines for the dimension
|
||||
for (const {
|
||||
container: childContainer, currentTransform: childCurrentTransform
|
||||
|
@ -279,13 +312,14 @@ function AddHorizontalBorrowerDimension(
|
|||
|
||||
function AddVerticalBorrowerDimension(
|
||||
xDim: number,
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
depth: number,
|
||||
currentTransform: [number, number],
|
||||
dimensions: React.ReactNode[],
|
||||
scale: number
|
||||
): void {
|
||||
const it = MakeRecursionDFSIterator(container, depth, currentTransform);
|
||||
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
|
||||
const marks = []; // list of vertical lines for the dimension
|
||||
for (const {
|
||||
container: childContainer, currentTransform: childCurrentTransform
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import { ReactSVGPanZoom, Tool, TOOL_PAN, Value } from 'react-svg-pan-zoom';
|
||||
import { Container } from './Elements/Container';
|
||||
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { Selector } from './Elements/Selector/Selector';
|
||||
import { DepthDimensionLayer } from './Elements/DepthDimensionLayer';
|
||||
import { MAX_FRAMERATE, SHOW_DIMENSIONS_PER_DEPTH } from '../../utils/default';
|
||||
|
@ -15,17 +15,13 @@ interface ISVGProps {
|
|||
viewerHeight: number
|
||||
width: number
|
||||
height: number
|
||||
containers: Map<string, IContainerModel>
|
||||
children: ContainerModel
|
||||
selected?: ContainerModel
|
||||
symbols: Map<string, ISymbolModel>
|
||||
selectContainer: (containerId: string) => void
|
||||
}
|
||||
|
||||
interface Viewer {
|
||||
viewerWidth: number
|
||||
viewerHeight: number
|
||||
}
|
||||
|
||||
export const ID = 'svg';
|
||||
|
||||
export function SVG(props: ISVGProps): JSX.Element {
|
||||
|
@ -55,6 +51,7 @@ export function SVG(props: ISVGProps): JSX.Element {
|
|||
let children: React.ReactNode | React.ReactNode[] = [];
|
||||
children = <Container
|
||||
key={`container-${props.children.properties.id}`}
|
||||
containers={props.containers}
|
||||
model={props.children}
|
||||
depth={0}
|
||||
scale={scale}
|
||||
|
@ -97,9 +94,9 @@ export function SVG(props: ISVGProps): JSX.Element {
|
|||
<svg {...properties}>
|
||||
{children}
|
||||
{SHOW_DIMENSIONS_PER_DEPTH
|
||||
? <DepthDimensionLayer scale={scale} roots={props.children} />
|
||||
? <DepthDimensionLayer containers={props.containers} scale={scale} roots={props.children} />
|
||||
: null}
|
||||
<DimensionLayer scale={scale} root={props.children} />
|
||||
<DimensionLayer containers={props.containers} scale={scale} root={props.children} />
|
||||
<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 */}
|
||||
</svg>
|
||||
|
|
|
@ -18,6 +18,7 @@ import { Settings } from '../Settings/Settings';
|
|||
import { IMessage } from '../../Interfaces/IMessage';
|
||||
import { DISABLE_API } from '../../utils/default';
|
||||
import { UseWorker, UseAsync } from './UseWorker';
|
||||
import { FindContainerById } from '../../utils/itertools';
|
||||
|
||||
export interface IUIProps {
|
||||
selectedContainer: IContainerModel | undefined
|
||||
|
@ -89,6 +90,12 @@ export function UI(props: IUIProps): JSX.Element {
|
|||
let leftChildren: JSX.Element = (<></>);
|
||||
let rightChildren: JSX.Element = (<></>);
|
||||
|
||||
const mainContainer = FindContainerById(props.current.containers, props.current.mainContainer)
|
||||
|
||||
if (mainContainer === undefined) {
|
||||
throw new Error('Tried to initialized UI but there is no main container!');
|
||||
}
|
||||
|
||||
switch (selectedSidebar) {
|
||||
case SidebarType.Components:
|
||||
leftSidebarTitle = 'Components';
|
||||
|
@ -100,7 +107,8 @@ export function UI(props: IUIProps): JSX.Element {
|
|||
/>;
|
||||
rightSidebarTitle = 'Elements';
|
||||
rightChildren = <ElementsList
|
||||
mainContainer={props.current.mainContainer}
|
||||
containers={props.current.containers}
|
||||
mainContainer={mainContainer}
|
||||
symbols={props.current.symbols}
|
||||
selectedContainer={props.selectedContainer}
|
||||
onPropertyChange={props.onPropertyChange}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { IGetFeedbackRequest } from '../../Interfaces/IGetFeedbackRequest';
|
|||
import { IGetFeedbackResponse } from '../../Interfaces/IGetFeedbackResponse';
|
||||
import { IMessage } from '../../Interfaces/IMessage';
|
||||
import { GetCircularReplacerKeepDataStructure } from '../../utils/saveload';
|
||||
import { API_GET_FEEDBACK_URL } from '../../../public/svgld-settings';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
const myWorker = window.Worker && new Worker('workers/message_worker.js');
|
||||
|
@ -15,7 +16,7 @@ export function UseWorker(
|
|||
// use webworker for the stringify to avoid freezing
|
||||
myWorker.postMessage({
|
||||
state,
|
||||
url: import.meta.env.VITE_API_GET_FEEDBACK_URL
|
||||
url: API_GET_FEEDBACK_URL
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
@ -37,7 +38,7 @@ export function UseAsync(
|
|||
ApplicationState: state
|
||||
};
|
||||
const dataParsed = JSON.stringify(request, GetCircularReplacerKeepDataStructure());
|
||||
fetch(import.meta.env.VITE_API_GET_FEEDBACK_URL, {
|
||||
fetch(API_GET_FEEDBACK_URL, {
|
||||
method: 'POST',
|
||||
headers: new Headers({
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
|
|
@ -3,7 +3,7 @@ import { IContainerModel } from '../../Interfaces/IContainerModel';
|
|||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
import { IPoint } from '../../Interfaces/IPoint';
|
||||
import { DIMENSION_MARGIN, USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default';
|
||||
import { MakeRecursionDFSIterator } from '../../utils/itertools';
|
||||
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
|
||||
import { BAR_WIDTH } from '../Bar/Bar';
|
||||
import { Canvas } from '../Canvas/Canvas';
|
||||
import { AddDimensions } from '../Canvas/DimensionLayer';
|
||||
|
@ -96,22 +96,32 @@ export function Viewer({
|
|||
viewerHeight: window.innerHeight
|
||||
});
|
||||
|
||||
const mainContainer = FindContainerById(current.containers, current.mainContainer);
|
||||
|
||||
if (mainContainer === undefined) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
UseSVGAutoResizerOnWindowResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
|
||||
UseSVGAutoResizerOnSidebar(isLeftSidebarOpen, isRightSidebarOpen, setViewer);
|
||||
|
||||
if (USE_EXPERIMENTAL_CANVAS_API) {
|
||||
function Draw(ctx: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint): void {
|
||||
const topDim = current.mainContainer.properties.y;
|
||||
const leftDim = current.mainContainer.properties.x;
|
||||
const rightDim = current.mainContainer.properties.x + current.mainContainer.properties.width;
|
||||
const bottomDim = current.mainContainer.properties.y + current.mainContainer.properties.height;
|
||||
if (mainContainer === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const topDim = mainContainer.properties.y;
|
||||
const leftDim = mainContainer.properties.x;
|
||||
const rightDim = mainContainer.properties.x + mainContainer.properties.width;
|
||||
const bottomDim = mainContainer.properties.y + mainContainer.properties.height;
|
||||
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
ctx.save();
|
||||
ctx.setTransform(scale, 0, 0, scale, translatePos.x, translatePos.y);
|
||||
ctx.fillStyle = '#000000';
|
||||
|
||||
const it = MakeRecursionDFSIterator(current.mainContainer, 0, [0, 0]);
|
||||
const it = MakeRecursionDFSIterator(mainContainer, current.containers, 0, [0, 0]);
|
||||
for (const { container, depth, currentTransform } of it) {
|
||||
const [x, y] = [
|
||||
container.properties.x + currentTransform[0],
|
||||
|
@ -130,6 +140,7 @@ export function Viewer({
|
|||
rightDim,
|
||||
depth,
|
||||
scale,
|
||||
current.containers,
|
||||
container,
|
||||
currentTransform
|
||||
);
|
||||
|
@ -160,13 +171,14 @@ export function Viewer({
|
|||
className={marginClasses}
|
||||
viewerWidth={viewer.viewerWidth}
|
||||
viewerHeight={viewer.viewerHeight}
|
||||
width={current.mainContainer?.properties.width}
|
||||
height={current.mainContainer?.properties.height}
|
||||
width={mainContainer.properties.width}
|
||||
height={mainContainer.properties.height}
|
||||
containers={current.containers}
|
||||
selected={selectedContainer}
|
||||
symbols={current.symbols}
|
||||
selectContainer={selectContainer}
|
||||
>
|
||||
{current.mainContainer}
|
||||
{mainContainer}
|
||||
</SVG>
|
||||
);
|
||||
}
|
||||
|
@ -188,6 +200,7 @@ function RenderDimensions(
|
|||
rightDim: number,
|
||||
depth: number,
|
||||
scale: number,
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
currentTransform: [number, number]
|
||||
): void {
|
||||
|
@ -197,7 +210,7 @@ function RenderDimensions(
|
|||
const containerBottomDim = bottomDim + (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||
const containerRightDim = rightDim + (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
|
||||
AddDimensions(ctx, container, dimMapped, currentTransform, scale, depth);
|
||||
AddDimensions(ctx, containers, container, dimMapped, currentTransform, scale, depth);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue