Implement Pattern

This commit is contained in:
Eric NGUYEN 2022-09-14 16:55:28 +02:00
parent c0a7a26874
commit f6e4238b3d
4 changed files with 290 additions and 77 deletions

View file

@ -11,6 +11,7 @@ import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import Swal from 'sweetalert2';
import { ApplyMargin, TransformX } from '../../../utils/svg';
import { PropertyType } from '../../../Enums/PropertyType';
import { ContainerOrPattern, GetPattern, IPattern } from '../../../Interfaces/IPattern';
/**
* Select a container
@ -220,82 +221,7 @@ export function AddContainers(
// Iterate over the containers
availableContainers.forEach((availableContainer, typeIndex) => {
// Get the preset properties from the API
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, current.symbols);
// Then, apply the behaviors on its siblings (mostly for flex)
ApplyBehaviorsOnSiblingsChildren(newContainer, current.symbols);
// Initialize default children of the container
InitializeDefaultChild(configuration, containerConfig, newContainer, newCounters);
// Add to the list of container id for logging purpose
containerIds.push(newContainer.properties.id);
AddNewContainerToParent(availableContainer, configuration, parentClone, index, typeIndex, newCounters, current.symbols, containerIds);
});
// Update the state
@ -311,6 +237,113 @@ export function AddContainers(
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
@ -380,9 +413,9 @@ function UpdateParentChildrenList(parentClone: IContainerModel | null | undefine
* @returns
*/
function InitializeDefaultChild(
newContainer: ContainerModel,
configuration: IConfiguration,
containerConfig: IAvailableContainer,
newContainer: ContainerModel,
newCounters: Record<string, number>
): void {
if (containerConfig.DefaultChildType === undefined) {
@ -404,6 +437,7 @@ function InitializeDefaultChild(
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;
@ -440,6 +474,7 @@ function InitializeDefaultChild(
// And push it the the parent children
parent.children.push(newChildContainer);
/// ///
// iterate
depth++;
@ -449,6 +484,116 @@ function InitializeDefaultChild(
}
}
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