Merged PR 173: Implements API methods through right click + more (read desc)

- Implements API methods through right click
- Refactor events
- Refactor usage of setHistory and setHistoryCurrentStep into a single function
- Update ContainerOperations documentations
- Added AddContainers in order to add multiple containers + refactor AddContainer to use it
- Fix regression
- Fix AddContainer at index
This commit is contained in:
Eric Nguyen 2022-08-30 14:45:29 +00:00
parent 79c6874240
commit 57e6c9a156
20 changed files with 652 additions and 291 deletions

View file

@ -1 +1,2 @@
VITE_API_URL=http://localhost:5000 VITE_API_FETCH_URL=http://localhost:5000
VITE_API_POST_URL=http://localhost:5000/ApplicationState

View file

@ -1,2 +1,3 @@
VITE_API_URL=https://localhost/SmartMenuiserieTemplate/Service.svc/GetSVGLayoutConfiguration VITE_API_FETCH_URL=https://localhost/SmartMenuiserieTemplate/Service.svc/GetSVGLayoutConfiguration
VITE_API_POST_URL=https://localhost/SmartMenuiserieTemplate/Service.svc/ApplicationState

View file

@ -1 +1,2 @@
VITE_API_URL=http://localhost:5000 VITE_API_FETCH_URL=http://localhost:5000
VITE_API_POST_URL=http://localhost:5000

View file

@ -9,7 +9,7 @@ const getCircularReplacer = () => {
return; return;
} }
if (key === 'Symbols') { if (key === 'symbols') {
return Array.from(value.entries()); return Array.from(value.entries());
} }

View file

@ -3,7 +3,7 @@ import { FetchConfiguration } from './api';
describe.concurrent('API test', () => { describe.concurrent('API test', () => {
it('Load environment', () => { it('Load environment', () => {
const url = import.meta.env.VITE_API_URL; const url = import.meta.env.VITE_API_FETCH_URL;
expect(url).toBe('http://localhost:5000'); expect(url).toBe('http://localhost:5000');
}); });

View file

@ -1,11 +1,15 @@
import { IConfiguration } from '../../Interfaces/IConfiguration'; import { IConfiguration } from '../../Interfaces/IConfiguration';
import { IHistoryState } from '../../Interfaces/IHistoryState';
import { ISetContainerListRequest } from '../../Interfaces/ISetContainerListRequest';
import { ISetContainerListResponse } from '../../Interfaces/ISetContainerListResponse';
import { GetCircularReplacer } from '../../utils/saveload';
/** /**
* Fetch the configuration from the API * Fetch the configuration from the API
* @returns {Configation} The model of the configuration for the application * @returns {Configation} The model of the configuration for the application
*/ */
export async function FetchConfiguration(): Promise<IConfiguration> { export async function FetchConfiguration(): Promise<IConfiguration> {
const url = `${import.meta.env.VITE_API_URL}`; const url = import.meta.env.VITE_API_FETCH_URL;
// The test library cannot use the Fetch API // The test library cannot use the Fetch API
// @ts-expect-error // @ts-expect-error
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
@ -28,3 +32,30 @@ export async function FetchConfiguration(): Promise<IConfiguration> {
xhr.send(); xhr.send();
}); });
} }
export async function SetContainerList(request: ISetContainerListRequest): Promise<ISetContainerListResponse> {
const url = import.meta.env.VITE_API_POST_URL;
const dataParsed = JSON.stringify(request, GetCircularReplacer());
// The test library cannot use the Fetch API
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (window.fetch) {
return await fetch(url, {
method: 'POST',
body: dataParsed
})
.then(async(response) =>
await response.json()
) as ISetContainerListResponse;
}
return await new Promise((resolve) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onreadystatechange = function() { // Call a function when the state changes.
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
resolve(JSON.parse(this.responseText));
}
};
xhr.send(dataParsed);
});
}

View file

@ -1,4 +1,3 @@
import { Dispatch, SetStateAction } from 'react';
import { IHistoryState } from '../../../Interfaces/IHistoryState'; import { IHistoryState } from '../../../Interfaces/IHistoryState';
import { IConfiguration } from '../../../Interfaces/IConfiguration'; import { IConfiguration } from '../../../Interfaces/IConfiguration';
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel'; import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
@ -16,14 +15,13 @@ import { PropertyType } from '../../../Enums/PropertyType';
/** /**
* Select a container * Select a container
* @param container Selected container * @param container Selected container
* @returns New history
*/ */
export function SelectContainer( export function SelectContainer(
containerId: string, containerId: string,
fullHistory: IHistoryState[], fullHistory: IHistoryState[],
historyCurrentStep: number, historyCurrentStep: number
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, ): IHistoryState[] {
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
const history = GetCurrentHistory(fullHistory, historyCurrentStep); const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1]; const current = history[history.length - 1];
@ -35,8 +33,7 @@ export function SelectContainer(
symbols: structuredClone(current.symbols), symbols: structuredClone(current.symbols),
selectedSymbolId: current.selectedSymbolId selectedSymbolId: current.selectedSymbolId
}); });
setHistory(history); return history;
setHistoryCurrentStep(history.length - 1);
} }
/** /**
@ -44,16 +41,13 @@ export function SelectContainer(
* @param containerId containerId of the container to delete * @param containerId containerId of the container to delete
* @param fullHistory History of the editor * @param fullHistory History of the editor
* @param historyCurrentStep Current step * @param historyCurrentStep Current step
* @param setHistory State setter for History * @returns New history
* @param setHistoryCurrentStep State setter for current step
*/ */
export function DeleteContainer( export function DeleteContainer(
containerId: string, containerId: string,
fullHistory: IHistoryState[], fullHistory: IHistoryState[],
historyCurrentStep: number, historyCurrentStep: number
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, ): IHistoryState[] {
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
const history = GetCurrentHistory(fullHistory, historyCurrentStep); const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1]; const current = history[history.length - 1];
@ -80,7 +74,7 @@ export function DeleteContainer(
} }
const newSymbols = structuredClone(current.symbols); const newSymbols = structuredClone(current.symbols);
UnlinkSymbol(newSymbols, container); UnlinkContainerFromSymbols(newSymbols, container);
const index = container.parent.children.indexOf(container); const index = container.parent.children.indexOf(container);
if (index > -1) { if (index > -1) {
@ -108,10 +102,21 @@ export function DeleteContainer(
symbols: newSymbols, symbols: newSymbols,
selectedSymbolId: current.selectedSymbolId selectedSymbolId: current.selectedSymbolId
}); });
setHistory(history); return history;
setHistoryCurrentStep(history.length - 1);
} }
/**
* Returns the next container that will be selected
* after the selectedContainer is removed.
* If the selected container is removed, select the sibling before,
* If there is no sibling, select the parent,
*
* @param mainContainerClone Main container
* @param selectedContainerId Current selected container
* @param parent Parent of the selected/deleted container
* @param index Index of the selected/deleted container
* @returns {IContainerModel} Next selected container
*/
function GetSelectedContainerOnDelete( function GetSelectedContainerOnDelete(
mainContainerClone: IContainerModel, mainContainerClone: IContainerModel,
selectedContainerId: string, selectedContainerId: string,
@ -125,7 +130,13 @@ function GetSelectedContainerOnDelete(
return newSelectedContainerId; return newSelectedContainerId;
} }
function UnlinkSymbol(symbols: Map<string, ISymbolModel>, container: IContainerModel): void { /**
* Unlink a container and its children to symbols
* (used when deleting a container)
* @param symbols Symbols to update
* @param container Container to unlink
*/
function UnlinkContainerFromSymbols(symbols: Map<string, ISymbolModel>, container: IContainerModel): void {
const it = MakeIterator(container); const it = MakeIterator(container);
for (const child of it) { for (const child of it) {
const symbol = symbols.get(child.properties.linkedSymbolId); const symbol = symbols.get(child.properties.linkedSymbolId);
@ -142,8 +153,6 @@ function UnlinkSymbol(symbols: Map<string, ISymbolModel>, container: IContainerM
* @param configuration Configuration of the App * @param configuration Configuration of the App
* @param fullHistory History of the editor * @param fullHistory History of the editor
* @param historyCurrentStep Current step * @param historyCurrentStep Current step
* @param setHistory State setter for History
* @param setHistoryCurrentStep State setter for current step
* @returns void * @returns void
*/ */
export function AddContainerToSelectedContainer( export function AddContainerToSelectedContainer(
@ -151,25 +160,21 @@ export function AddContainerToSelectedContainer(
selected: IContainerModel | undefined, selected: IContainerModel | undefined,
configuration: IConfiguration, configuration: IConfiguration,
fullHistory: IHistoryState[], fullHistory: IHistoryState[],
historyCurrentStep: number, historyCurrentStep: number
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, ): IHistoryState[] | null {
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
if (selected === null || if (selected === null ||
selected === undefined) { selected === undefined) {
return; return null;
} }
const parent = selected; const parent = selected;
AddContainer( return AddContainer(
parent.children.length, parent.children.length,
type, type,
parent.properties.id, parent.properties.id,
configuration, configuration,
fullHistory, fullHistory,
historyCurrentStep, historyCurrentStep
setHistory,
setHistoryCurrentStep
); );
} }
@ -181,23 +186,39 @@ export function AddContainerToSelectedContainer(
* @param configuration Configuration of the app * @param configuration Configuration of the app
* @param fullHistory History of the editor * @param fullHistory History of the editor
* @param historyCurrentStep Current step * @param historyCurrentStep Current step
* @param setHistory State setter of History
* @param setHistoryCurrentStep State setter of the current step
* @returns void * @returns void
*/ */
export function AddContainer( export function AddContainers(
index: number, index: number,
type: string, types: string[],
parentId: string, parentId: string,
configuration: IConfiguration, configuration: IConfiguration,
fullHistory: IHistoryState[], fullHistory: IHistoryState[],
historyCurrentStep: number, historyCurrentStep: number
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, ): IHistoryState[] {
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
const history = GetCurrentHistory(fullHistory, historyCurrentStep); const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1]; 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
types.forEach((type, typeIndex) => {
// Get the preset properties from the API // Get the preset properties from the API
const containerConfig = configuration.AvailableContainers const containerConfig = configuration.AvailableContainers
.find(option => option.Type === type); .find(option => option.Type === type);
@ -206,27 +227,13 @@ export function AddContainer(
throw new Error(`[AddContainer] Object type not found. Found: ${type}`); throw new Error(`[AddContainer] Object type not found. Found: ${type}`);
} }
// Set the counter of the object type in order to assign an unique id // Default margin
const newCounters = Object.assign({}, current.typeCounters);
UpdateCounters(newCounters, type);
const count = newCounters[type];
// Create maincontainer model
const clone: IContainerModel = structuredClone(current.mainContainer);
// Find the parent
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!');
}
const left: number = containerConfig.Margin?.left ?? 0; const left: number = containerConfig.Margin?.left ?? 0;
const bottom: number = containerConfig.Margin?.bottom ?? 0; const bottom: number = containerConfig.Margin?.bottom ?? 0;
const top: number = containerConfig.Margin?.top ?? 0; const top: number = containerConfig.Margin?.top ?? 0;
const right: number = containerConfig.Margin?.right ?? 0; const right: number = containerConfig.Margin?.right ?? 0;
// Default coordinates
let x = containerConfig.DefaultX ?? 0; let x = containerConfig.DefaultX ?? 0;
let y = containerConfig.DefaultY ?? 0; let y = containerConfig.DefaultY ?? 0;
let width = containerConfig.Width ?? containerConfig.MaxWidth ?? containerConfig.MinWidth ?? parentClone.properties.width; let width = containerConfig.Width ?? containerConfig.MaxWidth ?? containerConfig.MinWidth ?? parentClone.properties.width;
@ -234,7 +241,12 @@ export function AddContainer(
({ x, y, width, height } = ApplyMargin(x, y, width, height, left, bottom, top, right)); ({ x, y, width, height } = ApplyMargin(x, y, width, height, left, bottom, top, right));
x = ApplyAddMethod(index, containerConfig, parentClone, x); // 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( const defaultProperties = GetDefaultContainerProps(
type, type,
@ -257,38 +269,111 @@ export function AddContainer(
} }
); );
// Add it to the parent
if (index === parentClone.children.length) {
parentClone.children.push(newContainer); parentClone.children.push(newContainer);
UpdateParentChildrenList(parentClone); } else {
parentClone.children.splice(index, 0, newContainer);
}
/// Handle behaviors here ///
// Initialize default children of the container
InitializeDefaultChild(configuration, containerConfig, newContainer, newCounters); InitializeDefaultChild(configuration, containerConfig, newContainer, newCounters);
// Apply the behaviors (flex, rigid, anchor)
ApplyBehaviors(newContainer, current.symbols); ApplyBehaviors(newContainer, current.symbols);
// Then, apply the behaviors on its siblings (mostly for flex)
ApplyBehaviorsOnSiblings(newContainer, current.symbols); ApplyBehaviorsOnSiblings(newContainer, current.symbols);
// Sort the parent children by x
UpdateParentChildrenList(parentClone);
// Add to the list of container id for logging purpose
containerIds.push(newContainer.properties.id);
});
// Update the state // Update the state
history.push({ history.push({
lastAction: `Add ${newContainer.properties.id} in ${parentClone.properties.id}`, lastAction: `Add [${containerIds.join(', ')}] in ${parentClone.properties.id}`,
mainContainer: clone, mainContainer: clone,
selectedContainerId: parentClone.properties.id, selectedContainerId: parentClone.properties.id,
typeCounters: newCounters, typeCounters: newCounters,
symbols: structuredClone(current.symbols), symbols: structuredClone(current.symbols),
selectedSymbolId: current.selectedSymbolId selectedSymbolId: current.selectedSymbolId
}); });
setHistory(history);
setHistoryCurrentStep(history.length - 1); return history;
} }
/**
* 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,
[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 { function UpdateParentChildrenList(parentClone: IContainerModel | null | undefined): void {
if (parentClone === null || parentClone === undefined) { if (parentClone === null || parentClone === undefined) {
return; return;
} }
const children = parentClone.children;
parentClone.children.sort( parentClone.children.sort(
(a, b) => TransformX(a.properties.x, a.properties.width, a.properties.xPositionReference) - (a, b) => {
TransformX(b.properties.x, b.properties.width, b.properties.xPositionReference) 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( function InitializeDefaultChild(
configuration: IConfiguration, configuration: IConfiguration,
containerConfig: IAvailableContainer, containerConfig: IAvailableContainer,
@ -368,12 +453,18 @@ function InitializeDefaultChild(
* @param x Additionnal offset * @param x Additionnal offset
* @returns New offset * @returns New offset
*/ */
function ApplyAddMethod(index: number, containerConfig: IAvailableContainer, parent: IContainerModel, x: number): number { function ApplyAddMethod(
index: number,
containerConfig: IAvailableContainer,
parent: IContainerModel,
x: number
): number {
if (index > 0 && ( if (index > 0 && (
containerConfig.AddMethod === undefined || containerConfig.AddMethod === undefined ||
containerConfig.AddMethod === AddMethod.Append)) { containerConfig.AddMethod === AddMethod.Append)) {
// Append method (default) // Append method (default)
const lastChild: IContainerModel | undefined = parent.children.at(index - 1); const lastChild: IContainerModel | undefined = parent.children
.at(index - 1);
if (lastChild !== undefined) { if (lastChild !== undefined) {
x += (lastChild.properties.x + lastChild.properties.width); x += (lastChild.properties.x + lastChild.properties.width);
@ -394,10 +485,8 @@ export function OnPropertyChange(
type: PropertyType = PropertyType.Simple, type: PropertyType = PropertyType.Simple,
selected: IContainerModel | undefined, selected: IContainerModel | undefined,
fullHistory: IHistoryState[], fullHistory: IHistoryState[],
historyCurrentStep: number, historyCurrentStep: number
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, ): IHistoryState[] {
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
const history = GetCurrentHistory(fullHistory, historyCurrentStep); const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1]; const current = history[history.length - 1];
@ -423,8 +512,7 @@ export function OnPropertyChange(
symbols: structuredClone(current.symbols), symbols: structuredClone(current.symbols),
selectedSymbolId: current.selectedSymbolId selectedSymbolId: current.selectedSymbolId
}); });
setHistory(history); return history;
setHistoryCurrentStep(history.length - 1);
} }
/** /**
@ -465,6 +553,13 @@ function SetContainer(
UpdateParentChildrenList(container.parent); UpdateParentChildrenList(container.parent);
} }
/**
* Assign the property to a container depending on the type
* @param container Container in which the property will be applied to
* @param key Key/Id of the property
* @param value Value of the property
* @param type Type of the property
*/
function AssignProperty(container: ContainerModel, key: string, value: string | number | boolean, type: PropertyType): void { function AssignProperty(container: ContainerModel, key: string, value: string | number | boolean, type: PropertyType): void {
switch (type) { switch (type) {
case PropertyType.Style: case PropertyType.Style:
@ -477,7 +572,12 @@ function AssignProperty(container: ContainerModel, key: string, value: string |
(container.properties as any)[key] = value; (container.properties as any)[key] = value;
} }
/**
* Set the margin property
*/
function SetMargin(): void { function SetMargin(): void {
// We need to detect change in order to apply transformation to the width and height
// Knowing the current margin is not enough as we dont keep the original width and height
const oldMarginValue: number = (container.properties.margin as any)[key]; const oldMarginValue: number = (container.properties.margin as any)[key];
const diff = Number(value) - oldMarginValue; const diff = Number(value) - oldMarginValue;
switch (key) { switch (key) {
@ -500,6 +600,14 @@ function AssignProperty(container: ContainerModel, key: string, value: string |
} }
} }
/**
* Link a symbol to a container
* @param containerId Container id
* @param oldSymbolId Old Symbol id
* @param newSymbolId New Symbol id
* @param symbols Current list of symbols
* @returns
*/
function LinkSymbol( function LinkSymbol(
containerId: string, containerId: string,
oldSymbolId: string, oldSymbolId: string,
@ -519,10 +627,18 @@ function LinkSymbol(
newSymbol.linkedContainers.add(containerId); newSymbol.linkedContainers.add(containerId);
} }
/**
* Iterate over the siblings of newContainer and apply the behaviors
* @param newContainer
* @param symbols
* @returns
*/
function ApplyBehaviorsOnSiblings(newContainer: ContainerModel, symbols: Map<string, ISymbolModel>): void { function ApplyBehaviorsOnSiblings(newContainer: ContainerModel, symbols: Map<string, ISymbolModel>): void {
if (newContainer.parent === null || newContainer.parent === undefined) { if (newContainer.parent === null || newContainer.parent === undefined) {
return; return;
} }
newContainer.parent.children.filter(container => newContainer !== container).forEach(container => ApplyBehaviors(container, symbols)); newContainer.parent.children
.filter(container => newContainer !== container)
.forEach(container => ApplyBehaviors(container, symbols));
} }

View file

@ -0,0 +1,131 @@
import Swal from 'sweetalert2';
import { AddMethod } from '../../../Enums/AddMethod';
import { IAction } from '../../../Interfaces/IAction';
import { IConfiguration } from '../../../Interfaces/IConfiguration';
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { IHistoryState } from '../../../Interfaces/IHistoryState';
import { ISetContainerListRequest } from '../../../Interfaces/ISetContainerListRequest';
import { ISetContainerListResponse } from '../../../Interfaces/ISetContainerListResponse';
import { FindContainerById } from '../../../utils/itertools';
import { SetContainerList } from '../../API/api';
import { AddContainers, DeleteContainer } from './ContainerOperations';
export function GetAction(
action: IAction,
currentState: IHistoryState,
configuration: IConfiguration,
history: IHistoryState[],
historyCurrentStep: number,
setNewHistory: (newHistory: IHistoryState[]) => void
): (target: HTMLElement) => void {
return (target: HTMLElement) => {
const id = target.id;
const container = FindContainerById(currentState.mainContainer, id);
if (container === undefined) {
Swal.fire({
title: 'Error',
text: 'No container was selected on right click',
icon: 'error'
});
throw new Error(`[API:${action.Action}] No container was selected`);
}
/* eslint-disable @typescript-eslint/naming-convention */
const request: ISetContainerListRequest = {
Container: container,
Action: action.Action,
ApplicationState: currentState
};
/* eslint-enable */
SetContainerList(request)
.then((response: ISetContainerListResponse) => {
HandleSetContainerList(
action,
container,
response,
configuration,
history,
historyCurrentStep,
setNewHistory
);
});
};
}
function HandleSetContainerList(
action: IAction,
selectedContainer: IContainerModel,
response: ISetContainerListResponse,
configuration: IConfiguration,
history: IHistoryState[],
historyCurrentStep: number,
setNewHistory: (newHistory: IHistoryState[]) => void
): void {
switch (action.AddingBehavior) {
case AddMethod.Append:
setNewHistory(
AddContainers(
selectedContainer.children.length,
response.Containers.map(container => container.properties.type),
selectedContainer.properties.id,
configuration,
history,
historyCurrentStep
));
break;
case AddMethod.Insert:
break;
case AddMethod.Replace:
setNewHistory(
HandleReplace(
selectedContainer,
response,
configuration,
history,
historyCurrentStep
)
);
break;
}
}
function HandleReplace(
selectedContainer: IContainerModel,
response: ISetContainerListResponse,
configuration: IConfiguration,
history: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
if (selectedContainer.parent === undefined || selectedContainer.parent === null) {
throw new Error('[ReplaceContainer] Cannot replace a container that does not exists');
}
const index = selectedContainer.parent.children.indexOf(selectedContainer);
const types = response.Containers.map(container => container.properties.type);
const newHistoryBeforeDelete = AddContainers(
index + 1,
types,
selectedContainer.properties.parentId,
configuration,
history,
historyCurrentStep
);
const newHistoryAfterDelete = DeleteContainer(
selectedContainer.properties.id,
newHistoryBeforeDelete,
newHistoryBeforeDelete.length - 1
);
// Remove AddContainers from history
newHistoryAfterDelete.splice(newHistoryAfterDelete.length - 2, 1);
// Rename the last action by Replace
newHistoryAfterDelete[newHistoryAfterDelete.length - 1].lastAction =
`Replace ${selectedContainer.properties.id} by [${types.join(', ')}]`;
return newHistoryAfterDelete;
}

View file

@ -13,10 +13,8 @@ export function AddSymbol(
name: string, name: string,
configuration: IConfiguration, configuration: IConfiguration,
fullHistory: IHistoryState[], fullHistory: IHistoryState[],
historyCurrentStep: number, historyCurrentStep: number
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, ): IHistoryState[] {
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
const history = GetCurrentHistory(fullHistory, historyCurrentStep); const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1]; const current = history[history.length - 1];
@ -44,17 +42,14 @@ export function AddSymbol(
symbols: newSymbols, symbols: newSymbols,
selectedSymbolId: newSymbol.id selectedSymbolId: newSymbol.id
}); });
setHistory(history); return history;
setHistoryCurrentStep(history.length - 1);
} }
export function SelectSymbol( export function SelectSymbol(
symbolId: string, symbolId: string,
fullHistory: IHistoryState[], fullHistory: IHistoryState[],
historyCurrentStep: number, historyCurrentStep: number
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, ): IHistoryState[] {
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
const history = GetCurrentHistory(fullHistory, historyCurrentStep); const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1]; const current = history[history.length - 1];
@ -66,17 +61,14 @@ export function SelectSymbol(
symbols: structuredClone(current.symbols), symbols: structuredClone(current.symbols),
selectedSymbolId: symbolId selectedSymbolId: symbolId
}); });
setHistory(history); return history;
setHistoryCurrentStep(history.length - 1);
} }
export function DeleteSymbol( export function DeleteSymbol(
symbolId: string, symbolId: string,
fullHistory: IHistoryState[], fullHistory: IHistoryState[],
historyCurrentStep: number, historyCurrentStep: number
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, ): IHistoryState[] {
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
const history = GetCurrentHistory(fullHistory, historyCurrentStep); const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1]; const current = history[history.length - 1];
@ -89,7 +81,7 @@ export function DeleteSymbol(
const newMainContainer = structuredClone(current.mainContainer); const newMainContainer = structuredClone(current.mainContainer);
UnlinkContainers(symbol, newMainContainer); UnlinkSymbolFromContainers(symbol, newMainContainer);
newSymbols.delete(symbolId); newSymbols.delete(symbolId);
@ -101,13 +93,17 @@ export function DeleteSymbol(
symbols: newSymbols, symbols: newSymbols,
selectedSymbolId: symbolId selectedSymbolId: symbolId
}); });
setHistory(history); return history;
setHistoryCurrentStep(history.length - 1);
} }
function UnlinkContainers(symbol: ISymbolModel, newMainContainer: IContainerModel): void { /**
* Unlink a symbol to a container and its children
* @param symbol Symbol to remove
* @param root Container and its children to remove a symbol from
*/
function UnlinkSymbolFromContainers(symbol: ISymbolModel, root: IContainerModel): void {
symbol.linkedContainers.forEach((containerId) => { symbol.linkedContainers.forEach((containerId) => {
const container = FindContainerById(newMainContainer, containerId); const container = FindContainerById(root, containerId);
if (container === undefined) { if (container === undefined) {
return; return;
@ -127,10 +123,8 @@ export function OnPropertyChange(
key: string, key: string,
value: string | number | boolean, value: string | number | boolean,
fullHistory: IHistoryState[], fullHistory: IHistoryState[],
historyCurrentStep: number, historyCurrentStep: number
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, ): IHistoryState[] {
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
const history = GetCurrentHistory(fullHistory, historyCurrentStep); const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1]; const current = history[history.length - 1];
@ -166,6 +160,5 @@ export function OnPropertyChange(
symbols: newSymbols, symbols: newSymbols,
selectedSymbolId: symbol.id selectedSymbolId: symbol.id
}); });
setHistory(history); return history;
setHistoryCurrentStep(history.length - 1);
} }

View file

@ -4,15 +4,16 @@ 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 } from './Actions/ContainerOperations'; import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, OnPropertyChange, AddContainers } 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 EditorEvents from '../../Events/EditorEvents'; import { events as EVENTS } from '../../Events/EditorEvents';
import { IEditorState } from '../../Interfaces/IEditorState'; import { IEditorState } from '../../Interfaces/IEditorState';
import { MAX_HISTORY } from '../../utils/default'; import { MAX_HISTORY } from '../../utils/default';
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations'; import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations';
import { FindContainerById } from '../../utils/itertools'; import { FindContainerById } from '../../utils/itertools';
import { Menu } from '../Menu/Menu'; import { IMenuAction, Menu } from '../Menu/Menu';
import { GetAction } from './Actions/ContextMenuActions';
interface IEditorProps { interface IEditorProps {
root: Element | Document root: Element | Document
@ -22,11 +23,11 @@ interface IEditorProps {
} }
function InitActions( function InitActions(
menuActions: Map<any, any>, menuActions: Map<string, IMenuAction[]>,
configuration: IConfiguration,
history: IHistoryState[], history: IHistoryState[],
historyCurrentStep: number, historyCurrentStep: number,
setHistory: React.Dispatch<React.SetStateAction<IHistoryState[]>>, setNewHistory: (newHistory: IHistoryState[]) => void
setHistoryCurrentStep: React.Dispatch<React.SetStateAction<number>>
): void { ): void {
menuActions.set( menuActions.set(
'elements-sidebar-row', 'elements-sidebar-row',
@ -34,13 +35,12 @@ function InitActions(
text: 'Delete', text: 'Delete',
action: (target: HTMLElement) => { action: (target: HTMLElement) => {
const id = target.id; const id = target.id;
DeleteContainer( const newHistory = DeleteContainer(
id, id,
history, history,
historyCurrentStep, historyCurrentStep
setHistory,
setHistoryCurrentStep
); );
setNewHistory(newHistory);
} }
}] }]
); );
@ -50,16 +50,43 @@ function InitActions(
text: 'Delete', text: 'Delete',
action: (target: HTMLElement) => { action: (target: HTMLElement) => {
const id = target.id; const id = target.id;
DeleteSymbol( const newHistory = DeleteSymbol(
id, id,
history, history,
historyCurrentStep, historyCurrentStep
setHistory,
setHistoryCurrentStep
); );
setNewHistory(newHistory);
} }
}] }]
); );
// API Actions
for (const availableContainer of configuration.AvailableContainers) {
if (availableContainer.Actions === undefined) {
continue;
}
for (const action of availableContainer.Actions) {
if (menuActions.get(availableContainer.Type) === undefined) {
menuActions.set(availableContainer.Type, []);
}
const currentState = GetCurrentHistoryState(history, historyCurrentStep);
const newAction: IMenuAction = {
text: action.Label,
action: GetAction(
action,
currentState,
configuration,
history,
historyCurrentStep,
setNewHistory
)
};
menuActions.get(availableContainer.Type)?.push(newAction);
}
}
} }
function UseShortcuts( function UseShortcuts(
@ -90,11 +117,9 @@ function UseWindowEvents(
historyCurrentStep: number, historyCurrentStep: number,
configuration: IConfiguration, configuration: IConfiguration,
editorRef: React.RefObject<HTMLDivElement>, editorRef: React.RefObject<HTMLDivElement>,
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, setNewHistory: (newHistory: IHistoryState[]) => void
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void { ): void {
useEffect(() => { useEffect(() => {
const events = EditorEvents;
const editorState: IEditorState = { const editorState: IEditorState = {
history, history,
historyCurrentStep, historyCurrentStep,
@ -102,13 +127,12 @@ function UseWindowEvents(
}; };
const funcs = new Map<string, () => void>(); const funcs = new Map<string, () => void>();
for (const event of events) { for (const event of EVENTS) {
function Func(eventInitDict?: CustomEventInit): void { function Func(eventInitDict?: CustomEventInit): void {
return event.func( return event.func(
root, root,
editorState, editorState,
setHistory, setNewHistory,
setHistoryCurrentStep,
eventInitDict eventInitDict
); );
} }
@ -116,7 +140,7 @@ function UseWindowEvents(
funcs.set(event.name, Func); funcs.set(event.name, Func);
} }
return () => { return () => {
for (const event of events) { for (const event of EVENTS) {
const func = funcs.get(event.name); const func = funcs.get(event.name);
if (func === undefined) { if (func === undefined) {
continue; continue;
@ -127,11 +151,29 @@ function UseWindowEvents(
}); });
} }
/**
* Return a macro function to use both setHistory
* and setHistoryCurrentStep at the same time
* @param setHistory
* @param setHistoryCurrentStep
* @returns
*/
function UseNewHistoryState(
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): (newHistory: IHistoryState[]) => void {
return (newHistory) => {
setHistory(newHistory);
setHistoryCurrentStep(newHistory.length - 1);
};
}
export function Editor(props: IEditorProps): JSX.Element { export function Editor(props: IEditorProps): JSX.Element {
// States // States
const [history, setHistory] = React.useState<IHistoryState[]>(structuredClone(props.history)); const [history, setHistory] = React.useState<IHistoryState[]>(structuredClone(props.history));
const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep); const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep);
const editorRef = useRef<HTMLDivElement>(null); const editorRef = useRef<HTMLDivElement>(null);
const setNewHistory = UseNewHistoryState(setHistory, setHistoryCurrentStep);
// Events // Events
UseShortcuts(history, historyCurrentStep, setHistoryCurrentStep); UseShortcuts(history, historyCurrentStep, setHistoryCurrentStep);
@ -141,13 +183,18 @@ export function Editor(props: IEditorProps): JSX.Element {
historyCurrentStep, historyCurrentStep,
props.configuration, props.configuration,
editorRef, editorRef,
setHistory, setNewHistory
setHistoryCurrentStep
); );
// Context Menu // Context Menu
const menuActions = new Map(); const menuActions = new Map();
InitActions(menuActions, history, historyCurrentStep, setHistory, setHistoryCurrentStep); InitActions(
menuActions,
props.configuration,
history,
historyCurrentStep,
setNewHistory
);
// Render // Render
const configuration = props.configuration; const configuration = props.configuration;
@ -162,66 +209,62 @@ export function Editor(props: IEditorProps): JSX.Element {
historyCurrentStep={historyCurrentStep} historyCurrentStep={historyCurrentStep}
availableContainers={configuration.AvailableContainers} availableContainers={configuration.AvailableContainers}
availableSymbols={configuration.AvailableSymbols} availableSymbols={configuration.AvailableSymbols}
selectContainer={(container) => SelectContainer( selectContainer={(container) => setNewHistory(
SelectContainer(
container, container,
history, history,
historyCurrentStep, historyCurrentStep
setHistory, ))}
setHistoryCurrentStep deleteContainer={(containerId: string) => setNewHistory(
)} DeleteContainer(
deleteContainer={(containerId: string) => DeleteContainer(
containerId, containerId,
history, history,
historyCurrentStep, historyCurrentStep
setHistory, ))}
setHistoryCurrentStep onPropertyChange={(key, value, type) => setNewHistory(
)} OnPropertyChange(
onPropertyChange={(key, value, type) => OnPropertyChange(
key, value, type, key, value, type,
selected, selected,
history, history,
historyCurrentStep, historyCurrentStep
setHistory, ))}
setHistoryCurrentStep addContainer={(type) => {
)} const newHistory = AddContainerToSelectedContainer(
addContainer={(type) => AddContainerToSelectedContainer(
type, type,
selected, selected,
configuration, configuration,
history, history,
historyCurrentStep, historyCurrentStep
setHistory, );
setHistoryCurrentStep if (newHistory !== null) {
)} setNewHistory(newHistory);
addSymbol={(type) => AddSymbol( }
}}
addSymbol={(type) => setNewHistory(
AddSymbol(
type, type,
configuration, configuration,
history, history,
historyCurrentStep, historyCurrentStep
setHistory, ))}
setHistoryCurrentStep onSymbolPropertyChange={(key, value) => setNewHistory(
)} OnSymbolPropertyChange(
onSymbolPropertyChange={(key, value) => OnSymbolPropertyChange(
key, value, key, value,
history, history,
historyCurrentStep, historyCurrentStep
setHistory, ))}
setHistoryCurrentStep selectSymbol={(symbolId) => setNewHistory(
)} SelectSymbol(
selectSymbol={(symbolId) => SelectSymbol(
symbolId, symbolId,
history, history,
historyCurrentStep, historyCurrentStep
setHistory, ))}
setHistoryCurrentStep deleteSymbol={(symbolId) => setNewHistory(
)} DeleteSymbol(
deleteSymbol={(symbolId) => DeleteSymbol(
symbolId, symbolId,
history, history,
historyCurrentStep, historyCurrentStep
setHistory, ))}
setHistoryCurrentStep
)}
saveEditorAsJSON={() => SaveEditorAsJSON( saveEditorAsJSON={() => SaveEditorAsJSON(
history, history,
historyCurrentStep, historyCurrentStep,

View file

@ -50,7 +50,7 @@ export function ElementsSidebar(props: IElementsSidebarProps): JSX.Element {
return ( return (
<button type="button" <button type="button"
className={`w-full border-blue-500 elements-sidebar-row whitespace-pre className={`w-full border-blue-500 elements-sidebar-row whitespace-pre
text-left text-sm font-medium transition-all ${selectedClass}`} text-left text-sm font-medium transition-all ${container.properties.type} ${selectedClass}`}
id={key} id={key}
key={key} key={key}
style={style} style={style}

View file

@ -4,11 +4,11 @@ import { MenuItem } from './MenuItem';
interface IMenuProps { interface IMenuProps {
getListener: () => HTMLElement | null getListener: () => HTMLElement | null
actions: Map<string, IAction[]> actions: Map<string, IMenuAction[]>
className?: string className?: string
} }
export interface IAction { export interface IMenuAction {
/** displayed */ /** displayed */
text: string text: string
@ -75,23 +75,24 @@ export function Menu(props: IMenuProps): JSX.Element {
setTarget setTarget
); );
let children; const children: JSX.Element[] = [];
if (target !== undefined) { if (target !== undefined) {
let count = 0;
for (const className of target.classList) { for (const className of target.classList) {
count++;
const actions = props.actions.get(className); const actions = props.actions.get(className);
if (actions === undefined) { if (actions === undefined) {
continue; continue;
} }
children = actions.map((action, index) => actions.forEach((action, index) => {
<MenuItem children.push(<MenuItem
key={`contextmenu-item-${index}`} key={`contextmenu-item-${count}`}
className="contextmenu-item" className="contextmenu-item"
text={action.text} text={action.text}
onClick={() => action.action(target)} /> onClick={() => action.action(target)} />);
); });
break;
}; };
} }

View file

@ -5,61 +5,48 @@ import { IEditorState } from '../Interfaces/IEditorState';
import { IHistoryState } from '../Interfaces/IHistoryState'; import { IHistoryState } from '../Interfaces/IHistoryState';
import { ReviveState } from '../utils/saveload'; import { ReviveState } from '../utils/saveload';
function InitEditor(configuration: IConfiguration): void {
const initEditor = (configuration: IConfiguration): void => {
// faire comme la callback de fetch // faire comme la callback de fetch
} }
const getEditorState = ( function GetEditorState(root: Element | Document,
root: Element | Document, editorState: IEditorState): void {
editorState: IEditorState
): void => {
const customEvent = new CustomEvent<IEditorState>('getEditorState', { detail: editorState }); const customEvent = new CustomEvent<IEditorState>('getEditorState', { detail: editorState });
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
}; }
const getCurrentHistoryState = ( function GetCurrentHistoryState(root: Element | Document,
root: Element | Document, editorState: IEditorState): void {
editorState: IEditorState
): void => {
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'getCurrentHistoryState', 'getCurrentHistoryState',
{ detail: editorState.history[editorState.historyCurrentStep] }); { detail: editorState.history[editorState.historyCurrentStep] });
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
}; }
const appendNewState = ( function AppendNewState(root: Element | Document,
root: Element | Document,
editorState: IEditorState, editorState: IEditorState,
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, setNewHistory: (newHistory: IHistoryState[]) => void,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>, eventInitDict?: CustomEventInit): void {
eventInitDict?: CustomEventInit
): void => {
const state: IHistoryState = JSON.parse(eventInitDict?.detail.state); const state: IHistoryState = JSON.parse(eventInitDict?.detail.state);
ReviveState(state); ReviveState(state);
const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep);
history.push(state); history.push(state);
setHistory(history); setNewHistory(history);
setHistoryCurrentStep(history.length - 1); }
};
export interface IEditorEvent { export interface IEditorEvent {
name: string name: string
func: ( func: (
root: Element | Document, root: Element | Document,
editorState: IEditorState, editorState: IEditorState,
setHistory: Dispatch<SetStateAction<IHistoryState[]>>, setNewHistory: (newHistory: IHistoryState[]) => void,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
eventInitDict?: CustomEventInit eventInitDict?: CustomEventInit
) => void ) => void
} }
const events: IEditorEvent[] = [ export const events: IEditorEvent[] = [
{ name: 'getEditorState', func: getEditorState }, { name: 'getEditorState', func: GetEditorState },
{ name: 'getCurrentHistoryState', func: getCurrentHistoryState }, { name: 'getCurrentHistoryState', func: GetCurrentHistoryState },
{ name: 'appendNewState', func: appendNewState } { name: 'appendNewState', func: AppendNewState }
]; ];
export default events;

12
src/Interfaces/IAction.ts Normal file
View file

@ -0,0 +1,12 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { AddMethod } from '../Enums/AddMethod';
import { IImage } from './IImage';
export interface IAction {
Id: string
CustomLogo: IImage
Label: string
Description: string
Action: string
AddingBehavior: AddMethod
}

View file

@ -2,6 +2,7 @@
import React from 'react'; import React from 'react';
import { AddMethod } from '../Enums/AddMethod'; import { AddMethod } from '../Enums/AddMethod';
import { XPositionReference } from '../Enums/XPositionReference'; import { XPositionReference } from '../Enums/XPositionReference';
import { IAction } from './IAction';
import { IMargin } from './IMargin'; import { IMargin } from './IMargin';
/** Model of available container used in application configuration */ /** Model of available container used in application configuration */
@ -20,5 +21,6 @@ export interface IAvailableContainer {
CustomSVG?: string CustomSVG?: string
DefaultChildType?: string DefaultChildType?: string
Style?: React.CSSProperties Style?: React.CSSProperties
Actions?: IAction[]
UserData?: object UserData?: object
} }

View file

@ -0,0 +1,14 @@
import { IContainerModel } from './IContainerModel';
import { IHistoryState } from './IHistoryState';
/* eslint-disable @typescript-eslint/naming-convention */
export interface ISetContainerListRequest {
/** Name of the action declared in the API */
Action: string
/** Selected container */
Container: IContainerModel
/** Current application state */
ApplicationState: IHistoryState
}

View file

@ -0,0 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { IContainerModel } from './IContainerModel';
export interface ISetContainerListResponse {
Containers: IContainerModel[]
}

View file

@ -50,7 +50,7 @@ export function GetCircularReplacer(): (key: any, value: object | Map<string, an
return; return;
} }
if (key === 'Symbols') { if (key === 'symbols') {
return Array.from((value as Map<string, any>).entries()); return Array.from((value as Map<string, any>).entries());
} }

3
src/vite-env.d.ts vendored
View file

@ -1,7 +1,8 @@
/// <reference types="vite/client" /> /// <reference types="vite/client" />
interface ImportMetaEnv { interface ImportMetaEnv {
readonly VITE_API_URL: string readonly VITE_API_FETCH_URL: string
readonly VITE_API_POST_URL: string
// more env variables... // more env variables...
} }

View file

@ -90,6 +90,17 @@ const GetSVGLayoutConfiguration = () => {
<line x1="{width}" y1="0" x2="0" y2="{height}" stroke="black" style='{userData.styleLine}'></line> <line x1="{width}" y1="0" x2="0" y2="{height}" stroke="black" style='{userData.styleLine}'></line>
` `
, ,
Actions: [
{
Action: "SplitRemplissage",
Label: "Diviser le remplissage",
Description: "Diviser le remplissage en insérant un montant",
CustomLogo: {
Url: ""
},
AddingBehavior: 2
}
],
Style: { Style: {
fillOpacity: 1, fillOpacity: 1,
strokeWidth: 1, strokeWidth: 1,
@ -186,20 +197,30 @@ const FillHoleWithChassis = (request) => {
} }
} }
return { return {
ApplicationState: {
Containers: lstModels Containers: lstModels
}
}; };
}; };
const SplitRemplissage = (request) => { const SplitRemplissage = (request) => {
const lstModels = [ const lstModels = [
{ Type: 'Remplissage' }, {
{ Type: 'Montant' }, properties: {
{ Type: 'Remplissage' } type: 'Remplissage'
}
},
{
properties: {
type: 'Montant'
}
},
{
properties: {
type: 'Remplissage'
}
},
]; ];
return { return {
ApplicationState: { Containers: lstModels } Containers: lstModels
}; };
}; };