Fix eslint errors

This commit is contained in:
Eric NGUYEN 2023-02-23 13:54:38 +01:00
parent 5b3ab651e6
commit 4e41fda93a
87 changed files with 1003 additions and 702 deletions

View file

@ -2,14 +2,14 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { AddMethod } from '../../Enums/AddMethod'; import { AddMethod } from '../../Enums/AddMethod';
import { PositionReference } from '../../Enums/PositionReference'; import { PositionReference } from '../../Enums/PositionReference';
import { IAction } from '../../Interfaces/IAction'; import { type IAction } from '../../Interfaces/IAction';
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer'; import { type IAvailableContainer } from '../../Interfaces/IAvailableContainer';
import { IAvailableSymbol } from '../../Interfaces/IAvailableSymbol'; import { type IAvailableSymbol } from '../../Interfaces/IAvailableSymbol';
import { ICategory } from '../../Interfaces/ICategory'; import { type ICategory } from '../../Interfaces/ICategory';
import { IConfiguration } from '../../Interfaces/IConfiguration'; import { type IConfiguration } from '../../Interfaces/IConfiguration';
import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel'; import { type IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel';
import { IHistoryState } from '../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../Interfaces/IHistoryState';
import { IPattern } from '../../Interfaces/IPattern'; import { type IPattern } from '../../Interfaces/IPattern';
import { DEFAULT_DIMENSION_OPTION, DEFAULT_MAINCONTAINER_PROPS, GetDefaultContainerProps } from '../../utils/default'; import { DEFAULT_DIMENSION_OPTION, DEFAULT_MAINCONTAINER_PROPS, GetDefaultContainerProps } from '../../utils/default';
import { FetchConfiguration } from './api'; import { FetchConfiguration } from './api';
@ -95,9 +95,7 @@ describe.concurrent('Models test suite', () => {
expect(res4).toBe(true); expect(res4).toBe(true);
}); });
const mainContainer = new ContainerModel( const mainContainer = new ContainerModel(DEFAULT_MAINCONTAINER_PROPS);
DEFAULT_MAINCONTAINER_PROPS
);
const containers = new Map<string, IContainerModel>(); const containers = new Map<string, IContainerModel>();
const historyState: IHistoryState = { const historyState: IHistoryState = {
@ -244,7 +242,12 @@ describe.concurrent('Models test suite', () => {
'container', 'container',
0, 0,
null, null,
0, 0, 0, 0, availableContainerModel); 0,
0,
0,
0,
availableContainerModel
);
it('ContainerModel', async() => { it('ContainerModel', async() => {
const model: IContainerModel = { const model: IContainerModel = {

View file

@ -1,6 +1,6 @@
import { IConfiguration } from '../../Interfaces/IConfiguration'; import { type IConfiguration } from '../../Interfaces/IConfiguration';
import { ISetContainerListRequest } from '../../Interfaces/ISetContainerListRequest'; import { type ISetContainerListRequest } from '../../Interfaces/ISetContainerListRequest';
import { ISetContainerListResponse } from '../../Interfaces/ISetContainerListResponse'; import { type ISetContainerListResponse } from '../../Interfaces/ISetContainerListResponse';
import { GetCircularReplacer } from '../../utils/saveload'; import { GetCircularReplacer } from '../../utils/saveload';
/** /**
@ -9,16 +9,14 @@ import { GetCircularReplacer } from '../../utils/saveload';
*/ */
export async function FetchConfiguration(): Promise<IConfiguration> { export async function FetchConfiguration(): Promise<IConfiguration> {
const url = import.meta.env.VITE_API_FETCH_URL; const url = import.meta.env.VITE_API_FETCH_URL;
// The test library cannot use the Fetch API // @ts-expect-error The test library cannot use the Fetch API
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (window.fetch) { if (window.fetch) {
return await fetch(url, { return await fetch(url, {
method: 'POST' method: 'POST'
}) })
.then(async(response) => .then(async(response) =>
await response.json() await response.json()) as IConfiguration;
) as IConfiguration;
} }
return await new Promise((resolve) => { return await new Promise((resolve) => {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
@ -33,11 +31,13 @@ export async function FetchConfiguration(): Promise<IConfiguration> {
}); });
} }
export async function SetContainerList(request: ISetContainerListRequest, configurationUrl?: string): Promise<ISetContainerListResponse> { export async function SetContainerList(
request: ISetContainerListRequest,
configurationUrl?: string
): Promise<ISetContainerListResponse> {
const url = configurationUrl ?? import.meta.env.VITE_API_SET_CONTAINER_LIST_URL; const url = configurationUrl ?? import.meta.env.VITE_API_SET_CONTAINER_LIST_URL;
const dataParsed = JSON.stringify(request, GetCircularReplacer()); const dataParsed = JSON.stringify(request, GetCircularReplacer());
// The test library cannot use the Fetch API // @ts-expect-error The test library cannot use the Fetch API
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (window.fetch) { if (window.fetch) {
return await fetch(url, { return await fetch(url, {
@ -49,8 +49,7 @@ export async function SetContainerList(request: ISetContainerListRequest, config
body: dataParsed body: dataParsed
}) })
.then(async(response) => .then(async(response) =>
await response.json() await response.json()) as ISetContainerListResponse;
) as ISetContainerListResponse;
} }
return await new Promise((resolve) => { return await new Promise((resolve) => {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();

View file

@ -1,6 +1,6 @@
import { Dispatch, SetStateAction } from 'react'; import { type Dispatch, type SetStateAction } from 'react';
import { AppState } from '../../../Enums/AppState'; import { AppState } from '../../../Enums/AppState';
import { IEditorState } from '../../../Interfaces/IEditorState'; import { type IEditorState } from '../../../Interfaces/IEditorState';
import { Revive } from '../../../utils/saveload'; import { Revive } from '../../../utils/saveload';
export function LoadState( export function LoadState(

View file

@ -1,10 +1,10 @@
import { Dispatch, SetStateAction } from 'react'; import { type Dispatch, type SetStateAction } from 'react';
import { IConfiguration } from '../../../Interfaces/IConfiguration'; import { type IConfiguration } from '../../../Interfaces/IConfiguration';
import { FetchConfiguration } from '../../API/api'; import { FetchConfiguration } from '../../API/api';
import { IEditorState } from '../../../Interfaces/IEditorState'; import { type IEditorState } from '../../../Interfaces/IEditorState';
import { LoadState } from './Load';
import { DISABLE_API, GetDefaultEditorState } from '../../../utils/default'; import { DISABLE_API, GetDefaultEditorState } from '../../../utils/default';
import { AppState } from '../../../Enums/AppState'; import { type AppState } from '../../../Enums/AppState';
import { LoadState } from './Load';
export function NewEditor( export function NewEditor(
editorState: IEditorState, editorState: IEditorState,

View file

@ -2,16 +2,14 @@ import * as React from 'react';
import { beforeEach, describe, it } from 'vitest'; import { beforeEach, describe, it } from 'vitest';
import { AppState } from '../../Enums/AppState'; import { AppState } from '../../Enums/AppState';
import { FAST_BOOT } from '../../utils/default'; import { FAST_BOOT } from '../../utils/default';
import { fireEvent, render, RenderResult } from '../../utils/test-utils'; import { fireEvent, render, type RenderResult } from '../../utils/test-utils';
import { App } from './App'; import { App } from './App';
describe.concurrent('App', () => { describe.concurrent('App', () => {
let app: RenderResult; let app: RenderResult;
beforeEach(() => { beforeEach(() => {
app = render( app = render(<App root={document} />);
<App root={document} />
);
}); });
it('New editor', async() => { it('New editor', async() => {

View file

@ -1,15 +1,15 @@
import React, { Dispatch, SetStateAction, useContext, useEffect, useRef, useState } from 'react'; import React, { type Dispatch, type SetStateAction, useContext, useEffect, useRef, useState } from 'react';
import { UseCustomEvents } from '../../Events/AppEvents'; import { UseCustomEvents } from '../../Events/AppEvents';
import { MainMenu } from '../MainMenu/MainMenu'; import { MainMenu } from '../MainMenu/MainMenu';
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel'; import { ContainerModel, type IContainerModel } from '../../Interfaces/IContainerModel';
import { Editor } from '../Editor/Editor'; import { Editor } from '../Editor/Editor';
import { IEditorState } from '../../Interfaces/IEditorState'; import { type IEditorState } from '../../Interfaces/IEditorState';
import { LoadState } from './Actions/Load';
import { LoadEditor, NewEditor } from './Actions/MenuActions';
import { DEFAULT_CONFIG, DEFAULT_MAINCONTAINER_PROPS, FAST_BOOT } from '../../utils/default'; import { DEFAULT_CONFIG, DEFAULT_MAINCONTAINER_PROPS, FAST_BOOT } from '../../utils/default';
import { AppState } from '../../Enums/AppState'; import { AppState } from '../../Enums/AppState';
import { Loader } from '../Loader/Loader'; import { Loader } from '../Loader/Loader';
import { LanguageContext } from '../LanguageProvider/LanguageProvider'; import { LanguageContext } from '../LanguageProvider/LanguageProvider';
import { LoadEditor, NewEditor } from './Actions/MenuActions';
import { LoadState } from './Actions/Load';
// App will never have props // App will never have props
// eslint-disable-next-line @typescript-eslint/no-empty-interface // eslint-disable-next-line @typescript-eslint/no-empty-interface
@ -49,9 +49,7 @@ export function App(props: IAppProps): JSX.Element {
const appRef = useRef<HTMLDivElement>(null); const appRef = useRef<HTMLDivElement>(null);
const languageContext = useContext(LanguageContext); const languageContext = useContext(LanguageContext);
const defaultMainContainer = new ContainerModel( const defaultMainContainer = new ContainerModel(DEFAULT_MAINCONTAINER_PROPS);
DEFAULT_MAINCONTAINER_PROPS
);
const containers = new Map<string, IContainerModel>(); const containers = new Map<string, IContainerModel>();
containers.set(defaultMainContainer.properties.id, defaultMainContainer); containers.set(defaultMainContainer.properties.id, defaultMainContainer);
@ -116,15 +114,17 @@ export function App(props: IAppProps): JSX.Element {
setAppState(AppState.Loading); setAppState(AppState.Loading);
NewEditor( NewEditor(
editorState, editorState,
(newEditor) => setEditorState(newEditor), (newEditor) => { setEditorState(newEditor); },
() => setAppState(AppState.Loaded) () => { setAppState(AppState.Loaded); }
); );
}} }}
loadEditor={(files: FileList | null) => LoadEditor( loadEditor={(files: FileList | null) => {
LoadEditor(
files, files,
setEditorState, setEditorState,
setAppState setAppState
)} );
}}
/> />
</div> </div>
); );

View file

@ -13,8 +13,8 @@ import {
EnvelopeIcon as EnvolopeIconS, EnvelopeIcon as EnvolopeIconS,
Cog8ToothIcon as Cog8ToothIconS Cog8ToothIcon as Cog8ToothIconS
} from '@heroicons/react/24/solid'; } from '@heroicons/react/24/solid';
import { BarIcon } from './BarIcon';
import { Text } from '../Text/Text'; import { Text } from '../Text/Text';
import { BarIcon } from './BarIcon';
interface IBarProps { interface IBarProps {
className: string className: string
@ -38,7 +38,7 @@ export function Bar(props: IBarProps): JSX.Element {
<BarIcon <BarIcon
isActive={props.isComponentsOpen} isActive={props.isComponentsOpen}
title={Text({ textId: '@Components' })} title={Text({ textId: '@Components' })}
onClick={() => props.toggleComponents()}> onClick={() => { props.toggleComponents(); }}>
{ {
props.isComponentsOpen props.isComponentsOpen
? <CubeIconS className='heroicon' /> ? <CubeIconS className='heroicon' />
@ -48,7 +48,7 @@ export function Bar(props: IBarProps): JSX.Element {
<BarIcon <BarIcon
isActive={props.isSymbolsOpen} isActive={props.isSymbolsOpen}
title={Text({ textId: '@Symbols' })} title={Text({ textId: '@Symbols' })}
onClick={() => props.toggleSymbols()}> onClick={() => { props.toggleSymbols(); }}>
{ {
props.isSymbolsOpen props.isSymbolsOpen
? <LinkIconS className='heroicon' /> ? <LinkIconS className='heroicon' />
@ -58,7 +58,7 @@ export function Bar(props: IBarProps): JSX.Element {
<BarIcon <BarIcon
isActive={props.isMessagesOpen} isActive={props.isMessagesOpen}
title={Text({ textId: '@Messages' })} title={Text({ textId: '@Messages' })}
onClick={() => props.toggleMessages()}> onClick={() => { props.toggleMessages(); }}>
{ {
props.isMessagesOpen props.isMessagesOpen
? <EnvolopeIconS className='heroicon' /> ? <EnvolopeIconS className='heroicon' />
@ -69,7 +69,7 @@ export function Bar(props: IBarProps): JSX.Element {
<BarIcon <BarIcon
isActive={props.isHistoryOpen} isActive={props.isHistoryOpen}
title={Text({ textId: '@Timeline' })} title={Text({ textId: '@Timeline' })}
onClick={() => props.toggleTimeline()}> onClick={() => { props.toggleTimeline(); }}>
{ {
props.isHistoryOpen props.isHistoryOpen
? <ClockIconS className='heroicon' /> ? <ClockIconS className='heroicon' />
@ -79,7 +79,7 @@ export function Bar(props: IBarProps): JSX.Element {
<BarIcon <BarIcon
isActive={props.isSettingsOpen} isActive={props.isSettingsOpen}
title={Text({ textId: '@Settings' })} title={Text({ textId: '@Settings' })}
onClick={() => props.toggleSettings()}> onClick={() => { props.toggleSettings(); }}>
{ {
props.isSettingsOpen props.isSettingsOpen
? <Cog8ToothIconS className='heroicon' /> ? <Cog8ToothIconS className='heroicon' />

View file

@ -8,12 +8,14 @@ interface IBarIconProps {
} }
export function BarIcon(props: IBarIconProps): JSX.Element { export function BarIcon(props: IBarIconProps): JSX.Element {
const isActiveClasses = props.isActive ? 'border-l-4 border-blue-500 bg-slate-200' : ''; const isActiveClasses = props.isActive
? 'border-l-4 border-blue-500 bg-slate-200'
: '';
return ( return (
<button type="button" <button type="button"
className={`bar-btn group ${isActiveClasses}`} className={`bar-btn group ${isActiveClasses}`}
title={props.title} title={props.title}
onClick={() => props.onClick()} onClick={() => { props.onClick(); }}
> >
<span className='sidebar-tooltip group-hover:scale-100'>{props.title}</span> <span className='sidebar-tooltip group-hover:scale-100'>{props.title}</span>
{props.children} {props.children}

View file

@ -48,7 +48,12 @@ function Draw(
ctx, ctx,
mainContainer, mainContainer,
containers, containers,
leftDim, bottomDim, topDim, rightDim, scale); leftDim,
bottomDim,
topDim,
rightDim,
scale
);
// Draw symbols and symbol dimensions // Draw symbols and symbol dimensions
RenderSymbols(ctx, symbols, scale); RenderSymbols(ctx, symbols, scale);
@ -74,12 +79,10 @@ function Draw(
ctx.restore(); ctx.restore();
} }
function UseCanvas( function UseCanvas(draw: (context: CanvasRenderingContext2D,
draw: (context: CanvasRenderingContext2D,
frameCount: number, frameCount: number,
scale: number, scale: number,
translatePos: IPoint) => void translatePos: IPoint) => void): React.RefObject<HTMLCanvasElement> {
): React.RefObject<HTMLCanvasElement> {
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
const frameCount = useRef(0); const frameCount = useRef(0);
const translatePos = useRef({ const translatePos = useRef({
@ -169,9 +172,7 @@ function UseCanvas(
return canvasRef; return canvasRef;
} }
function UseSVGAutoResizer( function UseSVGAutoResizer(setViewer: React.Dispatch<React.SetStateAction<Viewer>>): void {
setViewer: React.Dispatch<React.SetStateAction<Viewer>>
): void {
React.useEffect(() => { React.useEffect(() => {
function OnResize(): void { function OnResize(): void {
setViewer({ setViewer({
@ -199,9 +200,7 @@ export function Canvas({
style, style,
drawParams drawParams
}: ICanvasProps): JSX.Element { }: ICanvasProps): JSX.Element {
const canvasRef = UseCanvas(( const canvasRef = UseCanvas((...CanvasProps) => {
...CanvasProps
) => {
Draw( Draw(
...CanvasProps, ...CanvasProps,
drawParams drawParams

View file

@ -1,5 +1,5 @@
import { NOTCHES_LENGTH } from '../../utils/default'; import { NOTCHES_LENGTH } from '../../utils/default';
import { IDimensionStyle } from '../SVG/Elements/Dimension'; import { type IDimensionStyle } from '../SVG/Elements/Dimension';
interface IDimensionProps { interface IDimensionProps {
id: string id: string

View file

@ -169,11 +169,13 @@ function AddHorizontalChildrenDimension(
let xChildrenStart = TransformX( let xChildrenStart = TransformX(
lastChild.properties.x, lastChild.properties.x,
lastChild.properties.width, lastChild.properties.width,
lastChild.properties.positionReference); lastChild.properties.positionReference
);
let xChildrenEnd = TransformX( let xChildrenEnd = TransformX(
lastChild.properties.x, lastChild.properties.x,
lastChild.properties.width, lastChild.properties.width,
lastChild.properties.positionReference); lastChild.properties.positionReference
);
// Find the min and max // Find the min and max
for (let i = container.children.length - 2; i >= 0; i--) { for (let i = container.children.length - 2; i >= 0; i--) {
@ -237,7 +239,8 @@ function AddVerticalChildrenDimension(
let yChildrenStart = TransformY( let yChildrenStart = TransformY(
lastChild.properties.y, lastChild.properties.y,
lastChild.properties.height, lastChild.properties.height,
lastChild.properties.positionReference); lastChild.properties.positionReference
);
let yChildrenEnd = yChildrenStart; let yChildrenEnd = yChildrenStart;
// Find the min and max // Find the min and max
@ -313,9 +316,7 @@ function AddHorizontalBorrowerDimension(
const restoredX = x + childCurrentTransform[0]; const restoredX = x + childCurrentTransform[0];
marks.push( marks.push(restoredX);
restoredX
);
} }
const restoredX = container.properties.x + currentTransform[0]; const restoredX = container.properties.x + currentTransform[0];
@ -373,9 +374,7 @@ function AddVerticalBorrowerDimension(
const restoredy = y + childCurrentTransform[1]; const restoredy = y + childCurrentTransform[1];
marks.push( marks.push(restoredy);
restoredy
);
} }
const restoredY = container.properties.y + currentTransform[1]; const restoredY = container.properties.y + currentTransform[1];

View file

@ -1,6 +1,5 @@
import { type IContainerModel } from '../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { type IHistoryState } from '../../Interfaces/IHistoryState'; import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { DIMENSION_MARGIN } from '../../utils/default'; import { DIMENSION_MARGIN } from '../../utils/default';
import { MakeRecursionDFSIterator } from '../../utils/itertools'; import { MakeRecursionDFSIterator } from '../../utils/itertools';
import { RenderContainer } from './Container'; import { RenderContainer } from './Container';

View file

@ -25,7 +25,11 @@ export function RenderContainerSelector(
props.selected.properties.height props.selected.properties.height
]; ];
({ x, y, width, height } = RemoveMargin(x, y, width, height, ({ x, y, width, height } = RemoveMargin(
x,
y,
width,
height,
props.selected.properties.margin.left, props.selected.properties.margin.left,
props.selected.properties.margin.bottom, props.selected.properties.margin.bottom,
props.selected.properties.margin.top, props.selected.properties.margin.top,

View file

@ -5,7 +5,8 @@ const IMAGE_CACHE = new Map<string, HTMLImageElement>();
export function RenderSymbol( export function RenderSymbol(
ctx: CanvasRenderingContext2D, ctx: CanvasRenderingContext2D,
symbol: ISymbolModel): void { symbol: ISymbolModel
): void {
const href = symbol.config.Image.Base64Image ?? symbol.config.Image.Url; const href = symbol.config.Image.Base64Image ?? symbol.config.Image.Url;
if (href === undefined) { if (href === undefined) {

View file

@ -1,6 +1,6 @@
import { ChevronRightIcon } from '@heroicons/react/24/outline'; import { ChevronRightIcon } from '@heroicons/react/24/outline';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { ICategory } from '../../Interfaces/ICategory'; import { type ICategory } from '../../Interfaces/ICategory';
import { TruncateString } from '../../utils/stringtools'; import { TruncateString } from '../../utils/stringtools';
interface ICategoryProps { interface ICategoryProps {
@ -20,23 +20,40 @@ export function Category(props: ICategoryProps): JSX.Element {
return ( return (
<div <div
className={` transition-all text-center ${isOpen ? 'overflow-hidden h-full' : `overflow-visible ${heightClass}`}`} className={` transition-all text-center ${isOpen
? 'overflow-hidden h-full'
: `overflow-visible ${heightClass}`}`}
key={categoryType} key={categoryType}
id={categoryType} id={categoryType}
title={categoryDisplayedText} title={categoryDisplayedText}
> >
<div <div
className={`flex flex-row group cursor-pointer ${heightClass}`} className={`flex flex-row group cursor-pointer ${heightClass}`}
onClick={() => setIsOpen(!isOpen)} onClick={() => { setIsOpen(!isOpen); }}
>
<span className={
`transition-all flex-1 h-full
justify-center sidebar-component-left
${isOpen
? 'rounded-b-none bg-slate-400/80 group-hover:bg-blue-600'
: ''}`}
> >
<span className={`transition-all flex-1 h-full justify-center sidebar-component-left ${isOpen ? 'rounded-b-none bg-slate-400/80 group-hover:bg-blue-600' : ''}`}>
{TruncateString(categoryDisplayedText, 25)} {TruncateString(categoryDisplayedText, 25)}
</span> </span>
<span className={`flex-none h-full justify-center sidebar-component-right ${isOpen ? 'rounded-b-none' : ''}`}> <span className={`flex-none h-full justify-center sidebar-component-right ${isOpen
<ChevronRightIcon className={`transition-all w-5 ${isOpen ? 'rotate-90' : ''}`} /> ? 'rounded-b-none'
: ''}`}>
<ChevronRightIcon className={`transition-all w-5 ${isOpen
? 'rotate-90'
: ''}`} />
</span> </span>
</div> </div>
<div className={`transition-all grid gap-2 p-2 rounded-b-lg ${isOpen ? 'visible overflow-auto bg-slate-400/80' : 'hidden overflow-hidden'}`}> <div className={
`transition-all grid gap-2 p-2 rounded-b-lg
${isOpen
? 'visible overflow-auto bg-slate-400/80'
: 'hidden overflow-hidden'}`}
>
{ props.children } { props.children }
</div> </div>
</div> </div>

View file

@ -80,7 +80,9 @@ export function CheckboxGroupButtons(props: ICheckboxGroupButtonsProps): JSX.Ele
* @returns The new selected values * @returns The new selected values
*/ */
function SetChecked(selectedValue: number, isChecked: boolean): number[] { function SetChecked(selectedValue: number, isChecked: boolean): number[] {
isChecked ? selectedOptions.add(selectedValue) : selectedOptions.delete(selectedValue); isChecked
? selectedOptions.add(selectedValue)
: selectedOptions.delete(selectedValue);
return [...selectedOptions]; return [...selectedOptions];
} }
} }

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { ChevronUpDownIcon } from '@heroicons/react/20/solid'; import { ChevronUpDownIcon } from '@heroicons/react/20/solid';
import { CheckboxGroupButtons } from './CheckboxGroupButtons';
import { Orientation } from '../../Enums/Orientation'; import { Orientation } from '../../Enums/Orientation';
import { CheckboxGroupButtons } from './CheckboxGroupButtons';
interface IOrientationCheckboxesProps { interface IOrientationCheckboxesProps {
id: string id: string

View file

@ -1,13 +1,13 @@
import { EyeIcon, EyeSlashIcon, XCircleIcon } from '@heroicons/react/24/outline'; import { EyeIcon, EyeSlashIcon, XCircleIcon } from '@heroicons/react/24/outline';
import * as React from 'react'; import * as React from 'react';
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer'; import { type Dispatch } from 'react';
import { ICategory } from '../../Interfaces/ICategory'; import { type IAvailableContainer } from '../../Interfaces/IAvailableContainer';
import { IContainerModel } from '../../Interfaces/IContainerModel'; import { type ICategory } from '../../Interfaces/ICategory';
import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { TruncateString } from '../../utils/stringtools'; import { TruncateString } from '../../utils/stringtools';
import { Category } from '../Category/Category'; import { Category } from '../Category/Category';
import { Text } from '../Text/Text'; import { Text } from '../Text/Text';
import { IReplaceContainer } from '../../Interfaces/IReplaceContainer'; import { type IReplaceContainer } from '../../Interfaces/IReplaceContainer';
import { Dispatch } from 'react';
interface IComponentsProps { interface IComponentsProps {
selectedContainer: IContainerModel | undefined selectedContainer: IContainerModel | undefined
@ -29,7 +29,9 @@ interface SidebarCategory {
export function Components(props: IComponentsProps): JSX.Element { export function Components(props: IComponentsProps): JSX.Element {
const [hideDisabled, setHideDisabled] = React.useState<boolean>(false); const [hideDisabled, setHideDisabled] = React.useState<boolean>(false);
const disabledTitle = hideDisabled ? Text({ textId: '@ShowDisabledComponents' }) : Text({ textId: '@HideDisabledComponents' }); const disabledTitle = hideDisabled
? Text({ textId: '@ShowDisabledComponents' })
: Text({ textId: '@HideDisabledComponents' });
const rootElements: Array<JSX.Element | undefined> = []; const rootElements: Array<JSX.Element | undefined> = [];
const categories = new Map<string, SidebarCategory>(props.categories.map(category => [ const categories = new Map<string, SidebarCategory>(props.categories.map(category => [
@ -42,8 +44,7 @@ export function Components(props: IComponentsProps): JSX.Element {
// build the categories (sorted with categories first) // build the categories (sorted with categories first)
categories.forEach((categoryWrapper, categoryName) => { categories.forEach((categoryWrapper, categoryName) => {
rootElements.push( rootElements.push(<Category
<Category
key={categoryName} key={categoryName}
category={categoryWrapper.category} category={categoryWrapper.category}
> >
@ -80,9 +81,9 @@ export function Components(props: IComponentsProps): JSX.Element {
className='w-full justify-center h-16 transition-all sidebar-component' className='w-full justify-center h-16 transition-all sidebar-component'
id={componentOption.Type} id={componentOption.Type}
title={componentOption.Type} title={componentOption.Type}
onClick={() => props.buttonOnClick(componentOption.Type)} onClick={() => { props.buttonOnClick(componentOption.Type); }}
draggable={true} draggable={true}
onDragStart={(event) => HandleDragStart(event)} onDragStart={(event) => { HandleDragStart(event); }}
disabled={disabled} disabled={disabled}
> >
{TruncateString(componentOption.DisplayedText ?? componentOption.Type, 25)} {TruncateString(componentOption.DisplayedText ?? componentOption.Type, 25)}

View file

@ -192,13 +192,17 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={props.properties.minWidth} min={props.properties.minWidth}
max={props.properties.maxWidth} max={props.properties.maxWidth}
value={(RemoveWidthMargin(props.properties.width, value={(RemoveWidthMargin(
props.properties.width,
props.properties.margin.left, props.properties.margin.left,
props.properties.margin.right)).toString()} props.properties.margin.right
)).toString()}
onChange={(value) => { onChange={(value) => {
props.onChange('width', ApplyWidthMargin(Number(value), props.onChange('width', ApplyWidthMargin(
Number(value),
props.properties.margin.left, props.properties.margin.left,
props.properties.margin.right)); props.properties.margin.right
));
}} }}
isDisabled={props.properties.isFlex}/> isDisabled={props.properties.isFlex}/>
<TextInputGroup <TextInputGroup
@ -231,13 +235,17 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={props.properties.minHeight} min={props.properties.minHeight}
max={props.properties.maxHeight} max={props.properties.maxHeight}
value={(RemoveWidthMargin(props.properties.height, value={(RemoveWidthMargin(
props.properties.height,
props.properties.margin.top, props.properties.margin.top,
props.properties.margin.bottom)).toString()} props.properties.margin.bottom
)).toString()}
onChange={(value) => { onChange={(value) => {
props.onChange('height', ApplyWidthMargin(Number(value), props.onChange('height', ApplyWidthMargin(
Number(value),
props.properties.margin.top, props.properties.margin.top,
props.properties.margin.bottom)); props.properties.margin.bottom
));
}} }}
isDisabled={props.properties.isFlex} isDisabled={props.properties.isFlex}
/> />
@ -270,7 +278,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={0} min={0}
value={(props.properties.margin.left ?? 0).toString()} value={(props.properties.margin.left ?? 0).toString()}
onChange={(value) => { props.onChange('left', Number(value), PropertyType.Margin); }}/> onChange={(value) => {
props.onChange('left', Number(value), PropertyType.Margin);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-mb`} id={`${props.properties.id}-mb`}
labelText={Text({ textId: '@ContainerMarginBottom' })} labelText={Text({ textId: '@ContainerMarginBottom' })}
@ -280,7 +290,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={0} min={0}
value={(props.properties.margin.bottom ?? 0).toString()} value={(props.properties.margin.bottom ?? 0).toString()}
onChange={(value) => { props.onChange('bottom', Number(value), PropertyType.Margin); }}/> onChange={(value) => {
props.onChange('bottom', Number(value), PropertyType.Margin);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-mt`} id={`${props.properties.id}-mt`}
labelText={Text({ textId: '@ContainerMarginTop' })} labelText={Text({ textId: '@ContainerMarginTop' })}
@ -290,7 +302,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={0} min={0}
value={(props.properties.margin.top ?? 0).toString()} value={(props.properties.margin.top ?? 0).toString()}
onChange={(value) => { props.onChange('top', Number(value), PropertyType.Margin); }}/> onChange={(value) => {
props.onChange('top', Number(value), PropertyType.Margin);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-mr`} id={`${props.properties.id}-mr`}
labelText={Text({ textId: '@ContainerMarginRight' })} labelText={Text({ textId: '@ContainerMarginRight' })}
@ -300,7 +314,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={0} min={0}
value={(props.properties.margin.right ?? 0).toString()} value={(props.properties.margin.right ?? 0).toString()}
onChange={(value) => { props.onChange('right', Number(value), PropertyType.Margin); }}/> onChange={(value) => {
props.onChange('right', Number(value), PropertyType.Margin);
}}/>
</div> </div>
</Category> </Category>
@ -377,7 +393,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
name='ShowSelfDimensions' name='ShowSelfDimensions'
labelText={Text({ textId: '@ContainerShowDimension' })} labelText={Text({ textId: '@ContainerShowDimension' })}
value={props.properties.dimensionOptions.selfDimensions.positions} value={props.properties.dimensionOptions.selfDimensions.positions}
onChange={(key, value) => { props.onChange(key, value, PropertyType.SelfDimension); }} onChange={(key, value) => {
props.onChange(key, value, PropertyType.SelfDimension);
}}
/> />
<InputGroup <InputGroup
labelText={Text({ textId: '@StyleStrokeColor' })} labelText={Text({ textId: '@StyleStrokeColor' })}
@ -386,7 +404,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='' inputClassName=''
type='color' type='color'
value={props.properties.dimensionOptions.selfDimensions.color ?? '#000000'} value={props.properties.dimensionOptions.selfDimensions.color ?? '#000000'}
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.SelfDimension); }}/> onChange={(e) => {
props.onChange('color', e.target.value, PropertyType.SelfDimension);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-selfDimensions-width`} id={`${props.properties.id}-selfDimensions-width`}
labelText={Text({ textId: '@StyleStrokeWidth' })} labelText={Text({ textId: '@StyleStrokeWidth' })}
@ -396,7 +416,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={0} min={0}
value={(props.properties.dimensionOptions.selfDimensions.width ?? 0).toString()} value={(props.properties.dimensionOptions.selfDimensions.width ?? 0).toString()}
onChange={(value) => { props.onChange('width', Number(value), PropertyType.SelfDimension); }}/> onChange={(value) => {
props.onChange('width', Number(value), PropertyType.SelfDimension);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-selfDimensions-dasharray`} id={`${props.properties.id}-selfDimensions-dasharray`}
labelText={Text({ textId: '@StyleStrokeDashArray' })} labelText={Text({ textId: '@StyleStrokeDashArray' })}
@ -405,7 +427,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='' inputClassName=''
type='text' type='text'
value={props.properties.dimensionOptions.selfDimensions.dashArray ?? ''} value={props.properties.dimensionOptions.selfDimensions.dashArray ?? ''}
onChange={(value) => { props.onChange('dashArray', value, PropertyType.SelfDimension); }}/> onChange={(value) => {
props.onChange('dashArray', value, PropertyType.SelfDimension);
}}/>
</div> </div>
} }
{ {
@ -417,7 +441,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
name='ShowSelfMarginsDimensions' name='ShowSelfMarginsDimensions'
labelText={Text({ textId: '@ContainerShowMarginsDimension' })} labelText={Text({ textId: '@ContainerShowMarginsDimension' })}
value={props.properties.dimensionOptions.selfMarginsDimensions.positions} value={props.properties.dimensionOptions.selfMarginsDimensions.positions}
onChange={(key, value) => { props.onChange(key, value, PropertyType.SelfMarginDimension); }} onChange={(key, value) => {
props.onChange(key, value, PropertyType.SelfMarginDimension);
}}
/> />
<InputGroup <InputGroup
labelText={Text({ textId: '@StyleStrokeColor' })} labelText={Text({ textId: '@StyleStrokeColor' })}
@ -426,7 +452,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='' inputClassName=''
type='color' type='color'
value={props.properties.dimensionOptions.selfMarginsDimensions.color ?? '#000000'} value={props.properties.dimensionOptions.selfMarginsDimensions.color ?? '#000000'}
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.SelfMarginDimension); }}/> onChange={(e) => {
props.onChange('color', e.target.value, PropertyType.SelfMarginDimension);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-selfMarginsDimensions-width`} id={`${props.properties.id}-selfMarginsDimensions-width`}
labelText={Text({ textId: '@StyleStrokeWidth' })} labelText={Text({ textId: '@StyleStrokeWidth' })}
@ -436,7 +464,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={0} min={0}
value={(props.properties.dimensionOptions.selfMarginsDimensions.width ?? 0).toString()} value={(props.properties.dimensionOptions.selfMarginsDimensions.width ?? 0).toString()}
onChange={(value) => { props.onChange('width', Number(value), PropertyType.SelfMarginDimension); }}/> onChange={(value) => {
props.onChange('width', Number(value), PropertyType.SelfMarginDimension);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-selfMarginsDimensions-dasharray`} id={`${props.properties.id}-selfMarginsDimensions-dasharray`}
labelText={Text({ textId: '@StyleStrokeDashArray' })} labelText={Text({ textId: '@StyleStrokeDashArray' })}
@ -445,7 +475,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='' inputClassName=''
type='text' type='text'
value={props.properties.dimensionOptions.selfMarginsDimensions.dashArray ?? ''} value={props.properties.dimensionOptions.selfMarginsDimensions.dashArray ?? ''}
onChange={(value) => { props.onChange('dashArray', value, PropertyType.SelfMarginDimension); }}/> onChange={(value) => {
props.onChange('dashArray', value, PropertyType.SelfMarginDimension);
}}/>
</div> </div>
} }
{ {
@ -457,7 +489,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
name='ShowChildrenDimensions' name='ShowChildrenDimensions'
labelText={Text({ textId: '@ContainerShowChildrenDimension' })} labelText={Text({ textId: '@ContainerShowChildrenDimension' })}
value={props.properties.dimensionOptions.childrenDimensions.positions} value={props.properties.dimensionOptions.childrenDimensions.positions}
onChange={(key, value) => { props.onChange(key, value, PropertyType.ChildrenDimensions); }} onChange={(key, value) => {
props.onChange(key, value, PropertyType.ChildrenDimensions);
}}
/> />
<InputGroup <InputGroup
labelText={Text({ textId: '@StyleStrokeColor' })} labelText={Text({ textId: '@StyleStrokeColor' })}
@ -466,7 +500,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='' inputClassName=''
type='color' type='color'
value={props.properties.dimensionOptions.childrenDimensions.color ?? '#000000'} value={props.properties.dimensionOptions.childrenDimensions.color ?? '#000000'}
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.ChildrenDimensions); }}/> onChange={(e) => {
props.onChange('color', e.target.value, PropertyType.ChildrenDimensions);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-childrenDimensions-width`} id={`${props.properties.id}-childrenDimensions-width`}
labelText={Text({ textId: '@StyleStrokeWidth' })} labelText={Text({ textId: '@StyleStrokeWidth' })}
@ -476,7 +512,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={0} min={0}
value={(props.properties.dimensionOptions.childrenDimensions.width ?? 0).toString()} value={(props.properties.dimensionOptions.childrenDimensions.width ?? 0).toString()}
onChange={(value) => { props.onChange('width', Number(value), PropertyType.ChildrenDimensions); }}/> onChange={(value) => {
props.onChange('width', Number(value), PropertyType.ChildrenDimensions);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-childrenDimensions-dasharray`} id={`${props.properties.id}-childrenDimensions-dasharray`}
labelText={Text({ textId: '@StyleStrokeDashArray' })} labelText={Text({ textId: '@StyleStrokeDashArray' })}
@ -485,7 +523,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='' inputClassName=''
type='text' type='text'
value={props.properties.dimensionOptions.childrenDimensions.dashArray ?? ''} value={props.properties.dimensionOptions.childrenDimensions.dashArray ?? ''}
onChange={(value) => { props.onChange('dashArray', value, PropertyType.ChildrenDimensions); }}/> onChange={(value) => {
props.onChange('dashArray', value, PropertyType.ChildrenDimensions);
}}/>
</div> </div>
} }
{ {
@ -497,7 +537,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
name='MarkPosition' name='MarkPosition'
value={props.properties.dimensionOptions.markPosition} value={props.properties.dimensionOptions.markPosition}
labelText={Text({ textId: '@ContainerMarkPosition' })} labelText={Text({ textId: '@ContainerMarkPosition' })}
onChange={(key, value) => { props.onChange(key, value, PropertyType.DimensionOptions); }} onChange={(key, value) => {
props.onChange(key, value, PropertyType.DimensionOptions);
}}
/> />
</div> </div>
<div className='grid grid-cols-1 gap-2'> <div className='grid grid-cols-1 gap-2'>
@ -507,7 +549,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
name='ShowDimensionWithMarks' name='ShowDimensionWithMarks'
labelText={Text({ textId: '@ContainerShowDimensionWithMarks' })} labelText={Text({ textId: '@ContainerShowDimensionWithMarks' })}
value={props.properties.dimensionOptions.dimensionWithMarks.positions} value={props.properties.dimensionOptions.dimensionWithMarks.positions}
onChange={(key, value) => { props.onChange(key, value, PropertyType.DimensionWithMarks); }} onChange={(key, value) => {
props.onChange(key, value, PropertyType.DimensionWithMarks);
}}
/> />
<InputGroup <InputGroup
labelText={Text({ textId: '@StyleStrokeColor' })} labelText={Text({ textId: '@StyleStrokeColor' })}
@ -516,7 +560,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='' inputClassName=''
type='color' type='color'
value={props.properties.dimensionOptions.dimensionWithMarks.color ?? '#000000'} value={props.properties.dimensionOptions.dimensionWithMarks.color ?? '#000000'}
onChange={(e) => { props.onChange('color', e.target.value, PropertyType.DimensionWithMarks); }}/> onChange={(e) => {
props.onChange('color', e.target.value, PropertyType.DimensionWithMarks);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-dimensionWithMarks-width`} id={`${props.properties.id}-dimensionWithMarks-width`}
labelText={Text({ textId: '@StyleStrokeWidth' })} labelText={Text({ textId: '@StyleStrokeWidth' })}
@ -526,7 +572,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={0} min={0}
value={(props.properties.dimensionOptions.dimensionWithMarks.width ?? 0).toString()} value={(props.properties.dimensionOptions.dimensionWithMarks.width ?? 0).toString()}
onChange={(value) => { props.onChange('width', Number(value), PropertyType.DimensionWithMarks); }}/> onChange={(value) => {
props.onChange('width', Number(value), PropertyType.DimensionWithMarks);
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-dimensionWithMarks-dasharray`} id={`${props.properties.id}-dimensionWithMarks-dasharray`}
labelText={Text({ textId: '@StyleStrokeDashArray' })} labelText={Text({ textId: '@StyleStrokeDashArray' })}
@ -535,7 +583,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='' inputClassName=''
type='text' type='text'
value={props.properties.dimensionOptions.dimensionWithMarks.dashArray ?? ''} value={props.properties.dimensionOptions.dimensionWithMarks.dashArray ?? ''}
onChange={(value) => { props.onChange('dashArray', value, PropertyType.DimensionWithMarks); }}/> onChange={(value) => {
props.onChange('dashArray', value, PropertyType.DimensionWithMarks);
}}/>
</div> </div>
</> </>
} }
@ -557,7 +607,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='col-span-3' inputClassName='col-span-3'
type='color' type='color'
value={props.properties.style.stroke ?? '#000000'} value={props.properties.style.stroke ?? '#000000'}
onChange={(e) => { props.onChange('stroke', e.target.value, PropertyType.Style); }}/> onChange={(e) => {
props.onChange('stroke', e.target.value, PropertyType.Style);
}}/>
<InputGroup <InputGroup
labelKey={`${props.properties.id}-strokeOpacity`} labelKey={`${props.properties.id}-strokeOpacity`}
labelText={Text({ textId: '@StyleStrokeOpacity' })} labelText={Text({ textId: '@StyleStrokeOpacity' })}
@ -569,7 +621,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
max={1} max={1}
step={0.01} step={0.01}
value={(props.properties.style.strokeOpacity ?? 1).toString()} value={(props.properties.style.strokeOpacity ?? 1).toString()}
onChange={(event) => { props.onChange('strokeOpacity', Number(event.target.value), PropertyType.Style); }} onChange={(event) => {
props.onChange('strokeOpacity', Number(event.target.value), PropertyType.Style);
}}
/> />
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-strokeWidth`} id={`${props.properties.id}-strokeWidth`}
@ -579,7 +633,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='col-span-3' inputClassName='col-span-3'
type='number' type='number'
value={(props.properties.style.strokeWidth ?? 1).toString()} value={(props.properties.style.strokeWidth ?? 1).toString()}
onChange={(value) => { props.onChange('strokeWidth', Number(value), PropertyType.Style); }} onChange={(value) => {
props.onChange('strokeWidth', Number(value), PropertyType.Style);
}}
/> />
<InputGroup <InputGroup
labelText={Text({ textId: '@StyleFill' })} labelText={Text({ textId: '@StyleFill' })}
@ -588,7 +644,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='col-span-3' inputClassName='col-span-3'
type='color' type='color'
value={props.properties.style.fill ?? '#000000'} value={props.properties.style.fill ?? '#000000'}
onChange={(e) => { props.onChange('fill', e.target.value, PropertyType.Style); }}/> onChange={(e) => {
props.onChange('fill', e.target.value, PropertyType.Style);
}}/>
<InputGroup <InputGroup
labelKey={`${props.properties.id}-fillOpacity`} labelKey={`${props.properties.id}-fillOpacity`}
labelText={Text({ textId: '@StyleFillOpacity' })} labelText={Text({ textId: '@StyleFillOpacity' })}
@ -600,7 +658,9 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
max={1} max={1}
step={0.01} step={0.01}
value={(props.properties.style.fillOpacity ?? 1).toString()} value={(props.properties.style.fillOpacity ?? 1).toString()}
onChange={(event) => { props.onChange('fillOpacity', Number(event.target.value), PropertyType.Style); }} onChange={(event) => {
props.onChange('fillOpacity', Number(event.target.value), PropertyType.Style);
}}
/> />
</div> </div>
</Category> </Category>

View file

@ -2,10 +2,10 @@ import { fireEvent, render, screen } from '@testing-library/react';
import * as React from 'react'; import * as React from 'react';
import { expect, describe, it, vi } from 'vitest'; import { expect, describe, it, vi } from 'vitest';
import { PositionReference } from '../../Enums/PositionReference'; import { PositionReference } from '../../Enums/PositionReference';
import { IContainerProperties } from '../../Interfaces/IContainerProperties'; import { type IContainerProperties } from '../../Interfaces/IContainerProperties';
import { Orientation } from '../../Enums/Orientation'; import { Orientation } from '../../Enums/Orientation';
import { ContainerProperties } from './ContainerProperties';
import { DEFAULT_DIMENSION_OPTION } from '../../utils/default'; import { DEFAULT_DIMENSION_OPTION } from '../../utils/default';
import { ContainerProperties } from './ContainerProperties';
describe.concurrent('Properties', () => { describe.concurrent('Properties', () => {
it('No properties', () => { it('No properties', () => {

View file

@ -2,8 +2,8 @@ import React from 'react';
import { type PropertyType } from '../../Enums/PropertyType'; import { type PropertyType } from '../../Enums/PropertyType';
import { type IContainerProperties } from '../../Interfaces/IContainerProperties'; import { type IContainerProperties } from '../../Interfaces/IContainerProperties';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { ContainerForm } from './ContainerForm';
import { type IContainerModel } from '../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { ContainerForm } from './ContainerForm';
interface IPropertiesProps { interface IPropertiesProps {
containers: Map<string, IContainerModel> containers: Map<string, IContainerModel>

View file

@ -3,12 +3,12 @@
*/ */
import { AddMethod } from '../../../Enums/AddMethod'; import { AddMethod } from '../../../Enums/AddMethod';
import { IAvailableContainer } from '../../../Interfaces/IAvailableContainer'; import { type IAvailableContainer } from '../../../Interfaces/IAvailableContainer';
import { IConfiguration } from '../../../Interfaces/IConfiguration'; import { type IConfiguration } from '../../../Interfaces/IConfiguration';
import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { IHistoryState } from '../../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../../Interfaces/IHistoryState';
import { IPattern, GetPattern, ContainerOrPattern } from '../../../Interfaces/IPattern'; import { type IPattern, GetPattern, type ContainerOrPattern } from '../../../Interfaces/IPattern';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { Orientation } from '../../../Enums/Orientation'; import { Orientation } from '../../../Enums/Orientation';
import { GetDefaultContainerProps } from '../../../utils/default'; import { GetDefaultContainerProps } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools'; import { FindContainerById } from '../../../utils/itertools';
@ -69,9 +69,7 @@ export function AddContainers(
const containers = structuredClone(current.containers); const containers = structuredClone(current.containers);
// Find the parent in the clone // Find the parent in the clone
const parentClone: IContainerModel | undefined = FindContainerById( const parentClone: IContainerModel | undefined = FindContainerById(containers, parentId);
containers, parentId
);
if (parentClone === null || parentClone === undefined) { if (parentClone === null || parentClone === undefined) {
throw new Error('[AddContainer] Container model was not found among children of the main container!'); throw new Error('[AddContainer] Container model was not found among children of the main container!');
@ -144,8 +142,14 @@ function AddNewContainerToParent(
const right: number = containerConfig.Margin?.right ?? 0; const right: number = containerConfig.Margin?.right ?? 0;
// Default coordinates // Default coordinates
let width = containerConfig.Width ?? containerConfig.MaxWidth ?? containerConfig.MinWidth ?? parentClone.properties.width; let width = containerConfig.Width ??
let height = containerConfig.Height ?? containerConfig.MaxHeight ?? containerConfig.MinHeight ?? parentClone.properties.height; containerConfig.MaxWidth ??
containerConfig.MinWidth ??
parentClone.properties.width;
let height = containerConfig.Height ??
containerConfig.MaxHeight ??
containerConfig.MinHeight ??
parentClone.properties.height;
let x = RestoreX(containerConfig.X ?? 0, width, containerConfig.PositionReference); let x = RestoreX(containerConfig.X ?? 0, width, containerConfig.PositionReference);
let y = RestoreY(containerConfig.Y ?? 0, height, containerConfig.PositionReference); let y = RestoreY(containerConfig.Y ?? 0, height, containerConfig.PositionReference);
@ -288,7 +292,8 @@ function InitializeDefaultChild(
configuration, configuration,
containers, containers,
parent, parent,
0, 0, 0,
0,
newCounters, newCounters,
symbols symbols
); );
@ -308,12 +313,17 @@ function InitializeChildrenWithPattern(
return; return;
} }
const configs: Map<string, IAvailableContainer> = new Map(configuration.AvailableContainers.map(config => [config.Type, config])); const configs = new Map<string, IAvailableContainer>(
const patterns: Map<string, IPattern> = new Map(configuration.Patterns.map(pattern => [pattern.id, pattern])); configuration.AvailableContainers.map(config => [config.Type, config])
);
const patterns = new Map<string, IPattern>(
configuration.Patterns.map(pattern => [pattern.id, pattern])
);
const containerOrPattern = GetPattern(patternId, configs, patterns); const containerOrPattern = GetPattern(patternId, configs, patterns);
if (containerOrPattern === undefined) { if (containerOrPattern === undefined) {
console.warn(`[InitializeChildrenWithPattern] PatternId ${patternId} was neither found as Pattern nor as IAvailableContainer`); console.warn('[InitializeChildrenWithPattern]' +
`PatternId ${patternId} was neither found as Pattern nor as IAvailableContainer`);
return; return;
} }
@ -356,7 +366,16 @@ function BuildPatterns(
while (levelSize-- !== 0) { while (levelSize-- !== 0) {
const node = queue.shift() as Node; const node = queue.shift() as Node;
const newParent = AddContainerInLevel(node, maxLevelSize, levelSize, configuration, containers, newCounters, symbols, configs); const newParent = AddContainerInLevel(
node,
maxLevelSize,
levelSize,
configuration,
containers,
newCounters,
symbols,
configs
);
if (newParent === undefined) { if (newParent === undefined) {
// node.pattern is not a IPattern, there is no children to iterate // node.pattern is not a IPattern, there is no children to iterate
@ -411,7 +430,8 @@ function AddContainerInLevel(
configuration, configuration,
containers, containers,
node.parent, node.parent,
index, 0, index,
0,
newCounters, newCounters,
symbols symbols
); );
@ -429,7 +449,8 @@ function AddContainerInLevel(
// and set the new parent as the child of this parent // and set the new parent as the child of this parent
const container = configs.get(pattern.wrapper); const container = configs.get(pattern.wrapper);
if (container === undefined) { if (container === undefined) {
console.warn(`[InitializeChildrenFromPattern] IAvailableContainer from pattern was not found in the configuration: ${pattern.wrapper}. console.warn('[InitializeChildrenFromPattern]' +
` IAvailableContainer from pattern was not found in the configuration: ${pattern.wrapper}.
Process will ignore the container.`); Process will ignore the container.`);
return { parent, pattern }; return { parent, pattern };
} }
@ -439,7 +460,8 @@ function AddContainerInLevel(
configuration, configuration,
containers, containers,
parent, parent,
0, 0, 0,
0,
newCounters, newCounters,
symbols, symbols,
false false

View file

@ -1,15 +1,15 @@
import { IHistoryState } from '../../../Interfaces/IHistoryState'; import Swal from 'sweetalert2';
import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IHistoryState } from '../../../Interfaces/IHistoryState';
import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { FindContainerById, MakeDFSIterator } from '../../../utils/itertools'; import { FindContainerById, MakeDFSIterator } from '../../../utils/itertools';
import { GetCurrentHistory } from '../Editor'; import { GetCurrentHistory } from '../Editor';
import { ApplyBehaviors, ApplyBehaviorsOnSiblings, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors'; import { ApplyBehaviors, ApplyBehaviorsOnSiblings, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import Swal from 'sweetalert2';
import { PropertyType } from '../../../Enums/PropertyType'; import { PropertyType } from '../../../Enums/PropertyType';
import { TransformX, TransformY } from '../../../utils/svg'; import { TransformX, TransformY } from '../../../utils/svg';
import { Orientation } from '../../../Enums/Orientation'; import { Orientation } from '../../../Enums/Orientation';
import { type IConfiguration } from '../../../Interfaces/IConfiguration';
import { AddContainers } from './AddContainer'; import { AddContainers } from './AddContainer';
import { IConfiguration } from '../../../Interfaces/IConfiguration';
/** /**
* Select a container * Select a container
@ -82,7 +82,8 @@ export function DeleteContainer(
const container = FindContainerById(containers, containerId); const container = FindContainerById(containers, containerId);
if (container === undefined) { if (container === undefined) {
throw new Error(`[DeleteContainer] Tried to delete a container that is not present in the main container: ${containerId}`); throw new Error('[DeleteContainer]' +
`Tried to delete a container that is not present in the main container: ${containerId}`);
} }
const parent = FindContainerById(containers, container.properties.parentId); const parent = FindContainerById(containers, container.properties.parentId);
@ -94,7 +95,8 @@ export function DeleteContainer(
text: 'Deleting the main container is not allowed!', text: 'Deleting the main container is not allowed!',
icon: 'error' icon: 'error'
}); });
throw new Error('[DeleteContainer] Tried to delete the main container! Deleting the main container is not allowed!'); throw new Error('[DeleteContainer]' +
'Tried to delete the main container! Deleting the main container is not allowed!');
} }
if (container === null || container === undefined) { if (container === null || container === undefined) {
@ -168,7 +170,9 @@ export function ReplaceByContainer(
containerParent.children.indexOf(containerId), containerParent.children.indexOf(containerId),
[{ Type: newContainerId }], [{ Type: newContainerId }],
containerParent.properties.id, containerParent.properties.id,
configuration, fullHistory, historyCurrentStep configuration,
fullHistory,
historyCurrentStep
); );
const historyDelete = DeleteContainer(containerId, historyAdd.history, historyCurrentStep + 1); const historyDelete = DeleteContainer(containerId, historyAdd.history, historyCurrentStep + 1);
@ -294,8 +298,7 @@ export function SortChildren(
const children = parent.children; const children = parent.children;
if (!isHorizontal) { if (!isHorizontal) {
parent.children.sort( parent.children.sort((aId, bId) => {
(aId, bId) => {
const a = FindContainerById(containers, aId); const a = FindContainerById(containers, aId);
const b = FindContainerById(containers, bId); const b = FindContainerById(containers, bId);
@ -315,13 +318,11 @@ export function SortChildren(
const indexA = children.indexOf(aId); const indexA = children.indexOf(aId);
const indexB = children.indexOf(bId); const indexB = children.indexOf(bId);
return indexA - indexB; return indexA - indexB;
} });
);
return; return;
} }
parent.children.sort( parent.children.sort((aId, bId) => {
(aId, bId) => {
const a = FindContainerById(containers, aId); const a = FindContainerById(containers, aId);
const b = FindContainerById(containers, bId); const b = FindContainerById(containers, bId);
@ -341,8 +342,7 @@ export function SortChildren(
const indexA = children.indexOf(aId); const indexA = children.indexOf(aId);
const indexB = children.indexOf(bId); const indexB = children.indexOf(bId);
return indexA - indexB; return indexA - indexB;
} });
);
} }
/** /**
@ -357,7 +357,8 @@ export function SortChildren(
function SetContainer( function SetContainer(
containers: Map<string, IContainerModel>, containers: Map<string, IContainerModel>,
container: IContainerModel, container: IContainerModel,
key: string, value: string | number | boolean | number[], key: string,
value: string | number | boolean | number[],
type: PropertyType, type: PropertyType,
symbols: Map<string, ISymbolModel> symbols: Map<string, ISymbolModel>
): void { ): void {
@ -396,7 +397,12 @@ function SetContainer(
* @param value Value of the property * @param value Value of the property
* @param type Type of the property * @param type Type of the property
*/ */
function AssignProperty(container: IContainerModel, key: string, value: string | number | boolean | number[], type: PropertyType): void { function AssignProperty(
container: IContainerModel,
key: string,
value: string | number | boolean | number[],
type: PropertyType
): void {
switch (type) { switch (type) {
case PropertyType.Style: case PropertyType.Style:
(container.properties.style as any)[key] = value; (container.properties.style as any)[key] = value;

View file

@ -1,22 +1,22 @@
import Swal from 'sweetalert2'; import Swal from 'sweetalert2';
import { Dispatch, SetStateAction } from 'react'; import { type Dispatch, type SetStateAction } from 'react';
import { AddMethod } from '../../../Enums/AddMethod'; import { AddMethod } from '../../../Enums/AddMethod';
import { IAction } from '../../../Interfaces/IAction'; import { type IAction } from '../../../Interfaces/IAction';
import { IConfiguration } from '../../../Interfaces/IConfiguration'; import { type IConfiguration } from '../../../Interfaces/IConfiguration';
import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { IHistoryState } from '../../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../../Interfaces/IHistoryState';
import { ISetContainerListRequest } from '../../../Interfaces/ISetContainerListRequest'; import { type ISetContainerListRequest } from '../../../Interfaces/ISetContainerListRequest';
import { ISetContainerListResponse } from '../../../Interfaces/ISetContainerListResponse'; import { type ISetContainerListResponse } from '../../../Interfaces/ISetContainerListResponse';
import { DISABLE_API } from '../../../utils/default'; import { DISABLE_API } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools'; import { FindContainerById } from '../../../utils/itertools';
import { SetContainerList } from '../../API/api'; import { SetContainerList } from '../../API/api';
import { IMenuAction } from '../../Menu/Menu'; import { type IMenuAction } from '../../Menu/Menu';
import { GetCurrentHistoryState } from '../Editor'; import { GetCurrentHistoryState } from '../Editor';
import { Text } from '../../Text/Text';
import { type IReplaceContainer } from '../../../Interfaces/IReplaceContainer';
import { AddContainers } from './AddContainer'; import { AddContainers } from './AddContainer';
import { DeleteContainer } from './ContainerOperations'; import { DeleteContainer } from './ContainerOperations';
import { DeleteSymbol } from './SymbolOperations'; import { DeleteSymbol } from './SymbolOperations';
import { Text } from '../../Text/Text';
import { IReplaceContainer } from '../../../Interfaces/IReplaceContainer';
export function InitActions( export function InitActions(
menuActions: Map<string, IMenuAction[]>, menuActions: Map<string, IMenuAction[]>,
@ -63,7 +63,9 @@ export function InitActions(
shortcut: '<kbd>R</kbd>', shortcut: '<kbd>R</kbd>',
action: (target: HTMLElement) => { action: (target: HTMLElement) => {
const targetContainer = FindContainerById(history[historyCurrentStep].containers, target.id); const targetContainer = FindContainerById(history[historyCurrentStep].containers, target.id);
const targetAvailableContainer = configuration.AvailableContainers.find((availableContainer) => availableContainer.Type === targetContainer?.properties.type); const targetAvailableContainer = configuration.AvailableContainers.find(
(availableContainer) => availableContainer.Type === targetContainer?.properties.type
);
if (targetAvailableContainer === undefined) { if (targetAvailableContainer === undefined) {
return; return;
@ -188,7 +190,10 @@ function GetAction(
}; };
} }
function GetPreviousAndNextSiblings(containers: Map<string, IContainerModel>, container: IContainerModel): { prev: IContainerModel | undefined, next: IContainerModel | undefined } { function GetPreviousAndNextSiblings(
containers: Map<string, IContainerModel>,
container: IContainerModel
): { prev: IContainerModel | undefined, next: IContainerModel | undefined } {
let prev; let prev;
let next; let next;
const parent = FindContainerById(containers, container.properties.parentId); const parent = FindContainerById(containers, container.properties.parentId);
@ -230,22 +235,21 @@ function HandleSetContainerList(
selectedContainer.properties.id, selectedContainer.properties.id,
configuration, configuration,
history, history,
historyCurrentStep); historyCurrentStep
);
setNewHistory(newHistory); setNewHistory(newHistory);
break; break;
} }
case AddMethod.Replace: case AddMethod.Replace:
setNewHistory( setNewHistory(HandleReplace(
HandleReplace(
containers, containers,
selectedContainer, selectedContainer,
response, response,
configuration, configuration,
history, history,
historyCurrentStep historyCurrentStep
) ));
);
break; break;
case AddMethod.ReplaceParent: { case AddMethod.ReplaceParent: {
const parent = FindContainerById(containers, selectedContainer.properties.parentId); const parent = FindContainerById(containers, selectedContainer.properties.parentId);
@ -257,16 +261,14 @@ function HandleSetContainerList(
}); });
return; return;
} }
setNewHistory( setNewHistory(HandleReplace(
HandleReplace(
containers, containers,
parent, parent,
response, response,
configuration, configuration,
history, history,
historyCurrentStep historyCurrentStep
) ));
);
break; break;
} }
} }

View file

@ -1,8 +1,8 @@
import { IHistoryState } from '../../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../../Interfaces/IHistoryState';
import { IConfiguration } from '../../../Interfaces/IConfiguration'; import { type 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 '../../../Interfaces/IEditorState'; import { type IEditorState } from '../../../Interfaces/IEditorState';
import { SHOW_SELECTOR_TEXT } from '../../../utils/default'; import { SHOW_SELECTOR_TEXT } from '../../../utils/default';
export function SaveEditorAsJSON( export function SaveEditorAsJSON(
@ -11,7 +11,9 @@ export function SaveEditorAsJSON(
configuration: IConfiguration configuration: IConfiguration
): void { ): void {
const exportName = 'state.json'; const exportName = 'state.json';
const spaces = import.meta.env.DEV ? 4 : 0; const spaces = import.meta.env.DEV
? 4
: 0;
const editorState: IEditorState = { const editorState: IEditorState = {
history, history,
historyCurrentStep, historyCurrentStep,

View file

@ -1,5 +1,5 @@
import { Dispatch, SetStateAction } from 'react'; import { type Dispatch, type SetStateAction } from 'react';
import { IHistoryState } from '../../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../../Interfaces/IHistoryState';
import { ENABLE_SHORTCUTS } from '../../../utils/default'; import { ENABLE_SHORTCUTS } from '../../../utils/default';
export function OnKey( export function OnKey(

View file

@ -4,7 +4,7 @@ import { type IHistoryState } from '../../../Interfaces/IHistoryState';
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { GetDefaultSymbolModel } from '../../../utils/default'; import { GetDefaultSymbolModel } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools'; import { FindContainerById } from '../../../utils/itertools';
import {RestoreX, RestoreY} from '../../../utils/svg'; import { RestoreX, RestoreY } from '../../../utils/svg';
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors'; import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { GetCurrentHistory, GetCurrentHistoryState, UpdateCounters } from '../Editor'; import { GetCurrentHistory, GetCurrentHistoryState, UpdateCounters } from '../Editor';
import { AddContainers } from './AddContainer'; import { AddContainers } from './AddContainer';
@ -212,5 +212,7 @@ function LinkContainer(
): void { ): void {
const oldSymbol = symbols.get(container.properties.linkedSymbolId); const oldSymbol = symbols.get(container.properties.linkedSymbolId);
LinkSymbol(container.properties.id, oldSymbol, symbol); LinkSymbol(container.properties.id, oldSymbol, symbol);
container.properties.linkedSymbolId = symbol !== undefined ? symbol.id : ''; container.properties.linkedSymbolId = symbol !== undefined
? symbol.id
: '';
} }

View file

@ -13,20 +13,23 @@
* or make them lose their property as a rigid body * or make them lose their property as a rigid body
*/ */
import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { Orientation } from '../../../Enums/Orientation'; import { Orientation } from '../../../Enums/Orientation';
import { ConstraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors';
import { FindContainerById } from '../../../utils/itertools'; import { FindContainerById } from '../../../utils/itertools';
import { ConstraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors';
/** /**
* Impose the container position to its siblings * Impose the container position to its siblings
* Apply the following modification to the overlapping rigid body container : * Apply the following modification to the overlapping rigid body container :
* @param container Container to impose its position * @param container Container to impose its position
*/ */
export function ApplyAnchor(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): IContainerModel { export function ApplyAnchor(
containers: Map<string, IContainerModel>,
container: IContainerModel,
parent: IContainerModel
): IContainerModel {
const rigidBodies: IContainerModel[] = []; const rigidBodies: IContainerModel[] = [];
parent.children.forEach( parent.children.forEach(childId => {
childId => {
const child = FindContainerById(containers, childId); const child = FindContainerById(containers, childId);
if (child === undefined) { if (child === undefined) {

View file

@ -1,5 +1,5 @@
import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { APPLY_BEHAVIORS_ON_CHILDREN, ENABLE_RIGID, ENABLE_SWAP } from '../../../utils/default'; import { APPLY_BEHAVIORS_ON_CHILDREN, ENABLE_RIGID, ENABLE_SWAP } from '../../../utils/default';
import { FindContainerById, MakeChildrenIterator } from '../../../utils/itertools'; import { FindContainerById, MakeChildrenIterator } from '../../../utils/itertools';
import { ApplyAnchor, GetOverlappingContainers } from './AnchorBehaviors'; import { ApplyAnchor, GetOverlappingContainers } from './AnchorBehaviors';
@ -14,7 +14,11 @@ import { ApplySymbol } from './SymbolBehaviors';
* @param container Container to recalculate its positions * @param container Container to recalculate its positions
* @returns Updated container * @returns Updated container
*/ */
export function ApplyBehaviors(containers: Map<string, IContainerModel>, container: IContainerModel, symbols: Map<string, ISymbolModel>): IContainerModel { export function ApplyBehaviors(
containers: Map<string, IContainerModel>,
container: IContainerModel,
symbols: Map<string, ISymbolModel>
): IContainerModel {
try { try {
const symbol = symbols.get(container.properties.linkedSymbolId); const symbol = symbols.get(container.properties.linkedSymbolId);
if (container.properties.linkedSymbolId !== '' && symbol !== undefined) { if (container.properties.linkedSymbolId !== '' && symbol !== undefined) {
@ -67,7 +71,8 @@ export function ApplyBehaviors(containers: Map<string, IContainerModel>, contain
export function ApplyBehaviorsOnSiblingsChildren( export function ApplyBehaviorsOnSiblingsChildren(
containers: Map<string, IContainerModel>, containers: Map<string, IContainerModel>,
newContainer: IContainerModel, newContainer: IContainerModel,
symbols: Map<string, ISymbolModel>): void { symbols: Map<string, ISymbolModel>
): void {
const parent = FindContainerById(containers, newContainer.properties.parentId); const parent = FindContainerById(containers, newContainer.properties.parentId);
if (parent === null || parent === undefined) { if (parent === null || parent === undefined) {
return; return;
@ -102,7 +107,11 @@ export function ApplyBehaviorsOnSiblingsChildren(
* @param symbols * @param symbols
* @returns * @returns
*/ */
export function ApplyBehaviorsOnSiblings(containers: Map<string, IContainerModel>, newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void { export function ApplyBehaviorsOnSiblings(
containers: Map<string, IContainerModel>,
newContainer: IContainerModel,
symbols: Map<string, ISymbolModel>
): void {
const parent = FindContainerById(containers, newContainer.properties.parentId); const parent = FindContainerById(containers, newContainer.properties.parentId);
if (parent === null || parent === undefined) { if (parent === null || parent === undefined) {
return; return;
@ -132,7 +141,11 @@ export function ApplyBehaviorsOnSiblings(containers: Map<string, IContainerModel
} }
}); });
} }
function UpdateWarning(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): void { function UpdateWarning(
containers: Map<string, IContainerModel>,
container: IContainerModel,
parent: IContainerModel
): void {
const targetContainers: IContainerModel[] = []; const targetContainers: IContainerModel[] = [];
parent.children.forEach((child) => { parent.children.forEach((child) => {
@ -146,7 +159,8 @@ function UpdateWarning(containers: Map<string, IContainerModel>, container: ICon
}); });
const overlappingContainers = GetOverlappingContainers(container, targetContainers); const overlappingContainers = GetOverlappingContainers(container, targetContainers);
if (overlappingContainers.length > 0) { if (overlappingContainers.length > 0) {
container.properties.warning = `There are overlapping containers: ${overlappingContainers.map(c => c.properties.id).join(' ')}`; container.properties.warning = 'There are overlapping containers: ' +
`${overlappingContainers.map(c => c.properties.id).join(' ')}`;
} else { } else {
container.properties.warning = ''; container.properties.warning = '';
} }

View file

@ -1,4 +1,4 @@
import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { Orientation } from '../../../Enums/Orientation'; import { Orientation } from '../../../Enums/Orientation';
import { Simplex } from '../../../utils/simplex'; import { Simplex } from '../../../utils/simplex';
import { ApplyWidthMargin, ApplyXMargin } from '../../../utils/svg'; import { ApplyWidthMargin, ApplyXMargin } from '../../../utils/svg';
@ -14,12 +14,20 @@ interface IFlexibleGroup {
* Flex the container and its siblings (mutate) * Flex the container and its siblings (mutate)
* @returns Flexed container * @returns Flexed container
*/ */
export function Flex(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): void { export function Flex(
containers: Map<string, IContainerModel>,
container: IContainerModel,
parent: IContainerModel
): void {
const isVertical = parent.properties.orientation === Orientation.Vertical; const isVertical = parent.properties.orientation === Orientation.Vertical;
if (isVertical) { if (isVertical) {
const wantedWidth = Math.min(container.properties.maxWidth, parent.properties.width); const wantedWidth = Math.min(container.properties.maxWidth, parent.properties.width);
container.properties.width = ApplyWidthMargin(wantedWidth, container.properties.margin.left, container.properties.margin.right); container.properties.width = ApplyWidthMargin(
wantedWidth,
container.properties.margin.left,
container.properties.margin.right
);
const flexibleGroups = GetVerticalFlexibleGroups(containers, parent); const flexibleGroups = GetVerticalFlexibleGroups(containers, parent);
for (const flexibleGroup of flexibleGroups) { for (const flexibleGroup of flexibleGroups) {
FlexGroupVertically(flexibleGroup); FlexGroupVertically(flexibleGroup);
@ -28,7 +36,11 @@ export function Flex(containers: Map<string, IContainerModel>, container: IConta
} }
const wantedHeight = Math.min(container.properties.maxHeight, parent.properties.height); const wantedHeight = Math.min(container.properties.maxHeight, parent.properties.height);
container.properties.height = ApplyWidthMargin(wantedHeight, container.properties.margin.top, container.properties.margin.bottom); container.properties.height = ApplyWidthMargin(
wantedHeight,
container.properties.margin.top,
container.properties.margin.bottom
);
const flexibleGroups = GetHorizontalFlexibleGroups(containers, parent); const flexibleGroups = GetHorizontalFlexibleGroups(containers, parent);
for (const flexibleGroup of flexibleGroups) { for (const flexibleGroup of flexibleGroups) {
FlexGroupHorizontally(flexibleGroup); FlexGroupHorizontally(flexibleGroup);
@ -40,7 +52,10 @@ export function Flex(containers: Map<string, IContainerModel>, container: IConta
* @param parent Parent in which the flexible children will be set in groups * @param parent Parent in which the flexible children will be set in groups
* @returns a list of groups of flexible containers * @returns a list of groups of flexible containers
*/ */
export function GetHorizontalFlexibleGroups(containers: Map<string, IContainerModel>, parent: IContainerModel): IFlexibleGroup[] { export function GetHorizontalFlexibleGroups(
containers: Map<string, IContainerModel>,
parent: IContainerModel
): IFlexibleGroup[] {
const flexibleGroups: IFlexibleGroup[] = []; const flexibleGroups: IFlexibleGroup[] = [];
let group: IContainerModel[] = []; let group: IContainerModel[] = [];
let offset = 0; let offset = 0;
@ -158,7 +173,11 @@ function FlexGroupHorizontally(flexibleGroup: IFlexibleGroup): void {
continue; continue;
} }
sibling.properties.x = ApplyXMargin(right, sibling.properties.margin.left); sibling.properties.x = ApplyXMargin(right, sibling.properties.margin.left);
sibling.properties.width = ApplyWidthMargin(wantedWidth, sibling.properties.margin.left, sibling.properties.margin.right); sibling.properties.width = ApplyWidthMargin(
wantedWidth,
sibling.properties.margin.left,
sibling.properties.margin.right
);
right += wantedWidth; right += wantedWidth;
} }
@ -174,7 +193,11 @@ function FlexGroupHorizontally(flexibleGroup: IFlexibleGroup): void {
// apply the solutions // apply the solutions
for (let i = 0; i < flexibleContainers.length; i++) { for (let i = 0; i < flexibleContainers.length; i++) {
flexibleContainers[i].properties.width = ApplyWidthMargin(solutions[i], flexibleContainers[i].properties.margin.left, flexibleContainers[i].properties.margin.right); flexibleContainers[i].properties.width = ApplyWidthMargin(
solutions[i],
flexibleContainers[i].properties.margin.left,
flexibleContainers[i].properties.margin.right
);
} }
// move the containers // move the containers
@ -229,7 +252,11 @@ function FlexGroupVertically(flexibleGroup: IFlexibleGroup): void {
continue; continue;
} }
sibling.properties.y = ApplyXMargin(right, sibling.properties.margin.top); sibling.properties.y = ApplyXMargin(right, sibling.properties.margin.top);
sibling.properties.height = ApplyWidthMargin(wantedHeight, sibling.properties.margin.top, sibling.properties.margin.bottom); sibling.properties.height = ApplyWidthMargin(
wantedHeight,
sibling.properties.margin.top,
sibling.properties.margin.bottom
);
right += wantedHeight; right += wantedHeight;
} }
@ -245,7 +272,11 @@ function FlexGroupVertically(flexibleGroup: IFlexibleGroup): void {
// apply the solutions // apply the solutions
for (let i = 0; i < flexibleContainers.length; i++) { for (let i = 0; i < flexibleContainers.length; i++) {
flexibleContainers[i].properties.height = ApplyWidthMargin(solutions[i], flexibleContainers[i].properties.margin.top, flexibleContainers[i].properties.margin.bottom); flexibleContainers[i].properties.height = ApplyWidthMargin(
solutions[i],
flexibleContainers[i].properties.margin.top,
flexibleContainers[i].properties.margin.bottom
);
} }
// move the containers // move the containers

View file

@ -1,4 +1,4 @@
import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { Orientation } from '../../../Enums/Orientation'; import { Orientation } from '../../../Enums/Orientation';
import { MakeChildrenIterator, ReversePairwise } from '../../../utils/itertools'; import { MakeChildrenIterator, ReversePairwise } from '../../../utils/itertools';
import { Flex } from './FlexBehaviors'; import { Flex } from './FlexBehaviors';

View file

@ -6,8 +6,8 @@
* If the contraints fails, an error message will be returned * If the contraints fails, an error message will be returned
*/ */
import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { ISizePointer } from '../../../Interfaces/ISizePointer'; import { type ISizePointer } from '../../../Interfaces/ISizePointer';
import { Orientation } from '../../../Enums/Orientation'; import { Orientation } from '../../../Enums/Orientation';
import { ENABLE_HARD_RIGID } from '../../../utils/default'; import { ENABLE_HARD_RIGID } from '../../../utils/default';
import { FindContainerById, MakeChildrenIterator } from '../../../utils/itertools'; import { FindContainerById, MakeChildrenIterator } from '../../../utils/itertools';
@ -141,9 +141,7 @@ export function ConstraintBodyInsideUnallocatedWidth(
// Check if there is still some space // Check if there is still some space
if (availableWidths.length === 0) { if (availableWidths.length === 0) {
throw new Error( throw new Error('No available space found on the parent container. Try to free the parent a bit.');
'No available space found on the parent container. Try to free the parent a bit.'
);
} }
const containerId = container.properties.id; const containerId = container.properties.id;
@ -158,8 +156,7 @@ export function ConstraintBodyInsideUnallocatedWidth(
// Check if the container actually fit inside // Check if the container actually fit inside
// It will usually fit if it was alrady fitting // It will usually fit if it was alrady fitting
const availableWidthFound = availableWidths.find((width) => const availableWidthFound = availableWidths.find((width) =>
IsFitting(containerHeight, width) IsFitting(containerHeight, width));
);
if (availableWidthFound === undefined) { if (availableWidthFound === undefined) {
const { x, width } = TrySqueeze(containerY, containerHeight, containerMinHeight, containerId, availableWidths); const { x, width } = TrySqueeze(containerY, containerHeight, containerMinHeight, containerId, availableWidths);
@ -189,8 +186,7 @@ export function ConstraintBodyInsideUnallocatedWidth(
// Check if the container actually fit inside // Check if the container actually fit inside
// It will usually fit if it was alrady fitting // It will usually fit if it was alrady fitting
const availableWidthFound = availableWidths.find((width) => const availableWidthFound = availableWidths.find((width) =>
IsFitting(containerWidth, width) IsFitting(containerWidth, width));
);
if (availableWidthFound === undefined) { if (availableWidthFound === undefined) {
const { x, width } = TrySqueeze(containerX, containerWidth, containerMinWidth, containerId, availableWidths); const { x, width } = TrySqueeze(containerX, containerWidth, containerMinWidth, containerId, availableWidths);
@ -242,11 +238,14 @@ function TrySqueeze(
}; };
} }
function SortAvailableWidthsByClosest(containerX: number, containerWidth: number, availableWidths: ISizePointer[]): void { function SortAvailableWidthsByClosest(
containerX: number,
containerWidth: number,
availableWidths: ISizePointer[]
): void {
const middle = containerX + containerWidth / 2; const middle = containerX + containerWidth / 2;
// Sort the available width to find the space with the closest position // Sort the available width to find the space with the closest position
availableWidths.sort( availableWidths.sort((width1, width2) => {
(width1, width2) => {
let compared1X = width1.x; let compared1X = width1.x;
if (width1.x < containerX) { if (width1.x < containerX) {
compared1X = width1.x + width1.width - containerWidth; compared1X = width1.x + width1.width - containerWidth;
@ -258,8 +257,7 @@ function SortAvailableWidthsByClosest(containerX: number, containerWidth: number
} }
return Math.abs(compared1X - middle) - Math.abs(compared2X - middle); return Math.abs(compared1X - middle) - Math.abs(compared2X - middle);
} });
);
} }
/** /**
@ -268,8 +266,10 @@ function SortAvailableWidthsByClosest(containerX: number, containerWidth: number
* @param sizePointer Size space to check * @param sizePointer Size space to check
* @returns * @returns
*/ */
function IsFitting(containerWidth: number, function IsFitting(
sizePointer: ISizePointer): boolean { containerWidth: number,
sizePointer: ISizePointer
): boolean {
return containerWidth <= sizePointer.width; return containerWidth <= sizePointer.width;
} }
@ -280,7 +280,8 @@ function IsFitting(containerWidth: number,
* (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 {ISizePointer[]} 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(
x: number, x: number,
@ -303,8 +304,12 @@ function GetAvailableWidths(
if (child === exception) { if (child === exception) {
continue; continue;
} }
const childX = isHorizontal ? child.properties.x : child.properties.y; const childX = isHorizontal
const childWidth = isHorizontal ? child.properties.width : child.properties.height; ? child.properties.x
: child.properties.y;
const childWidth = isHorizontal
? child.properties.width
: child.properties.height;
// get the space of the child that is inside the parent // get the space of the child that is inside the parent
let newUnallocatedSpace: ISizePointer[] = []; let newUnallocatedSpace: ISizePointer[] = [];

View file

@ -2,15 +2,16 @@
* Swap two flex container when one is overlapping another * Swap two flex container when one is overlapping another
*/ */
import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { Orientation } from '../../../Enums/Orientation'; import { Orientation } from '../../../Enums/Orientation';
import { GetHorizontallyOverlappingContainers, GetVerticallyOverlappingContainers } from './AnchorBehaviors';
import { MakeChildrenIterator } from '../../../utils/itertools'; import { MakeChildrenIterator } from '../../../utils/itertools';
import { GetHorizontallyOverlappingContainers, GetVerticallyOverlappingContainers } from './AnchorBehaviors';
export function ApplySwap( export function ApplySwap(
containers: Map<string, IContainerModel>, containers: Map<string, IContainerModel>,
container: IContainerModel, container: IContainerModel,
parent: IContainerModel): void { parent: IContainerModel
): void {
const children = [...MakeChildrenIterator(containers, parent.children)]; const children = [...MakeChildrenIterator(containers, parent.children)];
const isVertical = parent.properties.orientation === Orientation.Vertical; const isVertical = parent.properties.orientation === Orientation.Vertical;
@ -36,7 +37,10 @@ export function SwapHorizontally(container: IContainerModel, children: IContaine
} }
// swap positions // swap positions
[overlappingContainer.properties.x, container.properties.x] = [container.properties.x, overlappingContainer.properties.x]; [
overlappingContainer.properties.x,
container.properties.x
] = [container.properties.x, overlappingContainer.properties.x];
const indexContainer = children.indexOf(container); const indexContainer = children.indexOf(container);
const indexOverlapping = children.indexOf(overlappingContainer); const indexOverlapping = children.indexOf(overlappingContainer);
[children[indexContainer], children[indexOverlapping]] = [children[indexOverlapping], children[indexContainer]]; [children[indexContainer], children[indexOverlapping]] = [children[indexOverlapping], children[indexContainer]];
@ -56,7 +60,10 @@ export function SwapVertically(container: IContainerModel, children: IContainerM
} }
// swap positions // swap positions
[overlappingContainer.properties.y, container.properties.y] = [container.properties.y, overlappingContainer.properties.y]; [
overlappingContainer.properties.y,
container.properties.y
] = [container.properties.y, overlappingContainer.properties.y];
const indexContainer = children.indexOf(container); const indexContainer = children.indexOf(container);
const indexOverlapping = children.indexOf(overlappingContainer); const indexOverlapping = children.indexOf(overlappingContainer);
[children[indexContainer], children[indexOverlapping]] = [children[indexOverlapping], children[indexContainer]]; [children[indexContainer], children[indexOverlapping]] = [children[indexOverlapping], children[indexContainer]];

View file

@ -3,16 +3,22 @@ import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { ApplyParentTransform, FindContainerById } from '../../../utils/itertools'; import { ApplyParentTransform, FindContainerById } from '../../../utils/itertools';
import { RestoreX, RestoreY, TransformX, TransformY } from '../../../utils/svg'; import { RestoreX, RestoreY, TransformX, TransformY } from '../../../utils/svg';
export function ApplySymbol(containers: Map<string, IContainerModel>, export function ApplySymbol(
containers: Map<string, IContainerModel>,
container: IContainerModel, container: IContainerModel,
symbol: ISymbolModel): IContainerModel { symbol: ISymbolModel
): IContainerModel {
if (symbol.isVertical) { if (symbol.isVertical) {
container.properties.y = TransformY(symbol.offset, container.properties.y = TransformY(
symbol.offset,
symbol.height, symbol.height,
symbol.config.PositionReference); symbol.config.PositionReference
container.properties.y = RestoreY(container.properties.y, );
container.properties.y = RestoreY(
container.properties.y,
container.properties.height, container.properties.height,
container.properties.positionReference); container.properties.positionReference
);
const parent = FindContainerById(containers, container.properties.parentId); const parent = FindContainerById(containers, container.properties.parentId);
let y = 0; let y = 0;
if (parent !== undefined && parent !== null) { if (parent !== undefined && parent !== null) {
@ -24,10 +30,13 @@ export function ApplySymbol(containers: Map<string, IContainerModel>,
container.properties.x = TransformX( container.properties.x = TransformX(
symbol.offset, symbol.offset,
symbol.width, symbol.width,
symbol.config.PositionReference); symbol.config.PositionReference
container.properties.x = RestoreX(container.properties.x, );
container.properties.x = RestoreX(
container.properties.x,
container.properties.width, container.properties.width,
container.properties.positionReference); container.properties.positionReference
);
const parent = FindContainerById(containers, container.properties.parentId); const parent = FindContainerById(containers, container.properties.parentId);
let x = 0; let x = 0;
if (parent !== undefined && parent !== null) { if (parent !== undefined && parent !== null) {

View file

@ -3,17 +3,22 @@ import './Editor.scss';
import { type IConfiguration } from '../../Interfaces/IConfiguration'; import { type IConfiguration } from '../../Interfaces/IConfiguration';
import { type IHistoryState } from '../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../Interfaces/IHistoryState';
import { UI } from '../UI/UI'; import { UI } from '../UI/UI';
import { UseCustomEvents, UseEditorListener } from '../../Events/EditorEvents';
import { MAX_HISTORY } from '../../utils/default';
import { FindContainerById } from '../../utils/itertools';
import { Menu } from '../Menu/Menu';
import { type IReplaceContainer } from '../../Interfaces/IReplaceContainer';
import { SelectContainer, DeleteContainer, OnPropertyChange, ReplaceByContainer } from './Actions/ContainerOperations'; import { SelectContainer, DeleteContainer, OnPropertyChange, ReplaceByContainer } from './Actions/ContainerOperations';
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save'; import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save';
import { OnKey } from './Actions/Shortcuts'; import { OnKey } from './Actions/Shortcuts';
import { UseCustomEvents, UseEditorListener } from '../../Events/EditorEvents'; import {
import { MAX_HISTORY } from '../../utils/default'; AddSymbol,
import { AddSymbol, OnPropertyChange as OnSymbolPropertyChange, DeleteSymbol, SelectSymbol } from './Actions/SymbolOperations'; OnPropertyChange as OnSymbolPropertyChange,
import { FindContainerById } from '../../utils/itertools'; DeleteSymbol,
import { Menu } from '../Menu/Menu'; SelectSymbol
} from './Actions/SymbolOperations';
import { InitActions } from './Actions/ContextMenuActions'; import { InitActions } from './Actions/ContextMenuActions';
import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer'; import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer';
import { type IReplaceContainer } from '../../Interfaces/IReplaceContainer';
interface IEditorProps { interface IEditorProps {
root: Element | Document root: Element | Document
@ -61,16 +66,20 @@ function UseNewHistoryState(
): (newHistory: IHistoryState[], historyCurrentStep?: number) => void { ): (newHistory: IHistoryState[], historyCurrentStep?: number) => void {
return (newHistory, historyCurrentStep?: number) => { return (newHistory, historyCurrentStep?: number) => {
setHistory(newHistory); setHistory(newHistory);
setHistoryCurrentStep(historyCurrentStep !== undefined && historyCurrentStep !== null ? historyCurrentStep : newHistory.length - 1); setHistoryCurrentStep(historyCurrentStep !== undefined && historyCurrentStep !== null
? historyCurrentStep
: newHistory.length - 1);
}; };
} }
export function Editor(props: IEditorProps): JSX.Element { export function Editor(props: IEditorProps): JSX.Element {
// States // States
const [history, setHistory] = React.useState<IHistoryState[]>(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 [replaceContainer, setReplaceContainer] = React.useState<IReplaceContainer>({ isReplacing: false, id: undefined, category: undefined }); const [
replaceContainer,
setReplaceContainer
] = React.useState<IReplaceContainer>({ isReplacing: false, id: undefined, category: undefined });
const editorRef = useRef<HTMLDivElement>(null); const editorRef = useRef<HTMLDivElement>(null);
const setNewHistory = UseNewHistoryState(setHistory, setHistoryCurrentStep); const setNewHistory = UseNewHistoryState(setHistory, setHistoryCurrentStep);
@ -86,9 +95,7 @@ export function Editor(props: IEditorProps): JSX.Element {
setHistoryCurrentStep, setHistoryCurrentStep,
() => { () => {
const current = GetCurrentHistoryState(history, historyCurrentStep); const current = GetCurrentHistoryState(history, historyCurrentStep);
setNewHistory( setNewHistory(DeleteContainer(current.selectedContainerId, history, historyCurrentStep));
DeleteContainer(current.selectedContainerId, history, historyCurrentStep)
);
}, },
ResetState ResetState
); );
@ -134,25 +141,24 @@ export function Editor(props: IEditorProps): JSX.Element {
}} }}
replaceContainer={replaceContainer} replaceContainer={replaceContainer}
selectContainer={(container) => { selectContainer={(container) => {
setNewHistory( setNewHistory(SelectContainer(
SelectContainer(
container, container,
history, history,
historyCurrentStep historyCurrentStep
)); ));
}} }}
deleteContainer={(containerId: string) => { deleteContainer={(containerId: string) => {
setNewHistory( setNewHistory(DeleteContainer(
DeleteContainer(
containerId, containerId,
history, history,
historyCurrentStep historyCurrentStep
)); ));
}} }}
onPropertyChange={(key, value, type) => { onPropertyChange={(key, value, type) => {
setNewHistory( setNewHistory(OnPropertyChange(
OnPropertyChange( key,
key, value, type, value,
type,
selected, selected,
history, history,
historyCurrentStep historyCurrentStep
@ -183,20 +189,17 @@ export function Editor(props: IEditorProps): JSX.Element {
} }
}} }}
addContainerAt={(index, type, parent) => { addContainerAt={(index, type, parent) => {
setNewHistory( setNewHistory(AddContainer(
AddContainer(
index, index,
type, type,
parent, parent,
configuration, configuration,
history, history,
historyCurrentStep historyCurrentStep
) ));
);
}} }}
addSymbol={(type) => { addSymbol={(type) => {
setNewHistory( setNewHistory(AddSymbol(
AddSymbol(
type, type,
configuration, configuration,
history, history,
@ -204,24 +207,22 @@ export function Editor(props: IEditorProps): JSX.Element {
)); ));
}} }}
onSymbolPropertyChange={(key, value) => { onSymbolPropertyChange={(key, value) => {
setNewHistory( setNewHistory(OnSymbolPropertyChange(
OnSymbolPropertyChange( key,
key, value, value,
history, history,
historyCurrentStep historyCurrentStep
)); ));
}} }}
selectSymbol={(symbolId) => { selectSymbol={(symbolId) => {
setNewHistory( setNewHistory(SelectSymbol(
SelectSymbol(
symbolId, symbolId,
history, history,
historyCurrentStep historyCurrentStep
)); ));
}} }}
deleteSymbol={(symbolId) => { deleteSymbol={(symbolId) => {
setNewHistory( setNewHistory(DeleteSymbol(
DeleteSymbol(
symbolId, symbolId,
history, history,
historyCurrentStep historyCurrentStep

View file

@ -88,7 +88,8 @@ function HandleOnDrop(
addContainer( addContainer(
targetContainer.children.length, targetContainer.children.length,
type, type,
targetContainer.properties.id); targetContainer.properties.id
);
return; return;
} }
@ -113,7 +114,8 @@ function HandleOnDrop(
addContainer( addContainer(
targetContainer.children.length, targetContainer.children.length,
type, type,
targetContainer.properties.id); targetContainer.properties.id
);
} else { } else {
const index = parent.children.indexOf(targetContainer.properties.id); const index = parent.children.indexOf(targetContainer.properties.id);
addContainer( addContainer(
@ -219,12 +221,10 @@ function ElementsListRow(
? 'border-l-blue-400 group-hover:border-l-blue-300' ? 'border-l-blue-400 group-hover:border-l-blue-300'
: 'border-l-slate-400 group-hover:border-l-slate-300'; : 'border-l-slate-400 group-hover:border-l-slate-300';
for (let i = 0; i < depth; i++) { for (let i = 0; i < depth; i++) {
verticalBars.push( verticalBars.push(<span
<span
key={`${key}-${i}`} key={`${key}-${i}`}
className={`h-full border-l-2 pr-2 ${verticalBarSelectedClass}`} className={`h-full border-l-2 pr-2 ${verticalBarSelectedClass}`}
></span> ></span>);
);
} }
const buttonSelectedClass: string = isSelected const buttonSelectedClass: string = isSelected
@ -241,7 +241,9 @@ function ElementsListRow(
style={style} style={style}
title={container.properties.warning} title={container.properties.warning}
onClick={() => { selectContainer(container.properties.id); }} onClick={() => { selectContainer(container.properties.id); }}
onDrop={(event) => { HandleOnDrop(event, containers, mainContainer, addContainer); }} onDrop={(event) => {
HandleOnDrop(event, containers, mainContainer, addContainer);
}}
onDragOver={(event) => { HandleDragOver(event, mainContainer); }} onDragOver={(event) => { HandleDragOver(event, mainContainer); }}
onDragLeave={(event) => { HandleDragLeave(event); }} onDragLeave={(event) => { HandleDragLeave(event); }}
> >

View file

@ -6,14 +6,18 @@ interface IFloatingButtonProps {
className: string className: string
} }
function ToggleState(isHidden: boolean, function ToggleState(
setHidden: React.Dispatch<React.SetStateAction<boolean>>): void { isHidden: boolean,
setHidden: React.Dispatch<React.SetStateAction<boolean>>
): void {
setHidden(!isHidden); setHidden(!isHidden);
} }
export function FloatingButton(props: IFloatingButtonProps): JSX.Element { export function FloatingButton(props: IFloatingButtonProps): JSX.Element {
const [isHidden, setHidden] = React.useState(true); const [isHidden, setHidden] = React.useState(true);
const buttonListClasses = isHidden ? 'invisible opacity-0' : 'visible opacity-100'; const buttonListClasses = isHidden
? 'invisible opacity-0'
: 'visible opacity-100';
const icon = isHidden const icon = isHidden
? <Bars3Icon className="floating-btn" /> ? <Bars3Icon className="floating-btn" />
: <XMarkIcon className="floating-btn" />; : <XMarkIcon className="floating-btn" />;
@ -24,9 +28,10 @@ export function FloatingButton(props: IFloatingButtonProps): JSX.Element {
{props.children} {props.children}
</div> </div>
<button type="button" <button type="button"
className={'transition-all w-14 h-14 p-2 align-middle items-center justify-center rounded-full bg-blue-500 hover:bg-blue-800'} className={'transition-all w-14 h-14 p-2 align-middle' +
' items-center justify-center rounded-full bg-blue-500 hover:bg-blue-800'}
title='Open menu' title='Open menu'
onClick={() => ToggleState(isHidden, setHidden)} onClick={() => { ToggleState(isHidden, setHidden); }}
> >
{icon} {icon}
</button> </button>

View file

@ -1,19 +1,23 @@
import * as React from 'react'; import * as React from 'react';
import { CameraIcon, ArrowUpOnSquareIcon } from '@heroicons/react/24/outline'; import { CameraIcon, ArrowUpOnSquareIcon } from '@heroicons/react/24/outline';
import { type IUIProps } from '../UI/UI';
import { FloatingButton } from './FloatingButton'; import { FloatingButton } from './FloatingButton';
import { IUIProps } from '../UI/UI';
export function MenuButton(props: IUIProps): JSX.Element { export function MenuButton(props: IUIProps): JSX.Element {
return <FloatingButton className={'fixed z-10 flex flex-col gap-2 items-center bottom-12 right-12'}> return <FloatingButton className={'fixed z-10 flex flex-col gap-2 items-center bottom-12 right-12'}>
<button type="button" <button type="button"
className={'transition-all w-10 h-10 p-2 align-middle items-center justify-center rounded-full bg-blue-500 hover:bg-blue-800'} className={'transition-all w-10 h-10 p-2' +
' align-middle items-center justify-center' +
' rounded-full bg-blue-500 hover:bg-blue-800'}
title='Export as JSON' title='Export as JSON'
onClick={props.saveEditorAsJSON} onClick={props.saveEditorAsJSON}
> >
<ArrowUpOnSquareIcon className="heroicon text-white" /> <ArrowUpOnSquareIcon className="heroicon text-white" />
</button> </button>
<button type="button" <button type="button"
className={'transition-all w-10 h-10 p-2 align-middle items-center justify-center rounded-full bg-blue-500 hover:bg-blue-800'} className={'transition-all w-10 h-10 p-2' +
' align-middle items-center justify-center' +
' rounded-full bg-blue-500 hover:bg-blue-800'}
title='Export as SVG' title='Export as SVG'
onClick={props.saveEditorAsSVG} onClick={props.saveEditorAsSVG}
> >

View file

@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { FixedSizeList as List } from 'react-window'; import { FixedSizeList as List } from 'react-window';
import { IHistoryState } from '../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../Interfaces/IHistoryState';
import { TITLE_BAR_HEIGHT } from '../Sidebar/Sidebar'; import { TITLE_BAR_HEIGHT } from '../Sidebar/Sidebar';
interface IHistoryProps { interface IHistoryProps {
@ -23,7 +23,7 @@ export function History(props: IHistoryProps): JSX.Element {
<button type="button" <button type="button"
key={reversedIndex} key={reversedIndex}
style={style} style={style}
onClick={() => props.jumpTo(reversedIndex)} onClick={() => { props.jumpTo(reversedIndex); }}
title={step.lastAction} title={step.lastAction}
className={`w-full elements-sidebar-row border-blue-500 whitespace-pre overflow-hidden className={`w-full elements-sidebar-row border-blue-500 whitespace-pre overflow-hidden
text-left text-sm font-medium transition-all ${selectedClass}`} text-left text-sm font-medium transition-all ${selectedClass}`}

View file

@ -37,7 +37,9 @@ export function TextInputGroup(props: ITextInputGroupProps): JSX.Element {
} }
} }
const warningClass = props.value !== value ? 'focus:border-yellow-300 border-yellow-300 focus:ring-yellow-300 ring-yellow-300' : ''; const warningClass = props.value !== value
? 'focus:border-yellow-300 border-yellow-300 focus:ring-yellow-300 ring-yellow-300'
: '';
return <> return <>
<label <label
@ -53,7 +55,7 @@ export function TextInputGroup(props: ITextInputGroupProps): JSX.Element {
className={`${className} ${props.inputClassName} ${warningClass}`} className={`${className} ${props.inputClassName} ${warningClass}`}
type={props.type} type={props.type}
value={value} value={value}
onChange={(event) => setValue(event.target.value)} onChange={(event) => { setValue(event.target.value); }}
onKeyUp={OnKeyUp} onKeyUp={OnKeyUp}
min={props.min} min={props.min}
max={props.max} max={props.max}

View file

@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { createContext, useState } from 'react'; import { createContext, useState } from 'react';
import { ILanguage } from '../../Interfaces/ILanguage'; import { type ILanguage } from '../../Interfaces/ILanguage';
import { languageOptions, translations } from '../../Translations/Translations'; import { languageOptions, translations } from '../../Translations/Translations';
import { DEFAULT_LANGUAGE } from '../../utils/default'; import { DEFAULT_LANGUAGE } from '../../utils/default';
@ -10,13 +10,17 @@ export const LanguageContext = createContext<ILanguage>({
dictionary: translations.en dictionary: translations.en
}); });
export function LanguageProvider({ children }: { children: React.ReactNode | React.ReactNode[] | undefined }): JSX.Element { export function LanguageProvider(
{ children }: { children: React.ReactNode | React.ReactNode[] | undefined }
): JSX.Element {
const [language, setLanguage] = useState(DEFAULT_LANGUAGE); const [language, setLanguage] = useState(DEFAULT_LANGUAGE);
const provider = { const provider = {
language, language,
dictionary: translations[language], dictionary: translations[language],
languageChange: (selected: string) => { languageChange: (selected: string) => {
const newLanguage = languageOptions[selected] !== undefined ? selected : DEFAULT_LANGUAGE; const newLanguage = languageOptions[selected] !== undefined
? selected
: DEFAULT_LANGUAGE;
setLanguage(newLanguage); setLanguage(newLanguage);
} }
}; };

View file

@ -45,7 +45,7 @@ export function MainMenu(props: IMainMenuProps): JSX.Element {
</label> </label>
</form> </form>
<button type="button" <button type="button"
onClick={() => setWindowState(WindowState.Main)} onClick={() => { setWindowState(WindowState.Main); }}
className='normal-btn block className='normal-btn block
mt-8 ' mt-8 '
> >
@ -70,7 +70,7 @@ export function MainMenu(props: IMainMenuProps): JSX.Element {
<button <button
type="button" type="button"
className='mainmenu-btn' className='mainmenu-btn'
onClick={() => setWindowState(WindowState.Load)} onClick={() => { setWindowState(WindowState.Load); }}
> >
{Text({ textId: '@LoadConfigFile' })} {Text({ textId: '@LoadConfigFile' })}
</button> </button>

View file

@ -1,6 +1,6 @@
import useSize from '@react-hook/size'; import useSize from '@react-hook/size';
import * as React from 'react'; import * as React from 'react';
import { IPoint } from '../../Interfaces/IPoint'; import { type IPoint } from '../../Interfaces/IPoint';
import { MenuItem } from './MenuItem'; import { MenuItem } from './MenuItem';
interface IMenuProps { interface IMenuProps {
@ -97,15 +97,22 @@ export function Menu(props: IMenuProps): JSX.Element {
AddUniversalActions(props, children, count, target); AddUniversalActions(props, children, count, target);
} }
const visible = isOpen && children.length > 0 ? 'visible opacity-1' : 'invisible opacity-0'; const visible = isOpen && children.length > 0
? 'visible opacity-1'
: 'invisible opacity-0';
const isOutOfBoundHorizontally = contextMenuPosition.x + menuWidth > window.innerWidth; const isOutOfBoundHorizontally = contextMenuPosition.x + menuWidth > window.innerWidth;
const isOutOfBoundVertically = contextMenuPosition.y + menuHeight > window.innerHeight; const isOutOfBoundVertically = contextMenuPosition.y + menuHeight > window.innerHeight;
const finalHorizontalPosition = isOutOfBoundHorizontally ? contextMenuPosition.x - menuWidth : contextMenuPosition.x; const finalHorizontalPosition = isOutOfBoundHorizontally
const finalVerticalPosition = isOutOfBoundVertically ? contextMenuPosition.y - menuWidth : contextMenuPosition.y; ? contextMenuPosition.x - menuWidth
: contextMenuPosition.x;
const finalVerticalPosition = isOutOfBoundVertically
? contextMenuPosition.y - menuWidth
: contextMenuPosition.y;
return ( return (
<div <div
ref={menuRef} ref={menuRef}
className={`fixed context-menu ${MENU_VERTICAL_PADDING_CLASS} ${MENU_WIDTH_CLASS} ${props.className ?? ''} ${visible}`} className={'fixed context-menu' +
`${MENU_VERTICAL_PADDING_CLASS} ${MENU_WIDTH_CLASS} ${props.className ?? ''} ${visible}`}
style={{ style={{
left: finalHorizontalPosition, left: finalHorizontalPosition,
top: finalVerticalPosition top: finalVerticalPosition
@ -137,7 +144,7 @@ function AddClassSpecificActions(
text={action.text} text={action.text}
title={action.title} title={action.title}
shortcut={action.shortcut} shortcut={action.shortcut}
onClick={() => action.action(target)} />); onClick={() => { action.action(target); }} />);
}); });
children.push(<hr key={`contextmenu-hr-${count}`} className='border-slate-400' />); children.push(<hr key={`contextmenu-hr-${count}`} className='border-slate-400' />);
} }
@ -156,7 +163,7 @@ function AddUniversalActions(props: IMenuProps, children: JSX.Element[], count:
text={action.text} text={action.text}
title={action.title} title={action.title}
shortcut={action.shortcut} shortcut={action.shortcut}
onClick={() => action.action(target)} />); onClick={() => { action.action(target); }} />);
}); });
} }

View file

@ -13,7 +13,7 @@ export function MenuItem(props: IMenuItemProps): JSX.Element {
<button type="button" <button type="button"
className={`flex place-content-between ${props.className ?? ''}`} className={`flex place-content-between ${props.className ?? ''}`}
title={props.title} title={props.title}
onClick={() => props.onClick()}> onClick={() => { props.onClick(); }}>
{props.text} {props.text}
<span dangerouslySetInnerHTML={{ __html: props.shortcut ?? '' }} /> <span dangerouslySetInnerHTML={{ __html: props.shortcut ?? '' }} />
</button> </button>

View file

@ -2,8 +2,8 @@ import { TrashIcon } from '@heroicons/react/24/outline';
import * as React from 'react'; import * as React from 'react';
import { FixedSizeList as List } from 'react-window'; import { FixedSizeList as List } from 'react-window';
import { MessageType } from '../../Enums/MessageType'; import { MessageType } from '../../Enums/MessageType';
import { IHistoryState } from '../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../Interfaces/IHistoryState';
import { IMessage } from '../../Interfaces/IMessage'; import { type IMessage } from '../../Interfaces/IMessage';
import { TITLE_BAR_HEIGHT } from '../Sidebar/Sidebar'; import { TITLE_BAR_HEIGHT } from '../Sidebar/Sidebar';
import { Text } from '../Text/Text'; import { Text } from '../Text/Text';
@ -14,7 +14,7 @@ interface IMessagesProps {
} }
export function Messages(props: IMessagesProps): JSX.Element { export function Messages(props: IMessagesProps): JSX.Element {
function Row({ index, style }: {index: number, style: React.CSSProperties}): JSX.Element { function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element {
const reversedIndex = (props.messages.length - 1) - index; const reversedIndex = (props.messages.length - 1) - index;
const message = props.messages[reversedIndex]; const message = props.messages[reversedIndex];
let classType = ''; let classType = '';

View file

@ -1,5 +1,11 @@
import React from 'react'; import React from 'react';
import { Bars3BottomLeftIcon, Bars3CenterLeftIcon, Bars3Icon, Bars3BottomRightIcon, Bars2Icon } from '@heroicons/react/24/outline'; import {
Bars3BottomLeftIcon,
Bars3CenterLeftIcon,
Bars3Icon,
Bars3BottomRightIcon,
Bars2Icon
} from '@heroicons/react/24/outline';
import { PositionReference } from '../../Enums/PositionReference'; import { PositionReference } from '../../Enums/PositionReference';
import { RadioGroupButtons } from './RadioGroupButtons'; import { RadioGroupButtons } from './RadioGroupButtons';

View file

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { IInputGroup } from '../../Interfaces/IInputGroup'; import { type IInputGroup } from '../../Interfaces/IInputGroup';
interface IRadioGroupButtonsProps { interface IRadioGroupButtonsProps {
name: string name: string

View file

@ -1,7 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import { Interweave, Node } from 'interweave'; import { Interweave, type Node } from 'interweave';
import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { IContainerProperties } from '../../../Interfaces/IContainerProperties'; import { type IContainerProperties } from '../../../Interfaces/IContainerProperties';
import { Camelize } from '../../../utils/stringtools'; import { Camelize } from '../../../utils/stringtools';
import { SHOW_TEXT } from '../../../utils/default'; import { SHOW_TEXT } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools'; import { FindContainerById } from '../../../utils/itertools';
@ -19,8 +19,7 @@ interface IContainerProps {
* @returns Render the container * @returns Render the container
*/ */
export function Container(props: IContainerProps): JSX.Element { export function Container(props: IContainerProps): JSX.Element {
const containersElements = props.model.children.map( const containersElements = props.model.children.map(childId => {
childId => {
const child = FindContainerById(props.containers, childId); const child = FindContainerById(props.containers, childId);
if (child === undefined) { if (child === undefined) {
@ -67,7 +66,7 @@ export function Container(props: IContainerProps): JSX.Element {
width={width} width={width}
height={height} height={height}
style={style} style={style}
onClick={() => props.selectContainer(props.model.properties.id)} onClick={() => { props.selectContainer(props.model.properties.id); }}
> >
</rect>); </rect>);
@ -108,7 +107,7 @@ function CreateReactCustomSVG(customSVG: string, properties: IContainerPropertie
function Transform(node: HTMLElement, children: Node[], properties: IContainerProperties): React.ReactNode { function Transform(node: HTMLElement, children: Node[], properties: IContainerProperties): React.ReactNode {
const supportedTags = ['line', 'path', 'rect']; const supportedTags = ['line', 'path', 'rect'];
if (supportedTags.includes(node.tagName.toLowerCase())) { if (supportedTags.includes(node.tagName.toLowerCase())) {
const attributes: { [att: string]: string | object | null } = {}; const attributes: Record<string, string | object | null> = {};
node.getAttributeNames().forEach(attName => { node.getAttributeNames().forEach(attName => {
const attributeValue = node.getAttribute(attName); const attributeValue = node.getAttribute(attName);
if (attributeValue === null) { if (attributeValue === null) {

View file

@ -11,9 +11,9 @@ import {
} from '../../../utils/default'; } from '../../../utils/default';
import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools'; import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools';
import { TransformX, TransformY } from '../../../utils/svg'; import { TransformX, TransformY } from '../../../utils/svg';
import { Dimension } from './Dimension';
import { type IContainerModel } from '../../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { Dimension } from './Dimension';
interface IDimensionLayerProps { interface IDimensionLayerProps {
containers: Map<string, IContainerModel> containers: Map<string, IContainerModel>
@ -160,7 +160,8 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps):
return dimensions; return dimensions;
} }
function AddHorizontalSymbolDimension(symbol: ISymbolModel, function AddHorizontalSymbolDimension(
symbol: ISymbolModel,
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number, scale: number,
depth: number depth: number
@ -173,8 +174,7 @@ function AddHorizontalSymbolDimension(symbol: ISymbolModel,
const text = width const text = width
.toFixed(0) .toFixed(0)
.toString(); .toString();
dimensions.push( dimensions.push(<Dimension
<Dimension
key={id} key={id}
id={id} id={id}
xStart={0} xStart={0}
@ -183,12 +183,12 @@ function AddHorizontalSymbolDimension(symbol: ISymbolModel,
yEnd={-offset} yEnd={-offset}
text={text} text={text}
scale={scale} scale={scale}
style={DEFAULT_DIMENSION_SYMBOL_STYLE}/> style={DEFAULT_DIMENSION_SYMBOL_STYLE}/>);
);
} }
} }
function AddVerticalSymbolDimension(symbol: ISymbolModel, function AddVerticalSymbolDimension(
symbol: ISymbolModel,
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number, scale: number,
depth: number depth: number
@ -201,8 +201,7 @@ function AddVerticalSymbolDimension(symbol: ISymbolModel,
const text = height const text = height
.toFixed(0) .toFixed(0)
.toString(); .toString();
dimensions.push( dimensions.push(<Dimension
<Dimension
key={id} key={id}
id={id} id={id}
xStart={-offset} xStart={-offset}
@ -211,8 +210,7 @@ function AddVerticalSymbolDimension(symbol: ISymbolModel,
yEnd={0} yEnd={0}
text={text} text={text}
scale={scale} scale={scale}
style={DEFAULT_DIMENSION_SYMBOL_STYLE}/> style={DEFAULT_DIMENSION_SYMBOL_STYLE}/>);
);
} }
} }
@ -249,12 +247,16 @@ function AddHorizontalChildrenDimension(
return; return;
} }
let xChildrenStart = TransformX(lastChild.properties.x, let xChildrenStart = TransformX(
lastChild.properties.x,
lastChild.properties.width, lastChild.properties.width,
lastChild.properties.positionReference); lastChild.properties.positionReference
let xChildrenEnd = TransformX(lastChild.properties.x, );
let xChildrenEnd = TransformX(
lastChild.properties.x,
lastChild.properties.width, lastChild.properties.width,
lastChild.properties.positionReference); lastChild.properties.positionReference
);
// Find the min and max // Find the min and max
for (let i = container.children.length - 2; i >= 0; i--) { for (let i = container.children.length - 2; i >= 0; i--) {
@ -316,12 +318,16 @@ function AddVerticalChildrenDimension(
return; return;
} }
let yChildrenStart = TransformY(lastChild.properties.y, let yChildrenStart = TransformY(
lastChild.properties.y,
lastChild.properties.height, lastChild.properties.height,
lastChild.properties.positionReference); lastChild.properties.positionReference
let yChildrenEnd = TransformY(lastChild.properties.y, );
let yChildrenEnd = TransformY(
lastChild.properties.y,
lastChild.properties.height, lastChild.properties.height,
lastChild.properties.positionReference); lastChild.properties.positionReference
);
// Find the min and max // Find the min and max
for (let i = container.children.length - 2; i >= 0; i--) { for (let i = container.children.length - 2; i >= 0; i--) {
@ -398,9 +404,7 @@ function AddHorizontalBorrowerDimension(
const restoredX = x + childCurrentTransform[0]; const restoredX = x + childCurrentTransform[0];
marks.push( marks.push(restoredX);
restoredX
);
} }
const restoredX = container.properties.x + currentTransform[0]; const restoredX = container.properties.x + currentTransform[0];
@ -458,9 +462,7 @@ function AddVerticalBorrowerDimension(
const restoredy = y + childCurrentTransform[1]; const restoredy = y + childCurrentTransform[1];
marks.push( marks.push(restoredy);
restoredy
);
} }
const restoredY = container.properties.y + currentTransform[1]; const restoredY = container.properties.y + currentTransform[1];
@ -515,8 +517,7 @@ function AddVerticalSelfDimension(
[yStart, yEnd] = [yEnd, yStart]; [yStart, yEnd] = [yEnd, yStart];
} }
dimensions.push( dimensions.push(<Dimension
<Dimension
key={idVert} key={idVert}
id={idVert} id={idVert}
xStart={xDim} xStart={xDim}
@ -525,8 +526,7 @@ function AddVerticalSelfDimension(
yEnd={yEnd} yEnd={yEnd}
text={textVert} text={textVert}
scale={scale} scale={scale}
style={style}/> style={style}/>);
);
} }
function AddHorizontalSelfDimension( function AddHorizontalSelfDimension(
@ -544,8 +544,7 @@ function AddHorizontalSelfDimension(
const text = width const text = width
.toFixed(0) .toFixed(0)
.toString(); .toString();
dimensions.push( dimensions.push(<Dimension
<Dimension
key={id} key={id}
id={id} id={id}
xStart={xStart} xStart={xStart}
@ -554,8 +553,7 @@ function AddHorizontalSelfDimension(
yEnd={yDim} yEnd={yDim}
text={text} text={text}
scale={scale} scale={scale}
style={style}/> style={style}/>);
);
} }
function AddHorizontalSelfMarginsDimension( function AddHorizontalSelfMarginsDimension(
@ -574,8 +572,7 @@ function AddHorizontalSelfMarginsDimension(
const text = left const text = left
.toFixed(0) .toFixed(0)
.toString(); .toString();
dimensions.push( dimensions.push(<Dimension
<Dimension
key={id} key={id}
id={id} id={id}
xStart={xStart} xStart={xStart}
@ -584,8 +581,7 @@ function AddHorizontalSelfMarginsDimension(
yEnd={yDim} yEnd={yDim}
text={text} text={text}
scale={scale} scale={scale}
style={style}/> style={style}/>);
);
} }
const right = container.properties.margin.right; const right = container.properties.margin.right;
@ -596,8 +592,7 @@ function AddHorizontalSelfMarginsDimension(
const text = right const text = right
.toFixed(0) .toFixed(0)
.toString(); .toString();
dimensions.push( dimensions.push(<Dimension
<Dimension
key={id} key={id}
id={id} id={id}
xStart={xStart} xStart={xStart}
@ -606,8 +601,7 @@ function AddHorizontalSelfMarginsDimension(
yEnd={yDim} yEnd={yDim}
text={text} text={text}
scale={scale} scale={scale}
style={style}/> style={style}/>);
);
} }
} }
@ -633,8 +627,7 @@ function AddVerticalSelfMarginDimension(
[yStart, yEnd] = [yEnd, yStart]; [yStart, yEnd] = [yEnd, yStart];
} }
dimensions.push( dimensions.push(<Dimension
<Dimension
key={idVert} key={idVert}
id={idVert} id={idVert}
xStart={xDim} xStart={xDim}
@ -643,8 +636,7 @@ function AddVerticalSelfMarginDimension(
yEnd={yEnd} yEnd={yEnd}
text={textVert} text={textVert}
scale={scale} scale={scale}
style={style}/> style={style}/>);
);
} }
const bottom = container.properties.margin.bottom; const bottom = container.properties.margin.bottom;
if (bottom != null && bottom > 0) { if (bottom != null && bottom > 0) {
@ -659,8 +651,7 @@ function AddVerticalSelfMarginDimension(
[yStart, yEnd] = [yEnd, yStart]; [yStart, yEnd] = [yEnd, yStart];
} }
dimensions.push( dimensions.push(<Dimension
<Dimension
key={idVert} key={idVert}
id={idVert} id={idVert}
xStart={xDim} xStart={xDim}
@ -669,7 +660,6 @@ function AddVerticalSelfMarginDimension(
yEnd={yEnd} yEnd={yEnd}
text={textVert} text={textVert}
scale={scale} scale={scale}
style={style}/> style={style}/>);
);
} }
} }

View file

@ -26,7 +26,11 @@ export function SelectorContainer(props: ISelectorContainerProps): JSX.Element {
props.selected.properties.height props.selected.properties.height
]; ];
({ x, y, width, height } = RemoveMargin(x, y, width, height, ({ x, y, width, height } = RemoveMargin(
x,
y,
width,
height,
props.selected.properties.margin.left, props.selected.properties.margin.left,
props.selected.properties.margin.bottom, props.selected.properties.margin.bottom,
props.selected.properties.margin.top, props.selected.properties.margin.top,

View file

@ -1,6 +1,6 @@
import '../Selector.scss'; import '../Selector.scss';
import * as React from 'react'; import * as React from 'react';
import { DIMENSION_MARGIN, SYMBOL_MARGIN } from '../../../../utils/default'; import { SYMBOL_MARGIN } from '../../../../utils/default';
import { type ISymbolModel } from '../../../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../../../Interfaces/ISymbolModel';
import { Selector } from '../Selector/Selector'; import { Selector } from '../Selector/Selector';

View file

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { Symbol } from './Symbol'; import { Symbol } from './Symbol';
interface ISymbolLayerProps { interface ISymbolLayerProps {
@ -10,13 +10,11 @@ interface ISymbolLayerProps {
export function SymbolLayer(props: ISymbolLayerProps): JSX.Element { export function SymbolLayer(props: ISymbolLayerProps): JSX.Element {
const symbols: JSX.Element[] = []; const symbols: JSX.Element[] = [];
props.symbols.forEach((symbol) => { props.symbols.forEach((symbol) => {
symbols.push( symbols.push(<Symbol
<Symbol
key={`symbol-${symbol.id}`} key={`symbol-${symbol.id}`}
model={symbol} model={symbol}
scale={props.scale} scale={props.scale}
/> />);
);
}); });
return ( return (
<g> <g>

View file

@ -1,13 +1,13 @@
import * as React from 'react'; import * as React from 'react';
import { ReactSVGPanZoom, type Tool, TOOL_PAN, type Value, ALIGN_CENTER } from 'react-svg-pan-zoom'; import { ReactSVGPanZoom, type Tool, TOOL_PAN, type Value, ALIGN_CENTER } from 'react-svg-pan-zoom';
import { MAX_FRAMERATE } from '../../utils/default';
import { type DrawParams } from '../Viewer/Viewer';
import { Container } from './Elements/Container'; import { Container } from './Elements/Container';
import { SelectorContainer } from './Elements/SelectorContainer/SelectorContainer'; import { SelectorContainer } from './Elements/SelectorContainer/SelectorContainer';
import { MAX_FRAMERATE } from '../../utils/default';
import { SymbolLayer } from './Elements/SymbolLayer'; import { SymbolLayer } from './Elements/SymbolLayer';
import { DimensionLayer } from './Elements/DimensionLayer'; import { DimensionLayer } from './Elements/DimensionLayer';
import { SelectorSymbol } from './Elements/SelectorSymbol/SelectorSymbol'; import { SelectorSymbol } from './Elements/SelectorSymbol/SelectorSymbol';
import { type IToolbarProps, Toolbar } from './SVGReactPanZoom/ui-toolbar/toolbar'; import { type IToolbarProps, Toolbar } from './SVGReactPanZoom/ui-toolbar/toolbar';
import { type DrawParams } from '../Viewer/Viewer';
interface ISVGProps { interface ISVGProps {
className?: string className?: string

View file

@ -47,8 +47,12 @@ export class ToolbarButton extends React.Component<IToolbarButtonProps, IToolbar
display: 'block', display: 'block',
width: '24px', width: '24px',
height: '24px', height: '24px',
margin: [POSITION_TOP, POSITION_BOTTOM].includes(this.props.toolbarPosition) ? '2px 1px' : '1px 2px', margin: [POSITION_TOP, POSITION_BOTTOM].includes(this.props.toolbarPosition)
color: this.props.active || this.state.hover ? this.props.activeColor : '#FFF', ? '2px 1px'
: '1px 2px',
color: this.props.active || this.state.hover
? this.props.activeColor
: '#FFF',
transition: 'color 200ms ease', transition: 'color 200ms ease',
background: 'none', background: 'none',
padding: '0px', padding: '0px',

View file

@ -118,18 +118,34 @@ export function Toolbar({
const style: React.CSSProperties = { const style: React.CSSProperties = {
// position // position
position: 'absolute', position: 'absolute',
transform: [POSITION_TOP, POSITION_BOTTOM].includes(position) ? 'translate(-50%, 0px)' : 'none', transform: [POSITION_TOP, POSITION_BOTTOM].includes(position)
top: [POSITION_LEFT, POSITION_RIGHT, POSITION_TOP].includes(position) ? '5px' : 'unset', ? 'translate(-50%, 0px)'
left: [POSITION_TOP, POSITION_BOTTOM].includes(position) ? '50%' : (POSITION_LEFT === position ? '5px' : 'unset'), : 'none',
right: [POSITION_RIGHT].includes(position) ? '5px' : 'unset', top: [POSITION_LEFT, POSITION_RIGHT, POSITION_TOP].includes(position)
bottom: [POSITION_BOTTOM].includes(position) ? '5px' : 'unset', ? '5px'
: 'unset',
left: [POSITION_TOP, POSITION_BOTTOM].includes(position)
? '50%'
: (POSITION_LEFT === position
? '5px'
: 'unset'),
right: [POSITION_RIGHT].includes(position)
? '5px'
: 'unset',
bottom: [POSITION_BOTTOM].includes(position)
? '5px'
: 'unset',
// inner styling // inner styling
backgroundColor: 'rgba(19, 20, 22, 0.90)', backgroundColor: 'rgba(19, 20, 22, 0.90)',
borderRadius: '2px', borderRadius: '2px',
display: 'flex', display: 'flex',
flexDirection: isHorizontal ? 'row' : 'column', flexDirection: isHorizontal
padding: isHorizontal ? '1px 2px' : '2px 1px' ? 'row'
: 'column',
padding: isHorizontal
? '1px 2px'
: '2px 1px'
}; };
return ( return (

View file

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { IInputGroup } from '../../Interfaces/IInputGroup'; import { type IInputGroup } from '../../Interfaces/IInputGroup';
interface ISelectProps { interface ISelectProps {
labelKey?: string labelKey?: string

View file

@ -9,7 +9,9 @@ interface IToggleSidebarProps {
export function ToggleSideBar({ title, checked, onClick }: IToggleSidebarProps): JSX.Element { export function ToggleSideBar({ title, checked, onClick }: IToggleSidebarProps): JSX.Element {
return ( return (
<div className={`${(checked ? 'bg-slate-400 hover:bg-slate-500' : 'bg-slate-300 hover:bg-slate-400')}`}> <div className={`${(checked
? 'bg-slate-400 hover:bg-slate-500'
: 'bg-slate-300 hover:bg-slate-400')}`}>
<button <button
className={'w-full py-2'} className={'w-full py-2'}
type='button' type='button'

View file

@ -13,22 +13,26 @@ interface ISymbolFormProps {
onChange: (key: string, value: string | number | boolean) => void onChange: (key: string, value: string | number | boolean) => void
} }
function Restore(offset: number, function Restore(
offset: number,
isVertical: boolean, isVertical: boolean,
height: number, height: number,
width: number, width: number,
position: PositionReference | undefined): number { position: PositionReference | undefined
): number {
if (isVertical) { if (isVertical) {
return RestoreY(offset, height, position); return RestoreY(offset, height, position);
} else { } else {
return RestoreX(offset, width, position); return RestoreX(offset, width, position);
} }
} }
function Transform(offset: number, function Transform(
offset: number,
isVertical: boolean, isVertical: boolean,
height: number, height: number,
width: number, width: number,
position: PositionReference | undefined): number { position: PositionReference | undefined
): number {
if (isVertical) { if (isVertical) {
return TransformY(offset, height, position); return TransformY(offset, height, position);
} else { } else {
@ -63,18 +67,24 @@ export function SymbolForm(props: ISymbolFormProps): JSX.Element {
labelClassName='' labelClassName=''
inputClassName='' inputClassName=''
type='number' type='number'
value={Transform(props.symbol.offset, value={Transform(
props.symbol.offset,
props.symbol.isVertical, props.symbol.isVertical,
props.symbol.height, props.symbol.height,
props.symbol.width, props.symbol.width,
props.symbol.config.PositionReference).toString()} props.symbol.config.PositionReference
).toString()}
onChange={(value) => { onChange={(value) => {
props.onChange('offset', props.onChange(
Restore(Number(value), 'offset',
Restore(
Number(value),
props.symbol.isVertical, props.symbol.isVertical,
props.symbol.height, props.symbol.height,
props.symbol.width, props.symbol.width,
props.symbol.config.PositionReference)); props.symbol.config.PositionReference
)
);
}} /> }} />
<ToggleButton <ToggleButton
labelText={Text({ textId: '@IsVertical' })} labelText={Text({ textId: '@IsVertical' })}

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { ISymbolModel } from '../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { SymbolForm } from './SymbolForm'; import { SymbolForm } from './SymbolForm';
interface ISymbolPropertiesProps { interface ISymbolPropertiesProps {

View file

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { IAvailableSymbol } from '../../Interfaces/IAvailableSymbol'; import { type IAvailableSymbol } from '../../Interfaces/IAvailableSymbol';
import { TruncateString } from '../../utils/stringtools'; import { TruncateString } from '../../utils/stringtools';
interface ISymbolsProps { interface ISymbolsProps {
@ -20,9 +20,9 @@ export function Symbols(props: ISymbolsProps): JSX.Element {
key={componentOption.Name} key={componentOption.Name}
id={componentOption.Name} id={componentOption.Name}
title={componentOption.Name} title={componentOption.Name}
onClick={() => props.buttonOnClick(componentOption.Name)} onClick={() => { props.buttonOnClick(componentOption.Name); }}
draggable={true} draggable={true}
onDragStart={(event) => HandleDragStart(event)} onDragStart={(event) => { HandleDragStart(event); }}
> >
<div> <div>
<img <img
@ -41,9 +41,9 @@ export function Symbols(props: ISymbolsProps): JSX.Element {
key={componentOption.Name} key={componentOption.Name}
id={componentOption.Name} id={componentOption.Name}
title={componentOption.Name} title={componentOption.Name}
onClick={() => props.buttonOnClick(componentOption.Name)} onClick={() => { props.buttonOnClick(componentOption.Name); }}
draggable={true} draggable={true}
onDragStart={(event) => HandleDragStart(event)} onDragStart={(event) => { HandleDragStart(event); }}
> >
{TruncateString(componentOption.Name, 5)} {TruncateString(componentOption.Name, 5)}

View file

@ -1,4 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { type Dispatch } from 'react';
import { ElementsSidebar } from '../ElementsSidebar/ElementsSidebar'; import { ElementsSidebar } from '../ElementsSidebar/ElementsSidebar';
import { History } from '../History/History'; import { History } from '../History/History';
import { Bar, BAR_WIDTH } from '../Bar/Bar'; import { Bar, BAR_WIDTH } from '../Bar/Bar';
@ -12,13 +13,12 @@ import { Viewer } from '../Viewer/Viewer';
import { Settings } from '../Settings/Settings'; import { Settings } from '../Settings/Settings';
import { type IMessage } from '../../Interfaces/IMessage'; import { type IMessage } from '../../Interfaces/IMessage';
import { DISABLE_API } from '../../utils/default'; import { DISABLE_API } from '../../utils/default';
import { UseWorker, UseAsync } from './UseWorker';
import { FindContainerById } from '../../utils/itertools'; import { FindContainerById } from '../../utils/itertools';
import { type IEditorState } from '../../Interfaces/IEditorState'; import { type IEditorState } from '../../Interfaces/IEditorState';
import { GetCurrentHistoryState } from '../Editor/Editor'; import { GetCurrentHistoryState } from '../Editor/Editor';
import { Text } from '../Text/Text'; import { Text } from '../Text/Text';
import { type IReplaceContainer } from '../../Interfaces/IReplaceContainer'; import { type IReplaceContainer } from '../../Interfaces/IReplaceContainer';
import { type Dispatch } from 'react'; import { UseWorker, UseAsync } from './UseWorker';
export interface IUIProps { export interface IUIProps {
editorState: IEditorState editorState: IEditorState
@ -223,7 +223,9 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
isLeftSidebarOpenClasses.add('left-sidebar-single'); isLeftSidebarOpenClasses.add('left-sidebar-single');
} }
const clickRestrictionsClasses = replaceContainer.isReplacing ? 'pointer-events-none opacity-50' : ''; const clickRestrictionsClasses = replaceContainer.isReplacing
? 'pointer-events-none opacity-50'
: '';
const isComponentsOpen = selectedSidebar === SidebarType.Components; const isComponentsOpen = selectedSidebar === SidebarType.Components;
const isSymbolsOpen = selectedSidebar === SidebarType.Symbols; const isSymbolsOpen = selectedSidebar === SidebarType.Symbols;

View file

@ -1,8 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import { IHistoryState } from '../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../Interfaces/IHistoryState';
import { IGetFeedbackRequest } from '../../Interfaces/IGetFeedbackRequest'; import { type IGetFeedbackRequest } from '../../Interfaces/IGetFeedbackRequest';
import { IGetFeedbackResponse } from '../../Interfaces/IGetFeedbackResponse'; import { type IGetFeedbackResponse } from '../../Interfaces/IGetFeedbackResponse';
import { IMessage } from '../../Interfaces/IMessage'; import { type IMessage } from '../../Interfaces/IMessage';
import { GetCircularReplacer } from '../../utils/saveload'; import { GetCircularReplacer } from '../../utils/saveload';
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
@ -11,7 +11,8 @@ const myWorker = window.Worker && new Worker('workers/message_worker.js');
export function UseWorker( export function UseWorker(
state: IHistoryState, state: IHistoryState,
configurationUrl: string | undefined, configurationUrl: string | undefined,
setMessages: React.Dispatch<React.SetStateAction<IMessage[]>>): void { setMessages: React.Dispatch<React.SetStateAction<IMessage[]>>
): void {
React.useEffect(() => { React.useEffect(() => {
// use webworker for the stringify to avoid freezing // use webworker for the stringify to avoid freezing
myWorker.postMessage({ myWorker.postMessage({
@ -32,7 +33,8 @@ export function UseWorker(
export function UseAsync( export function UseAsync(
state: IHistoryState, state: IHistoryState,
configurationUrl: string | undefined, configurationUrl: string | undefined,
setMessages: React.Dispatch<React.SetStateAction<IMessage[]>>): void { setMessages: React.Dispatch<React.SetStateAction<IMessage[]>>
): void {
React.useEffect(() => { React.useEffect(() => {
const request: IGetFeedbackRequest = { const request: IGetFeedbackRequest = {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
@ -48,8 +50,7 @@ export function UseAsync(
}), }),
body: dataParsed body: dataParsed
}) })
.then(async(response) => await response.json() .then(async(response) => await response.json())
)
.then(async(json: IGetFeedbackResponse) => { .then(async(json: IGetFeedbackResponse) => {
setMessages(json.messages); setMessages(json.messages);
}); });

View file

@ -1,4 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { useState } from 'react';
import { type IContainerModel } from '../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { type IHistoryState } from '../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../Interfaces/IHistoryState';
import { USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default'; import { USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default';
@ -6,7 +7,6 @@ import { FindContainerById } from '../../utils/itertools';
import { BAR_WIDTH } from '../Bar/Bar'; import { BAR_WIDTH } from '../Bar/Bar';
import { Canvas } from '../Canvas/Canvas'; import { Canvas } from '../Canvas/Canvas';
import { SelectorMode, SVG } from '../SVG/SVG'; import { SelectorMode, SVG } from '../SVG/SVG';
import { useState } from 'react';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
interface IViewerProps { interface IViewerProps {
@ -30,7 +30,9 @@ export interface DrawParams {
} }
function computeWidth(margin: number): number { function computeWidth(margin: number): number {
return window.innerWidth - (window.innerWidth < 768 ? BAR_WIDTH : margin); return window.innerWidth - (window.innerWidth < 768
? BAR_WIDTH
: margin);
} }
export function Viewer({ export function Viewer({

View file

@ -1,9 +1,9 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { AppState } from '../Enums/AppState'; import { AppState } from '../Enums/AppState';
import { IConfiguration } from '../Interfaces/IConfiguration'; import { type IConfiguration } from '../Interfaces/IConfiguration';
import { IEditorState } from '../Interfaces/IEditorState'; import { type IEditorState } from '../Interfaces/IEditorState';
import { IHistoryState } from '../Interfaces/IHistoryState'; import { type IHistoryState } from '../Interfaces/IHistoryState';
import { ILanguage } from '../Interfaces/ILanguage'; import { type ILanguage } from '../Interfaces/ILanguage';
import { languageOptions, translations } from '../Translations/Translations'; import { languageOptions, translations } from '../Translations/Translations';
import { GetDefaultEditorState as GetDefaultEditorStateAction } from '../utils/default'; import { GetDefaultEditorState as GetDefaultEditorStateAction } from '../utils/default';
import { Revive, ReviveHistory as ReviveHistoryAction } from '../utils/saveload'; import { Revive, ReviveHistory as ReviveHistoryAction } from '../utils/saveload';
@ -49,7 +49,7 @@ export function UseCustomEvents(
const funcs = new Map<string, () => void>(); const funcs = new Map<string, () => void>();
for (const event of events) { for (const event of events) {
function Func(eventInitDict?: CustomEventInit): void { function Func(eventInitDict?: CustomEventInit): void {
return event.func({ event.func({
root, root,
languageContext, languageContext,
setEditor, setEditor,

View file

@ -1,11 +1,21 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { AddContainer as AddContainerAction, AddContainerToSelectedContainer as AddContainerToSelectedContainerAction } from '../Components/Editor/Actions/AddContainer'; import {
import { DeleteContainer as DeleteContainerAction, SelectContainer as SelectContainerAction } from '../Components/Editor/Actions/ContainerOperations'; AddContainer as AddContainerAction,
import { AddSymbol as AddSymbolAction, DeleteSymbol as DeleteSymbolAction, SelectSymbol as SelectSymbolAction } from '../Components/Editor/Actions/SymbolOperations'; AddContainerToSelectedContainer as AddContainerToSelectedContainerAction
} from '../Components/Editor/Actions/AddContainer';
import {
DeleteContainer as DeleteContainerAction,
SelectContainer as SelectContainerAction
} from '../Components/Editor/Actions/ContainerOperations';
import {
AddSymbol as AddSymbolAction,
DeleteSymbol as DeleteSymbolAction,
SelectSymbol as SelectSymbolAction
} from '../Components/Editor/Actions/SymbolOperations';
import { GetCurrentHistory } from '../Components/Editor/Editor'; import { GetCurrentHistory } from '../Components/Editor/Editor';
import { IConfiguration } from '../Interfaces/IConfiguration'; import { type IConfiguration } from '../Interfaces/IConfiguration';
import { IEditorState } from '../Interfaces/IEditorState'; import { type IEditorState } from '../Interfaces/IEditorState';
import { IHistoryState } from '../Interfaces/IHistoryState'; import { type IHistoryState } from '../Interfaces/IHistoryState';
import { FindContainerById } from '../utils/itertools'; import { FindContainerById } from '../utils/itertools';
import { GetCircularReplacer } from '../utils/saveload'; import { GetCircularReplacer } from '../utils/saveload';
@ -61,7 +71,7 @@ export function UseCustomEvents(
for (const event of events) { for (const event of events) {
function Func(eventInitDict?: CustomEventInit): void { function Func(eventInitDict?: CustomEventInit): void {
return event.func({ event.func({
root, root,
editorState, editorState,
setNewHistory, setNewHistory,
@ -112,7 +122,9 @@ function GetEditorStateAsString({
root, root,
editorState editorState
}: IEditorEventParams): void { }: IEditorEventParams): void {
const spaces = import.meta.env.DEV ? 4 : 0; const spaces = import.meta.env.DEV
? 4
: 0;
const data = JSON.stringify(editorState, GetCircularReplacer(), spaces); const data = JSON.stringify(editorState, GetCircularReplacer(), spaces);
const customEvent = new CustomEvent<string>('getEditorStateAsString', { detail: data }); const customEvent = new CustomEvent<string>('getEditorStateAsString', { detail: data });
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
@ -137,7 +149,8 @@ function GetCurrentHistoryState({
}: IEditorEventParams): void { }: IEditorEventParams): void {
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'getCurrentHistoryState', 'getCurrentHistoryState',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }
@ -155,7 +168,8 @@ function AppendNewState({
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'appendNewState', 'appendNewState',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }
@ -184,7 +198,8 @@ function AddContainer({
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'addContainer', 'addContainer',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }
@ -214,7 +229,8 @@ function AddContainerToSelectedContainer({
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'addContainerToSelectedContainer', 'addContainerToSelectedContainer',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }
@ -246,7 +262,8 @@ function AppendContainer({
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'appendContainerToSelectedContainer', 'appendContainerToSelectedContainer',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }
@ -277,7 +294,8 @@ function AppendContainerToSelectedContainer({
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'appendContainerToSelectedContainer', 'appendContainerToSelectedContainer',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }
@ -302,7 +320,8 @@ function SelectContainer({
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'selectContainer', 'selectContainer',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }
@ -327,7 +346,8 @@ function DeleteContainer({
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'deleteContainer', 'deleteContainer',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }
@ -353,7 +373,8 @@ function AddSymbol({
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'AddSymbol', 'AddSymbol',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }
@ -378,7 +399,8 @@ function SelectSymbol({
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'SelectSymbol', 'SelectSymbol',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }
@ -402,6 +424,7 @@ function DeleteSymbol({
const customEvent = new CustomEvent<IHistoryState>( const customEvent = new CustomEvent<IHistoryState>(
'DeleteSymbol', 'DeleteSymbol',
{ detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }); { detail: structuredClone(editorState.history[editorState.historyCurrentStep]) }
);
root.dispatchEvent(customEvent); root.dispatchEvent(customEvent);
} }

View file

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { AddMethod } from '../Enums/AddMethod'; import { type AddMethod } from '../Enums/AddMethod';
import { IImage } from './IImage'; import { type IImage } from './IImage';
export interface IAction { export interface IAction {
Id: string Id: string

View file

@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { type AddMethod } from '../Enums/AddMethod'; import { type AddMethod } from '../Enums/AddMethod';
import { type PositionReference } from '../Enums/PositionReference'; import { type PositionReference } from '../Enums/PositionReference';
import { type Orientation } from '../Enums/Orientation';
import { type IAction } from './IAction'; import { type IAction } from './IAction';
import { type IMargin } from './IMargin'; import { type IMargin } from './IMargin';
import { type Orientation } from '../Enums/Orientation';
import { type IKeyValue } from './IKeyValue'; import { type IKeyValue } from './IKeyValue';
import { type IStyle } from './IStyle'; import { type IStyle } from './IStyle';
import { type IDimensions } from './IDimensions'; import { type IDimensions } from './IDimensions';

View file

@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { IAPIConfiguration } from './IAPIConfiguration'; import { type IAPIConfiguration } from './IAPIConfiguration';
import { IAvailableContainer } from './IAvailableContainer'; import { type IAvailableContainer } from './IAvailableContainer';
import { IAvailableSymbol } from './IAvailableSymbol'; import { type IAvailableSymbol } from './IAvailableSymbol';
import { ICategory } from './ICategory'; import { type ICategory } from './ICategory';
import { IPattern } from './IPattern'; import { type IPattern } from './IPattern';
/** Model of configuration for the application to configure it */ /** Model of configuration for the application to configure it */
export interface IConfiguration { export interface IConfiguration {

View file

@ -1,4 +1,4 @@
import { IContainerProperties } from './IContainerProperties'; import { type IContainerProperties } from './IContainerProperties';
export interface IContainerModel { export interface IContainerModel {
children: string[] children: string[]
@ -18,7 +18,8 @@ export class ContainerModel implements IContainerModel {
constructor( constructor(
properties: IContainerProperties, properties: IContainerProperties,
children: string[] = [], children: string[] = [],
userData = {}) { userData = {}
) {
this.properties = properties; this.properties = properties;
this.children = children; this.children = children;
this.userData = userData; this.userData = userData;

View file

@ -1,9 +1,9 @@
import { PositionReference } from '../Enums/PositionReference'; import { type PositionReference } from '../Enums/PositionReference';
import { IMargin } from './IMargin'; import { type Orientation } from '../Enums/Orientation';
import { Orientation } from '../Enums/Orientation'; import { type IMargin } from './IMargin';
import { IKeyValue } from './IKeyValue'; import { type IKeyValue } from './IKeyValue';
import { IStyle } from './IStyle'; import { type IStyle } from './IStyle';
import { IDimensions } from './IDimensions'; import { type IDimensions } from './IDimensions';
/** /**
* Properties of a container * Properties of a container

View file

@ -1,6 +1,6 @@
import { IDimensionOptions } from './IDimensionOptions'; import { type Orientation } from '../Enums/Orientation';
import { Orientation } from '../Enums/Orientation'; import { type IDimensionOptions } from './IDimensionOptions';
export interface IDimensions { export interface IDimensions {

View file

@ -1,5 +1,5 @@
import { IConfiguration } from './IConfiguration'; import { type IConfiguration } from './IConfiguration';
import { IHistoryState } from './IHistoryState'; import { type IHistoryState } from './IHistoryState';
export interface IEditorState { export interface IEditorState {
history: IHistoryState[] history: IHistoryState[]

View file

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { IHistoryState } from './IHistoryState'; import { type IHistoryState } from './IHistoryState';
export interface IGetFeedbackRequest { export interface IGetFeedbackRequest {
/** Current application state */ /** Current application state */

View file

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { IMessage } from './IMessage'; import { type IMessage } from './IMessage';
export interface IGetFeedbackResponse { export interface IGetFeedbackResponse {
messages: IMessage[] messages: IMessage[]

View file

@ -1,5 +1,5 @@
import { IContainerModel } from './IContainerModel'; import { type IContainerModel } from './IContainerModel';
import { ISymbolModel } from './ISymbolModel'; import { type ISymbolModel } from './ISymbolModel';
export interface IHistoryState { export interface IHistoryState {
/** Last editor action */ /** Last editor action */

View file

@ -1,4 +1,4 @@
import React from 'react'; import type React from 'react';
export interface IInputGroup { export interface IInputGroup {
key: string key: string

View file

@ -1,4 +1,4 @@
import { MessageType } from '../Enums/MessageType'; import { type MessageType } from '../Enums/MessageType';
export interface IMessage { export interface IMessage {
text: string text: string

View file

@ -1,4 +1,4 @@
import { IAvailableContainer } from './IAvailableContainer'; import { type IAvailableContainer } from './IAvailableContainer';
export interface IPattern { export interface IPattern {
/** /**

View file

@ -1,6 +1,6 @@
import { IAction } from './IAction'; import { type IAction } from './IAction';
import { IContainerModel } from './IContainerModel'; import { type IContainerModel } from './IContainerModel';
import { IHistoryState } from './IHistoryState'; import { type IHistoryState } from './IHistoryState';
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
export interface ISetContainerListRequest { export interface ISetContainerListRequest {

View file

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { AddMethod } from '../Enums/AddMethod'; import { type AddMethod } from '../Enums/AddMethod';
import { IAvailableContainer } from './IAvailableContainer'; import { type IAvailableContainer } from './IAvailableContainer';
export interface ISetContainerListResponse { export interface ISetContainerListResponse {
Containers: IAvailableContainer[] Containers: IAvailableContainer[]

View file

@ -5,13 +5,11 @@ import { LanguageProvider } from './Components/LanguageProvider/LanguageProvider
import './index.scss'; import './index.scss';
function RenderRoot(root: Element | Document): void { function RenderRoot(root: Element | Document): void {
ReactDOM.createRoot(root.querySelector('#root') as HTMLDivElement).render( ReactDOM.createRoot(root.querySelector('#root') as HTMLDivElement).render(<React.StrictMode>
<React.StrictMode>
<LanguageProvider> <LanguageProvider>
<App root={root}/> <App root={root}/>
</LanguageProvider> </LanguageProvider>
</React.StrictMode> </React.StrictMode>);
);
} }
// Specific for Modeler apps // Specific for Modeler apps

View file

@ -14,7 +14,9 @@ import { type IDimensionStyle } from '../Components/SVG/Elements/Dimension';
/// EDITOR DEFAULTS /// /// EDITOR DEFAULTS ///
/** Enable fast boot and disable main menu (0 = disabled, 1 = loading, 2 = loaded) */ /** Enable fast boot and disable main menu (0 = disabled, 1 = loading, 2 = loaded) */
export const FAST_BOOT = import.meta.env.PROD ? AppState.Loaded : AppState.MainMenu; export const FAST_BOOT = import.meta.env.PROD
? AppState.Loaded
: AppState.MainMenu;
/** Disable any call to the API (default = false) */ /** Disable any call to the API (default = false) */
export const DISABLE_API = false; export const DISABLE_API = false;
@ -89,7 +91,9 @@ export function GetDefaultEditorState(configuration: IConfiguration): IEditorSta
throw new Error('Cannot initialize project! Main container has an undefined size'); throw new Error('Cannot initialize project! Main container has an undefined size');
} }
const containerConfig = configuration.AvailableContainers.find(config => config.Type === configuration.MainContainer.Type); const containerConfig = configuration.AvailableContainers.find(
config => config.Type === configuration.MainContainer.Type
);
let mainContainerConfig: IContainerProperties; let mainContainerConfig: IContainerProperties;
if (containerConfig !== undefined) { if (containerConfig !== undefined) {
@ -128,9 +132,7 @@ export function GetDefaultEditorState(configuration: IConfiguration): IEditorSta
configuration.MainContainer configuration.MainContainer
); );
} }
const mainContainer = new ContainerModel( const mainContainer = new ContainerModel(mainContainerConfig);
mainContainerConfig
);
const containers = new Map<string, IContainerModel>(); const containers = new Map<string, IContainerModel>();
containers.set(mainContainer.properties.id, mainContainer); containers.set(mainContainer.properties.id, mainContainer);
@ -244,14 +246,16 @@ export const DEFAULT_MAINCONTAINER_PROPS: IContainerProperties = {
* @param containerConfig default config of the container sent by the API * @param containerConfig default config of the container sent by the API
* @returns {IContainerProperties} Default properties of a newly created container * @returns {IContainerProperties} Default properties of a newly created container
*/ */
export function GetDefaultContainerProps(type: string, export function GetDefaultContainerProps(
type: string,
typeCount: number, typeCount: number,
parent: IContainerModel | undefined | null, parent: IContainerModel | undefined | null,
x: number, x: number,
y: number, y: number,
width: number, width: number,
height: number, height: number,
containerConfig: IAvailableContainer): IContainerProperties { containerConfig: IAvailableContainer
): IContainerProperties {
const orientation = containerConfig.Orientation ?? Orientation.Horizontal; const orientation = containerConfig.Orientation ?? Orientation.Horizontal;
return ({ return ({
id: `${type}-${typeCount}`, id: `${type}-${typeCount}`,
@ -299,10 +303,12 @@ export function GetDefaultContainerProps(type: string,
}); });
} }
export function GetDefaultSymbolModel(name: string, export function GetDefaultSymbolModel(
name: string,
newCounters: Record<string, number>, newCounters: Record<string, number>,
type: string, type: string,
symbolConfig: IAvailableSymbol): ISymbolModel { symbolConfig: IAvailableSymbol
): ISymbolModel {
const id = `${name}-${newCounters[type]}`; const id = `${name}-${newCounters[type]}`;
return { return {
id, id,

View file

@ -1,6 +1,9 @@
import { IContainerModel } from '../Interfaces/IContainerModel'; import { type IContainerModel } from '../Interfaces/IContainerModel';
export function * MakeChildrenIterator(containers: Map<string, IContainerModel>, childrenIds: string[]): Generator<IContainerModel, void, unknown> { export function * MakeChildrenIterator(
containers: Map<string, IContainerModel>,
childrenIds: string[]
): Generator<IContainerModel, void, unknown> {
for (const childId of childrenIds) { for (const childId of childrenIds) {
const child = FindContainerById(containers, childId); const child = FindContainerById(containers, childId);
@ -15,7 +18,11 @@ export function * MakeChildrenIterator(containers: Map<string, IContainerModel>,
/** /**
* Returns a Generator iterating of over the children depth-first * Returns a Generator iterating of over the children depth-first
*/ */
export function * MakeDFSIterator(root: IContainerModel, containers: Map<string, IContainerModel>, enableHideChildrenInTreeview = false): Generator<IContainerModel, void, unknown> { export function * MakeDFSIterator(
root: IContainerModel,
containers: Map<string, IContainerModel>,
enableHideChildrenInTreeview = false
): Generator<IContainerModel, void, unknown> {
const queue: IContainerModel[] = [root]; const queue: IContainerModel[] = [root];
const visited = new Set<IContainerModel>(queue); const visited = new Set<IContainerModel>(queue);
while (queue.length > 0) { while (queue.length > 0) {
@ -51,7 +58,10 @@ export interface ContainerAndDepthAndTransform extends ContainerAndDepth {
/** /**
* Returns a Generator iterating of over the children depth-first * Returns a Generator iterating of over the children depth-first
*/ */
export function * MakeBFSIterator(root: IContainerModel, containers: Map<string, IContainerModel>): Generator<ContainerAndDepth, void, unknown> { export function * MakeBFSIterator(
root: IContainerModel,
containers: Map<string, IContainerModel>
): Generator<ContainerAndDepth, void, unknown> {
const queue: IContainerModel[] = [root]; const queue: IContainerModel[] = [root];
let depth = 0; let depth = 0;
while (queue.length > 0) { while (queue.length > 0) {
@ -141,7 +151,10 @@ export function GetDepth(containers: Map<string, IContainerModel>, parent: ICont
* Returns the absolute position by iterating to the parent * Returns the absolute position by iterating to the parent
* @returns The absolute position of the container * @returns The absolute position of the container
*/ */
export function GetAbsolutePosition(containers: Map<string, IContainerModel>, container: IContainerModel): [number, number] { export function GetAbsolutePosition(
containers: Map<string, IContainerModel>,
container: IContainerModel
): [number, number] {
const x = container.properties.x; const x = container.properties.x;
const y = container.properties.y; const y = container.properties.y;
const parent = FindContainerById(containers, container.properties.parentId) ?? null; const parent = FindContainerById(containers, container.properties.parentId) ?? null;
@ -230,7 +243,11 @@ export function ApplyParentTransform(
* @param id Id of the container to find * @param id Id of the container to find
* @returns The container found or undefined if not found * @returns The container found or undefined if not found
*/ */
export function FindContainerByIdDFS(root: IContainerModel, containers: Map<string, IContainerModel>, id: string): IContainerModel | undefined { export function FindContainerByIdDFS(
root: IContainerModel,
containers: Map<string, IContainerModel>,
id: string
): IContainerModel | undefined {
const it = MakeDFSIterator(root, containers); const it = MakeDFSIterator(root, containers);
for (const container of it) { for (const container of it) {
if (container.properties.id === id) { if (container.properties.id === id) {

View file

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { IEditorState } from '../Interfaces/IEditorState'; import { type IEditorState } from '../Interfaces/IEditorState';
import { IHistoryState } from '../Interfaces/IHistoryState'; import { type IHistoryState } from '../Interfaces/IHistoryState';
import { IContainerModel } from '../Interfaces/IContainerModel'; import { type IContainerModel } from '../Interfaces/IContainerModel';
import { ISymbolModel } from '../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../Interfaces/ISymbolModel';
/** /**
* Revive the Editor state * Revive the Editor state
@ -37,7 +37,10 @@ export function ReviveState(state: IHistoryState): void {
state.containers = new Map(containers.map(({ Key, Value }) => [Key, Value])); state.containers = new Map(containers.map(({ Key, Value }) => [Key, Value]));
} }
export function GetCircularReplacer(): (key: any, value: object | Map<string, any> | null) => object | null | undefined { export function GetCircularReplacer(): (
key: any,
value: object | Map<string, any> | null
) => object | null | undefined {
return (key: any, value: object | null) => { return (key: any, value: object | null) => {
if (key === 'containers') { if (key === 'containers') {
return [...(value as Map<string, any>).entries()] return [...(value as Map<string, any>).entries()]

View file

@ -112,7 +112,8 @@ function GetAllIndexes(arr: number[], val: number): number[] {
* - 4) the selected column must have 1 in the pivot and zeroes in the other rows * - 4) the selected column must have 1 in the pivot and zeroes in the other rows
* - 5) in the selected rows other columns (other than the selected column) * - 5) in the selected rows other columns (other than the selected column)
* must be divided by that pivot: coef / pivot * must be divided by that pivot: coef / pivot
* - 6) for the others cells, apply the pivot: new value = (-coefficient in the old col) * (coefficient in the new row) + old value * - 6) for the others cells, apply the pivot:
* new value = (-coefficient in the old col) * (coefficient in the new row) + old value
* - 7) if in the new matrix there are still negative values in the last row, * - 7) if in the new matrix there are still negative values in the last row,
* redo the algorithm with the new matrix as the base matrix * redo the algorithm with the new matrix as the base matrix
* - 8) otherwise returns the basic variable such as * - 8) otherwise returns the basic variable such as
@ -135,7 +136,9 @@ function ApplyMainLoop(oldMatrix: number[][], rowlength: number): number[][] {
// to avoid infinite loop try to select the least used selected index // to avoid infinite loop try to select the least used selected index
const pivotColIndex = GetLeastUsedIndex(indexes, indexesTried); const pivotColIndex = GetLeastUsedIndex(indexes, indexesTried);
// record the usage of index by incrementing // record the usage of index by incrementing
indexesTried[pivotColIndex] = indexesTried[pivotColIndex] !== undefined ? indexesTried[pivotColIndex] + 1 : 1; indexesTried[pivotColIndex] = indexesTried[pivotColIndex] !== undefined
? indexesTried[pivotColIndex] + 1
: 1;
// 2) find the smallest non negative non null ratio bi/xij (O(m)) // 2) find the smallest non negative non null ratio bi/xij (O(m))
const ratios = []; const ratios = [];
@ -210,7 +213,9 @@ function GetSolutions(nCols: number, finalMatrix: number[][]): number[] {
const col: number[] = []; const col: number[] = [];
for (let j = 0; j < finalMatrix.length; j++) { for (let j = 0; j < finalMatrix.length; j++) {
const row = finalMatrix[j]; const row = finalMatrix[j];
counts[row[i]] = counts[row[i]] !== undefined ? counts[row[i]] + 1 : 1; counts[row[i]] = counts[row[i]] !== undefined
? counts[row[i]] + 1
: 1;
col.push(row[i]); col.push(row[i]);
} }

View file

@ -6,5 +6,7 @@ export function TruncateString(str: string, num: number): string {
} }
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(''); return str.split('-').map((word, index) => index > 0
? word.charAt(0).toUpperCase() + word.slice(1)
: word).join('');
} }

View file

@ -1,6 +1,6 @@
/* eslint-disable import/export */ /* eslint-disable import/export */
import * as React from 'react'; import type * as React from 'react';
import { cleanup, render, RenderResult } from '@testing-library/react'; import { cleanup, render, type RenderResult } from '@testing-library/react';
import { afterEach } from 'vitest'; import { afterEach } from 'vitest';
afterEach(() => { afterEach(() => {