Implement associated container and default X, Y position for Symbol Related work items: #7537, #7540
342 lines
10 KiB
TypeScript
342 lines
10 KiB
TypeScript
import Swal from 'sweetalert2';
|
|
import { Dispatch, SetStateAction } from 'react';
|
|
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 { DISABLE_API } from '../../../utils/default';
|
|
import { FindContainerById } from '../../../utils/itertools';
|
|
import { SetContainerList } from '../../API/api';
|
|
import { IMenuAction } from '../../Menu/Menu';
|
|
import { GetCurrentHistoryState } from '../Editor';
|
|
import { AddContainers } from './AddContainer';
|
|
import { DeleteContainer } from './ContainerOperations';
|
|
import { DeleteSymbol } from './SymbolOperations';
|
|
import { Text } from '../../Text/Text';
|
|
|
|
export function InitActions(
|
|
menuActions: Map<string, IMenuAction[]>,
|
|
configuration: IConfiguration,
|
|
history: IHistoryState[],
|
|
historyCurrentStep: number,
|
|
setNewHistory: (newHistory: IHistoryState[]) => void,
|
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
|
): void {
|
|
menuActions.set(
|
|
'',
|
|
[
|
|
{
|
|
text: Text({ textId: '@Undo' }),
|
|
title: Text({ textId: '@UndoTitle' }),
|
|
shortcut: '<kbd>Ctrl</kbd>+<kbd>Z</kbd>',
|
|
action: () => {
|
|
if (historyCurrentStep <= 0) {
|
|
return;
|
|
}
|
|
setHistoryCurrentStep(historyCurrentStep - 1);
|
|
}
|
|
},
|
|
{
|
|
text: Text({ textId: '@Redo' }),
|
|
title: Text({ textId: '@RedoTitle' }),
|
|
shortcut: '<kbd>Ctrl</kbd>+<kbd>Y</kbd>',
|
|
action: () => {
|
|
if (historyCurrentStep >= history.length - 1) {
|
|
return;
|
|
}
|
|
setHistoryCurrentStep(historyCurrentStep + 1);
|
|
}
|
|
}
|
|
]
|
|
);
|
|
|
|
menuActions.set(
|
|
'elements-sidebar-row',
|
|
[{
|
|
text: Text({ textId: '@DeleteContainer' }),
|
|
title: Text({ textId: '@DeleteContainerTitle' }),
|
|
shortcut: '<kbd>Suppr</kbd>',
|
|
action: (target: HTMLElement) => {
|
|
const id = target.id;
|
|
const newHistory = DeleteContainer(
|
|
id,
|
|
history,
|
|
historyCurrentStep
|
|
);
|
|
setNewHistory(newHistory);
|
|
}
|
|
}]
|
|
);
|
|
|
|
menuActions.set(
|
|
'symbols-sidebar-row',
|
|
[{
|
|
text: Text({ textId: '@DeleteSymbol' }),
|
|
title: Text({ textId: '@DeleteSymbolTitle' }),
|
|
shortcut: '<kbd>Suppr</kbd>',
|
|
action: (target: HTMLElement) => {
|
|
const id = target.id;
|
|
const newHistory = DeleteSymbol(
|
|
id,
|
|
history,
|
|
historyCurrentStep
|
|
);
|
|
setNewHistory(newHistory);
|
|
}
|
|
}]
|
|
);
|
|
|
|
// API Actions
|
|
if (DISABLE_API) {
|
|
return;
|
|
}
|
|
|
|
for (const availableContainer of configuration.AvailableContainers) {
|
|
if (availableContainer.Actions === undefined || availableContainer.Actions === null) {
|
|
continue;
|
|
}
|
|
|
|
for (const action of availableContainer.Actions) {
|
|
if (menuActions.get(availableContainer.Type) === undefined) {
|
|
menuActions.set(availableContainer.Type, []);
|
|
}
|
|
|
|
const currentState = GetCurrentHistoryState(history, historyCurrentStep);
|
|
const newAction: IMenuAction = {
|
|
text: action.Label,
|
|
title: action.Description,
|
|
action: GetAction(
|
|
action,
|
|
currentState,
|
|
configuration,
|
|
history,
|
|
historyCurrentStep,
|
|
setNewHistory
|
|
)
|
|
};
|
|
|
|
menuActions.get(availableContainer.Type)?.push(newAction);
|
|
}
|
|
}
|
|
}
|
|
|
|
function GetAction(
|
|
action: IAction,
|
|
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.containers, 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 { prev, next } = GetPreviousAndNextSiblings(currentState.containers, container);
|
|
|
|
const request: ISetContainerListRequest = {
|
|
Container: container,
|
|
PreviousContainer: prev,
|
|
NextContainer: next,
|
|
Action: action,
|
|
ApplicationState: currentState
|
|
};
|
|
/* eslint-enable */
|
|
|
|
SetContainerList(request, configuration.APIConfiguration?.apiSetContainerListUrl)
|
|
.then((response: ISetContainerListResponse) => {
|
|
HandleSetContainerList(
|
|
action,
|
|
container,
|
|
response,
|
|
configuration,
|
|
history,
|
|
historyCurrentStep,
|
|
setNewHistory
|
|
);
|
|
});
|
|
};
|
|
}
|
|
|
|
function GetPreviousAndNextSiblings(containers: Map<string, IContainerModel>, container: IContainerModel): { prev: IContainerModel | undefined, next: IContainerModel | undefined } {
|
|
let prev;
|
|
let next;
|
|
const parent = FindContainerById(containers, container.properties.parentId);
|
|
if (parent !== undefined &&
|
|
parent !== null &&
|
|
parent.children.length > 1) {
|
|
const index = parent.children.indexOf(container.properties.id);
|
|
if (index > 0) {
|
|
prev = FindContainerById(containers, parent.children[index - 1]);
|
|
}
|
|
if (index < parent.children.length - 1) {
|
|
next = FindContainerById(containers, parent.children[index + 1]);
|
|
}
|
|
}
|
|
return { prev, next };
|
|
}
|
|
|
|
function HandleSetContainerList(
|
|
action: IAction,
|
|
selectedContainer: IContainerModel,
|
|
response: ISetContainerListResponse,
|
|
configuration: IConfiguration,
|
|
history: IHistoryState[],
|
|
historyCurrentStep: number,
|
|
setNewHistory: (newHistory: IHistoryState[]) => void
|
|
): void {
|
|
const addingBehavior = response.AddingBehavior ?? action.AddingBehavior;
|
|
const current = GetCurrentHistoryState(history, historyCurrentStep);
|
|
const containers = current.containers;
|
|
switch (addingBehavior) {
|
|
case AddMethod.Insert:
|
|
case AddMethod.Append: {
|
|
response.Containers.forEach(config => {
|
|
config.AddMethod = config.AddMethod ?? addingBehavior;
|
|
});
|
|
const { history: newHistory } = AddContainers(
|
|
selectedContainer.children.length,
|
|
response.Containers,
|
|
selectedContainer.properties.id,
|
|
configuration,
|
|
history,
|
|
historyCurrentStep);
|
|
|
|
setNewHistory(newHistory);
|
|
break;
|
|
}
|
|
case AddMethod.Replace:
|
|
setNewHistory(
|
|
HandleReplace(
|
|
containers,
|
|
selectedContainer,
|
|
response,
|
|
configuration,
|
|
history,
|
|
historyCurrentStep
|
|
)
|
|
);
|
|
break;
|
|
case AddMethod.ReplaceParent: {
|
|
const parent = FindContainerById(containers, selectedContainer.properties.parentId);
|
|
if (parent === undefined || parent === null) {
|
|
Swal.fire({
|
|
title: 'Error',
|
|
text: 'The selected container has not parent to replace',
|
|
icon: 'error'
|
|
});
|
|
return;
|
|
}
|
|
setNewHistory(
|
|
HandleReplace(
|
|
containers,
|
|
parent,
|
|
response,
|
|
configuration,
|
|
history,
|
|
historyCurrentStep
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function HandleReplace(
|
|
containers: Map<string, IContainerModel>,
|
|
selectedContainer: IContainerModel,
|
|
response: ISetContainerListResponse,
|
|
configuration: IConfiguration,
|
|
history: IHistoryState[],
|
|
historyCurrentStep: number
|
|
): IHistoryState[] {
|
|
const parent = FindContainerById(containers, selectedContainer.properties.parentId);
|
|
if (parent === undefined || parent === null) {
|
|
throw new Error('[ReplaceContainer] Cannot replace a container that does not exists');
|
|
}
|
|
|
|
const index = parent.children.indexOf(selectedContainer.properties.id);
|
|
|
|
const newHistoryAfterDelete = DeleteContainer(
|
|
selectedContainer.properties.id,
|
|
history,
|
|
historyCurrentStep
|
|
);
|
|
|
|
const { history: newHistoryBeforeDelete } = AddContainers(
|
|
index,
|
|
response.Containers,
|
|
selectedContainer.properties.parentId,
|
|
configuration,
|
|
newHistoryAfterDelete,
|
|
newHistoryAfterDelete.length - 1
|
|
);
|
|
|
|
// Remove AddContainers from history
|
|
if (import.meta.env.PROD) {
|
|
newHistoryBeforeDelete.splice(newHistoryBeforeDelete.length - 2, 1);
|
|
}
|
|
|
|
// Rename the last action by Replace
|
|
const types = response.Containers.map(container => container.Type);
|
|
newHistoryBeforeDelete[newHistoryBeforeDelete.length - 1].lastAction =
|
|
`Replace ${selectedContainer.properties.id} by [${types.join(', ')}]`;
|
|
|
|
return newHistoryBeforeDelete;
|
|
}
|
|
|
|
function HandleInsert(
|
|
containers: Map<string, IContainerModel>,
|
|
selectedContainer: IContainerModel,
|
|
response: ISetContainerListResponse,
|
|
configuration: IConfiguration,
|
|
history: IHistoryState[],
|
|
historyCurrentStep: number
|
|
): IHistoryState[] {
|
|
const parent = FindContainerById(containers, selectedContainer.properties.id);
|
|
if (parent === undefined || parent === null) {
|
|
throw new Error('[InsertContainer] Cannot insert in a container that does not exists');
|
|
}
|
|
|
|
const index = parent.children.indexOf(selectedContainer.properties.id);
|
|
|
|
const newHistoryAfterDelete = DeleteContainer(
|
|
selectedContainer.properties.id,
|
|
history,
|
|
historyCurrentStep
|
|
);
|
|
|
|
const { history: newHistoryBeforeDelete } = AddContainers(
|
|
index,
|
|
response.Containers,
|
|
selectedContainer.properties.parentId,
|
|
configuration,
|
|
newHistoryAfterDelete,
|
|
newHistoryAfterDelete.length - 1
|
|
);
|
|
|
|
// Remove AddContainers from history
|
|
if (import.meta.env.PROD) {
|
|
newHistoryBeforeDelete.splice(newHistoryBeforeDelete.length - 2, 1);
|
|
}
|
|
|
|
// Rename the last action by Replace
|
|
const types = response.Containers.map(container => container.Type);
|
|
newHistoryBeforeDelete[newHistoryBeforeDelete.length - 1].lastAction =
|
|
`Replace ${selectedContainer.properties.id} by [${types.join(', ')}]`;
|
|
|
|
return newHistoryBeforeDelete;
|
|
}
|