Refactor Editor and module functions (#15)
All checks were successful
continuous-integration/drone/push Build is passing

Moved all module functions to separate utils modules

Replaced standard with standard with typescript

Extracted UI elements to separate component

Reviewed-on: https://git.siklos-chaneru.duckdns.org/Siklos/svg-layout-designer-react/pulls/15
This commit is contained in:
Siklos 2022-08-05 15:38:44 -04:00
parent 8e34d6b72a
commit 293af45144
26 changed files with 477 additions and 367 deletions

View file

@ -1,25 +1,27 @@
import * as React from 'react';
import './App.scss';
import { MainMenu } from './Components/MainMenu/MainMenu';
import { ContainerModel, findContainerById, IContainerModel, MakeIterator } from './Interfaces/ContainerModel';
import { ContainerModel, IContainerModel } from './Interfaces/ContainerModel';
import Editor, { IEditorState } from './Editor';
import { AvailableContainer } from './Interfaces/AvailableContainer';
import { Configuration } from './Interfaces/Configuration';
import { Revive } from './utils/saveload';
export interface IHistoryState {
MainContainer: IContainerModel | null,
SelectedContainer: IContainerModel | null,
SelectedContainerId: string,
MainContainer: IContainerModel | null
SelectedContainer: IContainerModel | null
SelectedContainerId: string
TypeCounters: Record<string, number>
}
// App will never have props
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface IAppProps {
}
interface IAppState {
configuration: Configuration,
history: IHistoryState[],
historyCurrentStep: number,
configuration: Configuration
history: IHistoryState[]
historyCurrentStep: number
isLoaded: boolean
}
@ -32,7 +34,12 @@ export class App extends React.Component<IAppProps> {
configuration: {
AvailableContainers: [],
AvailableSymbols: [],
MainContainer: {} as AvailableContainer
MainContainer: {
Type: 'EmptyContainer',
Width: 3000,
Height: 200,
Style: {}
}
},
history: [],
historyCurrentStep: 0,
@ -40,7 +47,7 @@ export class App extends React.Component<IAppProps> {
};
}
componentDidMount() {
componentDidMount(): void {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const state = urlParams.get('state');
@ -50,35 +57,39 @@ export class App extends React.Component<IAppProps> {
}
fetch(state)
.then((response) => response.json())
.then(
async(response) => await response.json(),
(error) => { throw new Error(error); }
)
.then((data: IEditorState) => {
this.LoadState(data);
});
}, (error) => { throw new Error(error); });
}
public NewEditor() {
public NewEditor(): void {
// Fetch the configuration from the API
fetchConfiguration().then((configuration: Configuration) => {
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'
}
);
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:
// Save the configuration and the new MainContainer
// and default the selected container to it
this.setState({
configuration,
history:
[
{
MainContainer,
@ -86,13 +97,14 @@ export class App extends React.Component<IAppProps> {
TypeCounters: {}
}
],
historyCurrentStep: 0,
isLoaded: true
} as IAppState);
});
historyCurrentStep: 0,
isLoaded: true
});
}, (error) => { throw new Error(error); }
);
}
public LoadEditor(files: FileList | null) {
public LoadEditor(files: FileList | null): void {
if (files === null) {
return;
}
@ -107,7 +119,7 @@ export class App extends React.Component<IAppProps> {
reader.readAsText(file);
}
private LoadState(editorState: IEditorState) {
private LoadState(editorState: IEditorState): void {
Revive(editorState);
this.setState({
@ -115,10 +127,10 @@ export class App extends React.Component<IAppProps> {
history: editorState.history,
historyCurrentStep: editorState.historyCurrentStep,
isLoaded: true
} as IAppState);
});
}
public render() {
public render(): JSX.Element {
if (this.state.isLoaded) {
return (
<div>
@ -149,16 +161,17 @@ export class App extends React.Component<IAppProps> {
export async function fetchConfiguration(): Promise<Configuration> {
const url = `${import.meta.env.VITE_API_URL}`;
// The test library cannot use the Fetch API
// @ts-ignore
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (window.fetch) {
return await fetch(url, {
method: 'POST'
})
.then((response) =>
response.json()
.then(async(response) =>
await response.json()
) as Configuration;
}
return new Promise((resolve) => {
return await new Promise((resolve) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onreadystatechange = function() { // Call a function when the state changes.
@ -169,38 +182,3 @@ export async function fetchConfiguration(): Promise<Configuration> {
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;
}
}