Merge branch 'master' into master.7687.ReplaceBy

This commit is contained in:
Eric NGUYEN 2023-02-14 16:44:50 +01:00
commit 9aec5ae751
38 changed files with 2346 additions and 1434 deletions

View file

@ -19,7 +19,8 @@ module.exports = {
}, },
ecmaVersion: 'latest', ecmaVersion: 'latest',
sourceType: 'module', sourceType: 'module',
project: './tsconfig.json' project: './tsconfig.json',
tsconfigRootDir: __dirname
}, },
plugins: [ plugins: [
'only-warn', 'only-warn',

View file

@ -9,7 +9,20 @@ You will be able to navigate through this document with the table of contents.
- [Table of contents](#table-of-contents) - [Table of contents](#table-of-contents)
- [I want to contribute](#i-want-to-contribute) - [I want to contribute](#i-want-to-contribute)
- [I want to contribute to the .NETFramework API](#i-want-to-contribute-to-the-netframework-api) - [I want to contribute to the .NETFramework API](#i-want-to-contribute-to-the-netframework-api)
- [Getting Started](#getting-started)
- [Before developing](#before-developing)
- [Testing](#testing)
- [Releasing](#releasing)
- [I want to contribute to the React component](#i-want-to-contribute-to-the-react-component) - [I want to contribute to the React component](#i-want-to-contribute-to-the-react-component)
- [Getting Started](#getting-started-1)
- [Before developing](#before-developing-1)
- [CORS](#cors)
- [Develop with Vite and pnpm](#develop-with-vite-and-pnpm)
- [Develop with mprocs](#develop-with-mprocs)
- [Testing the external API without .NETFramework or Windows](#testing-the-external-api-without-netframework-or-windows)
- [Setup debugging with chrome](#setup-debugging-with-chrome)
- [Testing](#testing-1)
- [Releasing](#releasing-1)
- [I want to report a bug](#i-want-to-report-a-bug) - [I want to report a bug](#i-want-to-report-a-bug)
- [Before submitting a bug report](#before-submitting-a-bug-report) - [Before submitting a bug report](#before-submitting-a-bug-report)
- [How do i submit a good bug report?](#how-do-i-submit-a-good-bug-report) - [How do i submit a good bug report?](#how-do-i-submit-a-good-bug-report)
@ -102,6 +115,14 @@ Then run the following command to run the projet in a dev environment:
pnpm dev pnpm dev
``` ```
### Develop with mprocs
[Mprocs](https://github.com/pvolok/mprocs) runs multiple commands in parallel and shows output of each command separately.
It is useful to run `vite` and the test server at the same time with `mprocs`.
Run `pnpm d` or `pnpm mprocs` to run mprocs.
### Testing the external API without .NETFramework or Windows ### Testing the external API without .NETFramework or Windows
Use the Node.js server in `/test-server` to simulate the api. Use the Node.js server in `/test-server` to simulate the api.

View file

@ -20,9 +20,12 @@ namespace SVGLDLibs.Models
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public string color; public string color;
/** width */
[DataMember(EmitDefaultValue = false)]
public double width;
/** color */
[DataMember(EmitDefaultValue = false)]
public string dashArray;
} }
} }

View file

@ -1,4 +1,4 @@
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Collections.Generic; using System.Collections.Generic;
namespace SVGLDLibs.Models namespace SVGLDLibs.Models
@ -26,5 +26,8 @@ namespace SVGLDLibs.Models
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public List<string> linkedContainers { get; set; } public List<string> linkedContainers { get; set; }
[DataMember(EmitDefaultValue = false)]
public List<bool> showDimension { get; set; }
} }
} }

7
mprocs.yaml Normal file
View file

@ -0,0 +1,7 @@
procs:
nvim:
shell: "nvim ."
vite:
shell: "npx vite"
test-server:
shell: "npx nodemon ./test-server/http.js"

View file

@ -4,10 +4,10 @@
"version": "v1.0.0", "version": "v1.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"d": "mprocs",
"dev": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
"build:dotnet": "dotnet build ./csharp/SVGLDLibs/SVGLDLibs/SVGLDLibs.csproj", "build:dotnet": "dotnet build ./csharp/SVGLDLibs/SVGLDLibs/SVGLDLibs.csproj",
"preview": "vite preview",
"linter": "eslint src", "linter": "eslint src",
"test": "vitest", "test": "vitest",
"test:ui": "vitest --ui", "test:ui": "vitest --ui",
@ -17,45 +17,48 @@
"coverage": "vitest run coverage" "coverage": "vitest run coverage"
}, },
"dependencies": { "dependencies": {
"@heroicons/react": "^2.0.11", "@heroicons/react": "^2.0.14",
"@react-hook/size": "^2.1.2", "@react-hook/size": "^2.1.2",
"interweave": "^13.0.0", "interweave": "^13.0.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-svg-pan-zoom": "^3.11.0", "react-svg-pan-zoom": "^3.11.0",
"react-window": "^1.8.7", "react-window": "^1.8.8",
"sweetalert2": "^11.4.34", "sweetalert2": "^11.7.1",
"sweetalert2-react-content": "^5.0.3" "sweetalert2-react-content": "^5.0.7"
}, },
"devDependencies": { "devDependencies": {
"@testing-library/dom": "^8.18.1", "@testing-library/dom": "^8.20.0",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",
"@types/react": "^18.0.21", "@types/react": "^18.0.27",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.10",
"@types/react-svg-pan-zoom": "^3.3.5", "@types/react-svg-pan-zoom": "^3.3.5",
"@types/react-window": "^1.8.5", "@types/react-window": "^1.8.5",
"@typescript-eslint/eslint-plugin": "^5.38.1", "@typescript-eslint/eslint-plugin": "^5.51.0",
"@typescript-eslint/parser": "^5.38.1", "@typescript-eslint/parser": "^5.51.0",
"@vitejs/plugin-react": "^2.1.0", "@vitejs/plugin-react": "^3.1.0",
"@vitest/ui": "^0.20.3", "@vitejs/plugin-react-swc": "^3.1.0",
"autoprefixer": "^10.4.12", "@vitest/ui": "^0.28.4",
"eslint": "^8.24.0", "autoprefixer": "^10.4.13",
"eslint": "^8.33.0",
"eslint-config-standard": "^17.0.0", "eslint-config-standard": "^17.0.0",
"eslint-config-standard-with-typescript": "^22.0.0", "eslint-config-standard-with-typescript": "^34.0.0",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.3.0", "eslint-plugin-n": "^15.6.1",
"eslint-plugin-only-warn": "^1.0.3", "eslint-plugin-only-warn": "^1.1.0",
"eslint-plugin-promise": "^6.0.1", "eslint-plugin-promise": "^6.1.1",
"eslint-plugin-react": "^7.31.8", "eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"jsdom": "^20.0.0", "jsdom": "^21.1.0",
"postcss": "^8.4.16", "mprocs": "^0.6.4",
"sass": "^1.55.0", "nodemon": "^2.0.20",
"tailwindcss": "^3.1.8", "postcss": "^8.4.21",
"typescript": "^4.8.3", "sass": "^1.58.0",
"vite": "^3.1.3", "tailwindcss": "^3.2.4",
"vitest": "^0.20.3" "typescript": "^4.9.5",
"vite": "^4.1.1",
"vitest": "^0.28.4"
} }
} }

2378
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@ import { IConfiguration } from '../../Interfaces/IConfiguration';
import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel'; import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel';
import { IHistoryState } from '../../Interfaces/IHistoryState'; import { IHistoryState } from '../../Interfaces/IHistoryState';
import { IPattern } from '../../Interfaces/IPattern'; import { IPattern } from '../../Interfaces/IPattern';
import { 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';
const CSHARP_WEB_API_BASE_URL = 'http://localhost:5209/'; const CSHARP_WEB_API_BASE_URL = 'http://localhost:5209/';
@ -144,23 +144,11 @@ describe.concurrent('Models test suite', () => {
PositionReference: 0, PositionReference: 0,
HideChildrenInTreeview: true, HideChildrenInTreeview: true,
DimensionOptions: { DimensionOptions: {
childrenDimensions: { childrenDimensions: DEFAULT_DIMENSION_OPTION,
color: '#000000', selfDimensions: DEFAULT_DIMENSION_OPTION,
positions: [] selfMarginsDimensions: DEFAULT_DIMENSION_OPTION,
},
selfDimensions: {
color: '#000000',
positions: []
},
selfMarginsDimensions: {
color: '#000000',
positions: []
},
markPosition: [], markPosition: [],
dimensionWithMarks: { dimensionWithMarks: DEFAULT_DIMENSION_OPTION
color: '#000000',
positions: []
}
}, },
IsHidden: true, IsHidden: true,
Blacklist: [ Blacklist: [

View file

@ -1,14 +1,22 @@
import * as React from 'react'; import * as React from 'react';
import { PropertyType } from '../../Enums/PropertyType'; import { PropertyType } from '../../Enums/PropertyType';
import { IContainerProperties } from '../../Interfaces/IContainerProperties'; import { type IContainerProperties } from '../../Interfaces/IContainerProperties';
import { ISymbolModel } from '../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { import {
SHOW_BORROWER_DIMENSIONS, SHOW_BORROWER_DIMENSIONS,
SHOW_CHILDREN_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS,
SHOW_SELF_DIMENSIONS, SHOW_SELF_DIMENSIONS, SHOW_SELF_MARGINS_DIMENSIONS
SHOW_SELF_MARGINS_DIMENSIONS
} from '../../utils/default'; } from '../../utils/default';
import { ApplyWidthMargin, ApplyXMargin, RemoveWidthMargin, RemoveXMargin, RestoreX, RestoreY, TransformX, TransformY } from '../../utils/svg'; import {
ApplyWidthMargin,
ApplyXMargin,
RemoveWidthMargin,
RemoveXMargin,
RestoreX,
RestoreY,
TransformX,
TransformY
} from '../../utils/svg';
import { Text } from '../Text/Text'; import { Text } from '../Text/Text';
import { InputGroup } from '../InputGroup/InputGroup'; import { InputGroup } from '../InputGroup/InputGroup';
import { TextInputGroup } from '../InputGroup/TextInputGroup'; import { TextInputGroup } from '../InputGroup/TextInputGroup';
@ -38,7 +46,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='' inputClassName=''
type='string' type='string'
value={props.properties.displayedText?.toString()} value={props.properties.displayedText?.toString()}
onChange={(value) => props.onChange('displayedText', value)} /> onChange={(value) => { props.onChange('displayedText', value); }}/>
<OrientationSelector <OrientationSelector
id='orientation' id='orientation'
name='Orientation' name='Orientation'
@ -109,7 +117,8 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
props.properties.width, props.properties.width,
props.properties.positionReference props.properties.positionReference
).toString()} ).toString()}
onChange={(value) => props.onChange( onChange={(value) => {
props.onChange(
'x', 'x',
ApplyXMargin( ApplyXMargin(
RestoreX( RestoreX(
@ -119,7 +128,8 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
), ),
props.properties.margin.left props.properties.margin.left
) )
)} /> );
}}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-y`} id={`${props.properties.id}-y`}
labelText={Text({ textId: '@ContainerY' })} labelText={Text({ textId: '@ContainerY' })}
@ -132,7 +142,8 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
props.properties.height, props.properties.height,
props.properties.positionReference props.properties.positionReference
).toString()} ).toString()}
onChange={(value) => props.onChange( onChange={(value) => {
props.onChange(
'y', 'y',
ApplyXMargin( ApplyXMargin(
RestoreY( RestoreY(
@ -142,7 +153,8 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
), ),
props.properties.margin.top props.properties.margin.top
) )
)} /> );
}}/>
</div> </div>
</Category> </Category>
@ -163,7 +175,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={1} min={1}
value={props.properties.minWidth.toString()} value={props.properties.minWidth.toString()}
onChange={(value) => props.onChange('minWidth', Number(value))} /> onChange={(value) => { props.onChange('minWidth', Number(value)); }}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-width`} id={`${props.properties.id}-width`}
labelText={Text({ textId: '@ContainerWidth' })} labelText={Text({ textId: '@ContainerWidth' })}
@ -174,7 +186,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
min={props.properties.minWidth} min={props.properties.minWidth}
max={props.properties.maxWidth} max={props.properties.maxWidth}
value={(RemoveWidthMargin(props.properties.width, props.properties.margin.left, props.properties.margin.right)).toString()} value={(RemoveWidthMargin(props.properties.width, props.properties.margin.left, props.properties.margin.right)).toString()}
onChange={(value) => props.onChange('width', ApplyWidthMargin(Number(value), props.properties.margin.left, props.properties.margin.right))} onChange={(value) => { props.onChange('width', ApplyWidthMargin(Number(value), props.properties.margin.left, props.properties.margin.right)); }}
isDisabled={props.properties.isFlex}/> isDisabled={props.properties.isFlex}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-maxWidth`} id={`${props.properties.id}-maxWidth`}
@ -185,7 +197,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={1} min={1}
value={props.properties.maxWidth.toString()} value={props.properties.maxWidth.toString()}
onChange={(value) => props.onChange('maxWidth', Number(value))} /> onChange={(value) => { props.onChange('maxWidth', Number(value)); }}/>
<div className='col-span-5 p-3'></div> <div className='col-span-5 p-3'></div>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-minHeight`} id={`${props.properties.id}-minHeight`}
@ -196,7 +208,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={1} min={1}
value={props.properties.minHeight.toString()} value={props.properties.minHeight.toString()}
onChange={(value) => props.onChange('minHeight', Number(value))} /> onChange={(value) => { props.onChange('minHeight', Number(value)); }}/>
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-height`} id={`${props.properties.id}-height`}
labelText={Text({ textId: '@ContainerHeight' })} labelText={Text({ textId: '@ContainerHeight' })}
@ -207,7 +219,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
min={props.properties.minHeight} min={props.properties.minHeight}
max={props.properties.maxHeight} max={props.properties.maxHeight}
value={(RemoveWidthMargin(props.properties.height, props.properties.margin.top, props.properties.margin.bottom)).toString()} value={(RemoveWidthMargin(props.properties.height, props.properties.margin.top, props.properties.margin.bottom)).toString()}
onChange={(value) => props.onChange('height', ApplyWidthMargin(Number(value), props.properties.margin.top, props.properties.margin.bottom))} onChange={(value) => { props.onChange('height', ApplyWidthMargin(Number(value), props.properties.margin.top, props.properties.margin.bottom)); }}
isDisabled={props.properties.isFlex} isDisabled={props.properties.isFlex}
/> />
<TextInputGroup <TextInputGroup
@ -219,7 +231,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
type='number' type='number'
min={1} min={1}
value={props.properties.maxHeight.toString()} value={props.properties.maxHeight.toString()}
onChange={(value) => props.onChange('maxHeight', Number(value))} /> onChange={(value) => { props.onChange('maxHeight', Number(value)); }}/>
</div> </div>
</Category> </Category>
@ -239,7 +251,7 @@ 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' })}
@ -249,7 +261,7 @@ 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' })}
@ -259,7 +271,7 @@ 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' })}
@ -269,7 +281,7 @@ 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>
@ -287,7 +299,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='ml-auto mr-auto block' inputClassName='ml-auto mr-auto block'
type={ToggleType.Full} type={ToggleType.Full}
checked={props.properties.isFlex} checked={props.properties.isFlex}
onChange={(event) => props.onChange('isFlex', event.target.checked)} onChange={(event) => { props.onChange('isFlex', event.target.checked); }}
/> />
<ToggleButton <ToggleButton
labelText={Text({ textId: '@ContainerAnchor' })} labelText={Text({ textId: '@ContainerAnchor' })}
@ -296,7 +308,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='ml-auto mr-auto block' inputClassName='ml-auto mr-auto block'
type={ToggleType.Full} type={ToggleType.Full}
checked={props.properties.isAnchor} checked={props.properties.isAnchor}
onChange={(event) => props.onChange('isAnchor', event.target.checked)} /> onChange={(event) => { props.onChange('isAnchor', event.target.checked); }}/>
</div> </div>
</Category> </Category>
@ -326,7 +338,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
value: symbol.id value: symbol.id
}))} }))}
value={props.properties.linkedSymbolId ?? ''} value={props.properties.linkedSymbolId ?? ''}
onChange={(event) => props.onChange('linkedSymbolId', event.target.value)} /> onChange={(event) => { props.onChange('linkedSymbolId', event.target.value); }}/>
</div> </div>
</Category> </Category>
@ -346,16 +358,35 @@ 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: '@Color' })} labelText={Text({ textId: '@StyleStrokeColor' })}
inputKey='color' inputKey='color'
labelClassName='' labelClassName=''
inputClassName='' inputClassName=''
type='color' type='color'
value={props.properties.dimensionOptions.selfDimensions.color} value={props.properties.dimensionOptions.selfDimensions.color}
onChange={(e) => props.onChange('color', e.target.value, PropertyType.SelfDimension)}/> onChange={(e) => { props.onChange('color', e.target.value, PropertyType.SelfDimension); }}/>
<TextInputGroup
id={`${props.properties.id}-selfDimensions-width`}
labelText={Text({ textId: '@StyleStrokeWidth' })}
inputKey='width'
labelClassName=''
inputClassName=''
type='number'
min={0}
value={(props.properties.dimensionOptions.selfDimensions.width ?? 0).toString()}
onChange={(value) => { props.onChange('width', Number(value), PropertyType.SelfDimension); }}/>
<TextInputGroup
id={`${props.properties.id}-selfDimensions-dasharray`}
labelText={Text({ textId: '@StyleStrokeDashArray' })}
inputKey='dashArray'
labelClassName=''
inputClassName=''
type='text'
value={props.properties.dimensionOptions.selfDimensions.dashArray ?? ''}
onChange={(value) => { props.onChange('dashArray', value, PropertyType.SelfDimension); }}/>
</div> </div>
} }
{ {
@ -367,16 +398,35 @@ 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: '@Color' })} labelText={Text({ textId: '@StyleStrokeColor' })}
inputKey='color' inputKey='color'
labelClassName='' labelClassName=''
inputClassName='' inputClassName=''
type='color' type='color'
value={props.properties.dimensionOptions.selfMarginsDimensions.color} value={props.properties.dimensionOptions.selfMarginsDimensions.color}
onChange={(e) => props.onChange('color', e.target.value, PropertyType.SelfMarginDimension)}/> onChange={(e) => { props.onChange('color', e.target.value, PropertyType.SelfMarginDimension); }}/>
<TextInputGroup
id={`${props.properties.id}-selfMarginsDimensions-width`}
labelText={Text({ textId: '@StyleStrokeWidth' })}
inputKey='width'
labelClassName=''
inputClassName=''
type='number'
min={0}
value={(props.properties.dimensionOptions.selfMarginsDimensions.width ?? 0).toString()}
onChange={(value) => { props.onChange('width', Number(value), PropertyType.SelfMarginDimension); }}/>
<TextInputGroup
id={`${props.properties.id}-selfMarginsDimensions-dasharray`}
labelText={Text({ textId: '@StyleStrokeDashArray' })}
inputKey='dashArray'
labelClassName=''
inputClassName=''
type='text'
value={props.properties.dimensionOptions.selfMarginsDimensions.dashArray ?? ''}
onChange={(value) => { props.onChange('dashArray', value, PropertyType.SelfMarginDimension); }}/>
</div> </div>
} }
{ {
@ -388,16 +438,35 @@ 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: '@Color' })} labelText={Text({ textId: '@StyleStrokeColor' })}
inputKey='color' inputKey='color'
labelClassName='' labelClassName=''
inputClassName='' inputClassName=''
type='color' type='color'
value={props.properties.dimensionOptions.childrenDimensions.color} value={props.properties.dimensionOptions.childrenDimensions.color}
onChange={(e) => props.onChange('color', e.target.value, PropertyType.ChildrenDimensions)}/> onChange={(e) => { props.onChange('color', e.target.value, PropertyType.ChildrenDimensions); }}/>
<TextInputGroup
id={`${props.properties.id}-childrenDimensions-width`}
labelText={Text({ textId: '@StyleStrokeWidth' })}
inputKey='width'
labelClassName=''
inputClassName=''
type='number'
min={0}
value={(props.properties.dimensionOptions.childrenDimensions.width ?? 0).toString()}
onChange={(value) => { props.onChange('width', Number(value), PropertyType.ChildrenDimensions); }}/>
<TextInputGroup
id={`${props.properties.id}-childrenDimensions-dasharray`}
labelText={Text({ textId: '@StyleStrokeDashArray' })}
inputKey='dashArray'
labelClassName=''
inputClassName=''
type='text'
value={props.properties.dimensionOptions.childrenDimensions.dashArray ?? ''}
onChange={(value) => { props.onChange('dashArray', value, PropertyType.ChildrenDimensions); }}/>
</div> </div>
} }
{ {
@ -409,7 +478,7 @@ 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'>
@ -419,16 +488,35 @@ 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: '@Color' })} labelText={Text({ textId: '@StyleStrokeColor' })}
inputKey='color' inputKey='color'
labelClassName='' labelClassName=''
inputClassName='' inputClassName=''
type='color' type='color'
value={props.properties.dimensionOptions.dimensionWithMarks.color} value={props.properties.dimensionOptions.dimensionWithMarks.color}
onChange={(e) => props.onChange('color', e.target.value, PropertyType.DimensionWithMarks)}/> onChange={(e) => { props.onChange('color', e.target.value, PropertyType.DimensionWithMarks); }}/>
<TextInputGroup
id={`${props.properties.id}-dimensionWithMarks-width`}
labelText={Text({ textId: '@StyleStrokeWidth' })}
inputKey='width'
labelClassName=''
inputClassName=''
type='number'
min={0}
value={(props.properties.dimensionOptions.dimensionWithMarks.width ?? 0).toString()}
onChange={(value) => { props.onChange('width', Number(value), PropertyType.DimensionWithMarks); }}/>
<TextInputGroup
id={`${props.properties.id}-dimensionWithMarks-dasharray`}
labelText={Text({ textId: '@StyleStrokeDashArray' })}
inputKey='dashArray'
labelClassName=''
inputClassName=''
type='text'
value={props.properties.dimensionOptions.dimensionWithMarks.dashArray ?? ''}
onChange={(value) => { props.onChange('dashArray', value, PropertyType.DimensionWithMarks); }}/>
</div> </div>
</> </>
} }
@ -451,7 +539,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='col-span-3' inputClassName='col-span-3'
type='string' type='string'
value={props.properties.style.stroke ?? 'black'} value={props.properties.style.stroke ?? 'black'}
onChange={(value) => props.onChange('stroke', value, PropertyType.Style)} onChange={(value) => { props.onChange('stroke', value, PropertyType.Style); }}
/> />
<InputGroup <InputGroup
labelKey={`${props.properties.id}-strokeOpacity`} labelKey={`${props.properties.id}-strokeOpacity`}
@ -464,7 +552,7 @@ 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`}
@ -474,7 +562,7 @@ 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); }}
/> />
<TextInputGroup <TextInputGroup
id={`${props.properties.id}-fill`} id={`${props.properties.id}-fill`}
@ -484,7 +572,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
inputClassName='col-span-3' inputClassName='col-span-3'
type='string' type='string'
value={props.properties.style.fill ?? 'black'} value={props.properties.style.fill ?? 'black'}
onChange={(value) => props.onChange('fill', value, PropertyType.Style)} onChange={(value) => { props.onChange('fill', value, PropertyType.Style); }}
/> />
<InputGroup <InputGroup
labelKey={`${props.properties.id}-fillOpacity`} labelKey={`${props.properties.id}-fillOpacity`}
@ -497,7 +585,7 @@ 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

@ -4,11 +4,12 @@ import { expect, describe, it, vi } from 'vitest';
import { PositionReference } from '../../Enums/PositionReference'; import { PositionReference } from '../../Enums/PositionReference';
import { IContainerProperties } from '../../Interfaces/IContainerProperties'; import { IContainerProperties } from '../../Interfaces/IContainerProperties';
import { Orientation } from '../../Enums/Orientation'; import { Orientation } from '../../Enums/Orientation';
import { Properties } from './ContainerProperties'; import { ContainerProperties } from './ContainerProperties';
import { DEFAULT_DIMENSION_OPTION } from '../../utils/default';
describe.concurrent('Properties', () => { describe.concurrent('Properties', () => {
it('No properties', () => { it('No properties', () => {
render(<Properties render(<ContainerProperties
properties={undefined} properties={undefined}
onChange={() => {}} onChange={() => {}}
symbols={new Map()} symbols={new Map()}
@ -43,23 +44,11 @@ describe.concurrent('Properties', () => {
warning: '', warning: '',
hideChildrenInTreeview: false, hideChildrenInTreeview: false,
dimensionOptions: { dimensionOptions: {
childrenDimensions: { childrenDimensions: DEFAULT_DIMENSION_OPTION,
color: '#000000', selfDimensions: DEFAULT_DIMENSION_OPTION,
positions: [] selfMarginsDimensions: DEFAULT_DIMENSION_OPTION,
},
selfDimensions: {
color: '#000000',
positions: []
},
selfMarginsDimensions: {
color: '#000000',
positions: []
},
markPosition: [], markPosition: [],
dimensionWithMarks: { dimensionWithMarks: DEFAULT_DIMENSION_OPTION
color: '#000000',
positions: []
}
} }
}; };
@ -67,7 +56,7 @@ describe.concurrent('Properties', () => {
(prop as any)[key] = value; (prop as any)[key] = value;
}); });
const { container, rerender } = render(<Properties const { container, rerender } = render(<ContainerProperties
properties={prop} properties={prop}
onChange={handleChange} onChange={handleChange}
symbols={new Map()} symbols={new Map()}
@ -108,7 +97,7 @@ describe.concurrent('Properties', () => {
expect(prop.parentId).toBe('parentId'); expect(prop.parentId).toBe('parentId');
expect(prop.x).toBe(2); expect(prop.x).toBe(2);
expect(prop.y).toBe(2); expect(prop.y).toBe(2);
rerender(<Properties rerender(<ContainerProperties
properties={Object.assign({}, prop)} properties={Object.assign({}, prop)}
onChange={handleChange} onChange={handleChange}
symbols={new Map()} symbols={new Map()}

View file

@ -10,7 +10,7 @@ interface IPropertiesProps {
onChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void onChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void
} }
export function Properties(props: IPropertiesProps): JSX.Element { export function ContainerProperties(props: IPropertiesProps): JSX.Element {
if (props.properties === undefined) { if (props.properties === undefined) {
return <div></div>; return <div></div>;
} }

View file

@ -38,6 +38,30 @@ export function SelectContainer(
return history; return history;
} }
/**
* Deselect a container
* @returns New history
*/
export function DeselectContainer(
containerId: string,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
history.push({
lastAction: `Deselect ${containerId}`,
mainContainer: current.mainContainer,
containers: structuredClone(current.containers),
selectedContainerId: 'undefined',
typeCounters: Object.assign({}, current.typeCounters),
symbols: structuredClone(current.symbols),
selectedSymbolId: current.selectedSymbolId
});
return history;
}
/** /**
* Delete a container * Delete a container
* @param containerId containerId of the container to delete * @param containerId containerId of the container to delete

View file

@ -53,6 +53,7 @@ export function SaveEditorAsSVG(): void {
svg.replaceChildren(...mainSvg); svg.replaceChildren(...mainSvg);
// remove the selector // remove the selector
// TODO: Fix this with SelectorMode != Nothing or with some html magic
const group = svg.children[svg.children.length - 1]; const group = svg.children[svg.children.length - 1];
group.removeChild(group.children[group.children.length - 1]); group.removeChild(group.children[group.children.length - 1]);
if (SHOW_SELECTOR_TEXT) { if (SHOW_SELECTOR_TEXT) {

View file

@ -1,14 +1,17 @@
import * as React from 'react'; import * as React from 'react';
import { useState } from 'react';
import useSize from '@react-hook/size'; import useSize from '@react-hook/size';
import { FixedSizeList as List } from 'react-window'; import { FixedSizeList as List } from 'react-window';
import { Properties } from '../ContainerProperties/ContainerProperties'; import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
import { ContainerProperties } from '../ContainerProperties/ContainerProperties';
import { IContainerModel } from '../../Interfaces/IContainerModel'; import { IContainerModel } from '../../Interfaces/IContainerModel';
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools'; import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
import { ISymbolModel } from '../../Interfaces/ISymbolModel'; import { ISymbolModel } from '../../Interfaces/ISymbolModel';
import { PropertyType } from '../../Enums/PropertyType'; import { PropertyType } from '../../Enums/PropertyType';
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline'; import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
import { Text } from '../Text/Text';
interface IElementsListProps { interface IElementsSideBarProps {
containers: Map<string, IContainerModel> containers: Map<string, IContainerModel>
mainContainer: IContainerModel mainContainer: IContainerModel
symbols: Map<string, ISymbolModel> symbols: Map<string, ISymbolModel>
@ -20,6 +23,8 @@ interface IElementsListProps {
) => void ) => void
selectContainer: (containerId: string) => void selectContainer: (containerId: string) => void
addContainer: (index: number, type: string, parent: string) => void addContainer: (index: number, type: string, parent: string) => void
isExpanded: boolean
onExpandChange: () => void
} }
function RemoveBorderClasses(target: HTMLButtonElement, exception: string = ''): void { function RemoveBorderClasses(target: HTMLButtonElement, exception: string = ''): void {
@ -119,10 +124,11 @@ function HandleOnDrop(
} }
} }
export function ElementsList(props: IElementsListProps): JSX.Element { export function ElementsSideBar(props: IElementsSideBarProps): JSX.Element {
// States // States
const divRef = React.useRef<HTMLDivElement>(null); const divRef = React.useRef<HTMLDivElement>(null);
const [,height] = useSize(divRef); const [,height] = useSize(divRef);
const [showProperties, setShowProperties] = useState(props.isExpanded);
// Render // Render
const it = MakeRecursionDFSIterator(props.mainContainer, props.containers, 0, [0, 0], true); const it = MakeRecursionDFSIterator(props.mainContainer, props.containers, 0, [0, 0], true);
@ -160,10 +166,21 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
} }
return ( return (
<div className='h-full flex flex-col'> <div className='flex flex-row h-full w-full' >
<div ref={divRef} className='h-1/2'> {showProperties &&
<div className='flex flex-1 flex-col w-64 border-r-2 border-slate-400'>
<ContainerProperties
properties={props.selectedContainer?.properties}
symbols={props.symbols}
onChange={props.onPropertyChange}
/>
</div>
}
<div className='flex w-64' ref={divRef}>
<div className='w-6'>
<ToggleSideBar title={Text({ textId: '@Properties' })} checked={showProperties} onChange={(newValue) => { setShowProperties(newValue); props.onExpandChange(); }} />
</div>
<List <List
className="List divide-y divide-black overflow-y-auto"
itemCount={containers.length} itemCount={containers.length}
itemSize={35} itemSize={35}
height={height} height={height}
@ -172,13 +189,6 @@ export function ElementsList(props: IElementsListProps): JSX.Element {
{Row} {Row}
</List> </List>
</div> </div>
<div className='grow overflow-auto'>
<Properties
properties={props.selectedContainer?.properties}
symbols={props.symbols}
onChange={props.onPropertyChange}
/>
</div>
</div> </div>
); );
} }

View file

@ -1,100 +0,0 @@
import * as React from 'react';
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { DIMENSION_MARGIN } from '../../../utils/default';
import { GetAbsolutePosition, MakeBFSIterator } from '../../../utils/itertools';
import { TransformX } from '../../../utils/svg';
import { Dimension } from './Dimension';
interface IDimensionLayerProps {
containers: Map<string, IContainerModel>
roots: IContainerModel | IContainerModel[] | null
scale?: number
}
function GetDimensionsNodes(
containers: Map<string, IContainerModel>,
root: IContainerModel,
scale: number
): React.ReactNode[] {
const it = MakeBFSIterator(root, containers);
const dimensions: React.ReactNode[] = [];
let currentDepth = 0;
let min = Infinity;
let max = -Infinity;
let lastY = 0;
for (const { container, depth } of it) {
if (currentDepth !== depth) {
AddNewDimension(currentDepth, min, max, lastY, scale, '#000000', dimensions);
currentDepth = depth;
min = Infinity;
max = -Infinity;
}
const absoluteX = GetAbsolutePosition(containers, container)[0];
const x = TransformX(absoluteX, container.properties.width, container.properties.positionReference);
lastY = container.properties.y + container.properties.height;
if (x < min) {
min = x;
}
if (x > max) {
max = x;
}
}
AddNewDimension(currentDepth, min, max, lastY, scale, '#000000', dimensions);
return dimensions;
}
/**
* A layer containing all dimension
* @param props
* @returns
*/
export function DepthDimensionLayer(props: IDimensionLayerProps): JSX.Element {
let dimensions: React.ReactNode[] = [];
const scale = props.scale ?? 1;
if (Array.isArray(props.roots)) {
props.roots.forEach(child => {
dimensions.concat(GetDimensionsNodes(props.containers, child, scale));
});
} else if (props.roots !== null) {
dimensions = GetDimensionsNodes(props.containers, props.roots, scale);
}
return (
<g>
{dimensions}
</g>
);
}
function AddNewDimension(currentDepth: number, min: number, max: number, lastY: number, scale: number, color: string, dimensions: React.ReactNode[]): void {
const id = `dim-depth-${currentDepth}`;
const xStart = min;
const xEnd = max;
const y = lastY + (DIMENSION_MARGIN * (currentDepth + 1)) / scale;
const width = xEnd - xStart;
const text = width
.toFixed(0)
.toString();
if (width === 0) {
return;
}
dimensions.push(
<Dimension
key={id}
id={id}
xStart={xStart}
yStart={y}
xEnd={xEnd}
yEnd={y}
text={text}
scale={scale}
color={color}
/>
);
}

View file

@ -1,6 +1,9 @@
import * as React from 'react'; import * as React from 'react';
import { type IDimensionOptions } from '../../../Interfaces/IDimensionOptions';
import { NOTCHES_LENGTH } from '../../../utils/default'; import { NOTCHES_LENGTH } from '../../../utils/default';
export type IDimensionStyle = Omit<IDimensionOptions, 'positions'>;
interface IDimensionProps { interface IDimensionProps {
id: string id: string
xStart: number xStart: number
@ -8,7 +11,7 @@ interface IDimensionProps {
xEnd: number xEnd: number
yEnd: number yEnd: number
text: string text: string
color: string style: IDimensionStyle
scale?: number scale?: number
} }
@ -28,8 +31,9 @@ function ApplyParametric(x0: number, t: number, vx: number): number {
export function Dimension(props: IDimensionProps): JSX.Element { export function Dimension(props: IDimensionProps): JSX.Element {
const scale = props.scale ?? 1; const scale = props.scale ?? 1;
const style: React.CSSProperties = { const style: React.CSSProperties = {
stroke: props.color, stroke: props.style.color,
strokeWidth: 2 / scale strokeWidth: (props.style.width ?? 2) / scale,
strokeDasharray: props.style.dashArray
}; };
/// We need to find the points of the notches /// We need to find the points of the notches
@ -79,9 +83,11 @@ export function Dimension(props: IDimensionProps): JSX.Element {
x2={endBottomX} x2={endBottomX}
y2={endBottomY} y2={endBottomY}
style={style}/> style={style}/>
<text textAnchor={'middle'} alignmentBaseline={'central'} <text
x={textX} x={textX}
y={textY} y={textY}
textAnchor={'middle'}
alignmentBaseline={'central'}
style={{ style={{
transform: `rotate(${rotation}turn) scale(${1 / scale})`, transform: `rotate(${rotation}turn) scale(${1 / scale})`,
transformOrigin: `${textX}px ${textY}px` transformOrigin: `${textX}px ${textY}px`

View file

@ -1,7 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { Orientation } from '../../../Enums/Orientation'; import { Orientation } from '../../../Enums/Orientation';
import { Position } from '../../../Enums/Position'; import { Position } from '../../../Enums/Position';
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { import {
DIMENSION_MARGIN, DIMENSION_MARGIN,
SHOW_BORROWER_DIMENSIONS, SHOW_BORROWER_DIMENSIONS,
@ -11,10 +10,13 @@ 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 { Dimension, type IDimensionStyle } from './Dimension';
import { type IContainerModel } from '../../../Interfaces/IContainerModel';
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
interface IDimensionLayerProps { interface IDimensionLayerProps {
containers: Map<string, IContainerModel> containers: Map<string, IContainerModel>
symbols: Map<string, ISymbolModel>
root: IContainerModel root: IContainerModel
scale: number scale: number
} }
@ -58,7 +60,7 @@ function ActionByPosition(
* @param param0 Object with the root container and the scale of the svg * @param param0 Object with the root container and the scale of the svg
* @returns A list of dimensions * @returns A list of dimensions
*/ */
function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.ReactNode[] { function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps): React.ReactNode[] {
const it = MakeRecursionDFSIterator(root, containers, 0, [0, 0]); const it = MakeRecursionDFSIterator(root, containers, 0, [0, 0]);
const dimensions: React.ReactNode[] = []; const dimensions: React.ReactNode[] = [];
const topDim = root.properties.y; const topDim = root.properties.y;
@ -87,8 +89,8 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
container, container,
currentTransform, currentTransform,
dimensions, dimensions,
scale, scale
container.properties.dimensionOptions.selfDimensions.color] ]
); );
} }
@ -102,8 +104,8 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
container, container,
currentTransform, currentTransform,
dimensions, dimensions,
scale, scale
container.properties.dimensionOptions.selfMarginsDimensions.color] ]
); );
} }
@ -111,6 +113,7 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
ActionByPosition( ActionByPosition(
dimMapped, dimMapped,
container.properties.dimensionOptions.dimensionWithMarks.positions, container.properties.dimensionOptions.dimensionWithMarks.positions,
AddHorizontalBorrowerDimension, AddHorizontalBorrowerDimension,
AddVerticalBorrowerDimension, AddVerticalBorrowerDimension,
[ [
@ -119,8 +122,8 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
depth, depth,
currentTransform, currentTransform,
dimensions, dimensions,
scale, scale
container.properties.dimensionOptions.dimensionWithMarks.color] ]
); );
} }
@ -135,8 +138,21 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
container, container,
currentTransform, currentTransform,
dimensions, dimensions,
scale
]
);
}
}
let startDepthSymbols: number = 0;
for (const symbol of symbols) {
if (symbol[1].showDimension) {
startDepthSymbols++;
AddHorizontalSymbolDimension(
symbol[1],
dimensions,
scale, scale,
container.properties.dimensionOptions.childrenDimensions.color] startDepthSymbols
); );
} }
} }
@ -144,6 +160,40 @@ function Dimensions({ containers, root, scale }: IDimensionLayerProps): React.Re
return dimensions; return dimensions;
} }
function AddHorizontalSymbolDimension(
symbol: ISymbolModel,
dimensions: React.ReactNode[],
scale: number,
depth: number
): void {
const width = symbol.x + (symbol.width / 2);
if (width != null && width > 0) {
const id = `dim-y-margin-left${symbol.width.toFixed(0)}-${symbol.id}`;
const offset = (DIMENSION_MARGIN * (depth + 1)) / scale;
const text = width
.toFixed(0)
.toString();
// TODO: Put this in default.ts
const defaultDimensionSymbolStyle: IDimensionStyle = {
color: 'black'
};
dimensions.push(
<Dimension
key={id}
id={id}
xStart={0}
yStart={-offset}
xEnd={width}
yEnd={-offset}
text={text}
scale={scale}
style={defaultDimensionSymbolStyle}/>
);
}
}
/** /**
* A layer containing all dimension * A layer containing all dimension
* @param props * @param props
@ -165,10 +215,10 @@ function AddHorizontalChildrenDimension(
container: IContainerModel, container: IContainerModel,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number, scale: number
color: string
): void { ): void {
const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`; const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`;
const style = container.properties.dimensionOptions.childrenDimensions;
const lastChildId = container.children[container.children.length - 1]; const lastChildId = container.children[container.children.length - 1];
const lastChild = FindContainerById(containers, lastChildId); const lastChild = FindContainerById(containers, lastChildId);
@ -218,7 +268,7 @@ function AddHorizontalChildrenDimension(
yEnd={yDim} yEnd={yDim}
text={textChildren} text={textChildren}
scale={scale} scale={scale}
color={color}/>); style={style}/>);
} }
function AddVerticalChildrenDimension( function AddVerticalChildrenDimension(
@ -228,11 +278,10 @@ function AddVerticalChildrenDimension(
container: IContainerModel, container: IContainerModel,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number, scale: number
color: string
): void { ): void {
const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`; const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`;
const style = container.properties.dimensionOptions.childrenDimensions;
const lastChildId = container.children[container.children.length - 1]; const lastChildId = container.children[container.children.length - 1];
const lastChild = FindContainerById(containers, lastChildId); const lastChild = FindContainerById(containers, lastChildId);
@ -287,7 +336,7 @@ function AddVerticalChildrenDimension(
yEnd={yChildrenEnd + offset} yEnd={yChildrenEnd + offset}
text={textChildren} text={textChildren}
scale={scale} scale={scale}
color={color} style={style}
/>); />);
} }
@ -298,9 +347,9 @@ function AddHorizontalBorrowerDimension(
depth: number, depth: number,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number, scale: number
color: string
): void { ): void {
const style = container.properties.dimensionOptions.dimensionWithMarks;
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform); const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
const marks = []; // list of vertical lines for the dimension const marks = []; // list of vertical lines for the dimension
for (const { for (const {
@ -345,7 +394,7 @@ function AddHorizontalBorrowerDimension(
yEnd={yDim} yEnd={yDim}
text={value.toFixed(0)} text={value.toFixed(0)}
scale={scale} scale={scale}
color={color}/>); style={style}/>);
count++; count++;
} }
} }
@ -358,9 +407,9 @@ function AddVerticalBorrowerDimension(
depth: number, depth: number,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number, scale: number
color: string
): void { ): void {
const style = container.properties.dimensionOptions.dimensionWithMarks;
const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform); const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform);
const marks = []; // list of vertical lines for the dimension const marks = []; // list of vertical lines for the dimension
for (const { for (const {
@ -410,7 +459,7 @@ function AddVerticalBorrowerDimension(
yEnd={next} yEnd={next}
text={value.toFixed(0)} text={value.toFixed(0)}
scale={scale} scale={scale}
color={color}/>); style={style}/>);
count++; count++;
} }
} }
@ -421,9 +470,9 @@ function AddVerticalSelfDimension(
container: IContainerModel, container: IContainerModel,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number, scale: number
color: string
): void { ): void {
const style = container.properties.dimensionOptions.selfDimensions;
const height = container.properties.height; const height = container.properties.height;
const idVert = `dim-x${xDim.toFixed(0)}-${container.properties.id}`; const idVert = `dim-x${xDim.toFixed(0)}-${container.properties.id}`;
let yStart = container.properties.y + currentTransform[1] + height; let yStart = container.properties.y + currentTransform[1] + height;
@ -446,19 +495,18 @@ function AddVerticalSelfDimension(
yEnd={yEnd} yEnd={yEnd}
text={textVert} text={textVert}
scale={scale} scale={scale}
color={color}/> style={style}/>
); );
} }
function AddHorizontalSelfDimension( function AddHorizontalSelfDimension(
yDim: number, yDim: number,
container: IContainerModel, container: IContainerModel,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number, scale: number
color: string
): void { ): void {
const style = container.properties.dimensionOptions.selfDimensions;
const width = container.properties.width; const width = container.properties.width;
const id = `dim-y${yDim.toFixed(0)}-${container.properties.id}`; const id = `dim-y${yDim.toFixed(0)}-${container.properties.id}`;
const xStart = container.properties.x + currentTransform[0]; const xStart = container.properties.x + currentTransform[0];
@ -476,19 +524,18 @@ function AddHorizontalSelfDimension(
yEnd={yDim} yEnd={yDim}
text={text} text={text}
scale={scale} scale={scale}
color={color}/> style={style}/>
); );
} }
function AddHorizontalSelfMarginsDimension( function AddHorizontalSelfMarginsDimension(
yDim: number, yDim: number,
container: IContainerModel, container: IContainerModel,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number, scale: number
color: string
): void { ): void {
const style = container.properties.dimensionOptions.selfMarginsDimensions;
const left = container.properties.margin.left; const left = container.properties.margin.left;
if (left != null) { if (left != null) {
const id = `dim-y-margin-left${yDim.toFixed(0)}-${container.properties.id}`; const id = `dim-y-margin-left${yDim.toFixed(0)}-${container.properties.id}`;
@ -507,7 +554,7 @@ function AddHorizontalSelfMarginsDimension(
yEnd={yDim} yEnd={yDim}
text={text} text={text}
scale={scale} scale={scale}
color={color}/> style={style}/>
); );
} }
@ -529,7 +576,7 @@ function AddHorizontalSelfMarginsDimension(
yEnd={yDim} yEnd={yDim}
text={text} text={text}
scale={scale} scale={scale}
color={color}/> style={style}/>
); );
} }
} }
@ -540,9 +587,9 @@ function AddVerticalSelfMarginDimension(
container: IContainerModel, container: IContainerModel,
currentTransform: [number, number], currentTransform: [number, number],
dimensions: React.ReactNode[], dimensions: React.ReactNode[],
scale: number, scale: number
color: string
): void { ): void {
const style = container.properties.dimensionOptions.selfMarginsDimensions;
const top = container.properties.margin.top; const top = container.properties.margin.top;
if (top != null) { if (top != null) {
const idVert = `dim-x-margin-top${xDim.toFixed(0)}-${container.properties.id}`; const idVert = `dim-x-margin-top${xDim.toFixed(0)}-${container.properties.id}`;
@ -566,7 +613,7 @@ function AddVerticalSelfMarginDimension(
yEnd={yEnd} yEnd={yEnd}
text={textVert} text={textVert}
scale={scale} scale={scale}
color={color}/> style={style}/>
); );
} }
const bottom = container.properties.margin.bottom; const bottom = container.properties.margin.bottom;
@ -592,7 +639,7 @@ function AddVerticalSelfMarginDimension(
yEnd={yEnd} yEnd={yEnd}
text={textVert} text={textVert}
scale={scale} scale={scale}
color={color}/> style={style}/>
); );
} }
} }

View file

@ -1,38 +1,18 @@
import './Selector.scss'; import '../Selector.scss';
import * as React from 'react'; import * as React from 'react';
import { IContainerModel } from '../../../../Interfaces/IContainerModel';
import { SHOW_SELECTOR_TEXT } from '../../../../utils/default'; import { SHOW_SELECTOR_TEXT } from '../../../../utils/default';
import { GetAbsolutePosition } from '../../../../utils/itertools';
import { RemoveMargin } from '../../../../utils/svg';
interface ISelectorProps { interface ISelectorProps {
containers: Map<string, IContainerModel> text: string
selected?: IContainerModel x: number
scale?: number y: number
width: number
height: number
scale: number
style?: React.CSSProperties
} }
export function Selector(props: ISelectorProps): JSX.Element { export function Selector({ text, x, y, width, height, scale, style: overrideStyle }: ISelectorProps): JSX.Element {
if (props.selected === undefined || props.selected === null) {
return (
<rect visibility={'hidden'}>
</rect>
);
}
const scale = (props.scale ?? 1);
let [x, y] = GetAbsolutePosition(props.containers, props.selected);
let [width, height] = [
props.selected.properties.width,
props.selected.properties.height
];
({ x, y, width, height } = RemoveMargin(x, y, width, height,
props.selected.properties.margin.left,
props.selected.properties.margin.bottom,
props.selected.properties.margin.top,
props.selected.properties.margin.right
));
const xText = x + width / 2; const xText = x + width / 2;
const yText = y + height / 2; const yText = y + height / 2;
@ -43,7 +23,8 @@ export function Selector(props: ISelectorProps): JSX.Element {
transitionProperty: 'all', transitionProperty: 'all',
transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
transitionDuration: '150ms', transitionDuration: '150ms',
animation: 'fadein 750ms ease-in alternate infinite' animation: 'fadein 750ms ease-in alternate infinite',
...overrideStyle
}; };
return ( return (
@ -65,7 +46,7 @@ export function Selector(props: ISelectorProps): JSX.Element {
transformBox: 'fill-box' transformBox: 'fill-box'
}} }}
> >
{props.selected.properties.displayedText} { text }
</text> </text>
: null} : null}
</> </>

View file

@ -0,0 +1,46 @@
import '../Selector.scss';
import * as React from 'react';
import { type IContainerModel } from '../../../../Interfaces/IContainerModel';
import { GetAbsolutePosition } from '../../../../utils/itertools';
import { RemoveMargin } from '../../../../utils/svg';
import { Selector } from '../Selector/Selector';
interface ISelectorContainerProps {
containers: Map<string, IContainerModel>
selected?: IContainerModel
scale?: number
}
export function SelectorContainer(props: ISelectorContainerProps): JSX.Element {
if (props.selected === undefined || props.selected === null) {
return (
<rect visibility={'hidden'}>
</rect>
);
}
const scale = (props.scale ?? 1);
let [x, y] = GetAbsolutePosition(props.containers, props.selected);
let [width, height] = [
props.selected.properties.width,
props.selected.properties.height
];
({ x, y, width, height } = RemoveMargin(x, y, width, height,
props.selected.properties.margin.left,
props.selected.properties.margin.bottom,
props.selected.properties.margin.top,
props.selected.properties.margin.right
));
return (
<Selector
text={props.selected.properties.displayedText}
x={x}
y={y}
width={width}
height={height}
scale={scale}
/>
);
}

View file

@ -0,0 +1,47 @@
import '../Selector.scss';
import * as React from 'react';
import { SYMBOL_MARGIN } from '../../../../utils/default';
import { type ISymbolModel } from '../../../../Interfaces/ISymbolModel';
import { Selector } from '../Selector/Selector';
interface ISelectorSymbolProps {
symbols: Map<string, ISymbolModel>
selected?: ISymbolModel
scale?: number
}
export function SelectorSymbol(props: ISelectorSymbolProps): JSX.Element {
if (props.selected === undefined || props.selected === null) {
return (
<rect visibility={'hidden'}>
</rect>
);
}
const scale = (props.scale ?? 1);
const [width, height] = [
props.selected.width / scale,
props.selected.height / scale
];
const [x, y] = [
props.selected.x + props.selected.width / 2,
-SYMBOL_MARGIN - height];
const style: React.CSSProperties = {
transform: 'translateX(-50%)',
transformBox: 'fill-box'
};
return (
<Selector
text={props.selected.displayedText}
x={x}
y={y}
width={width}
height={height}
scale={scale}
style={style}
/>
);
}

View file

@ -1,13 +1,13 @@
import * as React from 'react'; import * as React from 'react';
import { ReactSVGPanZoom, Tool, TOOL_PAN, Value } from 'react-svg-pan-zoom'; import { ReactSVGPanZoom, type Tool, TOOL_PAN, type Value } from 'react-svg-pan-zoom';
import { Container } from './Elements/Container'; import { Container } from './Elements/Container';
import { IContainerModel } from '../../Interfaces/IContainerModel'; import { IContainerModel } from '../../Interfaces/IContainerModel';
import { Selector } from './Elements/Selector/Selector'; import { SelectorContainer } from './Elements/SelectorContainer/SelectorContainer';
import { DepthDimensionLayer } from './Elements/DepthDimensionLayer'; import { MAX_FRAMERATE } from '../../utils/default';
import { MAX_FRAMERATE, SHOW_DIMENSIONS_PER_DEPTH } from '../../utils/default';
import { SymbolLayer } from './Elements/SymbolLayer'; import { SymbolLayer } from './Elements/SymbolLayer';
import { ISymbolModel } from '../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { DimensionLayer } from './Elements/DimensionLayer'; import { DimensionLayer } from './Elements/DimensionLayer';
import { SelectorSymbol } from './Elements/SelectorSymbol/SelectorSymbol';
interface ISVGProps { interface ISVGProps {
className?: string className?: string
@ -17,11 +17,19 @@ interface ISVGProps {
height: number height: number
containers: Map<string, IContainerModel> containers: Map<string, IContainerModel>
children: IContainerModel children: IContainerModel
selected?: IContainerModel selectedContainer?: IContainerModel
symbols: Map<string, ISymbolModel> symbols: Map<string, ISymbolModel>
selectedSymbol?: ISymbolModel
selectorMode: SelectorMode
selectContainer: (containerId: string) => void selectContainer: (containerId: string) => void
} }
export enum SelectorMode {
Nothing,
Containers,
Symbols
}
export const ID = 'svg'; export const ID = 'svg';
export function SVG(props: ISVGProps): JSX.Element { export function SVG(props: ISVGProps): JSX.Element {
@ -49,8 +57,7 @@ export function SVG(props: ISVGProps): JSX.Element {
xmlns xmlns
}; };
let children: React.ReactNode | React.ReactNode[] = []; const children: React.ReactNode | React.ReactNode[] = <Container
children = <Container
key={`container-${props.children.properties.id}`} key={`container-${props.children.properties.id}`}
containers={props.containers} containers={props.containers}
model={props.children} model={props.children}
@ -59,6 +66,25 @@ export function SVG(props: ISVGProps): JSX.Element {
selectContainer={props.selectContainer} selectContainer={props.selectContainer}
/>; />;
function Selector(): JSX.Element {
switch (props.selectorMode) {
case SelectorMode.Containers:
return <SelectorContainer
containers={props.containers}
scale={scale}
selected={props.selectedContainer}
/>;
case SelectorMode.Symbols:
return <SelectorSymbol
symbols={props.symbols}
scale={scale}
selected={props.selectedSymbol}
/>;
default:
return <></>;
}
}
return ( return (
<div id={ID} className={props.className}> <div id={ID} className={props.className}>
<ReactSVGPanZoom <ReactSVGPanZoom
@ -94,12 +120,9 @@ export function SVG(props: ISVGProps): JSX.Element {
> >
<svg {...properties}> <svg {...properties}>
{children} {children}
{SHOW_DIMENSIONS_PER_DEPTH <DimensionLayer containers={props.containers} symbols={props.symbols} scale={scale} root={props.children} />
? <DepthDimensionLayer containers={props.containers} scale={scale} roots={props.children} />
: null}
<DimensionLayer containers={props.containers} scale={scale} root={props.children} />
<SymbolLayer scale={scale} symbols={props.symbols} /> <SymbolLayer scale={scale} symbols={props.symbols} />
<Selector containers={props.containers} scale={scale} selected={props.selected} /> {/* leave this at the end so it can be removed during the svg export */} <Selector />
</svg> </svg>
</ReactSVGPanZoom> </ReactSVGPanZoom>
</div> </div>

View file

@ -0,0 +1,4 @@
.text-vertical{
text-align: right;
writing-mode: vertical-rl;
}

View file

@ -0,0 +1,23 @@
import * as React from 'react';
import './ToggleSideBar.scss';
interface IToggleSidebarProps {
title: string
checked: boolean
onChange: (newValue: boolean) => void
}
export function ToggleSideBar({ title, checked, onChange }: IToggleSidebarProps): JSX.Element {
return (
<div className={`${(checked ? 'bg-slate-400 hover:bg-slate-500' : 'bg-slate-300 hover:bg-slate-400')}`}>
<button
className={'w-full py-2'}
type='button'
onClick={() => onChange(!checked)}
>
<p className='text-vertical'>{title}
</p>
</button>
</div>
);
}

View file

@ -4,6 +4,8 @@ import { RestoreX, TransformX } from '../../utils/svg';
import { InputGroup } from '../InputGroup/InputGroup'; import { InputGroup } from '../InputGroup/InputGroup';
import { TextInputGroup } from '../InputGroup/TextInputGroup'; import { TextInputGroup } from '../InputGroup/TextInputGroup';
import { Text } from '../Text/Text'; import { Text } from '../Text/Text';
import { PropertyType } from '../../Enums/PropertyType';
import { ToggleButton } from '../ToggleButton/ToggleButton';
interface ISymbolFormProps { interface ISymbolFormProps {
symbol: ISymbolModel symbol: ISymbolModel
@ -60,6 +62,13 @@ export function SymbolForm(props: ISymbolFormProps): JSX.Element {
min={0} min={0}
value={props.symbol.width.toString()} value={props.symbol.width.toString()}
onChange={(value) => props.onChange('width', Number(value))} /> onChange={(value) => props.onChange('width', Number(value))} />
<ToggleButton
labelText={Text({ textId: '@ShowDimension' })}
inputKey='showDimension'
labelClassName=''
inputClassName=''
checked={props.symbol.showDimension}
onChange={(e) => props.onChange('showDimension', e.target.checked)}/>
</div> </div>
); );
} }

View file

@ -1,22 +1,56 @@
import * as React from 'react'; import * as React from 'react';
import useSize from '@react-hook/size'; import useSize from '@react-hook/size';
import { FixedSizeList as List } from 'react-window'; import { FixedSizeList as List } from 'react-window';
import { ISymbolModel } from '../../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { SymbolProperties } from '../SymbolProperties/SymbolProperties'; import { SymbolProperties } from '../SymbolProperties/SymbolProperties';
import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
import { Text } from '../Text/Text';
import { useState } from 'react';
interface ISymbolsSidebarProps { interface ISymbolsSidebarProps {
selectedSymbolId: string selectedSymbolId: string
symbols: Map<string, ISymbolModel> symbols: Map<string, ISymbolModel>
onPropertyChange: (key: string, value: string | number | boolean) => void onPropertyChange: (key: string, value: string | number | boolean) => void
selectSymbol: (symbolId: string) => void selectSymbol: (symbolId: string) => void
isExpanded: boolean
onExpandChange: (isExpanded: boolean) => void
} }
export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element { export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
// States // States
const divRef = React.useRef<HTMLDivElement>(null); const divRef = React.useRef<HTMLDivElement>(null);
const height = useSize(divRef)[1]; const height = useSize(divRef)[1];
const [showProperties, setShowProperties] = useState(props.isExpanded);
// Render // Render
const symbols = [...props.symbols.values()]; const symbols = [...props.symbols.values()];
const selectedSymbol = props.symbols.get(props.selectedSymbolId);
return (
<div className='flex flex-row h-full w-full'>
{showProperties && <div className='flex flex-1 flex-col w-64 border-r-2 border-slate-400'>
{(selectedSymbol == null) && <h1 className={'p-4'}>{Text({ textId: '@NoSymbolSelected' })}</h1>}
<SymbolProperties
symbol={selectedSymbol}
symbols={props.symbols}
onChange={props.onPropertyChange}
/>
</div>}
<div className={'flex w-64'} ref={divRef}>
<div className='w-6'>
<ToggleSideBar title={Text({ textId: '@Properties' })} checked={showProperties} onChange={(newValue) => { setShowProperties(newValue); props.onExpandChange(newValue); }} />
</div>
<List
itemCount={symbols.length}
itemSize={35}
height={height}
width={'100%'}
>
{Row}
</List>
</div>
</div>
);
function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element { function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element {
const symbol = symbols[index]; const symbol = symbols[index];
const key = symbol.id; const key = symbol.id;
@ -33,33 +67,10 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
id={key} id={key}
key={key} key={key}
style={style} style={style}
onClick={() => props.selectSymbol(key)} onClick={() => { props.selectSymbol(key); }}
> >
{text} {text}
</button> </button>
); );
} }
return (
<div className='h-full'>
<div ref={divRef} className='h-1/2 text-gray-800'>
<List
className='List divide-y divide-black'
itemCount={symbols.length}
itemSize={35}
height={height}
width={'100%'}
>
{Row}
</List>
</div>
<div>
<SymbolProperties
symbol={props.symbols.get(props.selectedSymbolId)}
symbols={props.symbols}
onChange={props.onPropertyChange}
/>
</div>
</div>
);
} }

View file

@ -1,20 +1,20 @@
import * as React from 'react'; import * as React from 'react';
import { ElementsList } from '../ElementsList/ElementsList'; import { ElementsSideBar } from '../ElementsList/ElementsSideBar';
import { History } from '../History/History'; import { History } from '../History/History';
import { Bar } from '../Bar/Bar'; import { Bar, BAR_WIDTH } from '../Bar/Bar';
import { Symbols } from '../Symbols/Symbols'; import { Symbols } from '../Symbols/Symbols';
import { SymbolsSidebar } from '../SymbolsList/SymbolsList'; import { SymbolsSidebar } from '../SymbolsList/SymbolsSidebar';
import { PropertyType } from '../../Enums/PropertyType'; import { type PropertyType } from '../../Enums/PropertyType';
import { Messages } from '../Messages/Messages'; import { Messages } from '../Messages/Messages';
import { Sidebar } from '../Sidebar/Sidebar'; import { Sidebar } from '../Sidebar/Sidebar';
import { Components } from '../Components/Components'; import { Components } from '../Components/Components';
import { Viewer } from '../Viewer/Viewer'; import { Viewer } from '../Viewer/Viewer';
import { Settings } from '../Settings/Settings'; import { Settings } from '../Settings/Settings';
import { 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 { UseWorker, UseAsync } from './UseWorker';
import { FindContainerById } from '../../utils/itertools'; import { FindContainerById } from '../../utils/itertools';
import { 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 { IReplaceContainer } from '../../Interfaces/IReplaceContainer'; import { IReplaceContainer } from '../../Interfaces/IReplaceContainer';
@ -42,7 +42,9 @@ export interface IUIProps {
export enum SidebarType { export enum SidebarType {
None, None,
Components, Components,
ComponentsExpanded,
Symbols, Symbols,
SymbolsExpanded,
History, History,
Messages, Messages,
Settings Settings
@ -84,7 +86,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
); );
} }
// Please use setOrToggleSidebar rather than setSelectedSidebar so we can close the sidebar // Please use setOrToggleSidebar rather than setSelectedSidebar, so we can close the sidebar
const setOrToggleSidebar = UseSetOrToggleSidebar(selectedSidebar, setSelectedSidebar); const setOrToggleSidebar = UseSetOrToggleSidebar(selectedSidebar, setSelectedSidebar);
let leftSidebarTitle = ''; let leftSidebarTitle = '';
@ -99,6 +101,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
} }
const selectedContainer = FindContainerById(current.containers, current.selectedContainerId); const selectedContainer = FindContainerById(current.containers, current.selectedContainerId);
const selectedSymbol = current.symbols.get(current.selectedSymbolId);
switch (selectedSidebar) { switch (selectedSidebar) {
case SidebarType.Components: case SidebarType.Components:
@ -112,7 +115,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
replaceContainer={replaceContainer} replaceContainer={replaceContainer}
setReplaceContainer={setReplaceContainer}/>; setReplaceContainer={setReplaceContainer}/>;
rightSidebarTitle = Text({ textId: '@Elements' }); rightSidebarTitle = Text({ textId: '@Elements' });
rightChildren = <ElementsList rightChildren = <ElementsSideBar
containers={current.containers} containers={current.containers}
mainContainer={mainContainer} mainContainer={mainContainer}
symbols={current.symbols} symbols={current.symbols}
@ -120,9 +123,32 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
onPropertyChange={methods.onPropertyChange} onPropertyChange={methods.onPropertyChange}
selectContainer={methods.selectContainer} selectContainer={methods.selectContainer}
addContainer={methods.addContainerAt} addContainer={methods.addContainerAt}
isExpanded ={false}
onExpandChange={() => { setOrToggleSidebar(SidebarType.ComponentsExpanded); } }
/>;
break;
case SidebarType.ComponentsExpanded:
leftSidebarTitle = Text({ textId: '@Components' });
leftChildren = <Components
selectedContainer={selectedContainer}
componentOptions={configuration.AvailableContainers}
categories={configuration.Categories}
buttonOnClick={methods.addOrReplaceContainer}
replaceContainer={replaceContainer}
setReplaceContainer={setReplaceContainer}/>;
rightSidebarTitle = Text({ textId: '@Elements' });
rightChildren = <ElementsSideBar
containers={current.containers}
mainContainer={mainContainer}
symbols={current.symbols}
selectedContainer={selectedContainer}
onPropertyChange={methods.onPropertyChange}
selectContainer={methods.selectContainer}
addContainer={methods.addContainerAt}
isExpanded ={true}
onExpandChange={() => { setOrToggleSidebar(SidebarType.Components); } }
/>; />;
break; break;
case SidebarType.Symbols: case SidebarType.Symbols:
leftSidebarTitle = Text({ textId: '@SymbolsLeft' }); leftSidebarTitle = Text({ textId: '@SymbolsLeft' });
leftChildren = <Symbols leftChildren = <Symbols
@ -135,6 +161,24 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
symbols={current.symbols} symbols={current.symbols}
onPropertyChange={methods.onSymbolPropertyChange} onPropertyChange={methods.onSymbolPropertyChange}
selectSymbol={methods.selectSymbol} selectSymbol={methods.selectSymbol}
isExpanded ={false}
onExpandChange={() => { setOrToggleSidebar(SidebarType.SymbolsExpanded); } }
/>;
break;
case SidebarType.SymbolsExpanded:
leftSidebarTitle = Text({ textId: '@SymbolsLeft' });
leftChildren = <Symbols
componentOptions={configuration.AvailableSymbols}
buttonOnClick={methods.addSymbol}
/>;
rightSidebarTitle = Text({ textId: '@SymbolsRight' });
rightChildren = <SymbolsSidebar
selectedSymbolId={current.selectedSymbolId}
symbols={current.symbols}
onPropertyChange={methods.onSymbolPropertyChange}
selectSymbol={methods.selectSymbol}
isExpanded ={true}
onExpandChange={() => { setOrToggleSidebar(SidebarType.Symbols); }}
/>; />;
break; break;
@ -152,7 +196,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
leftChildren = <Messages leftChildren = <Messages
historyState={current} historyState={current}
messages={messages} messages={messages}
clearMessage={() => setMessages([])} clearMessage={() => { setMessages([]); }}
/>; />;
break; break;
@ -167,6 +211,7 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
const isLeftSidebarOpen = selectedSidebar !== SidebarType.None; const isLeftSidebarOpen = selectedSidebar !== SidebarType.None;
const isRightSidebarOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.Symbols; const isRightSidebarOpen = selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.Symbols;
const isRightSidebarOpenExpanded = selectedSidebar === SidebarType.ComponentsExpanded || selectedSidebar === SidebarType.SymbolsExpanded;
const isLeftSidebarOpenClasses = new Set<string>([ const isLeftSidebarOpenClasses = new Set<string>([
'left-sidebar', 'left-sidebar',
@ -178,35 +223,60 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
let isRightSidebarOpenClasses = 'right-0 -bottom-full md:-right-80 md:bottom-0'; let isRightSidebarOpenClasses = 'right-0 -bottom-full md:-right-80 md:bottom-0';
let marginSidebar = BAR_WIDTH;
const viewerMarginClasses = new Set<string>([
'ml-16'
]);
if (isLeftSidebarOpen) { if (isLeftSidebarOpen) {
isLeftSidebarOpenClasses.delete('-bottom-full'); isLeftSidebarOpenClasses.delete('-bottom-full');
isLeftSidebarOpenClasses.delete('md:-left-64'); isLeftSidebarOpenClasses.delete('md:-left-64');
isLeftSidebarOpenClasses.delete('md:bottom-0'); isLeftSidebarOpenClasses.delete('md:bottom-0');
marginSidebar += 256;
viewerMarginClasses.add(' md:ml-80');
} }
if (isRightSidebarOpen) { if (isRightSidebarOpen || isRightSidebarOpenExpanded) {
isRightSidebarOpenClasses = 'right-0'; isRightSidebarOpenClasses = 'right-0';
if (isRightSidebarOpenExpanded) {
viewerMarginClasses.add(' md:mr-[32rem]');
marginSidebar += 512;
} else {
viewerMarginClasses.add(' md:mr-64');
marginSidebar += 256;
}
} else { } else {
isLeftSidebarOpenClasses.delete('left-sidebar'); isLeftSidebarOpenClasses.delete('left-sidebar');
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 || selectedSidebar === SidebarType.ComponentsExpanded;
const isSymbolsOpen = selectedSidebar === SidebarType.Symbols || selectedSidebar === SidebarType.SymbolsExpanded;
return ( return (
<> <>
<Bar <Bar
className={clickRestrictionsClasses} className={clickRestrictionsClasses}
isComponentsOpen={selectedSidebar === SidebarType.Components} isComponentsOpen={isComponentsOpen}
isSymbolsOpen={selectedSidebar === SidebarType.Symbols} isSymbolsOpen={isSymbolsOpen}
isHistoryOpen={selectedSidebar === SidebarType.History} isHistoryOpen={selectedSidebar === SidebarType.History}
isMessagesOpen={selectedSidebar === SidebarType.Messages} isMessagesOpen={selectedSidebar === SidebarType.Messages}
isSettingsOpen={selectedSidebar === SidebarType.Settings} isSettingsOpen={selectedSidebar === SidebarType.Settings}
toggleComponents={() => { toggleComponents={() => {
if (selectedSidebar === SidebarType.ComponentsExpanded) {
setOrToggleSidebar(SidebarType.ComponentsExpanded);
} else {
setOrToggleSidebar(SidebarType.Components); setOrToggleSidebar(SidebarType.Components);
}
} } } }
toggleSymbols={() => { toggleSymbols={() => {
if (selectedSidebar === SidebarType.SymbolsExpanded) {
setOrToggleSidebar(SidebarType.SymbolsExpanded);
} else {
setOrToggleSidebar(SidebarType.Symbols); setOrToggleSidebar(SidebarType.Symbols);
}
} } } }
toggleTimeline={() => { toggleTimeline={() => {
setOrToggleSidebar(SidebarType.History); setOrToggleSidebar(SidebarType.History);
@ -225,12 +295,14 @@ export function UI({ editorState, replaceContainer, setReplaceContainer, ...meth
{ leftChildren } { leftChildren }
</Sidebar> </Sidebar>
<Viewer <Viewer
className={clickRestrictionsClasses} className={`${clickRestrictionsClasses} ${[...viewerMarginClasses.values()].join(' ')} w-full h-full`}
isLeftSidebarOpen={isLeftSidebarOpen}
isRightSidebarOpen={isRightSidebarOpen}
current={current} current={current}
isComponentsOpen={isComponentsOpen}
isSymbolsOpen={isSymbolsOpen}
selectedContainer={selectedContainer} selectedContainer={selectedContainer}
selectContainer={methods.selectContainer} selectContainer={methods.selectContainer}
selectedSymbol={selectedSymbol}
margin={marginSidebar}
/> />
<Sidebar <Sidebar
className={`right-sidebar ${isRightSidebarOpenClasses} ${clickRestrictionsClasses}`} className={`right-sidebar ${isRightSidebarOpenClasses} ${clickRestrictionsClasses}`}

View file

@ -1,58 +1,54 @@
import * as React from 'react'; import * as React from 'react';
import { IContainerModel } from '../../Interfaces/IContainerModel'; import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { IHistoryState } from '../../Interfaces/IHistoryState'; import { type IHistoryState } from '../../Interfaces/IHistoryState';
import { IPoint } from '../../Interfaces/IPoint'; import { type IPoint } from '../../Interfaces/IPoint';
import { DIMENSION_MARGIN, USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default'; import { DIMENSION_MARGIN, USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default';
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools'; import { FindContainerById, MakeRecursionDFSIterator } 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 { AddDimensions } from '../Canvas/DimensionLayer'; import { AddDimensions } from '../Canvas/DimensionLayer';
import { RenderSelector } from '../Canvas/Selector'; import { RenderSelector } from '../Canvas/Selector';
import { SVG } from '../SVG/SVG'; import { SelectorMode, SVG } from '../SVG/SVG';
import { RenderSymbol } from '../Canvas/Symbol'; import { RenderSymbol } from '../Canvas/Symbol';
import { useState } from 'react';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
interface IViewerProps { interface IViewerProps {
className: string className: string
isLeftSidebarOpen: boolean
isRightSidebarOpen: boolean
current: IHistoryState current: IHistoryState
selectedContainer: IContainerModel | undefined selectedContainer: IContainerModel | undefined
selectContainer: (containerId: string) => void selectContainer: (containerId: string) => void
selectedSymbol: ISymbolModel | undefined
margin: number
isComponentsOpen: boolean
isSymbolsOpen: boolean
} }
interface IViewer { export function Viewer({
viewerWidth: number className,
viewerHeight: number current,
selectedContainer,
selectContainer,
selectedSymbol,
margin,
isComponentsOpen,
isSymbolsOpen
}: IViewerProps): JSX.Element {
function computeWidth(margin: number): number {
return window.innerWidth - (window.innerWidth < 768 ? BAR_WIDTH : margin);
} }
function OnResize( const [windowSize, setWindowSize] = useState([
isLeftSidebarOpen: boolean, computeWidth(margin),
isRightSidebarOpen: boolean, window.innerHeight
setViewer: React.Dispatch<React.SetStateAction<IViewer>> ]);
): void {
let marginSidebar = BAR_WIDTH;
if (isLeftSidebarOpen) {
marginSidebar += 256;
}
if (isRightSidebarOpen) {
marginSidebar += 256;
}
const margin = window.innerWidth < 768 ? BAR_WIDTH : marginSidebar;
setViewer({
viewerWidth: window.innerWidth - margin,
viewerHeight: window.innerHeight
});
}
function UseSVGAutoResizerOnWindowResize(
isLeftSidebarOpen: boolean,
isRightSidebarOpen: boolean,
setViewer: React.Dispatch<React.SetStateAction<IViewer>>
): void {
React.useEffect(() => { React.useEffect(() => {
function SVGAutoResizer(): void { function SVGAutoResizer(): void {
OnResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer); setWindowSize([
computeWidth(margin),
window.innerHeight
]);
} }
window.addEventListener('resize', SVGAutoResizer); window.addEventListener('resize', SVGAutoResizer);
@ -61,42 +57,13 @@ function UseSVGAutoResizerOnWindowResize(
window.removeEventListener('resize', SVGAutoResizer); window.removeEventListener('resize', SVGAutoResizer);
}; };
}); });
}
function UseSVGAutoResizerOnSidebar(
isLeftSidebarOpen: boolean,
isRightSidebarOpen: boolean,
setViewer: React.Dispatch<React.SetStateAction<IViewer>>
): void {
React.useEffect(() => { React.useEffect(() => {
OnResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer); setWindowSize([
}, [isLeftSidebarOpen, isRightSidebarOpen, setViewer]); computeWidth(margin),
} window.innerHeight
]);
export function Viewer({ }, [margin]);
className,
isLeftSidebarOpen, isRightSidebarOpen,
current,
selectedContainer,
selectContainer
}: IViewerProps): JSX.Element {
let marginClasses = 'ml-16';
let marginSidebar = BAR_WIDTH;
if (isLeftSidebarOpen) {
marginClasses += ' md:ml-80';
marginSidebar += 256;
}
if (isRightSidebarOpen) {
marginClasses += ' md:mr-64';
marginSidebar += 256;
}
const margin = window.innerWidth < 768 ? BAR_WIDTH : marginSidebar;
const [viewer, setViewer] = React.useState<IViewer>({
viewerWidth: window.innerWidth - margin,
viewerHeight: window.innerHeight
});
const mainContainer = FindContainerById(current.containers, current.mainContainer); const mainContainer = FindContainerById(current.containers, current.mainContainer);
@ -104,8 +71,12 @@ export function Viewer({
return <></>; return <></>;
} }
UseSVGAutoResizerOnWindowResize(isLeftSidebarOpen, isRightSidebarOpen, setViewer); let selectorMode = SelectorMode.Nothing;
UseSVGAutoResizerOnSidebar(isLeftSidebarOpen, isRightSidebarOpen, setViewer); if (isComponentsOpen) {
selectorMode = SelectorMode.Containers;
} else if (isSymbolsOpen) {
selectorMode = SelectorMode.Symbols;
}
if (USE_EXPERIMENTAL_CANVAS_API) { if (USE_EXPERIMENTAL_CANVAS_API) {
function Draw(ctx: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint): void { function Draw(ctx: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint): void {
@ -171,14 +142,16 @@ export function Viewer({
return ( return (
<SVG <SVG
className={`${marginClasses} ${className}`} className={className}
viewerWidth={viewer.viewerWidth} viewerWidth={windowSize[0]}
viewerHeight={viewer.viewerHeight} viewerHeight={windowSize[1]}
width={mainContainer.properties.width} width={mainContainer.properties.width}
height={mainContainer.properties.height} height={mainContainer.properties.height}
containers={current.containers} containers={current.containers}
selected={selectedContainer} selectedContainer={selectedContainer}
symbols={current.symbols} symbols={current.symbols}
selectedSymbol={selectedSymbol}
selectorMode={selectorMode}
selectContainer={selectContainer} selectContainer={selectContainer}
> >
{mainContainer} {mainContainer}
@ -208,10 +181,11 @@ function RenderDimensions(
currentTransform: [number, number] currentTransform: [number, number]
): void { ): void {
ctx.save(); ctx.save();
const containerLeftDim = leftDim - (DIMENSION_MARGIN * (depth + 1)) / scale; const depthOffset = (DIMENSION_MARGIN * (depth + 1)) / scale;
const containerTopDim = topDim - (DIMENSION_MARGIN * (depth + 1)) / scale; const containerLeftDim = leftDim - depthOffset;
const containerBottomDim = bottomDim + (DIMENSION_MARGIN * (depth + 1)) / scale; const containerTopDim = topDim - depthOffset;
const containerRightDim = rightDim + (DIMENSION_MARGIN * (depth + 1)) / scale; const containerBottomDim = bottomDim + depthOffset;
const containerRightDim = rightDim + depthOffset;
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim]; const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
AddDimensions(ctx, containers, container, dimMapped, currentTransform, scale, depth); AddDimensions(ctx, containers, container, dimMapped, currentTransform, scale, depth);
ctx.restore(); ctx.restore();

View file

@ -27,6 +27,6 @@ export enum PropertyType {
SelfMarginDimension, SelfMarginDimension,
ChildrenDimensions, ChildrenDimensions,
DimensionWithMarks, DimensionWithMarks,
DimensionOptions
DimensionOptions
} }

View file

@ -1,12 +1,12 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { AddMethod } from '../Enums/AddMethod'; import { type AddMethod } from '../Enums/AddMethod';
import { PositionReference } from '../Enums/PositionReference'; import { type PositionReference } from '../Enums/PositionReference';
import { IAction } from './IAction'; import { type IAction } from './IAction';
import { IMargin } from './IMargin'; import { type IMargin } from './IMargin';
import { Orientation } from '../Enums/Orientation'; import { type Orientation } from '../Enums/Orientation';
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';
/** Model of available container used in application configuration */ /** Model of available container used in application configuration */
export interface IAvailableContainer { export interface IAvailableContainer {

View file

@ -1,10 +1,17 @@
import { Position } from '../Enums/Position'; import { type Position } from '../Enums/Position';
export interface IDimensionOptions { export interface IDimensionOptions {
positions: Position[] positions: Position[]
/** /**
* Stroke color * Stroke color
*/ */
color: string color?: string
/** stroke-width */
width?: number
/** stroke-dasharray */
dashArray?: string
} }

View file

@ -1,4 +1,4 @@
import { IAvailableSymbol } from './IAvailableSymbol'; import { type IAvailableSymbol } from './IAvailableSymbol';
export interface ISymbolModel { export interface ISymbolModel {
/** Identifier */ /** Identifier */
@ -26,4 +26,7 @@ export interface ISymbolModel {
/** List of linked container id */ /** List of linked container id */
linkedContainers: Set<string> linkedContainers: Set<string>
/** Dimensions options */
showDimension: boolean
} }

View file

@ -2,12 +2,14 @@
"@StartFromScratch": "Start from scratch", "@StartFromScratch": "Start from scratch",
"@LoadConfigFile": "Load a configuration file", "@LoadConfigFile": "Load a configuration file",
"@GoBack": "Go back", "@GoBack": "Go back",
"@Properties" : "Properties",
"@Components": "Components", "@Components": "Components",
"@Elements": "Elements", "@Elements": "Elements",
"@Symbols": "Symbols", "@Symbols": "Symbols",
"@SymbolsLeft": "Symbols", "@SymbolsLeft": "Symbols",
"@SymbolsRight": "Symbols", "@SymbolsRight": "Symbols",
"@NoSymbolSelected": "No symbol selected",
"@Timeline": "Timeline", "@Timeline": "Timeline",
"@Messages": "Messages", "@Messages": "Messages",
"@Settings": "Settings", "@Settings": "Settings",
@ -57,11 +59,14 @@
"@ContainerAlignmentInput": "Alignment", "@ContainerAlignmentInput": "Alignment",
"@ContainerAlignWithSymbol": "Align to symbol", "@ContainerAlignWithSymbol": "Align to symbol",
"@ContainerDimensions": "Dimensions", "@ContainerDimensions": "Dimensions",
"@ContainerShowDimension": "Show Dimension", "@ContainerShowDimension": "Show dimensions",
"@ContainerShowChildrenDimension": "Show surrounding dimension of children", "@ContainerShowChildrenDimension": "Show surrounding dimensions of children",
"@ContainerMarkPosition": "Mark the position for the parents", "@ContainerMarkPosition": "Mark the position for the parents",
"@ContainerShowDimensionWithMarks": "Show dimension with marked children", "@ContainerShowDimensionWithMarks": "Show dimensions with marked children",
"@ContainerShowMarginsDimension": "Show margins dimensions",
"@ContainerStyle": "Style", "@ContainerStyle": "Style",
"@StyleStrokeColor": "Stroke Color",
"@StyleStrokeDashArray": "Stroke Dash Array",
"@StyleStroke": "Stroke", "@StyleStroke": "Stroke",
"@StyleStrokeOpacity": "Stroke Opacity", "@StyleStrokeOpacity": "Stroke Opacity",
"@StyleStrokeWidth": "Stroke Width", "@StyleStrokeWidth": "Stroke Width",

View file

@ -2,12 +2,14 @@
"@StartFromScratch": "Partir de zéro", "@StartFromScratch": "Partir de zéro",
"@LoadConfigFile": "Charger un fichier de configuration", "@LoadConfigFile": "Charger un fichier de configuration",
"@GoBack": "Revenir", "@GoBack": "Revenir",
"@Properties" : "Propriétés",
"@Components": "Composants", "@Components": "Composants",
"@Elements": "Éléments", "@Elements": "Éléments",
"@Symbols": "Symboles", "@Symbols": "Symboles",
"@SymbolsLeft": "Symboles", "@SymbolsLeft": "Symboles",
"@SymbolsRight": "Symboles", "@SymbolsRight": "Symboles",
"@NoSymbolSelected": "Pas de symbol sélectionné",
"@Timeline": "Chronologie", "@Timeline": "Chronologie",
"@Messages": "Messages", "@Messages": "Messages",
"@Settings": "Paramètres", "@Settings": "Paramètres",
@ -61,7 +63,10 @@
"@ContainerShowChildrenDimension": "Afficher les cotations englobante des enfants", "@ContainerShowChildrenDimension": "Afficher les cotations englobante des enfants",
"@ContainerMarkPosition": "Marquer la position pour les parents", "@ContainerMarkPosition": "Marquer la position pour les parents",
"@ContainerShowDimensionWithMarks": "Afficher les cotations avec les enfants marqués", "@ContainerShowDimensionWithMarks": "Afficher les cotations avec les enfants marqués",
"@ContainerShowMarginsDimension": "Afficher les cotations des marges",
"@ContainerStyle": "Style", "@ContainerStyle": "Style",
"@StyleStrokeColor": "Couleur du tracé",
"@StyleStrokeDashArray": "Tableau de traits",
"@StyleStroke": "Tracé", "@StyleStroke": "Tracé",
"@StyleStrokeOpacity": "Opacité du tracé", "@StyleStrokeOpacity": "Opacité du tracé",
"@StyleStrokeWidth": "Epaisseur du tracé", "@StyleStrokeWidth": "Epaisseur du tracé",

View file

@ -21,10 +21,11 @@
.right-sidebar { .right-sidebar {
@apply fixed shadow-lg z-20 @apply fixed shadow-lg z-20
w-[calc(100%_-_4rem)] md:w-64
h-1/2 md:h-full bottom-0 md:bottom-0 h-1/2 md:h-full bottom-0 md:bottom-0
} }
.sidebar-title { .sidebar-title {
@apply p-3 md:p-5 font-bold h-12 md:h-16 @apply p-3 md:p-5 font-bold h-12 md:h-16
} }

View file

@ -1,13 +1,14 @@
import { PositionReference } from '../Enums/PositionReference'; import { PositionReference } from '../Enums/PositionReference';
import { IAvailableContainer } from '../Interfaces/IAvailableContainer'; import { type IAvailableContainer } from '../Interfaces/IAvailableContainer';
import { IAvailableSymbol } from '../Interfaces/IAvailableSymbol'; import { type IAvailableSymbol } from '../Interfaces/IAvailableSymbol';
import { IConfiguration } from '../Interfaces/IConfiguration'; import { type IConfiguration } from '../Interfaces/IConfiguration';
import { ContainerModel, IContainerModel } from '../Interfaces/IContainerModel'; import { ContainerModel, type IContainerModel } from '../Interfaces/IContainerModel';
import { IContainerProperties } from '../Interfaces/IContainerProperties'; import { type IContainerProperties } from '../Interfaces/IContainerProperties';
import { IEditorState } from '../Interfaces/IEditorState'; import { type IEditorState } from '../Interfaces/IEditorState';
import { ISymbolModel } from '../Interfaces/ISymbolModel'; import { type ISymbolModel } from '../Interfaces/ISymbolModel';
import { Orientation } from '../Enums/Orientation'; import { Orientation } from '../Enums/Orientation';
import { AppState } from '../Enums/AppState'; import { AppState } from '../Enums/AppState';
import { type IDimensionOptions } from '../Interfaces/IDimensionOptions';
/// EDITOR DEFAULTS /// /// EDITOR DEFAULTS ///
@ -65,7 +66,6 @@ export const SHOW_SELF_DIMENSIONS = true;
export const SHOW_SELF_MARGINS_DIMENSIONS = true; export const SHOW_SELF_MARGINS_DIMENSIONS = true;
export const SHOW_CHILDREN_DIMENSIONS = true; export const SHOW_CHILDREN_DIMENSIONS = true;
export const SHOW_BORROWER_DIMENSIONS = true; export const SHOW_BORROWER_DIMENSIONS = true;
export const SHOW_DIMENSIONS_PER_DEPTH = false;
export const DIMENSION_MARGIN = 50; export const DIMENSION_MARGIN = 50;
export const SYMBOL_MARGIN = 25; export const SYMBOL_MARGIN = 25;
export const NOTCHES_LENGTH = 10; export const NOTCHES_LENGTH = 10;
@ -187,6 +187,12 @@ const DEFAULT_CONTAINER_STYLE = {
strokeWidth: 2 strokeWidth: 2
}; };
export const DEFAULT_DIMENSION_OPTION: IDimensionOptions = {
positions: [],
color: '#000000',
width: 2
};
/** /**
* Default Main container properties * Default Main container properties
*/ */
@ -211,23 +217,11 @@ export const DEFAULT_MAINCONTAINER_PROPS: IContainerProperties = {
positionReference: PositionReference.TopLeft, positionReference: PositionReference.TopLeft,
hideChildrenInTreeview: false, hideChildrenInTreeview: false,
dimensionOptions: { dimensionOptions: {
childrenDimensions: { childrenDimensions: clone(DEFAULT_DIMENSION_OPTION),
color: '#000000', selfDimensions: clone(DEFAULT_DIMENSION_OPTION),
positions: [] selfMarginsDimensions: clone(DEFAULT_DIMENSION_OPTION),
},
selfDimensions: {
color: '#000000',
positions: []
},
selfMarginsDimensions: {
color: '#000000',
positions: []
},
markPosition: [], markPosition: [],
dimensionWithMarks: { dimensionWithMarks: clone(DEFAULT_DIMENSION_OPTION)
color: '#000000',
positions: []
}
}, },
warning: '', warning: '',
style: DEFAULT_CONTAINER_STYLE style: DEFAULT_CONTAINER_STYLE
@ -276,20 +270,20 @@ export function GetDefaultContainerProps(type: string,
hideChildrenInTreeview: containerConfig.HideChildrenInTreeview ?? false, hideChildrenInTreeview: containerConfig.HideChildrenInTreeview ?? false,
dimensionOptions: { dimensionOptions: {
childrenDimensions: { childrenDimensions: {
color: containerConfig.DimensionOptions?.childrenDimensions.color ?? '#000000', ...containerConfig.DimensionOptions?.selfDimensions,
positions: containerConfig.DimensionOptions?.childrenDimensions.positions ?? [] positions: containerConfig.DimensionOptions?.childrenDimensions.positions ?? []
}, },
selfDimensions: { selfDimensions: {
color: containerConfig.DimensionOptions?.selfDimensions.color ?? '#000000', ...containerConfig.DimensionOptions?.selfDimensions,
positions: containerConfig.DimensionOptions?.selfDimensions.positions ?? [] positions: containerConfig.DimensionOptions?.selfDimensions.positions ?? []
}, },
selfMarginsDimensions: { selfMarginsDimensions: {
color: containerConfig.DimensionOptions?.selfMarginsDimensions.color ?? '#000000', ...containerConfig.DimensionOptions?.selfMarginsDimensions,
positions: containerConfig.DimensionOptions?.selfMarginsDimensions.positions ?? [] positions: containerConfig.DimensionOptions?.selfMarginsDimensions.positions ?? []
}, },
markPosition: containerConfig.DimensionOptions?.markPosition ?? [], markPosition: containerConfig.DimensionOptions?.markPosition ?? [],
dimensionWithMarks: { dimensionWithMarks: {
color: containerConfig.DimensionOptions?.dimensionWithMarks.color ?? '#000000', ...containerConfig.DimensionOptions?.dimensionWithMarks,
positions: containerConfig.DimensionOptions?.dimensionWithMarks.positions ?? [] positions: containerConfig.DimensionOptions?.dimensionWithMarks.positions ?? []
} }
}, },
@ -313,6 +307,14 @@ export function GetDefaultSymbolModel(name: string,
x: 0, x: 0,
width: symbolConfig.Width ?? DEFAULT_SYMBOL_WIDTH, width: symbolConfig.Width ?? DEFAULT_SYMBOL_WIDTH,
height: symbolConfig.Height ?? DEFAULT_SYMBOL_HEIGHT, height: symbolConfig.Height ?? DEFAULT_SYMBOL_HEIGHT,
linkedContainers: new Set() linkedContainers: new Set(),
showDimension: false
}; };
} }
/**
* Macro function for JSON.parse(JSON.stringify(obj))
*/
function clone<T>(object: T): T {
return JSON.parse(JSON.stringify(object));
}

View file

@ -1,5 +1,5 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react-swc';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({