svg-layout-designer-react/src/App.tsx
Siklos f524e8f7b9
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Fix json selected container by saving its id
2022-08-04 19:30:48 +02:00

186 lines
4.9 KiB
TypeScript

import * as React from 'react';
import './App.scss';
import { MainMenu } from './Components/MainMenu/MainMenu';
import { ContainerModel, findContainerById, IContainerModel, MakeIterator } from './Components/SVG/Elements/ContainerModel';
import Editor, { IEditorState } from './Editor';
import { AvailableContainer } from './Interfaces/AvailableContainer';
import { Configuration } from './Interfaces/Configuration';
export interface IHistoryState {
MainContainer: IContainerModel | null,
SelectedContainer: IContainerModel | null,
SelectedContainerId: string,
TypeCounters: Record<string, number>
}
interface IAppProps {
}
interface IAppState {
configuration: Configuration,
history: IHistoryState[],
historyCurrentStep: number,
isLoaded: boolean
}
export class App extends React.Component<IAppProps> {
public state: IAppState;
constructor(props: IAppProps) {
super(props);
this.state = {
configuration: {
AvailableContainers: [],
AvailableSymbols: [],
MainContainer: {} as AvailableContainer
},
history: [],
historyCurrentStep: 0,
isLoaded: false
};
}
public NewEditor() {
// Fetch the configuration from the API
fetchConfiguration().then((configuration: Configuration) => {
// Set the main container from the given properties of the API
const MainContainer = new ContainerModel(
null,
{
id: 'main',
parentId: 'null',
x: 0,
y: 0,
width: configuration.MainContainer.Width,
height: configuration.MainContainer.Height,
fillOpacity: 0,
stroke: 'black'
}
);
// Save the configuration and the new MainContainer
// and default the selected container to it
this.setState({
configuration,
history:
[
{
MainContainer,
SelectedContainer: MainContainer,
TypeCounters: {}
}
],
historyCurrentStep: 0,
isLoaded: true
} as IAppState);
});
}
public LoadEditor(files: FileList | null) {
if (files === null) {
return;
}
const file = files[0];
const reader = new FileReader();
reader.addEventListener('load', () => {
const result = reader.result as string;
const editorState: IEditorState = JSON.parse(result);
Revive(editorState);
this.setState({
configuration: editorState.configuration,
history: editorState.history,
historyCurrentStep: editorState.historyCurrentStep,
isLoaded: true
} as IAppState);
});
reader.readAsText(file);
}
public render() {
if (this.state.isLoaded) {
return (
<div>
<Editor
configuration={this.state.configuration}
history={this.state.history}
historyCurrentStep={this.state.historyCurrentStep}
/>
</div>
);
} else {
return (
<div className='bg-blue-100 h-full w-full'>
<MainMenu
newEditor={() => this.NewEditor()}
loadEditor={(files: FileList | null) => this.LoadEditor(files)}
/>
</div>
);
}
}
}
/**
* Fetch the configuration from the API
* @returns {Configation} The model of the configuration for the application
*/
export async function fetchConfiguration(): Promise<Configuration> {
const url = `${import.meta.env.VITE_API_URL}`;
// The test library cannot use the Fetch API
// @ts-ignore
if (window.fetch) {
return await fetch(url, {
method: 'POST'
})
.then((response) =>
response.json()
) as Configuration;
}
return new Promise((resolve) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onreadystatechange = function() { // Call a function when the state changes.
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
resolve(JSON.parse(this.responseText));
}
};
xhr.send();
});
}
/**
* Revive the Editor state
* by setting the containers references to their parent
* @param editorState Editor state
*/
function Revive(editorState: IEditorState): void {
const history = editorState.history;
for (const state of history) {
if (state.MainContainer === null || state.MainContainer === undefined) {
continue;
}
const it = MakeIterator(state.MainContainer);
for (const container of it) {
const parentId = container.properties.parentId;
if (parentId === null) {
container.parent = null;
continue;
}
const parent = findContainerById(state.MainContainer, parentId);
if (parent === undefined) {
continue;
}
container.parent = parent;
}
const selected = findContainerById(state.MainContainer, state.SelectedContainerId);
if (selected === undefined) {
state.SelectedContainer = null;
continue;
}
state.SelectedContainer = selected;
}
}