svg-layout-designer-react/src/Components/Editor/Actions/SymbolOperations.ts
Carl Fuchs b09718d72d WIP2
2023-02-10 09:46:39 +01:00

216 lines
6.7 KiB
TypeScript

import { type IConfiguration } from '../../../Interfaces/IConfiguration';
import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { type IHistoryState } from '../../../Interfaces/IHistoryState';
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { GetDefaultSymbolModel } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools';
import {RestoreX, RestoreY} from '../../../utils/svg';
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { GetCurrentHistory, GetCurrentHistoryState, UpdateCounters } from '../Editor';
import { AddContainers } from './AddContainer';
import { LinkSymbol } from './ContainerOperations';
export function AddSymbol(
name: string,
configuration: IConfiguration,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
let history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
const symbolConfig = configuration.AvailableSymbols
.find(option => option.Name === name);
if (symbolConfig === undefined) {
throw new Error('[AddSymbol] Symbol could not be found in the config');
}
const type = `symbol-${name}`;
const typeCounters = structuredClone(current.typeCounters);
UpdateCounters(typeCounters, type);
const newSymbols = structuredClone(current.symbols);
const newSymbol: ISymbolModel = GetDefaultSymbolModel(name, typeCounters, type, symbolConfig);
const containers = structuredClone(current.containers);
if (newSymbol.isVertical) {
newSymbol.offset = RestoreY(newSymbol.offset, newSymbol.height, newSymbol.config.PositionReference);
} else {
newSymbol.offset = RestoreX(newSymbol.offset, newSymbol.width, newSymbol.config.PositionReference);
}
newSymbols.set(newSymbol.id, newSymbol);
history.push({
lastAction: `Add ${name}`,
mainContainer: current.mainContainer,
containers,
selectedContainerId: current.selectedContainerId,
typeCounters,
symbols: newSymbols,
selectedSymbolId: newSymbol.id
});
if (symbolConfig.AssociatedContainer !== undefined) {
const {
history: newHistory,
newContainers
} = AddContainers(
0,
[symbolConfig.AssociatedContainer],
current.mainContainer,
configuration,
history,
historyCurrentStep + 1
);
history = newHistory;
const newCurrent = GetCurrentHistoryState(newHistory, historyCurrentStep + 2);
const newerSymbol = newCurrent.symbols.get(newSymbol.id);
newContainers.forEach((newContainer) => {
LinkContainer(newerSymbol, newContainer, newSymbols);
});
}
return history;
}
export function SelectSymbol(
symbolId: string,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
history.push({
lastAction: `Select ${symbolId}`,
mainContainer: current.mainContainer,
containers: structuredClone(current.containers),
selectedContainerId: current.selectedContainerId,
typeCounters: structuredClone(current.typeCounters),
symbols: structuredClone(current.symbols),
selectedSymbolId: symbolId
});
return history;
}
export function DeleteSymbol(
symbolId: string,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
const newSymbols = structuredClone(current.symbols);
const symbol = newSymbols.get(symbolId);
if (symbol === undefined) {
throw new Error(`[DeleteSymbol] Could not find symbol in the current state!: ${symbolId}`);
}
const containers = structuredClone(current.containers);
UnlinkSymbolFromContainers(containers, symbol);
newSymbols.delete(symbolId);
history.push({
lastAction: `Select ${symbolId}`,
mainContainer: current.mainContainer,
containers,
selectedContainerId: current.selectedContainerId,
typeCounters: structuredClone(current.typeCounters),
symbols: newSymbols,
selectedSymbolId: symbolId
});
return history;
}
/**
* 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(containers: Map<string, IContainerModel>, symbol: ISymbolModel): void {
symbol.linkedContainers.forEach((containerId) => {
const container = FindContainerById(containers, containerId);
if (container === undefined) {
return;
}
container.properties.linkedSymbolId = '';
});
}
/**
* Handled the property change event in the properties form
* @param key Property name
* @param value New value of the property
* @returns void
*/
export function OnPropertyChange(
key: string,
value: string | number | boolean,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
if (current.selectedSymbolId === '') {
throw new Error('[OnSymbolPropertyChange] Property was changed before selecting a symbol');
}
const newSymbols: Map<string, ISymbolModel> = structuredClone(current.symbols);
const symbol = newSymbols.get(current.selectedSymbolId);
if (symbol === null || symbol === undefined) {
throw new Error('[OnSymbolPropertyChange] Symbol model was not found in state!');
}
(symbol as any)[key] = value;
const containers = structuredClone(current.containers);
symbol.linkedContainers.forEach((containerId) => {
const container = FindContainerById(containers, containerId);
if (container === undefined) {
return;
}
ApplyBehaviors(containers, container, newSymbols);
ApplyBehaviorsOnSiblingsChildren(containers, container, newSymbols);
});
history.push({
lastAction: `Change ${key} of ${symbol.id}`,
mainContainer: current.mainContainer,
containers,
selectedContainerId: current.selectedContainerId,
typeCounters: Object.assign({}, current.typeCounters),
symbols: newSymbols,
selectedSymbolId: symbol.id
});
return history;
}
/**
* Link a container to a symbol.
* If symbol is undefined, unlink the previous symbol of the container
* @param symbol
* @param container
* @param symbols
*/
function LinkContainer(
symbol: ISymbolModel | undefined,
container: IContainerModel,
symbols: Map<string, ISymbolModel>
): void {
const oldSymbol = symbols.get(container.properties.linkedSymbolId);
LinkSymbol(container.properties.id, oldSymbol, symbol);
container.properties.linkedSymbolId = symbol !== undefined ? symbol.id : '';
}