Implement events for external use + Rename interfaces with a I prefix + add some documentation #26
38 changed files with 228 additions and 116 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.drawio filter=lfs diff=lfs merge=lfs -text
|
BIN
docs/ComponentStructure.drawio
(Stored with Git LFS)
Normal file
BIN
docs/ComponentStructure.drawio
(Stored with Git LFS)
Normal file
Binary file not shown.
65
public/Interfaces.d.ts
vendored
Normal file
65
public/Interfaces.d.ts
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
declare interface IHistoryState {
|
||||||
|
LastAction: string
|
||||||
|
MainContainer: IContainerModel
|
||||||
|
SelectedContainer: IContainerModel | null
|
||||||
|
SelectedContainerId: string
|
||||||
|
TypeCounters: Record<string, number>
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface IAvailableContainer {
|
||||||
|
Type: string
|
||||||
|
Width: number
|
||||||
|
Height: number
|
||||||
|
XPositionReference?: XPositionReference
|
||||||
|
Style: React.CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface IEditorState {
|
||||||
|
history: IHistoryState[]
|
||||||
|
historyCurrentStep: number
|
||||||
|
configuration: IConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface IConfiguration {
|
||||||
|
AvailableContainers: IAvailableContainer[]
|
||||||
|
AvailableSymbols: IAvailableSymbol[]
|
||||||
|
MainContainer: IAvailableContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface IContainerModel {
|
||||||
|
children: IContainerModel[]
|
||||||
|
parent: IContainerModel | null
|
||||||
|
properties: IProperties
|
||||||
|
userData: Record<string, string | number>
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface IProperties extends React.CSSProperties {
|
||||||
|
id: string
|
||||||
|
parentId: string | null
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
isRigidBody: boolean
|
||||||
|
XPositionReference?: XPositionReference
|
||||||
|
}
|
||||||
|
|
||||||
|
declare enum XPositionReference {
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface IAvailableSymbol {
|
||||||
|
Name: string
|
||||||
|
XPositionReference: XPositionReference
|
||||||
|
Image: IImage
|
||||||
|
Width: number
|
||||||
|
Height: number
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface IImage {
|
||||||
|
Name: string
|
||||||
|
Url: string
|
||||||
|
Base64Image: string
|
||||||
|
Svg: string
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Configuration } from '../../Interfaces/Configuration';
|
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the configuration from the API
|
* Fetch the configuration from the API
|
||||||
* @returns {Configation} The model of the configuration for the application
|
* @returns {Configation} The model of the configuration for the application
|
||||||
*/
|
*/
|
||||||
export async function fetchConfiguration(): Promise<Configuration> {
|
export async function fetchConfiguration(): Promise<IConfiguration> {
|
||||||
const url = `${import.meta.env.VITE_API_URL}`;
|
const url = `${import.meta.env.VITE_API_URL}`;
|
||||||
// The test library cannot use the Fetch API
|
// The test library cannot use the Fetch API
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
@ -15,7 +15,7 @@ export async function fetchConfiguration(): Promise<Configuration> {
|
||||||
})
|
})
|
||||||
.then(async(response) =>
|
.then(async(response) =>
|
||||||
await response.json()
|
await response.json()
|
||||||
) as Configuration;
|
) as IConfiguration;
|
||||||
}
|
}
|
||||||
return await new Promise((resolve) => {
|
return await new Promise((resolve) => {
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
import { MainMenu } from '../MainMenu/MainMenu';
|
import { MainMenu } from '../MainMenu/MainMenu';
|
||||||
import { ContainerModel } from '../../Interfaces/ContainerModel';
|
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import Editor, { IEditorState } from '../Editor/Editor';
|
import Editor from '../Editor/Editor';
|
||||||
|
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||||
import { LoadState } from './Load';
|
import { LoadState } from './Load';
|
||||||
import { LoadEditor, NewEditor } from './MenuActions';
|
import { LoadEditor, NewEditor } from './MenuActions';
|
||||||
import { DEFAULT_CONFIG, DEFAULT_MAINCONTAINER_PROPS } from '../../utils/default';
|
import { DEFAULT_CONFIG, DEFAULT_MAINCONTAINER_PROPS } from '../../utils/default';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { Revive } from '../../utils/saveload';
|
import { Revive } from '../../utils/saveload';
|
||||||
import { IEditorState } from '../Editor/Editor';
|
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||||
|
|
||||||
export function LoadState(
|
export function LoadState(
|
||||||
editorState: IEditorState,
|
editorState: IEditorState,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { Configuration } from '../../Interfaces/Configuration';
|
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||||
import { ContainerModel } from '../../Interfaces/ContainerModel';
|
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { fetchConfiguration } from '../API/api';
|
import { fetchConfiguration } from '../API/api';
|
||||||
import { IEditorState } from '../Editor/Editor';
|
import { IEditorState } from "../../Interfaces/IEditorState";
|
||||||
import { LoadState } from './Load';
|
import { LoadState } from './Load';
|
||||||
|
|
||||||
export function NewEditor(
|
export function NewEditor(
|
||||||
|
@ -11,7 +11,7 @@ export function NewEditor(
|
||||||
): void {
|
): void {
|
||||||
// Fetch the configuration from the API
|
// Fetch the configuration from the API
|
||||||
fetchConfiguration()
|
fetchConfiguration()
|
||||||
.then((configuration: Configuration) => {
|
.then((configuration: IConfiguration) => {
|
||||||
// Set the main container from the given properties of the API
|
// Set the main container from the given properties of the API
|
||||||
const MainContainer = new ContainerModel(
|
const MainContainer = new ContainerModel(
|
||||||
null,
|
null,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { HistoryState } from '../../Interfaces/HistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { Configuration } from '../../Interfaces/Configuration';
|
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||||
import { ContainerModel, IContainerModel } from '../../Interfaces/ContainerModel';
|
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { findContainerById } from '../../utils/itertools';
|
import { findContainerById } from '../../utils/itertools';
|
||||||
import { getCurrentHistory } from './Editor';
|
import { getCurrentHistory } from './Editor';
|
||||||
|
|
||||||
|
@ -11,9 +11,9 @@ import { getCurrentHistory } from './Editor';
|
||||||
*/
|
*/
|
||||||
export function SelectContainer(
|
export function SelectContainer(
|
||||||
container: ContainerModel,
|
container: ContainerModel,
|
||||||
fullHistory: HistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
@ -46,9 +46,9 @@ export function SelectContainer(
|
||||||
*/
|
*/
|
||||||
export function DeleteContainer(
|
export function DeleteContainer(
|
||||||
containerId: string,
|
containerId: string,
|
||||||
fullHistory: HistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
@ -108,10 +108,10 @@ export function DeleteContainer(
|
||||||
*/
|
*/
|
||||||
export function AddContainerToSelectedContainer(
|
export function AddContainerToSelectedContainer(
|
||||||
type: string,
|
type: string,
|
||||||
configuration: Configuration,
|
configuration: IConfiguration,
|
||||||
fullHistory: HistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
@ -151,10 +151,10 @@ export function AddContainer(
|
||||||
index: number,
|
index: number,
|
||||||
type: string,
|
type: string,
|
||||||
parentId: string,
|
parentId: string,
|
||||||
configuration: Configuration,
|
configuration: IConfiguration,
|
||||||
fullHistory: HistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
|
|
@ -1,32 +1,29 @@
|
||||||
import React from 'react';
|
import React, { useRef } from 'react';
|
||||||
import './Editor.scss';
|
import './Editor.scss';
|
||||||
import { Configuration } from '../../Interfaces/Configuration';
|
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||||
import { SVG } from '../SVG/SVG';
|
import { SVG } from '../SVG/SVG';
|
||||||
import { HistoryState } from '../../Interfaces/HistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { UI } from '../UI/UI';
|
import { UI } from '../UI/UI';
|
||||||
import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, AddContainer } from './ContainerOperations';
|
import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, AddContainer } from './ContainerOperations';
|
||||||
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Save';
|
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Save';
|
||||||
import { onKeyDown } from './Shortcuts';
|
import { onKeyDown } from './Shortcuts';
|
||||||
import { OnPropertyChange, OnPropertiesSubmit } from './PropertiesOperations';
|
import { OnPropertyChange, OnPropertiesSubmit } from './PropertiesOperations';
|
||||||
|
import EditorEvents from '../../Events/EditorEvents';
|
||||||
|
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||||
|
|
||||||
interface IEditorProps {
|
interface IEditorProps {
|
||||||
configuration: Configuration
|
configuration: IConfiguration
|
||||||
history: HistoryState[]
|
history: IHistoryState[]
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEditorState {
|
export const getCurrentHistory = (history: IHistoryState[], historyCurrentStep: number): IHistoryState[] => history.slice(0, historyCurrentStep + 1);
|
||||||
history: HistoryState[]
|
export const getCurrentHistoryState = (history: IHistoryState[], historyCurrentStep: number): IHistoryState => history[historyCurrentStep];
|
||||||
historyCurrentStep: number
|
|
||||||
configuration: Configuration
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getCurrentHistory = (history: HistoryState[], historyCurrentStep: number): HistoryState[] => history.slice(0, historyCurrentStep + 1);
|
|
||||||
export const getCurrentHistoryState = (history: HistoryState[], historyCurrentStep: number): HistoryState => history[historyCurrentStep];
|
|
||||||
|
|
||||||
const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
const [history, setHistory] = React.useState<HistoryState[]>(structuredClone(props.history));
|
const [history, setHistory] = React.useState<IHistoryState[]>(structuredClone(props.history));
|
||||||
const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep);
|
const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep);
|
||||||
|
const editorRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const onKeyUp = (event: KeyboardEvent): void => onKeyDown(
|
const onKeyUp = (event: KeyboardEvent): void => onKeyDown(
|
||||||
|
@ -38,6 +35,17 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
|
|
||||||
window.addEventListener('keyup', onKeyUp);
|
window.addEventListener('keyup', onKeyUp);
|
||||||
|
|
||||||
|
const events = EditorEvents;
|
||||||
|
const editorState: IEditorState = {
|
||||||
|
history,
|
||||||
|
historyCurrentStep,
|
||||||
|
configuration: props.configuration
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const event of events) {
|
||||||
|
editorRef.current?.addEventListener(event.name, () => event.func(editorState));
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('keyup', onKeyUp);
|
window.removeEventListener('keyup', onKeyUp);
|
||||||
};
|
};
|
||||||
|
@ -46,7 +54,7 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
const configuration = props.configuration;
|
const configuration = props.configuration;
|
||||||
const current = getCurrentHistoryState(history, historyCurrentStep);
|
const current = getCurrentHistoryState(history, historyCurrentStep);
|
||||||
return (
|
return (
|
||||||
<div className="App font-sans h-full">
|
<div ref={editorRef} className="Editor font-sans h-full">
|
||||||
<UI
|
<UI
|
||||||
current={current}
|
current={current}
|
||||||
history={history}
|
history={history}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { IContainerModel, ContainerModel } from '../../Interfaces/ContainerModel';
|
import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { HistoryState } from '../../Interfaces/HistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import Properties from '../../Interfaces/Properties';
|
import IProperties from '../../Interfaces/IProperties';
|
||||||
import { findContainerById } from '../../utils/itertools';
|
import { findContainerById } from '../../utils/itertools';
|
||||||
import { getCurrentHistory } from './Editor';
|
import { getCurrentHistory } from './Editor';
|
||||||
import { RecalculatePhysics } from './RigidBodyBehaviors';
|
import { RecalculatePhysics } from './RigidBodyBehaviors';
|
||||||
|
@ -15,9 +15,9 @@ import { RecalculatePhysics } from './RigidBodyBehaviors';
|
||||||
export function OnPropertyChange(
|
export function OnPropertyChange(
|
||||||
key: string,
|
key: string,
|
||||||
value: string | number | boolean,
|
value: string | number | boolean,
|
||||||
fullHistory: HistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
@ -73,10 +73,10 @@ export function OnPropertyChange(
|
||||||
*/
|
*/
|
||||||
export function OnPropertiesSubmit(
|
export function OnPropertiesSubmit(
|
||||||
event: React.SyntheticEvent<HTMLFormElement>,
|
event: React.SyntheticEvent<HTMLFormElement>,
|
||||||
properties: Properties,
|
properties: IProperties,
|
||||||
fullHistory: HistoryState[],
|
fullHistory: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { SizePointer } from '../../Interfaces/SizePointer';
|
import { ISizePointer } from '../../Interfaces/ISizePointer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Transform the container into a rigid body"
|
* "Transform the container into a rigid body"
|
||||||
|
@ -167,17 +167,17 @@ function constraintBodyInsideUnallocatedWidth(
|
||||||
* (except the fact that disk space is divided by block).
|
* (except the fact that disk space is divided by block).
|
||||||
* @param container Container where to find an available width
|
* @param container Container where to find an available width
|
||||||
* @param exception Container to exclude of the widths (since a container will be moved, it might need to be excluded)
|
* @param exception Container to exclude of the widths (since a container will be moved, it might need to be excluded)
|
||||||
* @returns {SizePointer[]} Array of unallocated widths (x=position of the unallocated space, width=size of the allocated space)
|
* @returns {ISizePointer[]} Array of unallocated widths (x=position of the unallocated space, width=size of the allocated space)
|
||||||
*/
|
*/
|
||||||
function getAvailableWidths(
|
function getAvailableWidths(
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
exception: IContainerModel
|
exception: IContainerModel
|
||||||
): SizePointer[] {
|
): ISizePointer[] {
|
||||||
// Initialize the first size pointer
|
// Initialize the first size pointer
|
||||||
// which takes full width of the available space
|
// which takes full width of the available space
|
||||||
const x = 0;
|
const x = 0;
|
||||||
const width = Number(container.properties.width);
|
const width = Number(container.properties.width);
|
||||||
let unallocatedSpaces: SizePointer[] = [{ x, width }];
|
let unallocatedSpaces: ISizePointer[] = [{ x, width }];
|
||||||
|
|
||||||
// We will only uses containers that also have the rigid bodies
|
// We will only uses containers that also have the rigid bodies
|
||||||
// as out-of-bound or enormouse containers should be ignored
|
// as out-of-bound or enormouse containers should be ignored
|
||||||
|
@ -192,7 +192,7 @@ function getAvailableWidths(
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the space of the child that is inside the parent
|
// get the space of the child that is inside the parent
|
||||||
let newUnallocatedSpace: SizePointer[] = [];
|
let newUnallocatedSpace: ISizePointer[] = [];
|
||||||
|
|
||||||
// We will iterate on a mutable variable in order to divide it
|
// We will iterate on a mutable variable in order to divide it
|
||||||
for (const unallocatedSpace of unallocatedSpaces) {
|
for (const unallocatedSpace of unallocatedSpaces) {
|
||||||
|
@ -229,7 +229,7 @@ function getAvailableWidthsTwoLines(
|
||||||
max1: number,
|
max1: number,
|
||||||
min2: number,
|
min2: number,
|
||||||
max2: number
|
max2: number
|
||||||
): SizePointer[] {
|
): ISizePointer[] {
|
||||||
if (min2 < min1 && max2 > max1) {
|
if (min2 < min1 && max2 > max1) {
|
||||||
// object 2 is overlapping full width
|
// object 2 is overlapping full width
|
||||||
return [];
|
return [];
|
||||||
|
@ -276,5 +276,5 @@ function getAvailableWidthsTwoLines(
|
||||||
*/
|
*/
|
||||||
const isFitting = (
|
const isFitting = (
|
||||||
container: IContainerModel,
|
container: IContainerModel,
|
||||||
sizePointer: SizePointer
|
sizePointer: ISizePointer
|
||||||
): boolean => Number(container.properties.width) <= sizePointer.width;
|
): boolean => Number(container.properties.width) <= sizePointer.width;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { HistoryState } from '../../Interfaces/HistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { Configuration } from '../../Interfaces/Configuration';
|
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
||||||
import { getCircularReplacer } from '../../utils/saveload';
|
import { getCircularReplacer } from '../../utils/saveload';
|
||||||
import { ID } from '../SVG/SVG';
|
import { ID } from '../SVG/SVG';
|
||||||
import { IEditorState } from './Editor';
|
import { IEditorState } from '../../Interfaces/IEditorState';
|
||||||
|
|
||||||
export function SaveEditorAsJSON(
|
export function SaveEditorAsJSON(
|
||||||
history: HistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
configuration: Configuration
|
configuration: IConfiguration
|
||||||
): void {
|
): void {
|
||||||
const exportName = 'state';
|
const exportName = 'state';
|
||||||
const spaces = import.meta.env.DEV ? 4 : 0;
|
const spaces = import.meta.env.DEV ? 4 : 0;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { HistoryState } from '../../Interfaces/HistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
|
|
||||||
export function onKeyDown(
|
export function onKeyDown(
|
||||||
event: KeyboardEvent,
|
event: KeyboardEvent,
|
||||||
history: HistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { fireEvent, render, screen } from '../../utils/test-utils';
|
import { fireEvent, render, screen } from '../../utils/test-utils';
|
||||||
import { ElementsSidebar } from './ElementsSidebar';
|
import { ElementsSidebar } from './ElementsSidebar';
|
||||||
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
|
|
||||||
describe.concurrent('Elements sidebar', () => {
|
describe.concurrent('Elements sidebar', () => {
|
||||||
it('With a MainContainer', () => {
|
it('With a MainContainer', () => {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Properties } from '../Properties/Properties';
|
import { Properties } from '../Properties/Properties';
|
||||||
import ContainerProperties from '../../Interfaces/Properties';
|
import ContainerProperties from '../../Interfaces/IProperties';
|
||||||
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { getDepth, MakeIterator } from '../../utils/itertools';
|
import { getDepth, MakeIterator } from '../../utils/itertools';
|
||||||
import { Menu } from '../Menu/Menu';
|
import { Menu } from '../Menu/Menu';
|
||||||
import { MenuItem } from '../Menu/MenuItem';
|
import { MenuItem } from '../Menu/MenuItem';
|
||||||
import { handleDragLeave, handleDragOver, handleLeftClick, handleOnDrop, handleRightClick } from './MouseEventHandlers';
|
import { handleDragLeave, handleDragOver, handleLeftClick, handleOnDrop, handleRightClick } from './MouseEventHandlers';
|
||||||
import { Point } from '../../Interfaces/Point';
|
import { IPoint } from '../../Interfaces/IPoint';
|
||||||
|
|
||||||
interface IElementsSidebarProps {
|
interface IElementsSidebarProps {
|
||||||
MainContainer: IContainerModel
|
MainContainer: IContainerModel
|
||||||
|
@ -64,7 +64,7 @@ export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElement
|
||||||
// States
|
// States
|
||||||
const [isContextMenuOpen, setIsContextMenuOpen] = React.useState<boolean>(false);
|
const [isContextMenuOpen, setIsContextMenuOpen] = React.useState<boolean>(false);
|
||||||
const [onClickContainerId, setOnClickContainerId] = React.useState<string>('');
|
const [onClickContainerId, setOnClickContainerId] = React.useState<string>('');
|
||||||
const [contextMenuPosition, setContextMenuPosition] = React.useState<Point>({
|
const [contextMenuPosition, setContextMenuPosition] = React.useState<IPoint>({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0
|
y: 0
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { Point } from '../../Interfaces/Point';
|
import { IPoint } from '../../Interfaces/IPoint';
|
||||||
import { findContainerById } from '../../utils/itertools';
|
import { findContainerById } from '../../utils/itertools';
|
||||||
|
|
||||||
export function handleRightClick(
|
export function handleRightClick(
|
||||||
event: MouseEvent,
|
event: MouseEvent,
|
||||||
setIsContextMenuOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
setIsContextMenuOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
||||||
setOnClickContainerId: React.Dispatch<React.SetStateAction<string>>,
|
setOnClickContainerId: React.Dispatch<React.SetStateAction<string>>,
|
||||||
setContextMenuPosition: React.Dispatch<React.SetStateAction<Point>>
|
setContextMenuPosition: React.Dispatch<React.SetStateAction<IPoint>>
|
||||||
): void {
|
): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ export function handleRightClick(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contextMenuPosition: Point = { x: event.pageX, y: event.pageY };
|
const contextMenuPosition: IPoint = { x: event.pageX, y: event.pageY };
|
||||||
setIsContextMenuOpen(true);
|
setIsContextMenuOpen(true);
|
||||||
setOnClickContainerId(event.target.id);
|
setOnClickContainerId(event.target.id);
|
||||||
setContextMenuPosition(contextMenuPosition);
|
setContextMenuPosition(contextMenuPosition);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { HistoryState } from "../../Interfaces/HistoryState";
|
import { IHistoryState } from "../../Interfaces/IHistoryState";
|
||||||
|
|
||||||
interface IHistoryProps {
|
interface IHistoryProps {
|
||||||
history: HistoryState[]
|
history: IHistoryState[]
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
jumpTo: (move: number) => void
|
jumpTo: (move: number) => void
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import ContainerProperties from '../../Interfaces/Properties';
|
import ContainerProperties from '../../Interfaces/IProperties';
|
||||||
import { ToggleButton } from '../ToggleButton/ToggleButton';
|
import { ToggleButton } from '../ToggleButton/ToggleButton';
|
||||||
import { INPUT_TYPES } from './PropertiesInputTypes';
|
import { INPUT_TYPES } from './PropertiesInputTypes';
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { XPositionReference } from '../../../Enums/XPositionReference';
|
import { XPositionReference } from '../../../Enums/XPositionReference';
|
||||||
import { IContainerModel } from '../../../Interfaces/ContainerModel';
|
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { getDepth } from '../../../utils/itertools';
|
import { getDepth } from '../../../utils/itertools';
|
||||||
import { Dimension } from './Dimension';
|
import { Dimension } from './Dimension';
|
||||||
|
|
||||||
export interface IContainerProps {
|
interface IContainerProps {
|
||||||
model: IContainerModel
|
model: IContainerModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { IContainerModel } from '../../../Interfaces/ContainerModel';
|
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
import { getAbsolutePosition } from '../../../utils/itertools';
|
import { getAbsolutePosition } from '../../../utils/itertools';
|
||||||
|
|
||||||
interface ISelectorProps {
|
interface ISelectorProps {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { UncontrolledReactSVGPanZoom } from 'react-svg-pan-zoom';
|
import { UncontrolledReactSVGPanZoom } from 'react-svg-pan-zoom';
|
||||||
import { Container } from './Elements/Container';
|
import { Container } from './Elements/Container';
|
||||||
import { ContainerModel } from '../../Interfaces/ContainerModel';
|
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { Selector } from './Elements/Selector';
|
import { Selector } from './Elements/Selector';
|
||||||
import { BAR_WIDTH } from '../Bar/Bar';
|
import { BAR_WIDTH } from '../Bar/Bar';
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { AvailableContainer } from '../../Interfaces/AvailableContainer';
|
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
||||||
import { truncateString } from '../../utils/stringtools';
|
import { truncateString } from '../../utils/stringtools';
|
||||||
|
|
||||||
interface ISidebarProps {
|
interface ISidebarProps {
|
||||||
componentOptions: AvailableContainer[]
|
componentOptions: IAvailableContainer[]
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
buttonOnClick: (type: string) => void
|
buttonOnClick: (type: string) => void
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,23 +2,23 @@ import * as React from 'react';
|
||||||
import { ElementsSidebar } from '../ElementsSidebar/ElementsSidebar';
|
import { ElementsSidebar } from '../ElementsSidebar/ElementsSidebar';
|
||||||
import { Sidebar } from '../Sidebar/Sidebar';
|
import { Sidebar } from '../Sidebar/Sidebar';
|
||||||
import { History } from '../History/History';
|
import { History } from '../History/History';
|
||||||
import { AvailableContainer } from '../../Interfaces/AvailableContainer';
|
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
||||||
import { ContainerModel } from '../../Interfaces/ContainerModel';
|
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { HistoryState } from '../../Interfaces/HistoryState';
|
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { PhotographIcon, UploadIcon } from '@heroicons/react/outline';
|
import { PhotographIcon, UploadIcon } from '@heroicons/react/outline';
|
||||||
import { FloatingButton } from '../FloatingButton/FloatingButton';
|
import { FloatingButton } from '../FloatingButton/FloatingButton';
|
||||||
import { Bar } from '../Bar/Bar';
|
import { Bar } from '../Bar/Bar';
|
||||||
import Properties from '../../Interfaces/Properties';
|
import IProperties from '../../Interfaces/IProperties';
|
||||||
|
|
||||||
interface IUIProps {
|
interface IUIProps {
|
||||||
current: HistoryState
|
current: IHistoryState
|
||||||
history: HistoryState[]
|
history: IHistoryState[]
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
AvailableContainers: AvailableContainer[]
|
AvailableContainers: IAvailableContainer[]
|
||||||
SelectContainer: (container: ContainerModel) => void
|
SelectContainer: (container: ContainerModel) => void
|
||||||
DeleteContainer: (containerId: string) => void
|
DeleteContainer: (containerId: string) => void
|
||||||
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
||||||
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, properties: Properties) => void
|
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, properties: IProperties) => void
|
||||||
AddContainerToSelectedContainer: (type: string) => void
|
AddContainerToSelectedContainer: (type: string) => void
|
||||||
AddContainer: (index: number, type: string, parentId: string) => void
|
AddContainer: (index: number, type: string, parentId: string) => void
|
||||||
SaveEditorAsJSON: () => void
|
SaveEditorAsJSON: () => void
|
||||||
|
|
26
src/Events/EditorEvents.ts
Normal file
26
src/Events/EditorEvents.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { IEditorState } from '../Interfaces/IEditorState';
|
||||||
|
import { IHistoryState } from '../Interfaces/IHistoryState';
|
||||||
|
|
||||||
|
const getEditorState = (editorState: IEditorState): void => {
|
||||||
|
const customEvent = new CustomEvent<IEditorState>('getEditorState', { detail: editorState });
|
||||||
|
document.dispatchEvent(customEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurrentHistoryState = (editorState: IEditorState): void => {
|
||||||
|
const customEvent = new CustomEvent<IHistoryState>(
|
||||||
|
'getCurrentHistoryState',
|
||||||
|
{ detail: editorState.history[editorState.historyCurrentStep] });
|
||||||
|
document.dispatchEvent(customEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IEditorEvent {
|
||||||
|
name: string
|
||||||
|
func: (editorState: IEditorState) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const events: IEditorEvent[] = [
|
||||||
|
{ name: 'getEditorState', func: getEditorState },
|
||||||
|
{ name: 'getCurrentHistoryState', func: getCurrentHistoryState }
|
||||||
|
];
|
||||||
|
|
||||||
|
export default events;
|
|
@ -1,9 +0,0 @@
|
||||||
import { AvailableContainer } from './AvailableContainer';
|
|
||||||
import { AvailableSymbolModel } from './AvailableSymbol';
|
|
||||||
|
|
||||||
/** Model of configuration for the application to configure it */
|
|
||||||
export interface Configuration {
|
|
||||||
AvailableContainers: AvailableContainer[]
|
|
||||||
AvailableSymbols: AvailableSymbolModel[]
|
|
||||||
MainContainer: AvailableContainer
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { XPositionReference } from '../Enums/XPositionReference';
|
import { XPositionReference } from '../Enums/XPositionReference';
|
||||||
|
|
||||||
/** Model of available container used in application configuration */
|
/** Model of available container used in application configuration */
|
||||||
export interface AvailableContainer {
|
export interface IAvailableContainer {
|
||||||
Type: string
|
Type: string
|
||||||
Width: number
|
Width: number
|
||||||
Height: number
|
Height: number
|
|
@ -1,12 +1,12 @@
|
||||||
import { XPositionReference } from '../Enums/XPositionReference';
|
import { XPositionReference } from '../Enums/XPositionReference';
|
||||||
import { Image } from './Image';
|
import { IImage } from './IImage';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model of available symbol to configure the application */
|
* Model of available symbol to configure the application */
|
||||||
export interface AvailableSymbolModel {
|
export interface IAvailableSymbol {
|
||||||
Name: string
|
Name: string
|
||||||
XPositionReference: XPositionReference
|
XPositionReference: XPositionReference
|
||||||
Image: Image
|
Image: IImage
|
||||||
Width: number
|
Width: number
|
||||||
Height: number
|
Height: number
|
||||||
}
|
}
|
9
src/Interfaces/IConfiguration.ts
Normal file
9
src/Interfaces/IConfiguration.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { IAvailableContainer } from './IAvailableContainer';
|
||||||
|
import { IAvailableSymbol } from './IAvailableSymbol';
|
||||||
|
|
||||||
|
/** Model of configuration for the application to configure it */
|
||||||
|
export interface IConfiguration {
|
||||||
|
AvailableContainers: IAvailableContainer[]
|
||||||
|
AvailableSymbols: IAvailableSymbol[]
|
||||||
|
MainContainer: IAvailableContainer
|
||||||
|
}
|
|
@ -1,21 +1,21 @@
|
||||||
import Properties from './Properties';
|
import IProperties from './IProperties';
|
||||||
|
|
||||||
export interface IContainerModel {
|
export interface IContainerModel {
|
||||||
children: IContainerModel[]
|
children: IContainerModel[]
|
||||||
parent: IContainerModel | null
|
parent: IContainerModel | null
|
||||||
properties: Properties
|
properties: IProperties
|
||||||
userData: Record<string, string | number>
|
userData: Record<string, string | number>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ContainerModel implements IContainerModel {
|
export class ContainerModel implements IContainerModel {
|
||||||
public children: IContainerModel[];
|
public children: IContainerModel[];
|
||||||
public parent: IContainerModel | null;
|
public parent: IContainerModel | null;
|
||||||
public properties: Properties;
|
public properties: IProperties;
|
||||||
public userData: Record<string, string | number>;
|
public userData: Record<string, string | number>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
parent: IContainerModel | null,
|
parent: IContainerModel | null,
|
||||||
properties: Properties,
|
properties: IProperties,
|
||||||
children: IContainerModel[] = [],
|
children: IContainerModel[] = [],
|
||||||
userData = {}) {
|
userData = {}) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
8
src/Interfaces/IEditorState.tsx
Normal file
8
src/Interfaces/IEditorState.tsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { IConfiguration } from './IConfiguration';
|
||||||
|
import { IHistoryState } from './IHistoryState';
|
||||||
|
|
||||||
|
export interface IEditorState {
|
||||||
|
history: IHistoryState[]
|
||||||
|
historyCurrentStep: number
|
||||||
|
configuration: IConfiguration
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { IContainerModel } from './ContainerModel';
|
import { IContainerModel } from './IContainerModel';
|
||||||
|
|
||||||
export interface HistoryState {
|
export interface IHistoryState {
|
||||||
LastAction: string
|
LastAction: string
|
||||||
MainContainer: IContainerModel
|
MainContainer: IContainerModel
|
||||||
SelectedContainer: IContainerModel | null
|
SelectedContainer: IContainerModel | null
|
|
@ -1,5 +1,5 @@
|
||||||
/** Model of an image with multiple source */
|
/** Model of an image with multiple source */
|
||||||
export interface Image {
|
export interface IImage {
|
||||||
Name: string
|
Name: string
|
||||||
Url: string
|
Url: string
|
||||||
Base64Image: string
|
Base64Image: string
|
|
@ -1,4 +1,4 @@
|
||||||
export interface Point {
|
export interface IPoint {
|
||||||
x: number
|
x: number
|
||||||
y: number
|
y: number
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { XPositionReference } from '../Enums/XPositionReference';
|
import { XPositionReference } from '../Enums/XPositionReference';
|
||||||
|
|
||||||
export default interface Properties extends React.CSSProperties {
|
export default interface IProperties extends React.CSSProperties {
|
||||||
id: string
|
id: string
|
||||||
parentId: string | null
|
parentId: string | null
|
||||||
x: number
|
x: number
|
|
@ -3,7 +3,7 @@
|
||||||
* x being the address where the pointer is pointing
|
* x being the address where the pointer is pointing
|
||||||
* width being the overall (un)allocated space affected to the address
|
* width being the overall (un)allocated space affected to the address
|
||||||
*/
|
*/
|
||||||
export interface SizePointer {
|
export interface ISizePointer {
|
||||||
x: number
|
x: number
|
||||||
width: number
|
width: number
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import { Configuration } from '../Interfaces/Configuration';
|
import { IConfiguration } from '../Interfaces/IConfiguration';
|
||||||
import Properties from '../Interfaces/Properties';
|
import IProperties from '../Interfaces/IProperties';
|
||||||
|
|
||||||
export const DEFAULT_CONFIG: Configuration = {
|
export const DEFAULT_CONFIG: IConfiguration = {
|
||||||
AvailableContainers: [
|
AvailableContainers: [
|
||||||
{
|
{
|
||||||
Type: 'Container',
|
Type: 'Container',
|
||||||
|
@ -25,7 +25,7 @@ export const DEFAULT_CONFIG: Configuration = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_MAINCONTAINER_PROPS: Properties = {
|
export const DEFAULT_MAINCONTAINER_PROPS: IProperties = {
|
||||||
id: 'main',
|
id: 'main',
|
||||||
parentId: 'null',
|
parentId: 'null',
|
||||||
x: 0,
|
x: 0,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { IContainerModel } from '../Interfaces/ContainerModel';
|
import { IContainerModel } from '../Interfaces/IContainerModel';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Generator iterating of over the children depth-first
|
* Returns a Generator iterating of over the children depth-first
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { findContainerById, MakeIterator } from './itertools';
|
import { findContainerById, MakeIterator } from './itertools';
|
||||||
import { IEditorState } from '../Components/Editor/Editor';
|
import { IEditorState } from '../Interfaces/IEditorState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revive the Editor state
|
* Revive the Editor state
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue