186 lines
4.9 KiB
TypeScript
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;
|
|
}
|
|
}
|