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, 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 = 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 ): void { const oldSymbol = symbols.get(container.properties.linkedSymbolId); LinkSymbol(container.properties.id, oldSymbol, symbol); container.properties.linkedSymbolId = symbol !== undefined ? symbol.id : ''; }