Refactor Editor and module functions (#15)
All checks were successful
continuous-integration/drone/push Build is passing
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:
parent
8e34d6b72a
commit
293af45144
26 changed files with 477 additions and 367 deletions
136
src/App.tsx
136
src/App.tsx
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue