Move all AddContainer functions to a different file and fix default.ts

This commit is contained in:
Eric NGUYEN 2022-09-14 17:31:40 +02:00
parent fd06723fb9
commit 498ca9dc75
5 changed files with 469 additions and 487 deletions

View file

@ -0,0 +1,433 @@
/**
* This file is dedicated to the AddContainer
*/
import { AddMethod } from '../../../Enums/AddMethod';
import { IAvailableContainer } from '../../../Interfaces/IAvailableContainer';
import { IConfiguration } from '../../../Interfaces/IConfiguration';
import { IContainerModel, ContainerModel } from '../../../Interfaces/IContainerModel';
import { IHistoryState } from '../../../Interfaces/IHistoryState';
import { IPattern, GetPattern, ContainerOrPattern } from '../../../Interfaces/IPattern';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { GetDefaultContainerProps, DEFAULTCHILDTYPE_MAX_DEPTH, DEFAULTCHILDTYPE_ALLOW_CYCLIC } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools';
import { ApplyMargin } from '../../../utils/svg';
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { GetCurrentHistory, UpdateCounters } from '../Editor';
import { SortChildren } from './ContainerOperations';
/**
* Add a new container to a selected container
* @param type The type of container
* @param configuration Configuration of the App
* @param fullHistory History of the editor
* @param historyCurrentStep Current step
* @returns void
*/
export function AddContainerToSelectedContainer(
type: string,
selected: IContainerModel | undefined,
configuration: IConfiguration,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] | null {
if (selected === null ||
selected === undefined) {
return null;
}
const parent = selected;
return AddContainer(
parent.children.length,
type,
parent.properties.id,
configuration,
fullHistory,
historyCurrentStep
);
}
/**
* Create and add a new container at `index` in children of parent of `parentId`
* @param index Index where to insert to the new container
* @param type Type of container
* @param parentId Parent in which to insert the new container
* @param configuration Configuration of the app
* @param fullHistory History of the editor
* @param historyCurrentStep Current step
* @returns void
*/
export function AddContainers(
index: number,
availableContainers: IAvailableContainer[],
parentId: string,
configuration: IConfiguration,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
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);
// Find the parent in the clone
const parentClone: IContainerModel | undefined = FindContainerById(
clone, parentId
);
if (parentClone === null || parentClone === undefined) {
throw new Error('[AddContainer] Container model was not found among children of the main container!');
}
// Deep clone the counters
const newCounters = Object.assign({}, current.typeCounters);
// containerIds is used for logging purpose (see setHistory below)
const containerIds: string[] = [];
// 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);
});
// Update the state
history.push({
lastAction: `Add [${containerIds.join(', ')}] in ${parentClone.properties.id}`,
mainContainer: clone,
selectedContainerId: parentClone.properties.id,
typeCounters: newCounters,
symbols: structuredClone(current.symbols),
selectedSymbolId: current.selectedSymbolId
});
return history;
}
function AddNewContainerToParent(
availableContainer: IAvailableContainer,
configuration: IConfiguration,
parentClone: IContainerModel,
index: number,
typeIndex: number,
newCounters: Record<string, number>,
symbols: Map<string, ISymbolModel>,
containerIds: string[] = [],
initChilds: boolean = true
): ContainerModel {
const type = availableContainer.Type;
const defaultConfig = configuration.AvailableContainers
.find(option => option.Type === type);
if (defaultConfig === undefined) {
throw new Error(`[AddContainer] Object type not found among default config. Found: ${type}`);
}
const containerConfig = Object.assign(defaultConfig, availableContainer);
// Default margin
const left: number = containerConfig.Margin?.left ?? 0;
const bottom: number = containerConfig.Margin?.bottom ?? 0;
const top: number = containerConfig.Margin?.top ?? 0;
const right: number = containerConfig.Margin?.right ?? 0;
// Default coordinates
let x = containerConfig.X ?? 0;
let y = containerConfig.Y ?? 0;
let width = containerConfig.Width ?? containerConfig.MaxWidth ?? containerConfig.MinWidth ?? parentClone.properties.width;
let height = containerConfig.Height ?? parentClone.properties.height;
({ x, y, width, height } = ApplyMargin(x, y, width, height, left, bottom, top, right));
// Apply an add method (append or insert/replace)
x = ApplyAddMethod(index + typeIndex, containerConfig, parentClone, x);
// Set the counter of the object type in order to assign an unique id
UpdateCounters(newCounters, type);
const count = newCounters[type];
const defaultProperties = GetDefaultContainerProps(
type,
count,
parentClone,
x,
y,
width,
height,
containerConfig
);
// Create the container
const newContainer = new ContainerModel(
parentClone,
defaultProperties,
[],
{
type
}
);
// Add it to the parent
if (index === parentClone.children.length) {
parentClone.children.push(newContainer);
} else {
parentClone.children.splice(index, 0, newContainer);
}
// Sort the parent children by x
SortChildren(parentClone);
/// Handle behaviors here ///
// Apply the behaviors (flex, rigid, anchor)
ApplyBehaviors(newContainer, symbols);
// Then, apply the behaviors on its siblings (mostly for flex)
ApplyBehaviorsOnSiblingsChildren(newContainer, symbols);
// Initialize default children of the container
if (initChilds) {
if (containerConfig.DefaultChildType !== undefined) {
InitializeDefaultChild(
newContainer,
configuration,
containerConfig,
newCounters,
symbols
);
} else {
InitializeChildrenWithPattern(
newContainer,
configuration,
containerConfig,
newCounters,
symbols
);
}
}
// Add to the list of container id for logging purpose
containerIds.push(newContainer.properties.id);
return newContainer;
}
/**
* Create and add a new container at `index` in children of parent of `parentId`
* @param index Index where to insert to the new container
* @param type Type of container
* @param parentId Parent in which to insert the new container
* @param configuration Configuration of the app
* @param fullHistory History of the editor
* @param historyCurrentStep Current step
* @param setHistory State setter of History
* @param setHistoryCurrentStep State setter of the current step
* @returns new history
*/
export function AddContainer(
index: number,
type: string,
parentId: string,
configuration: IConfiguration,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
// just call AddContainers with an array on a single element
return AddContainers(
index,
// eslint-disable-next-line @typescript-eslint/naming-convention
[{ Type: type }],
parentId,
configuration,
fullHistory,
historyCurrentStep
);
}
/**
* Initialize the children of the container
* @param configuration Current configuration of the app
* @param containerConfig The new container config used to read the default child type
* @param newContainer The new container to initialize its default children
* @param newCounters Type counter used for unique ids
* @returns
*/
function InitializeDefaultChild(
newContainer: ContainerModel,
configuration: IConfiguration,
containerConfig: IAvailableContainer,
newCounters: Record<string, number>,
symbols: Map<string, ISymbolModel>
): void {
if (containerConfig.DefaultChildType === undefined) {
return;
}
let currentConfig = configuration.AvailableContainers
.find(option => option.Type === containerConfig.DefaultChildType);
let parent = newContainer;
let depth = 0;
const seen = new Set<string>([containerConfig.Type]);
while (currentConfig !== undefined &&
depth <= DEFAULTCHILDTYPE_MAX_DEPTH
) {
if (!DEFAULTCHILDTYPE_ALLOW_CYCLIC && seen.has(currentConfig.Type)) {
return;
}
seen.add(currentConfig.Type);
const newChildContainer = AddNewContainerToParent(
currentConfig,
configuration,
parent,
0, 0,
newCounters,
symbols
);
// iterate
depth++;
parent = newChildContainer;
currentConfig = configuration.AvailableContainers
.find(option => option.Type === (currentConfig as IAvailableContainer).DefaultChildType);
}
}
function InitializeChildrenWithPattern(
newContainer: ContainerModel,
configuration: IConfiguration,
containerConfig: IAvailableContainer,
newCounters: Record<string, number>,
symbols: Map<string, ISymbolModel>
): void {
const patternId = containerConfig.Pattern;
if (patternId === undefined || patternId === null) {
return;
}
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 containerOrPattern = GetPattern(patternId, configs, patterns);
const pattern = containerOrPattern as IPattern;
if (pattern.children === undefined) {
const container = containerOrPattern as IAvailableContainer;
// Add Container
AddNewContainerToParent(
container,
configuration,
newContainer,
0, 0,
newCounters,
symbols
);
}
// BFS over patterns
const rootNode: Node = {
containerOrPattern: pattern,
parent: newContainer
};
const queue: Node[] = [rootNode];
while (queue.length > 0) {
let levelSize = queue.length;
while (levelSize-- !== 0) {
const node = queue.shift() as Node;
const pattern = node.containerOrPattern as IPattern;
if (pattern.children === undefined) {
// Add Container
const containerConfig = node.containerOrPattern as IAvailableContainer;
AddNewContainerToParent(
containerConfig,
configuration,
node.parent,
0, 0,
newCounters,
symbols
);
continue;
}
let parent = node.parent;
if (pattern.wrapper !== undefined) {
// 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 {
parent = AddNewContainerToParent(
container,
configuration,
parent,
0, 0,
newCounters,
symbols,
undefined,
false
);
}
}
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;
}
const nextNode: Node = {
containerOrPattern: child,
parent
};
queue.push(nextNode);
}
}
}
}
interface Node {
containerOrPattern: ContainerOrPattern
parent: IContainerModel
}
/**
* Returns a new offset by applying an Add method (append, insert etc.)
* See AddMethod
* @param index Index of the container
* @param containerConfig Configuration of a container
* @param parent Parent container
* @param x Additionnal offset
* @returns New offset
*/
function ApplyAddMethod(
index: number,
containerConfig: IAvailableContainer,
parent: IContainerModel,
x: number
): number {
if (index > 0 && (
containerConfig.AddMethod === undefined ||
containerConfig.AddMethod === null ||
containerConfig.AddMethod === AddMethod.Append
)) {
// Append method (default)
const lastChild: IContainerModel | undefined = parent.children
.at(index - 1);
if (lastChild !== undefined) {
x += (lastChild.properties.x + lastChild.properties.width);
}
}
return x;
}

View file

@ -1,17 +1,12 @@
import { IHistoryState } from '../../../Interfaces/IHistoryState'; import { IHistoryState } from '../../../Interfaces/IHistoryState';
import { IConfiguration } from '../../../Interfaces/IConfiguration';
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel'; import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
import { FindContainerById, MakeIterator } from '../../../utils/itertools'; import { FindContainerById, MakeIterator } from '../../../utils/itertools';
import { GetCurrentHistory, UpdateCounters } from '../Editor'; import { GetCurrentHistory } from '../Editor';
import { AddMethod } from '../../../Enums/AddMethod';
import { IAvailableContainer } from '../../../Interfaces/IAvailableContainer';
import { GetDefaultContainerProps, DEFAULTCHILDTYPE_ALLOW_CYCLIC, DEFAULTCHILDTYPE_MAX_DEPTH } from '../../../utils/default';
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors'; import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel'; import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import Swal from 'sweetalert2'; import Swal from 'sweetalert2';
import { ApplyMargin, TransformX } from '../../../utils/svg';
import { PropertyType } from '../../../Enums/PropertyType'; import { PropertyType } from '../../../Enums/PropertyType';
import { ContainerOrPattern, GetPattern, IPattern } from '../../../Interfaces/IPattern'; import { TransformX } from '../../../utils/svg';
/** /**
* Select a container * Select a container
@ -148,483 +143,6 @@ function UnlinkContainerFromSymbols(symbols: Map<string, ISymbolModel>, containe
} }
} }
/**
* Add a new container to a selected container
* @param type The type of container
* @param configuration Configuration of the App
* @param fullHistory History of the editor
* @param historyCurrentStep Current step
* @returns void
*/
export function AddContainerToSelectedContainer(
type: string,
selected: IContainerModel | undefined,
configuration: IConfiguration,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] | null {
if (selected === null ||
selected === undefined) {
return null;
}
const parent = selected;
return AddContainer(
parent.children.length,
type,
parent.properties.id,
configuration,
fullHistory,
historyCurrentStep
);
}
/**
* Create and add a new container at `index` in children of parent of `parentId`
* @param index Index where to insert to the new container
* @param type Type of container
* @param parentId Parent in which to insert the new container
* @param configuration Configuration of the app
* @param fullHistory History of the editor
* @param historyCurrentStep Current step
* @returns void
*/
export function AddContainers(
index: number,
availableContainers: IAvailableContainer[],
parentId: string,
configuration: IConfiguration,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
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);
// Find the parent in the clone
const parentClone: IContainerModel | undefined = FindContainerById(
clone, parentId
);
if (parentClone === null || parentClone === undefined) {
throw new Error('[AddContainer] Container model was not found among children of the main container!');
}
// Deep clone the counters
const newCounters = Object.assign({}, current.typeCounters);
// containerIds is used for logging purpose (see setHistory below)
const containerIds: string[] = [];
// 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);
});
// Update the state
history.push({
lastAction: `Add [${containerIds.join(', ')}] in ${parentClone.properties.id}`,
mainContainer: clone,
selectedContainerId: parentClone.properties.id,
typeCounters: newCounters,
symbols: structuredClone(current.symbols),
selectedSymbolId: current.selectedSymbolId
});
return history;
}
function AddNewContainerToParent(
availableContainer: IAvailableContainer,
configuration: IConfiguration,
parentClone: IContainerModel,
index: number,
typeIndex: number,
newCounters: Record<string, number>,
symbols: Map<string, ISymbolModel>,
containerIds: string[],
initChilds: boolean = true
): ContainerModel {
const type = availableContainer.Type;
const defaultConfig = configuration.AvailableContainers
.find(option => option.Type === type);
if (defaultConfig === undefined) {
throw new Error(`[AddContainer] Object type not found among default config. Found: ${type}`);
}
const containerConfig = Object.assign(defaultConfig, availableContainer);
// Default margin
const left: number = containerConfig.Margin?.left ?? 0;
const bottom: number = containerConfig.Margin?.bottom ?? 0;
const top: number = containerConfig.Margin?.top ?? 0;
const right: number = containerConfig.Margin?.right ?? 0;
// Default coordinates
let x = containerConfig.X ?? 0;
let y = containerConfig.Y ?? 0;
let width = containerConfig.Width ?? containerConfig.MaxWidth ?? containerConfig.MinWidth ?? parentClone.properties.width;
let height = containerConfig.Height ?? parentClone.properties.height;
({ x, y, width, height } = ApplyMargin(x, y, width, height, left, bottom, top, right));
// Apply an add method (append or insert/replace)
x = ApplyAddMethod(index + typeIndex, containerConfig, parentClone, x);
// Set the counter of the object type in order to assign an unique id
UpdateCounters(newCounters, type);
const count = newCounters[type];
const defaultProperties = GetDefaultContainerProps(
type,
count,
parentClone,
x,
y,
width,
height,
containerConfig
);
// Create the container
const newContainer = new ContainerModel(
parentClone,
defaultProperties,
[],
{
type
}
);
// Add it to the parent
if (index === parentClone.children.length) {
parentClone.children.push(newContainer);
} else {
parentClone.children.splice(index, 0, newContainer);
}
// Sort the parent children by x
UpdateParentChildrenList(parentClone);
/// Handle behaviors here ///
// Apply the behaviors (flex, rigid, anchor)
ApplyBehaviors(newContainer, symbols);
// Then, apply the behaviors on its siblings (mostly for flex)
ApplyBehaviorsOnSiblingsChildren(newContainer, symbols);
// Initialize default children of the container
if (initChilds) {
if (containerConfig.DefaultChildType !== undefined) {
InitializeDefaultChild(
newContainer,
configuration,
containerConfig,
newCounters
);
} else {
InitializeChildrenWithPattern(
newContainer,
configuration,
containerConfig,
newCounters,
symbols
);
}
}
// Add to the list of container id for logging purpose
containerIds.push(newContainer.properties.id);
return newContainer;
}
/**
* Create and add a new container at `index` in children of parent of `parentId`
* @param index Index where to insert to the new container
* @param type Type of container
* @param parentId Parent in which to insert the new container
* @param configuration Configuration of the app
* @param fullHistory History of the editor
* @param historyCurrentStep Current step
* @param setHistory State setter of History
* @param setHistoryCurrentStep State setter of the current step
* @returns new history
*/
export function AddContainer(
index: number,
type: string,
parentId: string,
configuration: IConfiguration,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
// just call AddContainers with an array on a single element
return AddContainers(
index,
// eslint-disable-next-line @typescript-eslint/naming-convention
[{ Type: type }],
parentId,
configuration,
fullHistory,
historyCurrentStep
);
}
/**
* Sort the parent children by x
* @param parentClone The clone used for the sort
* @returns void
*/
function UpdateParentChildrenList(parentClone: IContainerModel | null | undefined): void {
if (parentClone === null || parentClone === undefined) {
return;
}
const children = parentClone.children;
parentClone.children.sort(
(a, b) => {
const xA = TransformX(a.properties.x, a.properties.width, a.properties.xPositionReference);
const xB = TransformX(b.properties.x, b.properties.width, b.properties.xPositionReference);
if (xA < xB) {
return -1;
}
if (xB < xA) {
return 1;
}
// xA = xB
const indexA = children.indexOf(a);
const indexB = children.indexOf(b);
return indexA - indexB;
}
);
}
/**
* Initialize the children of the container
* @param configuration Current configuration of the app
* @param containerConfig The new container config used to read the default child type
* @param newContainer The new container to initialize its default children
* @param newCounters Type counter used for unique ids
* @returns
*/
function InitializeDefaultChild(
newContainer: ContainerModel,
configuration: IConfiguration,
containerConfig: IAvailableContainer,
newCounters: Record<string, number>
): void {
if (containerConfig.DefaultChildType === undefined) {
return;
}
let currentConfig = configuration.AvailableContainers
.find(option => option.Type === containerConfig.DefaultChildType);
let parent = newContainer;
let depth = 0;
const seen = new Set<string>([containerConfig.Type]);
while (currentConfig !== undefined &&
depth <= DEFAULTCHILDTYPE_MAX_DEPTH
) {
if (!DEFAULTCHILDTYPE_ALLOW_CYCLIC && seen.has(currentConfig.Type)) {
return;
}
seen.add(currentConfig.Type);
/// TODO: REFACTOR this with AddContainerToParent ///
const left: number = currentConfig.Margin?.left ?? 0;
const bottom: number = currentConfig.Margin?.bottom ?? 0;
const top: number = currentConfig.Margin?.top ?? 0;
const right: number = currentConfig.Margin?.right ?? 0;
let x = currentConfig.X ?? 0;
let y = currentConfig.Y ?? 0;
let width = currentConfig.Width ?? currentConfig.MaxWidth ?? currentConfig.MinWidth ?? parent.properties.width;
let height = currentConfig.Height ?? parent.properties.height;
({ x, y, width, height } = ApplyMargin(x, y, width, height, left, bottom, top, right));
UpdateCounters(newCounters, currentConfig.Type);
const count = newCounters[currentConfig.Type];
const defaultChildProperties = GetDefaultContainerProps(
currentConfig.Type,
count,
parent,
x,
y,
width,
height,
currentConfig
);
// Create the container
const newChildContainer = new ContainerModel(
parent,
defaultChildProperties,
[],
{
type: currentConfig.Type
}
);
// And push it the the parent children
parent.children.push(newChildContainer);
/// ///
// iterate
depth++;
parent = newChildContainer;
currentConfig = configuration.AvailableContainers
.find(option => option.Type === (currentConfig as IAvailableContainer).DefaultChildType);
}
}
function InitializeChildrenWithPattern(
newContainer: ContainerModel,
configuration: IConfiguration,
containerConfig: IAvailableContainer,
newCounters: Record<string, number>,
symbols: Map<string, ISymbolModel>
): void {
const patternId = containerConfig.Pattern;
if (patternId === undefined || patternId === null) {
return;
}
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 containerOrPattern = GetPattern(patternId, configs, patterns);
const pattern = containerOrPattern as IPattern;
// Will store the ids of the iterated IAvailableContainer types
const debugContainerArr: string[] = [];
if (pattern.children === undefined) {
const container = containerOrPattern as IAvailableContainer;
// Add Container
AddNewContainerToParent(
container,
configuration,
newContainer,
0, 0,
newCounters,
symbols,
debugContainerArr
);
}
// BFS over patterns
const rootNode: Node = {
containerOrPattern: pattern,
parent: newContainer
};
const queue: Node[] = [rootNode];
while (queue.length > 0) {
let levelSize = queue.length;
while (levelSize-- !== 0) {
const node = queue.shift() as Node;
const pattern = node.containerOrPattern as IPattern;
if (pattern.children === undefined) {
// Add Container
const containerConfig = node.containerOrPattern as IAvailableContainer;
AddNewContainerToParent(
containerConfig,
configuration,
node.parent,
0, 0,
newCounters,
symbols,
debugContainerArr
);
continue;
}
let parent = node.parent;
if (pattern.wrapper !== undefined) {
// 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 {
parent = AddNewContainerToParent(
container,
configuration,
parent,
0, 0,
newCounters,
symbols,
debugContainerArr,
false
);
}
}
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;
}
const nextNode: Node = {
containerOrPattern: child,
parent
};
queue.push(nextNode);
}
}
}
}
interface Node {
containerOrPattern: ContainerOrPattern
parent: IContainerModel
}
/**
* Returns a new offset by applying an Add method (append, insert etc.)
* See AddMethod
* @param index Index of the container
* @param containerConfig Configuration of a container
* @param parent Parent container
* @param x Additionnal offset
* @returns New offset
*/
function ApplyAddMethod(
index: number,
containerConfig: IAvailableContainer,
parent: IContainerModel,
x: number
): number {
if (index > 0 && (
containerConfig.AddMethod === undefined ||
containerConfig.AddMethod === null ||
containerConfig.AddMethod === AddMethod.Append
)) {
// Append method (default)
const lastChild: IContainerModel | undefined = parent.children
.at(index - 1);
if (lastChild !== undefined) {
x += (lastChild.properties.x + lastChild.properties.width);
}
}
return x;
}
/** /**
* Handled the property change event in the properties form * Handled the property change event in the properties form
* @param key Property name * @param key Property name
@ -667,6 +185,34 @@ export function OnPropertyChange(
return history; return history;
} }
/**
* Sort the parent children by x
* @param parentClone The clone used for the sort
* @returns void
*/
export function SortChildren(parentClone: IContainerModel | null | undefined): void {
if (parentClone === null || parentClone === undefined) {
return;
}
const children = parentClone.children;
parentClone.children.sort(
(a, b) => {
const xA = TransformX(a.properties.x, a.properties.width, a.properties.xPositionReference);
const xB = TransformX(b.properties.x, b.properties.width, b.properties.xPositionReference);
if (xA < xB) {
return -1;
}
if (xB < xA) {
return 1;
}
// xA = xB
const indexA = children.indexOf(a);
const indexB = children.indexOf(b);
return indexA - indexB;
}
);
}
/** /**
* Set the container with properties and behaviors (mutate) * Set the container with properties and behaviors (mutate)
* @param container Container to update * @param container Container to update
@ -701,7 +247,7 @@ function SetContainer(
ApplyBehaviorsOnSiblingsChildren(container, symbols); ApplyBehaviorsOnSiblingsChildren(container, symbols);
// sort the children list by their position // sort the children list by their position
UpdateParentChildrenList(container.parent); SortChildren(container.parent);
} }
/** /**

View file

@ -8,7 +8,8 @@ import { ISetContainerListRequest } from '../../../Interfaces/ISetContainerListR
import { ISetContainerListResponse } from '../../../Interfaces/ISetContainerListResponse'; import { ISetContainerListResponse } from '../../../Interfaces/ISetContainerListResponse';
import { FindContainerById } from '../../../utils/itertools'; import { FindContainerById } from '../../../utils/itertools';
import { SetContainerList } from '../../API/api'; import { SetContainerList } from '../../API/api';
import { AddContainers, DeleteContainer } from './ContainerOperations'; import { AddContainers } from './AddContainer';
import { DeleteContainer } from './ContainerOperations';
export function GetAction( export function GetAction(
action: IAction, action: IAction,

View file

@ -4,7 +4,7 @@ import { IConfiguration } from '../../Interfaces/IConfiguration';
import { SVG } from '../SVG/SVG'; import { SVG } from '../SVG/SVG';
import { IHistoryState } from '../../Interfaces/IHistoryState'; import { IHistoryState } from '../../Interfaces/IHistoryState';
import { UI } from '../UI/UI'; import { UI } from '../UI/UI';
import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, OnPropertyChange, AddContainer } 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 { events as EVENTS } from '../../Events/EditorEvents';
@ -14,6 +14,7 @@ import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, Se
import { FindContainerById } from '../../utils/itertools'; import { FindContainerById } from '../../utils/itertools';
import { IMenuAction, Menu } from '../Menu/Menu'; import { IMenuAction, Menu } from '../Menu/Menu';
import { GetAction } from './Actions/ContextMenuActions'; import { GetAction } from './Actions/ContextMenuActions';
import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer';
interface IEditorProps { interface IEditorProps {
root: Element | Document root: Element | Document

View file

@ -94,6 +94,7 @@ export const DEFAULT_CONFIG: IConfiguration = {
} }
], ],
AvailableSymbols: [], AvailableSymbols: [],
Patterns: [],
MainContainer: { MainContainer: {
Type: 'Container', Type: 'Container',
Width: 800, Width: 800,