Merged PR 162: Implement symbols and other stuff (see desc)

Implement symbols
- Add, Remove, Select Container
- Form
- Link with container
- Symbol behavior application to container (move to x with xpositionreference)

Important changes
- Remove SelectedContainer from HistoryState, meaning that it will be slower for each load but will be faster for each operations* (SetHistory, SelectContainer, DeleteContainer, SymbolOperations)
- ElementsSidebar now opens with isSidebarOpen meaning that both sidebar will open on toggle
- Moved camelize, transformX, restoreX to different modules (stringtools.ts, svg.ts)
This commit is contained in:
Eric Nguyen 2022-08-22 13:58:32 +00:00
parent 58ef28fe89
commit 8b8d88f885
48 changed files with 1453 additions and 188 deletions

View file

@ -0,0 +1,180 @@
import { Dispatch, SetStateAction } from 'react';
import { IConfiguration } from '../../Interfaces/IConfiguration';
import { IContainerModel } from '../../Interfaces/IContainerModel';
import { IHistoryState } from '../../Interfaces/IHistoryState';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { DEFAULT_SYMBOL_HEIGHT, DEFAULT_SYMBOL_WIDTH } from '../../utils/default';
import { findContainerById } from '../../utils/itertools';
import { restoreX } from '../../utils/svg';
import { ApplyBehaviors } from './Behaviors/Behaviors';
import { getCurrentHistory, UpdateCounters } from './Editor';
export function AddSymbol(
name: string,
configuration: IConfiguration,
fullHistory: IHistoryState[],
historyCurrentStep: number,
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
const 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 newCounters = structuredClone(current.TypeCounters);
UpdateCounters(newCounters, type);
const newSymbols = structuredClone(current.Symbols);
// TODO: Put this in default.ts as GetDefaultConfig
const newSymbol: ISymbolModel = {
id: `${name}-${newCounters[type]}`,
type: name,
config: structuredClone(symbolConfig),
x: 0,
width: symbolConfig.Width ?? DEFAULT_SYMBOL_WIDTH,
height: symbolConfig.Height ?? DEFAULT_SYMBOL_HEIGHT,
linkedContainers: new Set()
};
newSymbol.x = restoreX(newSymbol.x, newSymbol.width, newSymbol.config.XPositionReference);
newSymbols.set(newSymbol.id, newSymbol);
history.push({
LastAction: `Add ${name}`,
MainContainer: structuredClone(current.MainContainer),
SelectedContainerId: current.SelectedContainerId,
TypeCounters: newCounters,
Symbols: newSymbols,
SelectedSymbolId: newSymbol.id
});
setHistory(history);
setHistoryCurrentStep(history.length - 1);
}
export function SelectSymbol(
symbolId: string,
fullHistory: IHistoryState[],
historyCurrentStep: number,
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
const history = getCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
history.push({
LastAction: `Select ${symbolId}`,
MainContainer: structuredClone(current.MainContainer),
SelectedContainerId: current.SelectedContainerId,
TypeCounters: structuredClone(current.TypeCounters),
Symbols: structuredClone(current.Symbols),
SelectedSymbolId: symbolId
});
setHistory(history);
setHistoryCurrentStep(history.length - 1);
}
export function DeleteSymbol(
symbolId: string,
fullHistory: IHistoryState[],
historyCurrentStep: number,
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
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 newMainContainer = structuredClone(current.MainContainer);
UnlinkContainers(symbol, newMainContainer);
newSymbols.delete(symbolId);
history.push({
LastAction: `Select ${symbolId}`,
MainContainer: newMainContainer,
SelectedContainerId: current.SelectedContainerId,
TypeCounters: structuredClone(current.TypeCounters),
Symbols: newSymbols,
SelectedSymbolId: symbolId
});
setHistory(history);
setHistoryCurrentStep(history.length - 1);
}
function UnlinkContainers(symbol: ISymbolModel, newMainContainer: IContainerModel) {
symbol.linkedContainers.forEach((containerId) => {
const container = findContainerById(newMainContainer, 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,
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
): void {
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 newMainContainer = structuredClone(current.MainContainer);
symbol.linkedContainers.forEach((containerId) => {
const container = findContainerById(newMainContainer, containerId);
if (container === undefined) {
return;
}
ApplyBehaviors(container, newSymbols);
});
history.push({
LastAction: `Change ${key} of ${symbol.id}`,
MainContainer: newMainContainer,
SelectedContainerId: current.SelectedContainerId,
TypeCounters: Object.assign({}, current.TypeCounters),
Symbols: newSymbols,
SelectedSymbolId: symbol.id
});
setHistory(history);
setHistoryCurrentStep(history.length - 1);
}