import React from 'react'; import './Editor.scss'; import Sidebar from './Components/Sidebar/Sidebar'; import { ElementsSidebar } from './Components/ElementsSidebar/ElementsSidebar'; import { AvailableContainer } from './Interfaces/AvailableContainer'; import { Configuration } from './Interfaces/Configuration'; import { SVG } from './Components/SVG/SVG'; import { History } from './Components/History/History'; import { ContainerModel, IContainerModel, MakeIterator } from './Components/SVG/Elements/ContainerModel'; import Properties from './Interfaces/Properties'; import { IHistoryState } from './App'; interface IEditorProps { configuration: Configuration, history: Array, historyCurrentStep: number } interface IEditorState { isSidebarOpen: boolean, isSVGSidebarOpen: boolean, isHistoryOpen: boolean, history: Array, historyCurrentStep: number, } class Editor extends React.Component { public state: IEditorState; constructor(props: IEditorProps) { super(props); this.state = { isSidebarOpen: true, isSVGSidebarOpen: false, isHistoryOpen: false, configuration: props.configuration, history: props.history, historyCurrentStep: props.historyCurrentStep } as IEditorState; } public getCurrentHistory = (): IHistoryState[] => this.state.history.slice(0, this.state.historyCurrentStep + 1); public getCurrentHistoryState = (): IHistoryState => this.state.history[this.state.historyCurrentStep]; /** * Toggle the components sidebar */ public ToggleSidebar() { this.setState({ isSidebarOpen: !this.state.isSidebarOpen } as IEditorState); } /** * Toggle the elements */ public ToggleElementsSidebar() { this.setState({ isSVGSidebarOpen: !this.state.isSVGSidebarOpen } as IEditorState); } /** * Toggle the elements */ public ToggleHistory() { this.setState({ isHistoryOpen: !this.state.isHistoryOpen } as IEditorState); } /** * Select a container * @param container Selected container */ public SelectContainer(container: ContainerModel) { const history = this.getCurrentHistory(); const current = history[history.length - 1]; this.setState({ history: history.concat([{ MainContainer: current.MainContainer, TypeCounters: current.TypeCounters, SelectedContainer: container }]), historyCurrentStep: history.length } as IEditorState); } /** * Handled the property change event in the properties form * @param key Property name * @param value New value of the property * @returns void */ public OnPropertyChange(key: string, value: string | number): void { const history = this.getCurrentHistory(); const current = history[history.length - 1]; if (current.SelectedContainer === null || current.SelectedContainer === undefined) { throw new Error('[OnPropertyChange] Property was changed before selecting a Container'); } if (current.MainContainer === null || current.MainContainer === undefined) { throw new Error('[OnPropertyChange] Property was changed before the main container was added'); } if (parent === null) { const clone: IContainerModel = structuredClone(current.SelectedContainer); (clone.properties as any)[key] = value; this.setState({ history: history.concat([{ SelectedContainer: clone, MainContainer: clone, TypeCounters: current.TypeCounters }]), historyCurrentStep: history.length } as IEditorState); return; } const clone: IContainerModel = structuredClone(current.MainContainer); const it = MakeIterator(clone); let container: ContainerModel | null = null; for (const child of it) { if (child.properties.id === current.SelectedContainer.properties.id) { container = child as ContainerModel; break; } } if (container === null) { throw new Error('[OnPropertyChange] Container model was not found among children of the main container!'); } (container.properties as any)[key] = value; this.setState( { history: history.concat([{ SelectedContainer: container, MainContainer: clone, TypeCounters: current.TypeCounters }]), historyCurrentStep: history.length } as IEditorState); } /** * Add a new container to a selected container * @param type The type of container * @returns void */ public AddContainer(type: string): void { const history = this.getCurrentHistory(); const current = history[history.length - 1]; if (current.SelectedContainer === null || current.SelectedContainer === undefined) { return; } if (current.MainContainer === null || current.MainContainer === undefined) { return; } // Get the preset properties from the API const properties = this.props.configuration.AvailableContainers.find(option => option.Type === type); if (properties === undefined) { throw new Error(`[AddContainer] Object type not found. Found: ${type}`); } // Set the counter of the object type in order to assign an unique id const newCounters = Object.assign({}, current.TypeCounters); if (newCounters[type] === null || newCounters[type] === undefined) { newCounters[type] = 0; } else { newCounters[type]++; } const count = newCounters[type]; // Create maincontainer model const structure: IContainerModel = structuredClone(current.MainContainer); const clone = Object.assign(new ContainerModel(null, {} as Properties), structure); // Find the parent const it = MakeIterator(clone); let parent: ContainerModel | null = null; for (const child of it) { if (child.properties.id === current.SelectedContainer.properties.id) { parent = child as ContainerModel; break; } } if (parent === null) { throw new Error('[OnPropertyChange] Container model was not found among children of the main container!'); } // Create the container const newContainer = new ContainerModel( parent, { id: `${type}-${count}`, x: 0, y: 0, width: properties?.Width, height: parent.properties.height, ...properties.Style } as Properties, [], { type } ); // And push it the the parent children parent.children.push(newContainer); // Update the state this.setState({ history: history.concat([{ MainContainer: clone, TypeCounters: newCounters, SelectedContainer: parent }]), historyCurrentStep: history.length } as IEditorState); } public jumpTo(move: number): void { this.setState({ historyCurrentStep: move } as IEditorState); } /** * Render the application * @returns {JSX.Element} Rendered JSX element */ render() { const current = this.getCurrentHistoryState(); return (
this.ToggleSidebar()} buttonOnClick={(type: string) => this.AddContainer(type)} /> this.ToggleElementsSidebar()} onPropertyChange={(key: string, value: string) => this.OnPropertyChange(key, value)} selectContainer={(container: ContainerModel) => this.SelectContainer(container)} /> this.ToggleHistory()} jumpTo={(move) => { this.jumpTo(move); }} /> { current.MainContainer }
); } } export default Editor;