diff --git a/.eslintrc.cjs b/.eslintrc.cjs index da73f62..153ac9f 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,12 +1,10 @@ -// TODO: https://eslint.org/docs/latest/rules/func-names -// TODO: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/naming-convention.md - module.exports = { env: { browser: true, es2021: true }, extends: [ + 'only-warn', 'plugin:react/recommended', 'standard-with-typescript' ], @@ -20,22 +18,67 @@ module.exports = { project: './tsconfig.json' }, plugins: [ - 'only-warn', 'react', 'react-hooks', '@typescript-eslint' ], rules: { + 'prefer-arrow-callback': 'error', + 'func-style': ['error', 'declaration'], 'space-before-function-paren': ['error', 'never'], - '@typescript-eslint/space-before-function-paren': ['error', 'never'], + + // Import/export + 'import/no-default-export': 'error', + + // Typescript overload indent: 'off', - '@typescript-eslint/indent': ['warn', 2, {SwitchCase: 1}], semi: 'off', - '@typescript-eslint/semi': ['warn', 'always'], + "camelcase": "off", 'no-unused-vars': 'off', + + // Typescript + '@typescript-eslint/space-before-function-paren': ['error', 'never'], + '@typescript-eslint/indent': ['warn', 2, {SwitchCase: 1}], + '@typescript-eslint/semi': ['warn', 'always'], '@typescript-eslint/no-unused-vars': 'error', '@typescript-eslint/ban-types': ['error'], '@typescript-eslint/no-floating-promises': 'off', // disabled cuz troublesome for SweetAlert since they never reject + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "default", + "format": ["camelCase"] + }, + { + 'selector': 'function', + 'format': ['PascalCase'] + }, + { + "selector": "variable", + "format": ["camelCase", "UPPER_CASE"] + }, + { + "selector": "parameter", + "format": ["camelCase"], + "leadingUnderscore": "allow" + }, + { + 'selector': ['enumMember', 'enum'], + 'format': ['PascalCase'] + }, + { + "selector": "memberLike", + "modifiers": ["private"], + "format": ["camelCase"], + "leadingUnderscore": "require" + }, + { + "selector": ['typeLike'], + "format": ["PascalCase"], + } + ], + + // React 'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks 'react-hooks/exhaustive-deps': 'warn' // Checks effect dependencies } diff --git a/src/Components/API/api.test.tsx b/src/Components/API/api.test.tsx index 55d63b1..1005d35 100644 --- a/src/Components/API/api.test.tsx +++ b/src/Components/API/api.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { fetchConfiguration } from './api'; +import { FetchConfiguration } from './api'; describe.concurrent('API test', () => { it('Load environment', () => { @@ -8,7 +8,7 @@ describe.concurrent('API test', () => { }); it('Fetch configuration', async() => { - const configuration = await fetchConfiguration(); + const configuration = await FetchConfiguration(); expect(configuration.MainContainer).toBeDefined(); expect(configuration.MainContainer.Height).toBeGreaterThan(0); expect(configuration.MainContainer.Width).toBeGreaterThan(0); diff --git a/src/Components/API/api.ts b/src/Components/API/api.ts index 9acea24..635b01b 100644 --- a/src/Components/API/api.ts +++ b/src/Components/API/api.ts @@ -4,7 +4,7 @@ import { IConfiguration } from '../../Interfaces/IConfiguration'; * Fetch the configuration from the API * @returns {Configation} The model of the configuration for the application */ -export async function fetchConfiguration(): Promise { +export async function FetchConfiguration(): Promise { const url = `${import.meta.env.VITE_API_URL}`; // The test library cannot use the Fetch API // @ts-expect-error diff --git a/src/Components/App/Actions/MenuActions.ts b/src/Components/App/Actions/MenuActions.ts index 4698e29..6bbf751 100644 --- a/src/Components/App/Actions/MenuActions.ts +++ b/src/Components/App/Actions/MenuActions.ts @@ -1,6 +1,6 @@ import { Dispatch, SetStateAction } from 'react'; import { IConfiguration } from '../../../Interfaces/IConfiguration'; -import { fetchConfiguration } from '../../API/api'; +import { FetchConfiguration } from '../../API/api'; import { IEditorState } from '../../../Interfaces/IEditorState'; import { LoadState } from './Load'; import { GetDefaultEditorState } from '../../../utils/default'; @@ -10,7 +10,7 @@ export function NewEditor( setLoaded: Dispatch> ): void { // Fetch the configuration from the API - fetchConfiguration() + FetchConfiguration() .then((configuration: IConfiguration) => { // Set the editor from the given properties of the API const editorState: IEditorState = GetDefaultEditorState(configuration); diff --git a/src/Components/App/App.tsx b/src/Components/App/App.tsx index 4bec5af..b0a2374 100644 --- a/src/Components/App/App.tsx +++ b/src/Components/App/App.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useState } from 'react'; +import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; import './App.scss'; import { MainMenu } from '../MainMenu/MainMenu'; import { ContainerModel } from '../../Interfaces/IContainerModel'; -import Editor from '../Editor/Editor'; +import { Editor } from '../Editor/Editor'; import { IEditorState } from '../../Interfaces/IEditorState'; import { LoadState } from './Actions/Load'; import { LoadEditor, NewEditor } from './Actions/MenuActions'; @@ -14,28 +14,11 @@ interface IAppProps { root: Element | Document } -export const App: React.FunctionComponent = (props) => { - const [isLoaded, setLoaded] = useState(false); - - const defaultMainContainer = new ContainerModel( - null, - DEFAULT_MAINCONTAINER_PROPS - ); - - const [editorState, setEditorState] = useState({ - configuration: DEFAULT_CONFIG, - history: [{ - LastAction: '', - MainContainer: defaultMainContainer, - SelectedContainerId: defaultMainContainer.properties.id, - TypeCounters: {}, - Symbols: new Map(), - SelectedSymbolId: '' - }], - historyCurrentStep: 0 - }); - - // TODO: move this into a variable +function UseHTTPGETStatePreloading( + isLoaded: boolean, + setEditorState: Dispatch>, + setLoaded: Dispatch> +): void { useEffect(() => { const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); @@ -56,6 +39,30 @@ export const App: React.FunctionComponent = (props) => { }, (error) => { throw new Error(error); }); } }); +}; + +export function App(props: IAppProps): JSX.Element { + const [isLoaded, setLoaded] = useState(false); + + const defaultMainContainer = new ContainerModel( + null, + DEFAULT_MAINCONTAINER_PROPS + ); + + const [editorState, setEditorState] = useState({ + configuration: DEFAULT_CONFIG, + history: [{ + lastAction: '', + mainContainer: defaultMainContainer, + selectedContainerId: defaultMainContainer.properties.id, + typeCounters: {}, + symbols: new Map(), + selectedSymbolId: '' + }], + historyCurrentStep: 0 + }); + + UseHTTPGETStatePreloading(isLoaded, setEditorState, setLoaded); if (isLoaded) { return ( diff --git a/src/Components/Bar/Bar.tsx b/src/Components/Bar/Bar.tsx index 22c617a..a8ed605 100644 --- a/src/Components/Bar/Bar.tsx +++ b/src/Components/Bar/Bar.tsx @@ -1,4 +1,4 @@ -import { ClockIcon, CubeIcon, LinkIcon, MapIcon } from '@heroicons/react/outline'; +import { ClockIcon, CubeIcon, LinkIcon } from '@heroicons/react/outline'; import * as React from 'react'; import { BarIcon } from './BarIcon'; @@ -7,34 +7,34 @@ interface IBarProps { isSymbolsOpen: boolean isElementsSidebarOpen: boolean isHistoryOpen: boolean - ToggleSidebar: () => void - ToggleSymbols: () => void - ToggleTimeline: () => void + toggleSidebar: () => void + toggleSymbols: () => void + toggleTimeline: () => void } export const BAR_WIDTH = 64; // 4rem -export const Bar: React.FC = (props) => { +export function Bar(props: IBarProps): JSX.Element { return (
props.ToggleSidebar()}> - + onClick={() => props.toggleSidebar()}> + props.ToggleSymbols()}> - + onClick={() => props.toggleSymbols()}> + props.ToggleTimeline()}> - + onClick={() => props.toggleTimeline()}> +
); -}; +} diff --git a/src/Components/Bar/BarIcon.tsx b/src/Components/Bar/BarIcon.tsx index 280044c..9324dc0 100644 --- a/src/Components/Bar/BarIcon.tsx +++ b/src/Components/Bar/BarIcon.tsx @@ -7,7 +7,7 @@ interface IBarIconProps { onClick: () => void } -export const BarIcon: React.FC = (props) => { +export function BarIcon(props: IBarIconProps): JSX.Element { const isActiveClasses = props.isActive ? 'border-l-4 border-blue-500 bg-slate-200' : ''; return ( ); -}; +} diff --git a/src/Components/ContainerProperties/ContainerForm.tsx b/src/Components/ContainerProperties/ContainerForm.tsx index 0feae02..711d3b2 100644 --- a/src/Components/ContainerProperties/ContainerForm.tsx +++ b/src/Components/ContainerProperties/ContainerForm.tsx @@ -2,9 +2,9 @@ import { MenuAlt2Icon, MenuAlt3Icon, MenuIcon } from '@heroicons/react/outline'; import * as React from 'react'; import { PropertyType } from '../../Enums/PropertyType'; import { XPositionReference } from '../../Enums/XPositionReference'; -import IContainerProperties from '../../Interfaces/IContainerProperties'; +import { IContainerProperties } from '../../Interfaces/IContainerProperties'; import { ISymbolModel } from '../../Interfaces/ISymbolModel'; -import { ApplyWidthMargin, ApplyXMargin, RemoveWidthMargin, RemoveXMargin, restoreX, transformX } from '../../utils/svg'; +import { ApplyWidthMargin, ApplyXMargin, RemoveWidthMargin, RemoveXMargin, RestoreX, TransformX } from '../../utils/svg'; import { InputGroup } from '../InputGroup/InputGroup'; import { RadioGroupButtons } from '../RadioGroupButtons/RadioGroupButtons'; import { Select } from '../Select/Select'; @@ -15,10 +15,8 @@ interface IContainerFormProps { onChange: (key: string, value: string | number | boolean, type?: PropertyType) => void } -const getCSSInputs = ( - properties: IContainerProperties, - onChange: (key: string, value: string | number | boolean, type: PropertyType) => void -): JSX.Element[] => { +function GetCSSInputs(properties: IContainerProperties, + onChange: (key: string, value: string | number | boolean, type: PropertyType) => void): JSX.Element[] { const groupInput: JSX.Element[] = []; for (const key in properties.style) { groupInput.push( onChange(key, event.target.value, PropertyType.STYLE)} - />); + onChange={(event) => onChange(key, event.target.value, PropertyType.Style)} />); } return groupInput; -}; +} -const ContainerForm: React.FunctionComponent = (props) => { +export function ContainerForm(props: IContainerFormProps): JSX.Element { return (
= (props) => { inputClassName='' type='string' value={props.properties.id.toString()} - isDisabled={true} - /> + isDisabled={true} /> = (props) => { inputClassName='' type='string' value={props.properties.parentId} - isDisabled={true} - /> + isDisabled={true} /> = (props) => { inputClassName='' type='string' value={props.properties.type} - isDisabled={true} - /> + isDisabled={true} /> = (props) => { inputClassName='' type='string' value={props.properties.displayedText?.toString()} - onChange={(event) => props.onChange('displayedText', event.target.value)} - /> + onChange={(event) => props.onChange('displayedText', event.target.value)} /> = (props) => { inputClassName='' type='number' isDisabled={props.properties.linkedSymbolId !== ''} - value={transformX(RemoveXMargin(props.properties.x, props.properties.margin.left), props.properties.width, props.properties.XPositionReference).toString()} + value={TransformX(RemoveXMargin(props.properties.x, props.properties.margin.left), props.properties.width, props.properties.xPositionReference).toString()} onChange={(event) => props.onChange( 'x', ApplyXMargin( - restoreX( + RestoreX( Number(event.target.value), props.properties.width, - props.properties.XPositionReference + props.properties.xPositionReference ), props.properties.margin.left ) - )} - /> + )} /> = (props) => { inputClassName='' type='number' value={(props.properties.y - (props.properties.margin?.top ?? 0)).toString()} - onChange={(event) => props.onChange('y', Number(event.target.value) + (props.properties.margin?.top ?? 0))} - /> + onChange={(event) => props.onChange('y', Number(event.target.value) + (props.properties.margin?.top ?? 0))} /> = (props) => { type='number' min={1} value={props.properties.minWidth.toString()} - onChange={(event) => props.onChange('minWidth', Number(event.target.value))} - /> + onChange={(event) => props.onChange('minWidth', Number(event.target.value))} /> = (props) => { type='number' min={1} value={props.properties.maxWidth.toString()} - onChange={(event) => props.onChange('maxWidth', Number(event.target.value))} - /> + onChange={(event) => props.onChange('maxWidth', Number(event.target.value))} /> = (props) => { min={props.properties.minWidth} value={(RemoveWidthMargin(props.properties.width, props.properties.margin.left, props.properties.margin.right)).toString()} onChange={(event) => props.onChange('width', ApplyWidthMargin(Number(event.target.value), props.properties.margin.left, props.properties.margin.right))} - isDisabled={props.properties.isFlex} - /> + isDisabled={props.properties.isFlex} /> = (props) => { type='number' min={0} value={(props.properties.height + (props.properties.margin?.top ?? 0) + (props.properties.margin?.bottom ?? 0)).toString()} - onChange={(event) => props.onChange('height', Number(event.target.value) - (props.properties.margin?.top ?? 0) - (props.properties.margin?.bottom ?? 0))} - /> + onChange={(event) => props.onChange('height', Number(event.target.value) - (props.properties.margin?.top ?? 0) - (props.properties.margin?.bottom ?? 0))} /> = (props) => { type='number' min={0} value={(props.properties.margin.left ?? 0).toString()} - onChange={(event) => props.onChange('left', Number(event.target.value), PropertyType.MARGIN)} - /> + onChange={(event) => props.onChange('left', Number(event.target.value), PropertyType.Margin)} /> = (props) => { type='number' min={0} value={(props.properties.margin.bottom ?? 0).toString()} - onChange={(event) => props.onChange('bottom', Number(event.target.value), PropertyType.MARGIN)} - /> + onChange={(event) => props.onChange('bottom', Number(event.target.value), PropertyType.Margin)} /> = (props) => { type='number' min={0} value={(props.properties.margin.top ?? 0).toString()} - onChange={(event) => props.onChange('top', Number(event.target.value), PropertyType.MARGIN)} - /> + onChange={(event) => props.onChange('top', Number(event.target.value), PropertyType.Margin)} /> = (props) => { type='number' min={0} value={(props.properties.margin.right ?? 0).toString()} - onChange={(event) => props.onChange('right', Number(event.target.value), PropertyType.MARGIN)} - /> + onChange={(event) => props.onChange('right', Number(event.target.value), PropertyType.Margin)} /> = (props) => { inputClassName='' type='checkbox' checked={props.properties.isFlex} - onChange={(event) => props.onChange('isFlex', event.target.checked)} - /> + onChange={(event) => props.onChange('isFlex', event.target.checked)} /> = (props) => { inputClassName='' type='checkbox' checked={props.properties.isAnchor} - onChange={(event) => props.onChange('isAnchor', event.target.checked)} - /> + onChange={(event) => props.onChange('isAnchor', event.target.checked)} /> = (props) => { value: XPositionReference.Right.toString() } ]} - onChange={(event) => props.onChange('XPositionReference', Number(event.target.value))} - /> + onChange={(event) => props.onChange('xPositionReference', Number(event.target.value))} /> = (props) => defaultChecked={props.defaultChecked} onChange={props.onChange} min={props.min} - disabled={props.isDisabled} - /> + disabled={props.isDisabled} /> ; -}; +} diff --git a/src/Components/MainMenu/MainMenu.tsx b/src/Components/MainMenu/MainMenu.tsx index c9fc0f1..7744ee1 100644 --- a/src/Components/MainMenu/MainMenu.tsx +++ b/src/Components/MainMenu/MainMenu.tsx @@ -6,14 +6,14 @@ interface IMainMenuProps { } enum WindowState { - MAIN, - LOAD, + Main, + Load, } -export const MainMenu: React.FC = (props) => { - const [windowState, setWindowState] = React.useState(WindowState.MAIN); +export function MainMenu(props: IMainMenuProps): JSX.Element { + const [windowState, setWindowState] = React.useState(WindowState.Main); switch (windowState) { - case WindowState.LOAD: + case WindowState.Load: return (
@@ -37,7 +37,7 @@ export const MainMenu: React.FC = (props) => { - +
); } diff --git a/src/Components/Menu/Menu.tsx b/src/Components/Menu/Menu.tsx index fde491f..53a5d0b 100644 --- a/src/Components/Menu/Menu.tsx +++ b/src/Components/Menu/Menu.tsx @@ -8,7 +8,7 @@ interface IMenuProps { children: React.ReactNode[] | React.ReactNode } -export const Menu: React.FC = (props) => { +export function Menu(props: IMenuProps): JSX.Element { const visible = props.isOpen ? 'visible opacity-1' : 'invisible opacity-0'; return (
= (props) => { left: props.x, top: props.y }}> - { props.children } + {props.children}
); -}; +} diff --git a/src/Components/Menu/MenuItem.tsx b/src/Components/Menu/MenuItem.tsx index 605c7c8..062ff56 100644 --- a/src/Components/Menu/MenuItem.tsx +++ b/src/Components/Menu/MenuItem.tsx @@ -6,11 +6,11 @@ interface IMenuItemProps { onClick: () => void } -export const MenuItem: React.FC = (props) => { +export function MenuItem(props: IMenuItemProps): JSX.Element { return ( ); -}; +} diff --git a/src/Components/RadioGroupButtons/RadioGroupButtons.tsx b/src/Components/RadioGroupButtons/RadioGroupButtons.tsx index 8fb620b..3288779 100644 --- a/src/Components/RadioGroupButtons/RadioGroupButtons.tsx +++ b/src/Components/RadioGroupButtons/RadioGroupButtons.tsx @@ -11,7 +11,7 @@ interface IRadioGroupButtonsProps { onChange?: (event: React.ChangeEvent) => void } -export const RadioGroupButtons: React.FunctionComponent = (props) => { +export function RadioGroupButtons(props: IRadioGroupButtonsProps): JSX.Element { let inputGroups; if (props.value !== undefined) { // dynamic @@ -24,8 +24,7 @@ export const RadioGroupButtons: React.FunctionComponent className={`peer m-2 ${props.inputClassName}`} value={inputGroup.value} checked={props.value === inputGroup.value} - onChange={props.onChange} - /> + onChange={props.onChange} /> @@ -42,8 +41,7 @@ export const RadioGroupButtons: React.FunctionComponent name={props.name} className={`peer m-2 ${props.inputClassName}`} value={inputGroup.value} - defaultChecked={props.defaultValue === inputGroup.value} - /> + defaultChecked={props.defaultValue === inputGroup.value} /> @@ -59,8 +57,8 @@ export const RadioGroupButtons: React.FunctionComponent
- { inputGroups } + {inputGroups}
); -}; +} diff --git a/src/Components/SVG/Elements/Container.tsx b/src/Components/SVG/Elements/Container.tsx index b13630d..6a3ba01 100644 --- a/src/Components/SVG/Elements/Container.tsx +++ b/src/Components/SVG/Elements/Container.tsx @@ -2,11 +2,11 @@ import * as React from 'react'; import { Interweave, Node } from 'interweave'; import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { DIMENSION_MARGIN, SHOW_CHILDREN_DIMENSIONS, SHOW_PARENT_DIMENSION, SHOW_TEXT } from '../../../utils/default'; -import { getDepth } from '../../../utils/itertools'; +import { GetDepth } from '../../../utils/itertools'; import { Dimension } from './Dimension'; -import IContainerProperties from '../../../Interfaces/IContainerProperties'; -import { transformX } from '../../../utils/svg'; -import { camelize } from '../../../utils/stringtools'; +import { IContainerProperties } from '../../../Interfaces/IContainerProperties'; +import { TransformX } from '../../../utils/svg'; +import { Camelize } from '../../../utils/stringtools'; interface IContainerProps { model: IContainerModel @@ -16,7 +16,7 @@ interface IContainerProps { * Render the container * @returns Render the container */ -export const Container: React.FC = (props: IContainerProps) => { +export function Container(props: IContainerProps): JSX.Element { const containersElements = props.model.children.map(child => ); const width: number = props.model.properties.width; @@ -52,7 +52,7 @@ export const Container: React.FC = (props: IContainerProps) => > ); // Dimension props - const depth = getDepth(props.model); + const depth = GetDepth(props.model); const dimensionMargin = DIMENSION_MARGIN * depth; const id = `dim-${props.model.properties.id}`; const xStart: number = 0; @@ -64,11 +64,7 @@ export const Container: React.FC = (props: IContainerProps) => let dimensionChildren: JSX.Element | null = null; if (props.model.children.length > 1 && SHOW_CHILDREN_DIMENSIONS) { const { - childrenId, - xChildrenStart, - xChildrenEnd, - yChildren, - textChildren + childrenId, xChildrenStart, xChildrenEnd, yChildren, textChildren } = GetChildrenDimensionProps(props, dimensionMargin); dimensionChildren = = (props: IContainerProps) => yStart={yChildren} yEnd={yChildren} strokeWidth={strokeWidth} - text={textChildren} - />; + text={textChildren} />; } return ( @@ -95,10 +90,8 @@ export const Container: React.FC = (props: IContainerProps) => yStart={yDim} yEnd={yDim} strokeWidth={strokeWidth} - text={text} - /> - : null - } + text={text} /> + : null} {dimensionChildren} {svg} {SHOW_TEXT @@ -112,23 +105,23 @@ export const Container: React.FC = (props: IContainerProps) => {containersElements} ); -}; +} function GetChildrenDimensionProps(props: IContainerProps, dimensionMargin: number): { childrenId: string, xChildrenStart: number, xChildrenEnd: number, yChildren: number, textChildren: string } { const childrenId = `dim-children-${props.model.properties.id}`; const lastChild = props.model.children[props.model.children.length - 1]; - let xChildrenStart = transformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.XPositionReference); - let xChildrenEnd = transformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.XPositionReference); + let xChildrenStart = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.xPositionReference); + let xChildrenEnd = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.xPositionReference); // Find the min and max for (let i = props.model.children.length - 2; i >= 0; i--) { const child = props.model.children[i]; - const left = transformX(child.properties.x, child.properties.width, child.properties.XPositionReference); + const left = TransformX(child.properties.x, child.properties.width, child.properties.xPositionReference); if (left < xChildrenStart) { xChildrenStart = left; } - const right = transformX(child.properties.x, child.properties.width, child.properties.XPositionReference); + const right = TransformX(child.properties.x, child.properties.width, child.properties.xPositionReference); if (right > xChildrenEnd) { xChildrenEnd = right; } @@ -145,11 +138,11 @@ function CreateReactCustomSVG(customSVG: string, props: IContainerProperties): R disableLineBreaks={true} content={customSVG} allowElements={true} - transform={(node, children) => transform(node, children, props)} + transform={(node, children) => Transform(node, children, props)} />; } -function transform(node: HTMLElement, children: Node[], props: IContainerProperties): React.ReactNode { +function Transform(node: HTMLElement, children: Node[], props: IContainerProperties): React.ReactNode { const supportedTags = ['line', 'path', 'rect']; if (supportedTags.includes(node.tagName.toLowerCase())) { const attributes: { [att: string]: string | object | null } = {}; @@ -170,7 +163,7 @@ function transform(node: HTMLElement, children: Node[], props: IContainerPropert const prop = Object.entries(props.userData).find(([key]) => `{${key}}` === userDataKey); if (prop !== undefined) { - attributes[camelize(attName)] = prop[1]; + attributes[Camelize(attName)] = prop[1]; return; } } @@ -179,16 +172,16 @@ function transform(node: HTMLElement, children: Node[], props: IContainerPropert // support for object const stringObject = attributeValue.slice(1, -1); const object: JSON = JSON.parse(stringObject); - attributes[camelize(attName)] = object; + attributes[Camelize(attName)] = object; return; } const prop = Object.entries(props).find(([key]) => `{${key}}` === attributeValue); if (prop !== undefined) { - attributes[camelize(attName)] = prop[1]; + attributes[Camelize(attName)] = prop[1]; return; } - attributes[camelize(attName)] = attributeValue; + attributes[Camelize(attName)] = attributeValue; }); return React.createElement(node.tagName.toLowerCase(), attributes, children); } diff --git a/src/Components/SVG/Elements/DepthDimensionLayer.tsx b/src/Components/SVG/Elements/DepthDimensionLayer.tsx index cbf7eeb..652c04a 100644 --- a/src/Components/SVG/Elements/DepthDimensionLayer.tsx +++ b/src/Components/SVG/Elements/DepthDimensionLayer.tsx @@ -1,15 +1,15 @@ import * as React from 'react'; import { ContainerModel } from '../../../Interfaces/IContainerModel'; import { DIMENSION_MARGIN } from '../../../utils/default'; -import { getAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools'; -import { transformX } from '../../../utils/svg'; +import { GetAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools'; +import { TransformX } from '../../../utils/svg'; import { Dimension } from './Dimension'; interface IDimensionLayerProps { roots: ContainerModel | ContainerModel[] | null } -const getDimensionsNodes = (root: ContainerModel): React.ReactNode[] => { +function GetDimensionsNodes(root: ContainerModel): React.ReactNode[] { const it = MakeBFSIterator(root); const dimensions: React.ReactNode[] = []; let currentDepth = 0; @@ -25,8 +25,8 @@ const getDimensionsNodes = (root: ContainerModel): React.ReactNode[] => { max = -Infinity; } - const absoluteX = getAbsolutePosition(container)[0]; - const x = transformX(absoluteX, container.properties.width, container.properties.XPositionReference); + const absoluteX = GetAbsolutePosition(container)[0]; + const x = TransformX(absoluteX, container.properties.width, container.properties.xPositionReference); lastY = container.properties.y + container.properties.height; if (x < min) { min = x; @@ -40,28 +40,28 @@ const getDimensionsNodes = (root: ContainerModel): React.ReactNode[] => { AddNewDimension(currentDepth, min, max, lastY, dimensions); return dimensions; -}; +} /** * A layer containing all dimension * @param props * @returns */ -export const DepthDimensionLayer: React.FC = (props: IDimensionLayerProps) => { +export function DepthDimensionLayer(props: IDimensionLayerProps): JSX.Element { let dimensions: React.ReactNode[] = []; if (Array.isArray(props.roots)) { props.roots.forEach(child => { - dimensions.concat(getDimensionsNodes(child)); + dimensions.concat(GetDimensionsNodes(child)); }); } else if (props.roots !== null) { - dimensions = getDimensionsNodes(props.roots); + dimensions = GetDimensionsNodes(props.roots); } return ( - { dimensions } + {dimensions} ); -}; +} function AddNewDimension(currentDepth: number, min: number, max: number, lastY: number, dimensions: React.ReactNode[]): void { const id = `dim-depth-${currentDepth}`; diff --git a/src/Components/SVG/Elements/Dimension.tsx b/src/Components/SVG/Elements/Dimension.tsx index c5f6f86..cf8876c 100644 --- a/src/Components/SVG/Elements/Dimension.tsx +++ b/src/Components/SVG/Elements/Dimension.tsx @@ -20,9 +20,11 @@ interface IDimensionProps { * @param vx Transform vector * @returns Returns a new coordinate from the origin coordinate */ -const applyParametric = (x0: number, t: number, vx: number): number => x0 + t * vx; +function ApplyParametric(x0: number, t: number, vx: number): number { + return x0 + t * vx; +} -export const Dimension: React.FC = (props: IDimensionProps) => { +export function Dimension(props: IDimensionProps): JSX.Element { const style: React.CSSProperties = { stroke: 'black' }; @@ -39,15 +41,15 @@ export const Dimension: React.FC = (props: IDimensionProps) => const [perpVecX, perpVecY] = [unitY, -unitX]; // Use the parametric function to get the coordinates (x = x0 + t * v.x) - const startTopX = applyParametric(props.xStart, NOTCHES_LENGTH, perpVecX); - const startTopY = applyParametric(props.yStart, NOTCHES_LENGTH, perpVecY); - const startBottomX = applyParametric(props.xStart, -NOTCHES_LENGTH, perpVecX); - const startBottomY = applyParametric(props.yStart, -NOTCHES_LENGTH, perpVecY); + const startTopX = ApplyParametric(props.xStart, NOTCHES_LENGTH, perpVecX); + const startTopY = ApplyParametric(props.yStart, NOTCHES_LENGTH, perpVecY); + const startBottomX = ApplyParametric(props.xStart, -NOTCHES_LENGTH, perpVecX); + const startBottomY = ApplyParametric(props.yStart, -NOTCHES_LENGTH, perpVecY); - const endTopX = applyParametric(props.xEnd, NOTCHES_LENGTH, perpVecX); - const endTopY = applyParametric(props.yEnd, NOTCHES_LENGTH, perpVecY); - const endBottomX = applyParametric(props.xEnd, -NOTCHES_LENGTH, perpVecX); - const endBottomY = applyParametric(props.yEnd, -NOTCHES_LENGTH, perpVecY); + const endTopX = ApplyParametric(props.xEnd, NOTCHES_LENGTH, perpVecX); + const endTopY = ApplyParametric(props.yEnd, NOTCHES_LENGTH, perpVecY); + const endBottomX = ApplyParametric(props.xEnd, -NOTCHES_LENGTH, perpVecX); + const endBottomY = ApplyParametric(props.yEnd, -NOTCHES_LENGTH, perpVecY); return ( @@ -57,24 +59,21 @@ export const Dimension: React.FC = (props: IDimensionProps) => x2={startBottomX} y2={startBottomY} strokeWidth={props.strokeWidth} - style={style} - /> + style={style} /> + style={style} /> + style={style} /> = (props: IDimensionProps) => ); -}; +} diff --git a/src/Components/SVG/Elements/DimensionLayer.tsx b/src/Components/SVG/Elements/DimensionLayer.tsx index 23d719e..d6f2b82 100644 --- a/src/Components/SVG/Elements/DimensionLayer.tsx +++ b/src/Components/SVG/Elements/DimensionLayer.tsx @@ -1,20 +1,20 @@ import * as React from 'react'; import { ContainerModel } from '../../../Interfaces/IContainerModel'; import { DIMENSION_MARGIN } from '../../../utils/default'; -import { getAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools'; +import { GetAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools'; import { Dimension } from './Dimension'; interface IDimensionLayerProps { roots: ContainerModel | ContainerModel[] | null } -const getDimensionsNodes = (root: ContainerModel): React.ReactNode[] => { +function GetDimensionsNodes(root: ContainerModel): React.ReactNode[] { const it = MakeBFSIterator(root); const dimensions: React.ReactNode[] = []; for (const { container, depth } of it) { const width = container.properties.width; const id = `dim-${container.properties.id}`; - const xStart = getAbsolutePosition(container)[0]; + const xStart = GetAbsolutePosition(container)[0]; const xEnd = xStart + width; const y = (container.properties.y + container.properties.height) + (DIMENSION_MARGIN * (depth + 1)); const strokeWidth = 1; @@ -28,30 +28,29 @@ const getDimensionsNodes = (root: ContainerModel): React.ReactNode[] => { xEnd={xEnd} yEnd={y} strokeWidth={strokeWidth} - text={text} - /> + text={text} /> ); } return dimensions; -}; +} /** * A layer containing all dimension * @param props * @returns */ -export const DimensionLayer: React.FC = (props: IDimensionLayerProps) => { +export function DimensionLayer(props: IDimensionLayerProps): JSX.Element { let dimensions: React.ReactNode[] = []; if (Array.isArray(props.roots)) { props.roots.forEach(child => { - dimensions.concat(getDimensionsNodes(child)); + dimensions.concat(GetDimensionsNodes(child)); }); } else if (props.roots !== null) { - dimensions = getDimensionsNodes(props.roots); + dimensions = GetDimensionsNodes(props.roots); } return ( - { dimensions } + {dimensions} ); -}; +} diff --git a/src/Components/SVG/Elements/Selector/Selector.tsx b/src/Components/SVG/Elements/Selector/Selector.tsx index d8fbef5..0c2a491 100644 --- a/src/Components/SVG/Elements/Selector/Selector.tsx +++ b/src/Components/SVG/Elements/Selector/Selector.tsx @@ -2,14 +2,14 @@ import './Selector.scss'; import * as React from 'react'; import { IContainerModel } from '../../../../Interfaces/IContainerModel'; import { SHOW_SELECTOR_TEXT } from '../../../../utils/default'; -import { getAbsolutePosition } from '../../../../utils/itertools'; +import { GetAbsolutePosition } from '../../../../utils/itertools'; import { RemoveMargin } from '../../../../utils/svg'; interface ISelectorProps { selected?: IContainerModel } -export const Selector: React.FC = (props) => { +export function Selector(props: ISelectorProps): JSX.Element { if (props.selected === undefined || props.selected === null) { return ( @@ -17,7 +17,7 @@ export const Selector: React.FC = (props) => { ); } - let [x, y] = getAbsolutePosition(props.selected); + let [x, y] = GetAbsolutePosition(props.selected); let [width, height] = [ props.selected.properties.width, props.selected.properties.height @@ -34,7 +34,7 @@ export const Selector: React.FC = (props) => { const yText = y + height / 2; const style: React.CSSProperties = { - stroke: '#3B82F6', // tw blue-500 + stroke: '#3B82F6', strokeWidth: 4, fillOpacity: 0, transitionProperty: 'all', @@ -63,4 +63,4 @@ export const Selector: React.FC = (props) => { : null} ); -}; +} diff --git a/src/Components/SVG/Elements/Symbol.tsx b/src/Components/SVG/Elements/Symbol.tsx index 816526f..1d200cd 100644 --- a/src/Components/SVG/Elements/Symbol.tsx +++ b/src/Components/SVG/Elements/Symbol.tsx @@ -7,7 +7,7 @@ interface ISymbolProps { model: ISymbolModel } -export const Symbol: React.FC = (props) => { +export function Symbol(props: ISymbolProps): JSX.Element { const href = props.model.config.Image.Base64Image ?? props.model.config.Image.Url; const hasSVG = props.model.config.Image.Svg !== undefined && props.model.config.Image.Svg !== null; @@ -21,8 +21,7 @@ export const Symbol: React.FC = (props) => { noWrap={true} disableLineBreaks={true} content={props.model.config.Image.Svg} - allowElements={true} - /> + allowElements={true} /> ); } @@ -33,7 +32,6 @@ export const Symbol: React.FC = (props) => { x={props.model.x} y={-DIMENSION_MARGIN} height={props.model.height} - width={props.model.width} - /> + width={props.model.width} /> ); -}; +} diff --git a/src/Components/SVG/Elements/SymbolLayer.tsx b/src/Components/SVG/Elements/SymbolLayer.tsx index 0a18a31..cd9e47d 100644 --- a/src/Components/SVG/Elements/SymbolLayer.tsx +++ b/src/Components/SVG/Elements/SymbolLayer.tsx @@ -6,7 +6,7 @@ interface ISymbolLayerProps { symbols: Map } -export const SymbolLayer: React.FC = (props) => { +export function SymbolLayer(props: ISymbolLayerProps): JSX.Element { const symbols: JSX.Element[] = []; props.symbols.forEach((symbol) => { symbols.push( @@ -15,9 +15,7 @@ export const SymbolLayer: React.FC = (props) => { }); return ( - { - symbols - } + {symbols} ); -}; +} diff --git a/src/Components/SVG/SVG.tsx b/src/Components/SVG/SVG.tsx index d068ce9..e79827c 100644 --- a/src/Components/SVG/SVG.tsx +++ b/src/Components/SVG/SVG.tsx @@ -25,7 +25,7 @@ interface Viewer { export const ID = 'svg'; -function resizeViewBox( +function ResizeViewBox( setViewer: React.Dispatch> ): void { setViewer({ @@ -34,26 +34,28 @@ function resizeViewBox( }); } -function useSVGAutoResizer( +function UseSVGAutoResizer( setViewer: React.Dispatch> ): void { React.useEffect(() => { - const onResize = (): void => resizeViewBox(setViewer); - window.addEventListener('resize', onResize); + function OnResize(): void { + return ResizeViewBox(setViewer); + } + window.addEventListener('resize', OnResize); return () => { - window.removeEventListener('resize', onResize); + window.removeEventListener('resize', OnResize); }; }); } -export const SVG: React.FC = (props: ISVGProps) => { +export function SVG(props: ISVGProps): JSX.Element { const [viewer, setViewer] = React.useState({ viewerWidth: window.innerWidth - BAR_WIDTH, viewerHeight: window.innerHeight }); - useSVGAutoResizer(setViewer); + UseSVGAutoResizer(setViewer); const xmlns = ''; const properties = { @@ -64,9 +66,9 @@ export const SVG: React.FC = (props: ISVGProps) => { let children: React.ReactNode | React.ReactNode[] = []; if (Array.isArray(props.children)) { - children = props.children.map(child => ); + children = props.children.map(child => ); } else if (props.children !== null) { - children = ; + children = ; } return ( @@ -84,16 +86,14 @@ export const SVG: React.FC = (props: ISVGProps) => { }} > - { children } - { - SHOW_DIMENSIONS_PER_DEPTH - ? - : null - } + {children} + {SHOW_DIMENSIONS_PER_DEPTH + ? + : null} {/* leave this at the end so it can be removed during the svg export */}
); -}; +} diff --git a/src/Components/Select/Select.tsx b/src/Components/Select/Select.tsx index b2b499d..2a9f51b 100644 --- a/src/Components/Select/Select.tsx +++ b/src/Components/Select/Select.tsx @@ -19,7 +19,7 @@ const className = ` focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 disabled:bg-slate-300 disabled:text-gray-500 disabled:border-slate-300 disabled:shadow-none`; -export const Select: React.FC = (props) => { +export function Select(props: ISelectProps): JSX.Element { const options = [( )]; @@ -40,7 +40,7 @@ export const Select: React.FC = (props) => { className={`mt-4 text-xs font-medium text-gray-800 ${props.labelClassName}`} htmlFor={props.inputKey} > - { props.labelText } + {props.labelText} ); -}; +} diff --git a/src/Components/Sidebar/Sidebar.test.tsx b/src/Components/Sidebar/Sidebar.test.tsx index 886496e..591796d 100644 --- a/src/Components/Sidebar/Sidebar.test.tsx +++ b/src/Components/Sidebar/Sidebar.test.tsx @@ -31,15 +31,17 @@ describe.concurrent('Sidebar', () => { }); it('With stuff', () => { - const Type = 'stuff'; + const type = 'stuff'; const handleButtonClick = vi.fn(); render( void } -function handleDragStart(event: React.DragEvent): void { +function HandleDragStart(event: React.DragEvent): void { event.dataTransfer.setData('type', (event.target as HTMLButtonElement).id); } -export const Sidebar: React.FC = (props: ISidebarProps) => { +export function Sidebar(props: ISidebarProps): JSX.Element { const listElements = props.componentOptions.map(componentOption => ); diff --git a/src/Components/SymbolProperties/SymbolForm.tsx b/src/Components/SymbolProperties/SymbolForm.tsx index 01182d8..31c8315 100644 --- a/src/Components/SymbolProperties/SymbolForm.tsx +++ b/src/Components/SymbolProperties/SymbolForm.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { ISymbolModel } from '../../Interfaces/ISymbolModel'; -import { restoreX, transformX } from '../../utils/svg'; +import { RestoreX, TransformX } from '../../utils/svg'; import { InputGroup } from '../InputGroup/InputGroup'; interface ISymbolFormProps { @@ -8,7 +8,8 @@ interface ISymbolFormProps { symbols: Map onChange: (key: string, value: string | number | boolean) => void } -const SymbolForm: React.FunctionComponent = (props) => { + +export function SymbolForm(props: ISymbolFormProps): JSX.Element { return (
= (props) => { inputClassName='' type='string' value={props.symbol.id.toString()} - isDisabled={true} - /> + isDisabled={true} /> props.onChange('x', restoreX(Number(event.target.value), props.symbol.width, props.symbol.config.XPositionReference))} - /> + value={TransformX(props.symbol.x, props.symbol.width, props.symbol.config.XPositionReference).toString()} + onChange={(event) => props.onChange('x', RestoreX(Number(event.target.value), props.symbol.width, props.symbol.config.XPositionReference))} /> = (props) => { type='number' min={0} value={props.symbol.height.toString()} - onChange={(event) => props.onChange('height', Number(event.target.value))} - /> + onChange={(event) => props.onChange('height', Number(event.target.value))} /> = (props) => { type='number' min={0} value={props.symbol.width.toString()} - onChange={(event) => props.onChange('width', Number(event.target.value))} - /> + onChange={(event) => props.onChange('width', Number(event.target.value))} />
); -}; - -export default SymbolForm; +} diff --git a/src/Components/SymbolProperties/SymbolProperties.tsx b/src/Components/SymbolProperties/SymbolProperties.tsx index 5e65206..1dff3e5 100644 --- a/src/Components/SymbolProperties/SymbolProperties.tsx +++ b/src/Components/SymbolProperties/SymbolProperties.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { ISymbolModel } from '../../Interfaces/ISymbolModel'; -import SymbolForm from './SymbolForm'; +import { SymbolForm } from './SymbolForm'; interface ISymbolPropertiesProps { symbol?: ISymbolModel @@ -8,7 +8,7 @@ interface ISymbolPropertiesProps { onChange: (key: string, value: string | number | boolean) => void } -export const SymbolProperties: React.FC = (props: ISymbolPropertiesProps) => { +export function SymbolProperties(props: ISymbolPropertiesProps): JSX.Element { if (props.symbol === undefined) { return
; } @@ -18,8 +18,7 @@ export const SymbolProperties: React.FC = (props: ISymbo + onChange={props.onChange} /> ); -}; +} diff --git a/src/Components/Symbols/Symbols.tsx b/src/Components/Symbols/Symbols.tsx index 6fdbc06..3188d88 100644 --- a/src/Components/Symbols/Symbols.tsx +++ b/src/Components/Symbols/Symbols.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { IAvailableSymbol } from '../../Interfaces/IAvailableSymbol'; -import { truncateString } from '../../utils/stringtools'; +import { TruncateString } from '../../utils/stringtools'; interface ISymbolsProps { componentOptions: IAvailableSymbol[] @@ -8,11 +8,11 @@ interface ISymbolsProps { buttonOnClick: (type: string) => void } -function handleDragStart(event: React.DragEvent): void { +function HandleDragStart(event: React.DragEvent): void { event.dataTransfer.setData('type', (event.target as HTMLButtonElement).id); } -export const Symbols: React.FC = (props: ISymbolsProps) => { +export function Symbols(props: ISymbolsProps): JSX.Element { const listElements = props.componentOptions.map(componentOption => { if (componentOption.Image.Url !== undefined || componentOption.Image.Base64Image !== undefined) { const url = componentOption.Image.Base64Image ?? componentOption.Image.Url; @@ -23,7 +23,7 @@ export const Symbols: React.FC = (props: ISymbolsProps) => { title={componentOption.Name} onClick={() => props.buttonOnClick(componentOption.Name)} draggable={true} - onDragStart={(event) => handleDragStart(event)} + onDragStart={(event) => HandleDragStart(event)} >
= (props: ISymbolsProps) => { />
- {truncateString(componentOption.Name, 5)} + {TruncateString(componentOption.Name, 5)}
); } @@ -44,10 +44,10 @@ export const Symbols: React.FC = (props: ISymbolsProps) => { title={componentOption.Name} onClick={() => props.buttonOnClick(componentOption.Name)} draggable={true} - onDragStart={(event) => handleDragStart(event)} + onDragStart={(event) => HandleDragStart(event)} > - {truncateString(componentOption.Name, 5)} + {TruncateString(componentOption.Name, 5)} ); }); diff --git a/src/Components/SymbolsSidebar/MouseEventHandlers.ts b/src/Components/SymbolsSidebar/MouseEventHandlers.ts index e7cd5f0..6021122 100644 --- a/src/Components/SymbolsSidebar/MouseEventHandlers.ts +++ b/src/Components/SymbolsSidebar/MouseEventHandlers.ts @@ -1,7 +1,7 @@ import { RefObject, Dispatch, SetStateAction, useEffect } from 'react'; import { IPoint } from '../../Interfaces/IPoint'; -export function useMouseEvents( +export function UseMouseEvents( isContextMenuOpen: boolean, elementRef: RefObject, setIsContextMenuOpen: Dispatch>, @@ -9,44 +9,48 @@ export function useMouseEvents( setContextMenuPosition: Dispatch> ): void { useEffect(() => { - const onContextMenu = (event: MouseEvent): void => handleRightClick( - event, - setIsContextMenuOpen, - setOnClickSymbolId, - setContextMenuPosition - ); + function OnContextMenu(event: MouseEvent): void { + return HandleRightClick( + event, + setIsContextMenuOpen, + setOnClickSymbolId, + setContextMenuPosition + ); + } - const onLeftClick = (): void => handleLeftClick( - isContextMenuOpen, - setIsContextMenuOpen, - setOnClickSymbolId - ); + function OnLeftClick(): void { + return HandleLeftClick( + isContextMenuOpen, + setIsContextMenuOpen, + setOnClickSymbolId + ); + } elementRef.current?.addEventListener( 'contextmenu', - onContextMenu + OnContextMenu ); window.addEventListener( 'click', - onLeftClick + OnLeftClick ); return () => { elementRef.current?.removeEventListener( 'contextmenu', - onContextMenu + OnContextMenu ); window.removeEventListener( 'click', - onLeftClick + OnLeftClick ); }; }); } -export function handleRightClick( +export function HandleRightClick( event: MouseEvent, setIsContextMenuOpen: React.Dispatch>, setOnClickSymbolId: React.Dispatch>, @@ -66,7 +70,7 @@ export function handleRightClick( setContextMenuPosition(contextMenuPosition); } -export function handleLeftClick( +export function HandleLeftClick( isContextMenuOpen: boolean, setIsContextMenuOpen: React.Dispatch>, setOnClickContainerId: React.Dispatch> diff --git a/src/Components/SymbolsSidebar/SymbolsSidebar.tsx b/src/Components/SymbolsSidebar/SymbolsSidebar.tsx index 2b020fc..01854d1 100644 --- a/src/Components/SymbolsSidebar/SymbolsSidebar.tsx +++ b/src/Components/SymbolsSidebar/SymbolsSidebar.tsx @@ -2,22 +2,22 @@ import * as React from 'react'; import { FixedSizeList as List } from 'react-window'; import { Menu } from '../Menu/Menu'; import { MenuItem } from '../Menu/MenuItem'; -import { handleLeftClick, handleRightClick, useMouseEvents } from './MouseEventHandlers'; +import { UseMouseEvents } from './MouseEventHandlers'; import { IPoint } from '../../Interfaces/IPoint'; import { ISymbolModel } from '../../Interfaces/ISymbolModel'; import { SymbolProperties } from '../SymbolProperties/SymbolProperties'; interface ISymbolsSidebarProps { - SelectedSymbolId: string + selectedSymbolId: string symbols: Map isOpen: boolean isHistoryOpen: boolean - OnPropertyChange: (key: string, value: string | number | boolean) => void - SelectSymbol: (symbolId: string) => void - DeleteSymbol: (containerid: string) => void + onPropertyChange: (key: string, value: string | number | boolean) => void + selectSymbol: (symbolId: string) => void + deleteSymbol: (containerid: string) => void } -export const SymbolsSidebar: React.FC = (props: ISymbolsSidebarProps): JSX.Element => { +export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element { // States const [isContextMenuOpen, setIsContextMenuOpen] = React.useState(false); const [onClickSymbolId, setOnClickSymbolId] = React.useState(''); @@ -29,7 +29,7 @@ export const SymbolsSidebar: React.FC = (props: ISymbolsSi const elementRef = React.useRef(null); // Event listeners - useMouseEvents( + UseMouseEvents( isContextMenuOpen, elementRef, setIsContextMenuOpen, @@ -46,35 +46,33 @@ export const SymbolsSidebar: React.FC = (props: ISymbolsSi } const containers = [...props.symbols.values()]; - const Row = ({ index, style }: {index: number, style: React.CSSProperties}): JSX.Element => { + function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element { const container = containers[index]; const key = container.id.toString(); const text = key; - const selectedClass: string = props.SelectedSymbolId !== '' && - props.SelectedSymbolId === container.id + const selectedClass: string = props.selectedSymbolId !== '' && + props.selectedSymbolId === container.id ? 'border-l-4 bg-slate-400/60 hover:bg-slate-400' : 'bg-slate-300/60 hover:bg-slate-300'; return ( ); - }; + } return (
- Elements + Elements
= (props: ISymbolsSi height={384} width={256} > - { Row } + {Row}
= (props: ISymbolsSi > { setIsContextMenuOpen(false); - props.DeleteSymbol(onClickSymbolId); - }} /> + props.deleteSymbol(onClickSymbolId); + } } /> + onChange={props.onPropertyChange} />
); -}; +} diff --git a/src/Components/ToggleButton/ToggleButton.tsx b/src/Components/ToggleButton/ToggleButton.tsx index 198bf99..37438ed 100644 --- a/src/Components/ToggleButton/ToggleButton.tsx +++ b/src/Components/ToggleButton/ToggleButton.tsx @@ -1,26 +1,26 @@ -import React, { FC } from 'react'; +import React from 'react'; import './ToggleButton.scss'; interface IToggleButtonProps { id: string text: string - type?: TOGGLE_TYPE + type?: ToggleType title: string checked: boolean onChange: React.ChangeEventHandler } -export enum TOGGLE_TYPE { - MATERIAL, +export enum ToggleType { + Material, IOS } -export const ToggleButton: FC = (props) => { +export function ToggleButton(props: IToggleButtonProps): JSX.Element { const id = `toggle-${props.id}`; - const type = props.type ?? TOGGLE_TYPE.MATERIAL; + const type = props.type ?? ToggleType.Material; let classLine = 'line w-10 h-4 bg-gray-400 rounded-full shadow-inner'; let classDot = 'dot absolute w-6 h-6 bg-white rounded-full shadow -left-1 -top-1 transition'; - if (type === TOGGLE_TYPE.IOS) { + if (type === ToggleType.IOS) { classLine = 'line block bg-gray-600 w-14 h-8 rounded-full'; classDot = 'dot absolute left-1 top-1 bg-white w-6 h-6 rounded-full transition'; } @@ -43,10 +43,10 @@ export const ToggleButton: FC = (props) => {
- { props.text } + {props.text}
); -}; +} diff --git a/src/Components/UI/UI.tsx b/src/Components/UI/UI.tsx index 3645096..198e0ed 100644 --- a/src/Components/UI/UI.tsx +++ b/src/Components/UI/UI.tsx @@ -14,23 +14,23 @@ import { SymbolsSidebar } from '../SymbolsSidebar/SymbolsSidebar'; import { PropertyType } from '../../Enums/PropertyType'; interface IUIProps { - SelectedContainer: IContainerModel | undefined + selectedContainer: IContainerModel | undefined current: IHistoryState history: IHistoryState[] historyCurrentStep: number - AvailableContainers: IAvailableContainer[] - AvailableSymbols: IAvailableSymbol[] - SelectContainer: (containerId: string) => void - DeleteContainer: (containerId: string) => void - OnPropertyChange: (key: string, value: string | number | boolean, type?: PropertyType) => void - AddContainer: (type: string) => void - AddSymbol: (type: string) => void - OnSymbolPropertyChange: (key: string, value: string | number | boolean) => void - SelectSymbol: (symbolId: string) => void - DeleteSymbol: (symbolId: string) => void - SaveEditorAsJSON: () => void - SaveEditorAsSVG: () => void - LoadState: (move: number) => void + availableContainers: IAvailableContainer[] + availableSymbols: IAvailableSymbol[] + selectContainer: (containerId: string) => void + deleteContainer: (containerId: string) => void + onPropertyChange: (key: string, value: string | number | boolean, type?: PropertyType) => void + addContainer: (type: string) => void + addSymbol: (type: string) => void + onSymbolPropertyChange: (key: string, value: string | number | boolean) => void + selectSymbol: (symbolId: string) => void + deleteSymbol: (symbolId: string) => void + saveEditorAsJSON: () => void + saveEditorAsSVG: () => void + loadState: (move: number) => void } function CloseOtherSidebars( @@ -41,7 +41,7 @@ function CloseOtherSidebars( setIsSymbolsOpen(false); } -export const UI: React.FunctionComponent = (props: IUIProps) => { +export function UI(props: IUIProps): JSX.Element { const [isSidebarOpen, setIsSidebarOpen] = React.useState(true); const [isSymbolsOpen, setIsSymbolsOpen] = React.useState(false); const [isHistoryOpen, setIsHistoryOpen] = React.useState(false); @@ -61,71 +61,63 @@ export const UI: React.FunctionComponent = (props: IUIProps) => { isSymbolsOpen={isSymbolsOpen} isElementsSidebarOpen={isSidebarOpen} isHistoryOpen={isHistoryOpen} - ToggleSidebar={() => { + toggleSidebar={() => { CloseOtherSidebars(setIsSidebarOpen, setIsSymbolsOpen); setIsSidebarOpen(!isSidebarOpen); - }} - ToggleSymbols={() => { + } } + toggleSymbols={() => { CloseOtherSidebars(setIsSidebarOpen, setIsSymbolsOpen); setIsSymbolsOpen(!isSymbolsOpen); - }} - ToggleTimeline={() => setIsHistoryOpen(!isHistoryOpen)} - /> + } } + toggleTimeline={() => setIsHistoryOpen(!isHistoryOpen)} /> + buttonOnClick={props.addContainer} /> + buttonOnClick={props.addSymbol} /> + onPropertyChange={props.onPropertyChange} + selectContainer={props.selectContainer} + deleteContainer={props.deleteContainer} /> + onPropertyChange={props.onSymbolPropertyChange} + selectSymbol={props.selectSymbol} + deleteSymbol={props.deleteSymbol} /> + jumpTo={props.loadState} /> ); -}; - -export default UI; +} diff --git a/src/Enums/PropertyType.ts b/src/Enums/PropertyType.ts index 0e9dd0d..e8584e3 100644 --- a/src/Enums/PropertyType.ts +++ b/src/Enums/PropertyType.ts @@ -8,15 +8,15 @@ export enum PropertyType { /** * Simple property: is not inside any object: id, x, width... (default) */ - SIMPLE, + Simple, /** * Style property: is inside the style object: stroke, fillOpacity... */ - STYLE, + Style, /** * Margin property: is inside the margin property: left, bottom, top, right... */ - MARGIN, + Margin, } diff --git a/src/Events/EditorEvents.ts b/src/Events/EditorEvents.ts index ea1ee39..3cc8978 100644 --- a/src/Events/EditorEvents.ts +++ b/src/Events/EditorEvents.ts @@ -1,5 +1,5 @@ import { Dispatch, SetStateAction } from 'react'; -import { getCurrentHistory } from '../Components/Editor/Editor'; +import { GetCurrentHistory } from '../Components/Editor/Editor'; import { IConfiguration } from '../Interfaces/IConfiguration'; import { IEditorState } from '../Interfaces/IEditorState'; import { IHistoryState } from '../Interfaces/IHistoryState'; @@ -38,7 +38,7 @@ const appendNewState = ( ): void => { const state: IHistoryState = JSON.parse(eventInitDict?.detail.state); ReviveState(state); - const history = getCurrentHistory(editorState.history, editorState.historyCurrentStep); + const history = GetCurrentHistory(editorState.history, editorState.historyCurrentStep); history.push(state); setHistory(history); diff --git a/src/Interfaces/IAvailableContainer.ts b/src/Interfaces/IAvailableContainer.ts index ff61db0..b8a5a2b 100644 --- a/src/Interfaces/IAvailableContainer.ts +++ b/src/Interfaces/IAvailableContainer.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import React from 'react'; import { AddMethod } from '../Enums/AddMethod'; import { XPositionReference } from '../Enums/XPositionReference'; diff --git a/src/Interfaces/IAvailableSymbol.ts b/src/Interfaces/IAvailableSymbol.ts index 02308d1..c6e6c1c 100644 --- a/src/Interfaces/IAvailableSymbol.ts +++ b/src/Interfaces/IAvailableSymbol.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import { XPositionReference } from '../Enums/XPositionReference'; import { IImage } from './IImage'; diff --git a/src/Interfaces/IConfiguration.ts b/src/Interfaces/IConfiguration.ts index a37647d..c44f41c 100644 --- a/src/Interfaces/IConfiguration.ts +++ b/src/Interfaces/IConfiguration.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import { IAvailableContainer } from './IAvailableContainer'; import { IAvailableSymbol } from './IAvailableSymbol'; diff --git a/src/Interfaces/IContainerModel.ts b/src/Interfaces/IContainerModel.ts index 6521f0a..5922ebe 100644 --- a/src/Interfaces/IContainerModel.ts +++ b/src/Interfaces/IContainerModel.ts @@ -1,4 +1,4 @@ -import IContainerProperties from './IContainerProperties'; +import { IContainerProperties } from './IContainerProperties'; export interface IContainerModel { children: IContainerModel[] diff --git a/src/Interfaces/IContainerProperties.ts b/src/Interfaces/IContainerProperties.ts index 2eaa5fd..379ec41 100644 --- a/src/Interfaces/IContainerProperties.ts +++ b/src/Interfaces/IContainerProperties.ts @@ -5,7 +5,7 @@ import { IMargin } from './IMargin'; /** * Properties of a container */ -export default interface IContainerProperties { +export interface IContainerProperties { /** id of the container */ id: string @@ -55,7 +55,7 @@ export default interface IContainerProperties { isFlex: boolean /** Horizontal alignment, also determines the visual location of x {Left = 0, Center, Right } */ - XPositionReference: XPositionReference + xPositionReference: XPositionReference /** * (optional) diff --git a/src/Interfaces/IHistoryState.ts b/src/Interfaces/IHistoryState.ts index f906af3..c0d8214 100644 --- a/src/Interfaces/IHistoryState.ts +++ b/src/Interfaces/IHistoryState.ts @@ -3,20 +3,20 @@ import { ISymbolModel } from './ISymbolModel'; export interface IHistoryState { /** Last editor action */ - LastAction: string + lastAction: string /** Reference to the main container */ - MainContainer: IContainerModel + mainContainer: IContainerModel /** Id of the selected container */ - SelectedContainerId: string + selectedContainerId: string /** Counter of type of container. Used for ids. */ - TypeCounters: Record + typeCounters: Record /** List of symbols */ - Symbols: Map + symbols: Map /** Selected symbols id */ - SelectedSymbolId: string + selectedSymbolId: string } diff --git a/src/Interfaces/IImage.ts b/src/Interfaces/IImage.ts index d22c2e1..7a9e5c4 100644 --- a/src/Interfaces/IImage.ts +++ b/src/Interfaces/IImage.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ /** * Model of an image with multiple source * It must at least have one source. diff --git a/src/main.tsx b/src/main.tsx index 5c51e5c..264c622 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -3,7 +3,7 @@ import ReactDOM from 'react-dom/client'; import { App } from './Components/App/App'; import './index.scss'; -function render(root: Element | Document): void { +function RenderRoot(root: Element | Document): void { ReactDOM.createRoot(root.querySelector('#root') as HTMLDivElement).render( @@ -12,10 +12,9 @@ function render(root: Element | Document): void { } namespace SVGLayoutDesigner { - export const Render = render; + export const Render = RenderRoot; } - (window as any).SVGLayoutDesigner = SVGLayoutDesigner; -render(document); +RenderRoot(document); diff --git a/src/utils/default.ts b/src/utils/default.ts index 98db426..74f1512 100644 --- a/src/utils/default.ts +++ b/src/utils/default.ts @@ -3,7 +3,7 @@ import { IAvailableContainer } from '../Interfaces/IAvailableContainer'; import { IAvailableSymbol } from '../Interfaces/IAvailableSymbol'; import { IConfiguration } from '../Interfaces/IConfiguration'; import { ContainerModel, IContainerModel } from '../Interfaces/IContainerModel'; -import IContainerProperties from '../Interfaces/IContainerProperties'; +import { IContainerProperties } from '../Interfaces/IContainerProperties'; import { IEditorState } from '../Interfaces/IEditorState'; import { ISymbolModel } from '../Interfaces/ISymbolModel'; @@ -36,7 +36,7 @@ export const APPLY_BEHAVIORS_ON_CHILDREN = true; /** * Returns the default editor state given the configuration */ -export const GetDefaultEditorState = (configuration: IConfiguration): IEditorState => { +export function GetDefaultEditorState(configuration: IConfiguration): IEditorState { const mainContainer = new ContainerModel( null, { @@ -50,22 +50,23 @@ export const GetDefaultEditorState = (configuration: IConfiguration): IEditorSta configuration, history: [ { - LastAction: '', - MainContainer: mainContainer, - SelectedContainerId: mainContainer.properties.id, - TypeCounters: {}, - Symbols: new Map(), - SelectedSymbolId: '' + lastAction: '', + mainContainer: mainContainer, + selectedContainerId: mainContainer.properties.id, + typeCounters: {}, + symbols: new Map(), + selectedSymbolId: '' } ], historyCurrentStep: 0 }; -}; +} /** * Default config when the API is not available */ export const DEFAULT_CONFIG: IConfiguration = { + /* eslint-disable @typescript-eslint/naming-convention */ AvailableContainers: [ { Type: 'Container', @@ -87,6 +88,7 @@ export const DEFAULT_CONFIG: IConfiguration = { stroke: 'black' } } + /* eslint-enable */ }; /** @@ -107,7 +109,7 @@ export const DEFAULT_MAINCONTAINER_PROPS: IContainerProperties = { height: Number(DEFAULT_CONFIG.MainContainer.Height), isAnchor: false, isFlex: false, - XPositionReference: XPositionReference.Left, + xPositionReference: XPositionReference.Left, style: { stroke: 'black', fillOpacity: 0 @@ -124,42 +126,40 @@ export const DEFAULT_MAINCONTAINER_PROPS: IContainerProperties = { * @param containerConfig default config of the container sent by the API * @returns {IContainerProperties} Default properties of a newly created container */ -export const GetDefaultContainerProps = ( - type: string, +export function GetDefaultContainerProps(type: string, typeCount: number, parent: IContainerModel, x: number, y: number, width: number, height: number, - containerConfig: IAvailableContainer -): IContainerProperties => ({ - id: `${type}-${typeCount}`, - type, - parentId: parent.properties.id, - linkedSymbolId: '', - displayedText: `${type}-${typeCount}`, - x, - y, - margin: containerConfig.Margin ?? {}, - width, - height, - isAnchor: false, - isFlex: containerConfig.IsFlex ?? containerConfig.Width === undefined, - XPositionReference: containerConfig.XPositionReference ?? XPositionReference.Left, - minWidth: containerConfig.MinWidth ?? 1, - maxWidth: containerConfig.MaxWidth ?? Number.MAX_SAFE_INTEGER, - customSVG: containerConfig.CustomSVG, - style: structuredClone(containerConfig.Style), - userData: structuredClone(containerConfig.UserData) -}); + containerConfig: IAvailableContainer): IContainerProperties { + return ({ + id: `${type}-${typeCount}`, + type, + parentId: parent.properties.id, + linkedSymbolId: '', + displayedText: `${type}-${typeCount}`, + x, + y, + margin: containerConfig.Margin ?? {}, + width, + height, + isAnchor: false, + isFlex: containerConfig.IsFlex ?? containerConfig.Width === undefined, + xPositionReference: containerConfig.XPositionReference ?? XPositionReference.Left, + minWidth: containerConfig.MinWidth ?? 1, + maxWidth: containerConfig.MaxWidth ?? Number.MAX_SAFE_INTEGER, + customSVG: containerConfig.CustomSVG, + style: structuredClone(containerConfig.Style), + userData: structuredClone(containerConfig.UserData) + }); +} -export const GetDefaultSymbolModel = ( - name: string, +export function GetDefaultSymbolModel(name: string, newCounters: Record, type: string, - symbolConfig: IAvailableSymbol -): ISymbolModel => { + symbolConfig: IAvailableSymbol): ISymbolModel { return { id: `${name}-${newCounters[type]}`, type: name, @@ -169,4 +169,4 @@ export const GetDefaultSymbolModel = ( height: symbolConfig.Height ?? DEFAULT_SYMBOL_HEIGHT, linkedContainers: new Set() }; -}; +} diff --git a/src/utils/itertools.ts b/src/utils/itertools.ts index faf1da9..c4bb188 100644 --- a/src/utils/itertools.ts +++ b/src/utils/itertools.ts @@ -54,7 +54,7 @@ export function * MakeBFSIterator(root: IContainerModel): Generator { next: T } -export function * pairwise(arr: T[]): Generator, void, unknown> { +export function * Pairwise(arr: T[]): Generator, void, unknown> { for (let i = 0; i < arr.length - 1; i++) { yield { cur: arr[i], next: arr[i + 1] }; } } -export function * reversePairwise(arr: T[]): Generator, void, unknown> { +export function * ReversePairwise(arr: T[]): Generator, void, unknown> { for (let i = arr.length - 1; i > 0; i--) { yield { cur: arr[i], next: arr[i - 1] }; } diff --git a/src/utils/saveload.ts b/src/utils/saveload.ts index 8c84f19..1fb76b6 100644 --- a/src/utils/saveload.ts +++ b/src/utils/saveload.ts @@ -1,4 +1,4 @@ -import { findContainerById, MakeIterator } from './itertools'; +import { FindContainerById, MakeIterator } from './itertools'; import { IEditorState } from '../Interfaces/IEditorState'; import { IHistoryState } from '../Interfaces/IHistoryState'; @@ -19,34 +19,32 @@ export function Revive(editorState: IEditorState): void { } } -export const ReviveState = ( - state: IHistoryState -): void => { - if (state.MainContainer === null || state.MainContainer === undefined) { +export function ReviveState(state: IHistoryState): void { + if (state.mainContainer === null || state.mainContainer === undefined) { return; } - state.Symbols = new Map(state.Symbols); - for (const symbol of state.Symbols.values()) { + state.symbols = new Map(state.symbols); + for (const symbol of state.symbols.values()) { symbol.linkedContainers = new Set(symbol.linkedContainers); } - const it = MakeIterator(state.MainContainer); + 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); + const parent = FindContainerById(state.mainContainer, parentId); if (parent === undefined) { continue; } container.parent = parent; } -}; +} -export const getCircularReplacer = (): (key: any, value: object | Map | null) => object | null | undefined => { +export function GetCircularReplacer(): (key: any, value: object | Map | null) => object | null | undefined { return (key: any, value: object | null) => { if (key === 'parent') { return; @@ -62,4 +60,4 @@ export const getCircularReplacer = (): (key: any, value: object | Map): number { +function GetLeastUsedIndex(indexes: number[], indexesTried: Record): number { let minUsed = Infinity; let minIndex = -1; for (const index of indexes) { diff --git a/src/utils/stringtools.ts b/src/utils/stringtools.ts index 2c858c0..e8a460c 100644 --- a/src/utils/stringtools.ts +++ b/src/utils/stringtools.ts @@ -1,10 +1,10 @@ -export function truncateString(str: string, num: number): string { +export function TruncateString(str: string, num: number): string { if (str.length <= num) { return str; } return `${str.slice(0, num)}...`; } -export function camelize(str: string): any { +export function Camelize(str: string): any { return str.split('-').map((word, index) => index > 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word).join(''); } diff --git a/src/utils/svg.ts b/src/utils/svg.ts index 32aa0e6..61f8291 100644 --- a/src/utils/svg.ts +++ b/src/utils/svg.ts @@ -10,7 +10,7 @@ import { XPositionReference } from '../Enums/XPositionReference'; * it is better to fix serialization with the reviver. */ -export function transformX(x: number, width: number, xPositionReference = XPositionReference.Left): number { +export function TransformX(x: number, width: number, xPositionReference = XPositionReference.Left): number { let transformedX = x; if (xPositionReference === XPositionReference.Center) { transformedX += width / 2; @@ -20,7 +20,7 @@ export function transformX(x: number, width: number, xPositionReference = XPosit return transformedX; } -export function restoreX(x: number, width: number, xPositionReference = XPositionReference.Left): number { +export function RestoreX(x: number, width: number, xPositionReference = XPositionReference.Left): number { let transformedX = x; if (xPositionReference === XPositionReference.Center) { transformedX -= width / 2; diff --git a/src/utils/test-utils.tsx b/src/utils/test-utils.tsx index 139850e..aec5ae7 100644 --- a/src/utils/test-utils.tsx +++ b/src/utils/test-utils.tsx @@ -7,14 +7,15 @@ afterEach(() => { cleanup(); }); -const customRender = (ui: React.ReactElement, options = {}): RenderResult => - render(ui, { +function CustomRender(ui: React.ReactElement, options = {}): RenderResult { + return render(ui, { // wrap provider(s) here if needed wrapper: ({ children }) => children, ...options }); +} export * from '@testing-library/react'; export { default as userEvent } from '@testing-library/user-event'; // override render export -export { customRender as render }; +export { CustomRender as render };