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 { restoreX } from '../SVG/Elements/Container'; import { ApplyBehaviors } from './Behaviors/Behaviors'; /** * 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, fullHistory: IHistoryState[], historyCurrentStep: number, setHistory: Dispatch>, setHistoryCurrentStep: Dispatch> ): void { const history = getCurrentHistory(fullHistory, historyCurrentStep); const current = history[history.length - 1]; if (current.SelectedContainer === null || current.SelectedContainer === undefined) { throw new Error('[OnPropertyChange] Property was changed before selecting a Container'); } const mainContainerClone: IContainerModel = structuredClone(current.MainContainer); const container: ContainerModel | undefined = findContainerById(mainContainerClone, current.SelectedContainer.properties.id); if (container === null || container === undefined) { throw new Error('[OnPropertyChange] Container model was not found among children of the main container!'); } if (isStyle) { (container.properties.style as any)[key] = value; } else { (container.properties as any)[key] = value; } ApplyBehaviors(container); history.push({ LastAction: `Change ${key} of ${container.properties.id}`, MainContainer: mainContainerClone, SelectedContainer: container, SelectedContainerId: container.properties.id, TypeCounters: Object.assign({}, current.TypeCounters) }); setHistory(history); setHistoryCurrentStep(history.length - 1); } /** * 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, fullHistory: IHistoryState[], historyCurrentStep: number, setHistory: Dispatch>, setHistoryCurrentStep: Dispatch> ): void { event.preventDefault(); const history = getCurrentHistory(fullHistory, historyCurrentStep); const current = history[history.length - 1]; if (current.SelectedContainer === null || current.SelectedContainer === undefined) { throw new Error('[OnPropertyChange] Property was changed before selecting a Container'); } const mainContainerClone: IContainerModel = structuredClone(current.MainContainer); const container: ContainerModel | undefined = findContainerById(mainContainerClone, current.SelectedContainer.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); history.push({ LastAction: `Change properties of ${container.properties.id}`, MainContainer: mainContainerClone, SelectedContainer: container, SelectedContainerId: container.properties.id, TypeCounters: Object.assign({}, current.TypeCounters) }); 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; };