216 lines
6.7 KiB
TypeScript
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 : '';
|
|
}
|