Merged PR 163: Remove the static form + rename some components for clarity
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
The static form is hard to maintain so I am removing it + rename some components for clarity + moved some utils files
This commit is contained in:
parent
7e3ccdee99
commit
66ea3b1b64
21 changed files with 150 additions and 523 deletions
|
@ -1,14 +1,14 @@
|
|||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { findContainerById, MakeIterator } from '../../utils/itertools';
|
||||
import { getCurrentHistory, UpdateCounters } from './Editor';
|
||||
import { AddMethod } from '../../Enums/AddMethod';
|
||||
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
||||
import { GetDefaultContainerProps, DEFAULTCHILDTYPE_ALLOW_CYCLIC, DEFAULTCHILDTYPE_MAX_DEPTH } from '../../utils/default';
|
||||
import { ApplyBehaviors } from './Behaviors/Behaviors';
|
||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
import { IHistoryState } from '../../../Interfaces/IHistoryState';
|
||||
import { IConfiguration } from '../../../Interfaces/IConfiguration';
|
||||
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { findContainerById, MakeIterator } from '../../../utils/itertools';
|
||||
import { getCurrentHistory, UpdateCounters } from '../Editor';
|
||||
import { AddMethod } from '../../../Enums/AddMethod';
|
||||
import { IAvailableContainer } from '../../../Interfaces/IAvailableContainer';
|
||||
import { GetDefaultContainerProps, DEFAULTCHILDTYPE_ALLOW_CYCLIC, DEFAULTCHILDTYPE_MAX_DEPTH } from '../../../utils/default';
|
||||
import { ApplyBehaviors } from '../Behaviors/Behaviors';
|
||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
||||
|
||||
/**
|
||||
* Select a container
|
||||
|
@ -71,7 +71,8 @@ export function DeleteContainer(
|
|||
if (container === null || container === undefined) {
|
||||
throw new Error('[DeleteContainer] Container model was not found among children of the main container!');
|
||||
}
|
||||
const newSymbols = structuredClone(current.Symbols)
|
||||
|
||||
const newSymbols = structuredClone(current.Symbols);
|
||||
UnlinkSymbol(newSymbols, container);
|
||||
|
||||
const index = container.parent.children.indexOf(container);
|
||||
|
@ -83,10 +84,12 @@ export function DeleteContainer(
|
|||
|
||||
// Select the previous container
|
||||
// or select the one above
|
||||
const SelectedContainer = findContainerById(mainContainerClone, current.SelectedContainerId) ??
|
||||
container.parent.children.at(index - 1) ??
|
||||
container.parent;
|
||||
const SelectedContainerId = SelectedContainer.properties.id;
|
||||
const SelectedContainerId = GetSelectedContainerOnDelete(
|
||||
mainContainerClone,
|
||||
current.SelectedContainerId,
|
||||
container.parent,
|
||||
index
|
||||
);
|
||||
|
||||
history.push({
|
||||
LastAction: `Delete ${containerId}`,
|
||||
|
@ -100,6 +103,14 @@ export function DeleteContainer(
|
|||
setHistoryCurrentStep(history.length - 1);
|
||||
}
|
||||
|
||||
function GetSelectedContainerOnDelete(mainContainerClone: IContainerModel, selectedContainerId: string, parent: IContainerModel, index: number): string {
|
||||
const SelectedContainer = findContainerById(mainContainerClone, selectedContainerId) ??
|
||||
parent.children.at(index - 1) ??
|
||||
parent;
|
||||
const SelectedContainerId = SelectedContainer.properties.id;
|
||||
return SelectedContainerId;
|
||||
}
|
||||
|
||||
function UnlinkSymbol(symbols: Map<string, ISymbolModel>, container: IContainerModel): void {
|
||||
const it = MakeIterator(container);
|
||||
for (const child of it) {
|
||||
|
@ -327,3 +338,82 @@ function ApplyAddMethod(index: number, containerConfig: IAvailableContainer, par
|
|||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
isStyle: boolean = false,
|
||||
selected: IContainerModel | undefined,
|
||||
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 (selected === null ||
|
||||
selected === undefined) {
|
||||
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
||||
}
|
||||
|
||||
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
||||
const container: ContainerModel | undefined = findContainerById(mainContainerClone, selected.properties.id);
|
||||
|
||||
if (container === null || container === undefined) {
|
||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||
}
|
||||
|
||||
const oldSymbolId = container.properties.linkedSymbolId;
|
||||
|
||||
if (isStyle) {
|
||||
(container.properties.style as any)[key] = value;
|
||||
} else {
|
||||
(container.properties as any)[key] = value;
|
||||
}
|
||||
|
||||
LinkSymbol(
|
||||
container.properties.id,
|
||||
oldSymbolId,
|
||||
container.properties.linkedSymbolId,
|
||||
current.Symbols
|
||||
);
|
||||
|
||||
ApplyBehaviors(container, current.Symbols);
|
||||
|
||||
history.push({
|
||||
LastAction: `Change ${key} of ${container.properties.id}`,
|
||||
MainContainer: mainContainerClone,
|
||||
SelectedContainerId: container.properties.id,
|
||||
TypeCounters: Object.assign({}, current.TypeCounters),
|
||||
Symbols: structuredClone(current.Symbols),
|
||||
SelectedSymbolId: current.SelectedSymbolId
|
||||
});
|
||||
setHistory(history);
|
||||
setHistoryCurrentStep(history.length - 1);
|
||||
}
|
||||
|
||||
function LinkSymbol(
|
||||
containerId: string,
|
||||
oldSymbolId: string,
|
||||
newSymbolId: string,
|
||||
symbols: Map<string, ISymbolModel>
|
||||
): void {
|
||||
const oldSymbol = symbols.get(oldSymbolId);
|
||||
const newSymbol = symbols.get(newSymbolId);
|
||||
|
||||
if (newSymbol === undefined) {
|
||||
if (oldSymbol !== undefined) {
|
||||
oldSymbol.linkedContainers.delete(containerId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
newSymbol.linkedContainers.add(containerId);
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||
import { getCircularReplacer } from '../../utils/saveload';
|
||||
import { ID } from '../SVG/SVG';
|
||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||
import { IHistoryState } from '../../../Interfaces/IHistoryState';
|
||||
import { IConfiguration } from '../../../Interfaces/IConfiguration';
|
||||
import { getCircularReplacer } from '../../../utils/saveload';
|
||||
import { ID } from '../../SVG/SVG';
|
||||
import { IEditorState } from '../../../Interfaces/IEditorState';
|
||||
|
||||
export function SaveEditorAsJSON(
|
||||
history: IHistoryState[],
|
|
@ -1,6 +1,6 @@
|
|||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
import { ENABLE_SHORTCUTS } from '../../utils/default';
|
||||
import { IHistoryState } from '../../../Interfaces/IHistoryState';
|
||||
import { ENABLE_SHORTCUTS } from '../../../utils/default';
|
||||
|
||||
export function onKeyDown(
|
||||
event: KeyboardEvent,
|
|
@ -1,13 +1,13 @@
|
|||
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';
|
||||
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,
|
|
@ -4,14 +4,13 @@ import { IConfiguration } from '../../Interfaces/IConfiguration';
|
|||
import { SVG } from '../SVG/SVG';
|
||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
import { UI } from '../UI/UI';
|
||||
import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, AddContainer } from './ContainerOperations';
|
||||
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Save';
|
||||
import { onKeyDown } from './Shortcuts';
|
||||
import { OnPropertyChange, OnPropertiesSubmit } from './PropertiesOperations';
|
||||
import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, AddContainer, OnPropertyChange } from './Actions/ContainerOperations';
|
||||
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save';
|
||||
import { onKeyDown } from './Actions/Shortcuts';
|
||||
import EditorEvents from '../../Events/EditorEvents';
|
||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||
import { MAX_HISTORY } from '../../utils/default';
|
||||
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './SymbolOperations';
|
||||
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations';
|
||||
import { findContainerById } from '../../utils/itertools';
|
||||
|
||||
interface IEditorProps {
|
||||
|
@ -113,14 +112,6 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
|||
setHistory,
|
||||
setHistoryCurrentStep
|
||||
)}
|
||||
OnPropertiesSubmit={(event) => OnPropertiesSubmit(
|
||||
event,
|
||||
selected,
|
||||
history,
|
||||
historyCurrentStep,
|
||||
setHistory,
|
||||
setHistoryCurrentStep
|
||||
)}
|
||||
AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer(
|
||||
type,
|
||||
selected,
|
||||
|
|
|
@ -1,223 +0,0 @@
|
|||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel';
|
||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||
import { findContainerById } from '../../utils/itertools';
|
||||
import { getCurrentHistory } from './Editor';
|
||||
import { ApplyBehaviors } from './Behaviors/Behaviors';
|
||||
import { restoreX } from '../../utils/svg';
|
||||
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||
|
||||
/**
|
||||
* 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,
|
||||
isStyle: boolean = false,
|
||||
selected: IContainerModel | undefined,
|
||||
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 (selected === null ||
|
||||
selected === undefined) {
|
||||
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
||||
}
|
||||
|
||||
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
||||
const container: ContainerModel | undefined = findContainerById(mainContainerClone, selected.properties.id);
|
||||
|
||||
if (container === null || container === undefined) {
|
||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||
}
|
||||
|
||||
const oldSymbolId = container.properties.linkedSymbolId;
|
||||
|
||||
if (isStyle) {
|
||||
(container.properties.style as any)[key] = value;
|
||||
} else {
|
||||
(container.properties as any)[key] = value;
|
||||
}
|
||||
|
||||
LinkSymbol(
|
||||
container.properties.id,
|
||||
oldSymbolId,
|
||||
container.properties.linkedSymbolId,
|
||||
current.Symbols
|
||||
);
|
||||
|
||||
ApplyBehaviors(container, current.Symbols);
|
||||
|
||||
history.push({
|
||||
LastAction: `Change ${key} of ${container.properties.id}`,
|
||||
MainContainer: mainContainerClone,
|
||||
SelectedContainerId: container.properties.id,
|
||||
TypeCounters: Object.assign({}, current.TypeCounters),
|
||||
Symbols: structuredClone(current.Symbols),
|
||||
SelectedSymbolId: current.SelectedSymbolId
|
||||
});
|
||||
setHistory(history);
|
||||
setHistoryCurrentStep(history.length - 1);
|
||||
}
|
||||
|
||||
function LinkSymbol(
|
||||
containerId: string,
|
||||
oldSymbolId: string,
|
||||
newSymbolId: string,
|
||||
symbols: Map<string, ISymbolModel>
|
||||
): void {
|
||||
const oldSymbol = symbols.get(oldSymbolId);
|
||||
const newSymbol = symbols.get(newSymbolId);
|
||||
|
||||
if (newSymbol === undefined) {
|
||||
if (oldSymbol !== undefined) {
|
||||
oldSymbol.linkedContainers.delete(containerId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
newSymbol.linkedContainers.add(containerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handled the property change event in the properties form
|
||||
* @param key Property name
|
||||
* @param properties Properties of the selected container
|
||||
* @returns void
|
||||
*/
|
||||
export function OnPropertiesSubmit(
|
||||
event: React.SyntheticEvent<HTMLFormElement>,
|
||||
selected: IContainerModel | undefined,
|
||||
fullHistory: IHistoryState[],
|
||||
historyCurrentStep: number,
|
||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||
): void {
|
||||
event.preventDefault();
|
||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||
const current = history[history.length - 1];
|
||||
|
||||
if (selected === null ||
|
||||
selected === undefined) {
|
||||
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
||||
}
|
||||
|
||||
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
||||
const container: ContainerModel | undefined = findContainerById(mainContainerClone, selected.properties.id);
|
||||
|
||||
if (container === null || container === undefined) {
|
||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||
}
|
||||
|
||||
// Assign container properties
|
||||
const form: HTMLFormElement = event.target as HTMLFormElement;
|
||||
for (const property in container.properties) {
|
||||
const input: HTMLInputElement | HTMLDivElement | null = form.querySelector(`#${property}`);
|
||||
|
||||
if (input === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input instanceof HTMLInputElement) {
|
||||
submitHTMLInput(input, container, property, form);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input instanceof HTMLDivElement) {
|
||||
submitRadioButtons(input, container, property);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign cssproperties
|
||||
for (const styleProperty in container.properties.style) {
|
||||
submitCSSForm(form, styleProperty, container);
|
||||
}
|
||||
|
||||
// Apply the behaviors
|
||||
ApplyBehaviors(container, current.Symbols);
|
||||
|
||||
history.push({
|
||||
LastAction: `Change properties of ${container.properties.id}`,
|
||||
MainContainer: mainContainerClone,
|
||||
SelectedContainerId: container.properties.id,
|
||||
TypeCounters: Object.assign({}, current.TypeCounters),
|
||||
Symbols: structuredClone(current.Symbols),
|
||||
SelectedSymbolId: current.SelectedSymbolId
|
||||
});
|
||||
setHistory(history);
|
||||
setHistoryCurrentStep(history.length - 1);
|
||||
}
|
||||
|
||||
const submitHTMLInput = (
|
||||
input: HTMLInputElement,
|
||||
container: IContainerModel,
|
||||
property: string,
|
||||
form: HTMLFormElement
|
||||
): void => {
|
||||
if (input.type !== 'number') {
|
||||
(container.properties as any)[property] = input.value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (property === 'x') {
|
||||
// Hardcoded fix for XPositionReference
|
||||
const x = RestoreX(form, input);
|
||||
(container.properties as any)[property] = x;
|
||||
return;
|
||||
}
|
||||
|
||||
(container.properties as any)[property] = Number(input.value);
|
||||
};
|
||||
|
||||
const submitCSSForm = (form: HTMLFormElement, styleProperty: string, container: ContainerModel): void => {
|
||||
const input: HTMLInputElement | null = form.querySelector(`#${styleProperty}`);
|
||||
if (input === null) {
|
||||
return;
|
||||
}
|
||||
(container.properties.style as any)[styleProperty] = input.value;
|
||||
};
|
||||
|
||||
const RestoreX = (
|
||||
form: HTMLFormElement,
|
||||
input: HTMLInputElement
|
||||
): number => {
|
||||
const inputWidth: HTMLInputElement | null = form.querySelector('#width');
|
||||
const inputRadio: HTMLDivElement | null = form.querySelector('#XPositionReference');
|
||||
if (inputWidth === null || inputRadio === null) {
|
||||
throw new Error('[OnPropertiesSubmit] Missing inputs for width or XPositionReference');
|
||||
}
|
||||
|
||||
const radiobutton: HTMLInputElement | null = inputRadio.querySelector('input[name="XPositionReference"]:checked');
|
||||
if (radiobutton === null) {
|
||||
throw new Error('[OnPropertiesSubmit] Missing inputs for XPositionReference');
|
||||
}
|
||||
|
||||
return restoreX(Number(input.value), Number(inputWidth.value), Number(radiobutton.value));
|
||||
};
|
||||
|
||||
const submitRadioButtons = (
|
||||
div: HTMLDivElement,
|
||||
container: IContainerModel,
|
||||
property: string
|
||||
): void => {
|
||||
const radiobutton: HTMLInputElement | null = div.querySelector(`input[name="${property}"]:checked`);
|
||||
if (radiobutton === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (radiobutton.type === 'radio') {
|
||||
(container.properties as any)[property] = Number(radiobutton.value);
|
||||
return;
|
||||
}
|
||||
|
||||
(container.properties as any)[property] = radiobutton.value;
|
||||
};
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue