diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 807e39a..108a875 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -19,7 +19,8 @@ module.exports = { }, ecmaVersion: 'latest', sourceType: 'module', - project: './tsconfig.json' + project: './tsconfig.json', + tsconfigRootDir: __dirname }, plugins: [ 'only-warn', diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 446b61f..0ee06e3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,20 @@ You will be able to navigate through this document with the table of contents. - [Table of contents](#table-of-contents) - [I want to contribute](#i-want-to-contribute) - [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) + - [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) - [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) @@ -102,6 +115,14 @@ Then run the following command to run the projet in a dev environment: 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 Use the Node.js server in `/test-server` to simulate the api. diff --git a/csharp/SVGLDLibs/SVGLDLibs/Models/DimensionOptions.cs b/csharp/SVGLDLibs/SVGLDLibs/Models/DimensionOptions.cs index a98e500..10c6542 100644 --- a/csharp/SVGLDLibs/SVGLDLibs/Models/DimensionOptions.cs +++ b/csharp/SVGLDLibs/SVGLDLibs/Models/DimensionOptions.cs @@ -1,28 +1,31 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; - -namespace SVGLDLibs.Models -{ - - [DataContract] - public class DimensionOptions - { - - /** positions of the dimension */ - [DataMember(EmitDefaultValue = false)] - public Position[] positions; - - /** color */ - [DataMember(EmitDefaultValue = false)] - public string color; - - - - - } - -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace SVGLDLibs.Models +{ + + [DataContract] + public class DimensionOptions + { + + /** positions of the dimension */ + [DataMember(EmitDefaultValue = false)] + public Position[] positions; + + /** color */ + [DataMember(EmitDefaultValue = false)] + public string color; + + /** width */ + [DataMember(EmitDefaultValue = false)] + public double width; + + /** color */ + [DataMember(EmitDefaultValue = false)] + public string dashArray; + } +} diff --git a/mprocs.yaml b/mprocs.yaml new file mode 100644 index 0000000..fa0511f --- /dev/null +++ b/mprocs.yaml @@ -0,0 +1,7 @@ +procs: + nvim: + shell: "nvim ." + vite: + shell: "npx vite" + test-server: + shell: "npx nodemon ./test-server/http.js" diff --git a/package.json b/package.json index 8fe114a..ff96249 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,10 @@ "version": "v1.0.0", "type": "module", "scripts": { + "d": "mprocs", "dev": "vite", "build": "tsc && vite build", "build:dotnet": "dotnet build ./csharp/SVGLDLibs/SVGLDLibs/SVGLDLibs.csproj", - "preview": "vite preview", "linter": "eslint src", "test": "vitest", "test:ui": "vitest --ui", @@ -52,6 +52,8 @@ "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "jsdom": "^21.1.0", + "mprocs": "^0.6.4", + "nodemon": "^2.0.20", "postcss": "^8.4.21", "sass": "^1.58.0", "tailwindcss": "^3.2.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc7fbe9..212f797 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,6 +28,8 @@ specifiers: eslint-plugin-react-hooks: ^4.6.0 interweave: ^13.0.0 jsdom: ^21.1.0 + mprocs: ^0.6.4 + nodemon: ^2.0.20 postcss: ^8.4.21 react: ^18.2.0 react-dom: ^18.2.0 @@ -77,6 +79,8 @@ devDependencies: eslint-plugin-react: 7.32.2_eslint@8.33.0 eslint-plugin-react-hooks: 4.6.0_eslint@8.33.0 jsdom: 21.1.0 + mprocs: 0.6.4 + nodemon: 2.0.20 postcss: 8.4.21 sass: 1.58.0 tailwindcss: 3.2.4_postcss@8.4.21 @@ -1172,6 +1176,10 @@ packages: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: true + /abbrev/1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: true + /acorn-globals/7.0.1: resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} dependencies: @@ -1600,6 +1608,18 @@ packages: ms: 2.1.3 dev: true + /debug/3.2.7_supports-color@5.5.0: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + supports-color: 5.5.0 + dev: true + /debug/4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -2507,6 +2527,10 @@ packages: safer-buffer: 2.1.2 dev: true + /ignore-by-default/1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + dev: true + /ignore/5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} @@ -3010,6 +3034,12 @@ packages: ufo: 1.0.1 dev: true + /mprocs/0.6.4: + resolution: {integrity: sha512-Y4eqnAjp3mjy0eT+zPoMQ+P/ISOzjgRG/4kh4I5cRA4Tv0rPxTCBRadn3+j+boMF5id7IoLhrVq9NFWFPuzD9A==} + engines: {node: '>=0.10.0'} + hasBin: true + dev: true + /mrmime/1.0.1: resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} engines: {node: '>=10'} @@ -3041,6 +3071,30 @@ packages: resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} dev: true + /nodemon/2.0.20: + resolution: {integrity: sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==} + engines: {node: '>=8.10.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + debug: 3.2.7_supports-color@5.5.0 + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 5.7.1 + simple-update-notifier: 1.1.0 + supports-color: 5.5.0 + touch: 3.1.0 + undefsafe: 2.0.5 + dev: true + + /nopt/1.0.10: + resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: true + /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -3352,6 +3406,10 @@ packages: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} dev: true + /pstree.remy/1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + dev: true + /punycode/2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} @@ -3549,11 +3607,21 @@ packages: dependencies: loose-envify: 1.4.0 + /semver/5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: true + /semver/6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true dev: true + /semver/7.0.0: + resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==} + hasBin: true + dev: true + /semver/7.3.8: resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} engines: {node: '>=10'} @@ -3586,6 +3654,13 @@ packages: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} dev: true + /simple-update-notifier/1.1.0: + resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==} + engines: {node: '>=8.10.0'} + dependencies: + semver: 7.0.0 + dev: true + /sirv/2.0.2: resolution: {integrity: sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==} engines: {node: '>= 10'} @@ -3831,6 +3906,13 @@ packages: engines: {node: '>=6'} dev: true + /touch/3.1.0: + resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==} + hasBin: true + dependencies: + nopt: 1.0.10 + dev: true + /tough-cookie/4.1.2: resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} engines: {node: '>=6'} @@ -3926,6 +4008,10 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /undefsafe/2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + dev: true + /universalify/0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} diff --git a/src/Components/API/api.test.tsx b/src/Components/API/api.test.tsx index 53f3ae0..08fcbad 100644 --- a/src/Components/API/api.test.tsx +++ b/src/Components/API/api.test.tsx @@ -10,7 +10,7 @@ import { IConfiguration } from '../../Interfaces/IConfiguration'; import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel'; import { IHistoryState } from '../../Interfaces/IHistoryState'; 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'; const CSHARP_WEB_API_BASE_URL = 'http://localhost:5209/'; @@ -144,23 +144,11 @@ describe.concurrent('Models test suite', () => { PositionReference: 0, HideChildrenInTreeview: true, DimensionOptions: { - childrenDimensions: { - color: '#000000', - positions: [] - }, - selfDimensions: { - color: '#000000', - positions: [] - }, - selfMarginsDimensions: { - color: '#000000', - positions: [] - }, + childrenDimensions: DEFAULT_DIMENSION_OPTION, + selfDimensions: DEFAULT_DIMENSION_OPTION, + selfMarginsDimensions: DEFAULT_DIMENSION_OPTION, markPosition: [], - dimensionWithMarks: { - color: '#000000', - positions: [] - } + dimensionWithMarks: DEFAULT_DIMENSION_OPTION }, IsHidden: true, Blacklist: [ diff --git a/src/Components/Bar/Bar.tsx b/src/Components/Bar/Bar.tsx index 8f28635..a693fc2 100644 --- a/src/Components/Bar/Bar.tsx +++ b/src/Components/Bar/Bar.tsx @@ -17,6 +17,7 @@ import { BarIcon } from './BarIcon'; import { Text } from '../Text/Text'; interface IBarProps { + className: string isComponentsOpen: boolean isSymbolsOpen: boolean isHistoryOpen: boolean @@ -33,7 +34,7 @@ export const BAR_WIDTH = 64; // 4rem export function Bar(props: IBarProps): JSX.Element { return ( -
+
> buttonOnClick: (type: string) => void } @@ -62,6 +66,10 @@ export function Components(props: IComponentsProps): JSX.Element { disabled = config.Blacklist?.find(type => type === componentOption.Type) !== undefined ?? false; } + if (props.replaceContainer.isReplacing && componentOption.Category !== props.replaceContainer.category) { + disabled = true; + } + if (disabled && hideDisabled) { return; } @@ -96,6 +104,15 @@ export function Components(props: IComponentsProps): JSX.Element { return (
+ {props.replaceContainer.isReplacing && + }
); -}; +} diff --git a/src/Components/ContainerProperties/ContainerForm.tsx b/src/Components/ContainerProperties/ContainerForm.tsx index 4bdc262..8b7a505 100644 --- a/src/Components/ContainerProperties/ContainerForm.tsx +++ b/src/Components/ContainerProperties/ContainerForm.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { PropertyType } from '../../Enums/PropertyType'; -import { IContainerProperties } from '../../Interfaces/IContainerProperties'; -import { ISymbolModel } from '../../Interfaces/ISymbolModel'; +import { type IContainerProperties } from '../../Interfaces/IContainerProperties'; +import { type ISymbolModel } from '../../Interfaces/ISymbolModel'; import { SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, @@ -46,7 +46,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element { inputClassName='' type='string' value={props.properties.displayedText?.toString()} - onChange={(value) => props.onChange('displayedText', value)}/> + onChange={(value) => { props.onChange('displayedText', value); }}/> props.onChange( - 'x', - ApplyXMargin( - RestoreX( - Number(value), - props.properties.width, - props.properties.positionReference - ), - props.properties.margin.left - ) - )}/> + onChange={(value) => { + props.onChange( + 'x', + ApplyXMargin( + RestoreX( + Number(value), + props.properties.width, + props.properties.positionReference + ), + props.properties.margin.left + ) + ); + }}/> props.onChange( - 'y', - ApplyXMargin( - RestoreY( - Number(value), - props.properties.height, - props.properties.positionReference - ), - props.properties.margin.top - ) - )}/> + onChange={(value) => { + props.onChange( + 'y', + ApplyXMargin( + RestoreY( + Number(value), + props.properties.height, + props.properties.positionReference + ), + props.properties.margin.top + ) + ); + }}/>
@@ -171,7 +175,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element { type='number' min={1} value={props.properties.minWidth.toString()} - onChange={(value) => props.onChange('minWidth', Number(value))}/> + onChange={(value) => { props.onChange('minWidth', Number(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}/> props.onChange('maxWidth', Number(value))}/> + onChange={(value) => { props.onChange('maxWidth', Number(value)); }}/>
props.onChange('minHeight', Number(value))}/> + onChange={(value) => { props.onChange('minHeight', Number(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} /> props.onChange('maxHeight', Number(value))}/> + onChange={(value) => { props.onChange('maxHeight', Number(value)); }}/>
@@ -247,7 +251,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element { type='number' min={0} 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); }}/> props.onChange('bottom', Number(value), PropertyType.Margin)}/> + onChange={(value) => { props.onChange('bottom', Number(value), PropertyType.Margin); }}/> props.onChange('top', Number(value), PropertyType.Margin)}/> + onChange={(value) => { props.onChange('top', Number(value), PropertyType.Margin); }}/> props.onChange('right', Number(value), PropertyType.Margin)}/> + onChange={(value) => { props.onChange('right', Number(value), PropertyType.Margin); }}/> @@ -295,7 +299,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element { inputClassName='ml-auto mr-auto block' type={ToggleType.Full} checked={props.properties.isFlex} - onChange={(event) => props.onChange('isFlex', event.target.checked)} + onChange={(event) => { props.onChange('isFlex', event.target.checked); }} /> props.onChange('isAnchor', event.target.checked)}/> + onChange={(event) => { props.onChange('isAnchor', event.target.checked); }}/> @@ -334,7 +338,7 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element { value: symbol.id }))} value={props.properties.linkedSymbolId ?? ''} - onChange={(event) => props.onChange('linkedSymbolId', event.target.value)}/> + onChange={(event) => { props.onChange('linkedSymbolId', event.target.value); }}/> @@ -347,168 +351,244 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
{ SHOW_SELF_DIMENSIONS && -
- props.onChange(key, value, PropertyType.SelfDimension)} - /> - props.onChange('color', e.target.value, PropertyType.SelfDimension)}/> -
+
+ { props.onChange(key, value, PropertyType.SelfDimension); }} + /> + { props.onChange('color', e.target.value, PropertyType.SelfDimension); }}/> + { props.onChange('width', Number(value), PropertyType.SelfDimension); }}/> + { props.onChange('dashArray', value, PropertyType.SelfDimension); }}/> +
} { SHOW_SELF_MARGINS_DIMENSIONS && -
- props.onChange(key, value, PropertyType.SelfMarginDimension)} - /> - props.onChange('color', e.target.value, PropertyType.SelfMarginDimension)}/> -
+
+ { props.onChange(key, value, PropertyType.SelfMarginDimension); }} + /> + { props.onChange('color', e.target.value, PropertyType.SelfMarginDimension); }}/> + { props.onChange('width', Number(value), PropertyType.SelfMarginDimension); }}/> + { props.onChange('dashArray', value, PropertyType.SelfMarginDimension); }}/> +
} { SHOW_CHILDREN_DIMENSIONS && -
- props.onChange(key, value, PropertyType.ChildrenDimensions)} - /> - props.onChange('color', e.target.value, PropertyType.ChildrenDimensions)}/> -
+
+ { props.onChange(key, value, PropertyType.ChildrenDimensions); }} + /> + { props.onChange('color', e.target.value, PropertyType.ChildrenDimensions); }}/> + { props.onChange('width', Number(value), PropertyType.ChildrenDimensions); }}/> + { props.onChange('dashArray', value, PropertyType.ChildrenDimensions); }}/> +
} { SHOW_BORROWER_DIMENSIONS && - <> -
- props.onChange(key, value, PropertyType.DimensionOptions)} - /> -
-
- props.onChange(key, value, PropertyType.DimensionWithMarks)} - /> - props.onChange('color', e.target.value, PropertyType.DimensionWithMarks)}/> -
- + <> +
+ { props.onChange(key, value, PropertyType.DimensionOptions); }} + /> +
+
+ { props.onChange(key, value, PropertyType.DimensionWithMarks); }} + /> + { props.onChange('color', e.target.value, PropertyType.DimensionWithMarks); }}/> + { props.onChange('width', Number(value), PropertyType.DimensionWithMarks); }}/> + { props.onChange('dashArray', value, PropertyType.DimensionWithMarks); }}/> +
+ }
{props.properties.style !== undefined && - -
- props.onChange('stroke', value, PropertyType.Style)} - /> - props.onChange('strokeOpacity', Number(event.target.value), PropertyType.Style)} - /> - props.onChange('strokeWidth', Number(value), PropertyType.Style)} - /> - props.onChange('fill', value, PropertyType.Style)} - /> - props.onChange('fillOpacity', Number(event.target.value), PropertyType.Style)} - /> -
-
+ +
+ { props.onChange('stroke', value, PropertyType.Style); }} + /> + { props.onChange('strokeOpacity', Number(event.target.value), PropertyType.Style); }} + /> + { props.onChange('strokeWidth', Number(value), PropertyType.Style); }} + /> + { props.onChange('fill', value, PropertyType.Style); }} + /> + { props.onChange('fillOpacity', Number(event.target.value), PropertyType.Style); }} + /> +
+
} ); diff --git a/src/Components/ContainerProperties/ContainerProperties.test.tsx b/src/Components/ContainerProperties/ContainerProperties.test.tsx index cd2da15..01c36bd 100644 --- a/src/Components/ContainerProperties/ContainerProperties.test.tsx +++ b/src/Components/ContainerProperties/ContainerProperties.test.tsx @@ -5,6 +5,7 @@ import { PositionReference } from '../../Enums/PositionReference'; import { IContainerProperties } from '../../Interfaces/IContainerProperties'; import { Orientation } from '../../Enums/Orientation'; import { ContainerProperties } from './ContainerProperties'; +import { DEFAULT_DIMENSION_OPTION } from '../../utils/default'; describe.concurrent('Properties', () => { it('No properties', () => { @@ -43,23 +44,11 @@ describe.concurrent('Properties', () => { warning: '', hideChildrenInTreeview: false, dimensionOptions: { - childrenDimensions: { - color: '#000000', - positions: [] - }, - selfDimensions: { - color: '#000000', - positions: [] - }, - selfMarginsDimensions: { - color: '#000000', - positions: [] - }, + childrenDimensions: DEFAULT_DIMENSION_OPTION, + selfDimensions: DEFAULT_DIMENSION_OPTION, + selfMarginsDimensions: DEFAULT_DIMENSION_OPTION, markPosition: [], - dimensionWithMarks: { - color: '#000000', - positions: [] - } + dimensionWithMarks: DEFAULT_DIMENSION_OPTION } }; diff --git a/src/Components/Editor/Actions/ContainerOperations.ts b/src/Components/Editor/Actions/ContainerOperations.ts index 82162e3..684b443 100644 --- a/src/Components/Editor/Actions/ContainerOperations.ts +++ b/src/Components/Editor/Actions/ContainerOperations.ts @@ -8,6 +8,8 @@ import Swal from 'sweetalert2'; import { PropertyType } from '../../../Enums/PropertyType'; import { TransformX, TransformY } from '../../../utils/svg'; import { Orientation } from '../../../Enums/Orientation'; +import { AddContainers } from './AddContainer'; +import { IConfiguration } from '../../../Interfaces/IConfiguration'; /** * Select a container @@ -133,6 +135,58 @@ export function DeleteContainer( return history; } +/** + * Replace a container + * @param containerId containerId of the container to delete + * @param newContainerId + * @param configuration + * @param fullHistory History of the editor + * @param historyCurrentStep Current step + * @returns New history + */ +export function ReplaceByContainer( + containerId: string, + newContainerId: string, + configuration: IConfiguration, + fullHistory: IHistoryState[], + historyCurrentStep: number +): IHistoryState[] { + const history = GetCurrentHistory(fullHistory, historyCurrentStep); + const current = history[history.length - 1]; + + const containerToReplace = FindContainerById(current.containers, containerId); + if (containerToReplace === undefined) { + return history; + } + + const containerParent = FindContainerById(current.containers, containerToReplace.properties.parentId); + if (containerParent === undefined) { + return history; + } + + const historyAdd = AddContainers( + containerParent.children.indexOf(containerId), + [{ Type: newContainerId }], + containerParent.properties.id, + configuration, fullHistory, historyCurrentStep + ); + + const historyDelete = DeleteContainer(containerId, historyAdd.history, historyCurrentStep + 1); + const currentDelete = historyDelete[historyDelete.length - 1]; + + fullHistory.push({ + lastAction: `Replace ${containerId} by ${newContainerId}`, + mainContainer: currentDelete.mainContainer, + containers: currentDelete.containers, + selectedContainerId: currentDelete.selectedContainerId, + typeCounters: Object.assign({}, currentDelete.typeCounters), + symbols: current.symbols, + selectedSymbolId: current.selectedSymbolId + }); + + return fullHistory; +} + /** * Returns the next container that will be selected * after the selectedContainer is removed. diff --git a/src/Components/Editor/Actions/ContextMenuActions.ts b/src/Components/Editor/Actions/ContextMenuActions.ts index 69a3121..9fb86f4 100644 --- a/src/Components/Editor/Actions/ContextMenuActions.ts +++ b/src/Components/Editor/Actions/ContextMenuActions.ts @@ -16,6 +16,7 @@ import { AddContainers } from './AddContainer'; import { DeleteContainer } from './ContainerOperations'; import { DeleteSymbol } from './SymbolOperations'; import { Text } from '../../Text/Text'; +import { IReplaceContainer } from '../../../Interfaces/IReplaceContainer'; export function InitActions( menuActions: Map, @@ -23,7 +24,8 @@ export function InitActions( history: IHistoryState[], historyCurrentStep: number, setNewHistory: (newHistory: IHistoryState[]) => void, - setHistoryCurrentStep: Dispatch> + setHistoryCurrentStep: Dispatch>, + setIsReplacingContainer: Dispatch> ): void { menuActions.set( '', @@ -56,9 +58,24 @@ export function InitActions( menuActions.set( 'elements-sidebar-row', [{ + text: Text({ textId: '@ReplaceByContainer' }), + title: Text({ textId: '@ReplaceByContainerTitle' }), + shortcut: 'R', + action: (target: HTMLElement) => { + const targetContainer = FindContainerById(history[historyCurrentStep].containers, target.id); + const targetAvailableContainer = configuration.AvailableContainers.find((availableContainer) => availableContainer.Type === targetContainer?.properties.type); + + if (targetAvailableContainer === undefined) { + return; + } + + setIsReplacingContainer({ isReplacing: true, id: target.id, category: targetAvailableContainer.Category }); + } + }, { text: Text({ textId: '@DeleteContainer' }), title: Text({ textId: '@DeleteContainerTitle' }), shortcut: 'Suppr', + action: (target: HTMLElement) => { const id = target.id; const newHistory = DeleteContainer( diff --git a/src/Components/Editor/Actions/Save.ts b/src/Components/Editor/Actions/Save.ts index ae7b4e9..2dc2f95 100644 --- a/src/Components/Editor/Actions/Save.ts +++ b/src/Components/Editor/Actions/Save.ts @@ -53,6 +53,7 @@ export function SaveEditorAsSVG(): void { svg.replaceChildren(...mainSvg); // remove the selector + // TODO: Fix this with SelectorMode != Nothing or with some html magic const group = svg.children[svg.children.length - 1]; group.removeChild(group.children[group.children.length - 1]); if (SHOW_SELECTOR_TEXT) { diff --git a/src/Components/Editor/Actions/Shortcuts.ts b/src/Components/Editor/Actions/Shortcuts.ts index a81660c..082770e 100644 --- a/src/Components/Editor/Actions/Shortcuts.ts +++ b/src/Components/Editor/Actions/Shortcuts.ts @@ -7,7 +7,8 @@ export function OnKey( history: IHistoryState[], historyCurrentStep: number, setHistoryCurrentStep: Dispatch>, - deleteAction: () => void + deleteAction: () => void, + resetState: () => void ): void { if (!ENABLE_SHORTCUTS) { return; @@ -27,5 +28,7 @@ export function OnKey( setHistoryCurrentStep(historyCurrentStep + 1); } else if (event.key === 'Delete') { deleteAction(); + } else if (event.key === 'Escape') { + resetState(); } } diff --git a/src/Components/Editor/Editor.tsx b/src/Components/Editor/Editor.tsx index 372ab06..095f644 100644 --- a/src/Components/Editor/Editor.tsx +++ b/src/Components/Editor/Editor.tsx @@ -1,9 +1,9 @@ -import React, { Dispatch, SetStateAction, useEffect, useRef } from 'react'; +import React, { type Dispatch, type SetStateAction, useEffect, useRef } from 'react'; import './Editor.scss'; -import { IConfiguration } from '../../Interfaces/IConfiguration'; -import { IHistoryState } from '../../Interfaces/IHistoryState'; +import { type IConfiguration } from '../../Interfaces/IConfiguration'; +import { type IHistoryState } from '../../Interfaces/IHistoryState'; import { UI } from '../UI/UI'; -import { SelectContainer, DeleteContainer, OnPropertyChange } from './Actions/ContainerOperations'; +import { SelectContainer, DeleteContainer, OnPropertyChange, ReplaceByContainer } from './Actions/ContainerOperations'; import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save'; import { OnKey } from './Actions/Shortcuts'; import { UseCustomEvents, UseEditorListener } from '../../Events/EditorEvents'; @@ -13,6 +13,7 @@ import { FindContainerById } from '../../utils/itertools'; import { Menu } from '../Menu/Menu'; import { InitActions } from './Actions/ContextMenuActions'; import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer'; +import { type IReplaceContainer } from '../../Interfaces/IReplaceContainer'; interface IEditorProps { root: Element | Document @@ -25,16 +26,18 @@ function UseShortcuts( history: IHistoryState[], historyCurrentStep: number, setHistoryCurrentStep: Dispatch>, - deleteAction: () => void + deleteAction: () => void, + resetState: () => void ): void { useEffect(() => { function OnKeyUp(event: KeyboardEvent): void { - return OnKey( + OnKey( event, history, historyCurrentStep, setHistoryCurrentStep, - deleteAction + deleteAction, + resetState ); } @@ -62,13 +65,20 @@ function UseNewHistoryState( }; } + export function Editor(props: IEditorProps): JSX.Element { // States const [history, setHistory] = React.useState(structuredClone(props.history)); const [historyCurrentStep, setHistoryCurrentStep] = React.useState(props.historyCurrentStep); + const [replaceContainer, setReplaceContainer] = React.useState({ isReplacing: false, id: undefined, category: undefined }); + const editorRef = useRef(null); const setNewHistory = UseNewHistoryState(setHistory, setHistoryCurrentStep); + function ResetState(): void { + setReplaceContainer({ isReplacing: false, id: undefined, category: undefined }); + } + // Events UseShortcuts( history, @@ -79,7 +89,8 @@ export function Editor(props: IEditorProps): JSX.Element { setNewHistory( DeleteContainer(current.selectedContainerId, history, historyCurrentStep) ); - } + }, + ResetState ); UseCustomEvents( props.root, @@ -104,7 +115,8 @@ export function Editor(props: IEditorProps): JSX.Element { history, historyCurrentStep, setNewHistory, - setHistoryCurrentStep + setHistoryCurrentStep, + setReplaceContainer ); // Render @@ -113,87 +125,118 @@ export function Editor(props: IEditorProps): JSX.Element { const selected = FindContainerById(current.containers, current.selectedContainerId); return ( -
+
setNewHistory( - SelectContainer( - container, - history, - historyCurrentStep - ))} - deleteContainer={(containerId: string) => setNewHistory( - DeleteContainer( - containerId, - history, - historyCurrentStep - ))} - onPropertyChange={(key, value, type) => setNewHistory( - OnPropertyChange( - key, value, type, - selected, - history, - historyCurrentStep - ))} - addContainer={(type) => { + replaceContainer={replaceContainer} + selectContainer={(container) => { + setNewHistory( + SelectContainer( + container, + history, + historyCurrentStep + )); + }} + deleteContainer={(containerId: string) => { + setNewHistory( + DeleteContainer( + containerId, + history, + historyCurrentStep + )); + }} + onPropertyChange={(key, value, type) => { + setNewHistory( + OnPropertyChange( + key, value, type, + selected, + history, + historyCurrentStep + )); + }} + addOrReplaceContainer={(type) => { if (selected === null || selected === undefined) { return; } - - setNewHistory(AddContainerToSelectedContainer( - type, - selected, - configuration, - history, - historyCurrentStep - )); + if (replaceContainer.isReplacing && replaceContainer.id !== undefined) { + const newHistory = ReplaceByContainer( + replaceContainer.id, + type, + configuration, + history, + historyCurrentStep + ); + setReplaceContainer({ isReplacing: false, id: undefined, category: undefined }); + setNewHistory(newHistory); + } else { + setNewHistory(AddContainerToSelectedContainer( + type, + selected, + configuration, + history, + historyCurrentStep + )); + } }} - addContainerAt={(index, type, parent) => setNewHistory( - AddContainer( - index, - type, - parent, - configuration, + addContainerAt={(index, type, parent) => { + setNewHistory( + AddContainer( + index, + type, + parent, + configuration, + history, + historyCurrentStep + ) + ); + }} + addSymbol={(type) => { + setNewHistory( + AddSymbol( + type, + configuration, + history, + historyCurrentStep + )); + }} + onSymbolPropertyChange={(key, value) => { + setNewHistory( + OnSymbolPropertyChange( + key, value, + history, + historyCurrentStep + )); + }} + selectSymbol={(symbolId) => { + setNewHistory( + SelectSymbol( + symbolId, + history, + historyCurrentStep + )); + }} + deleteSymbol={(symbolId) => { + setNewHistory( + DeleteSymbol( + symbolId, + history, + historyCurrentStep + )); + }} + saveEditorAsJSON={() => { + SaveEditorAsJSON( history, - historyCurrentStep - ) - )} - addSymbol={(type) => setNewHistory( - AddSymbol( - type, - configuration, - history, - historyCurrentStep - ))} - onSymbolPropertyChange={(key, value) => setNewHistory( - OnSymbolPropertyChange( - key, value, - history, - historyCurrentStep - ))} - selectSymbol={(symbolId) => setNewHistory( - SelectSymbol( - symbolId, - history, - historyCurrentStep - ))} - deleteSymbol={(symbolId) => setNewHistory( - DeleteSymbol( - symbolId, - history, - historyCurrentStep - ))} - saveEditorAsJSON={() => SaveEditorAsJSON( - history, - historyCurrentStep, - configuration - )} - saveEditorAsSVG={() => SaveEditorAsSVG()} - loadState={(move) => setHistoryCurrentStep(move)} + historyCurrentStep, + configuration + ); + }} + saveEditorAsSVG={() => { SaveEditorAsSVG(); }} + loadState={(move) => { setHistoryCurrentStep(move); }} + setReplaceContainer={setReplaceContainer} /> editorRef.current} diff --git a/src/Components/Menu/Menu.tsx b/src/Components/Menu/Menu.tsx index a0fbc03..57c3445 100644 --- a/src/Components/Menu/Menu.tsx +++ b/src/Components/Menu/Menu.tsx @@ -21,6 +21,7 @@ export interface IMenuAction { /** function to be called on button click */ action: (target: HTMLElement) => void + } function UseMouseEvents( @@ -139,7 +140,7 @@ function AddClassSpecificActions( onClick={() => action.action(target)} />); }); children.push(
); - }; + } return count; } diff --git a/src/Components/SVG/Elements/DepthDimensionLayer.tsx b/src/Components/SVG/Elements/DepthDimensionLayer.tsx deleted file mode 100644 index f4bff37..0000000 --- a/src/Components/SVG/Elements/DepthDimensionLayer.tsx +++ /dev/null @@ -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 - roots: IContainerModel | IContainerModel[] | null - scale?: number -} - -function GetDimensionsNodes( - containers: Map, - 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 ( - - {dimensions} - - ); -} - -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( - - ); -} diff --git a/src/Components/SVG/Elements/Dimension.tsx b/src/Components/SVG/Elements/Dimension.tsx index 0848bea..30469be 100644 --- a/src/Components/SVG/Elements/Dimension.tsx +++ b/src/Components/SVG/Elements/Dimension.tsx @@ -1,6 +1,9 @@ import * as React from 'react'; +import { type IDimensionOptions } from '../../../Interfaces/IDimensionOptions'; import { NOTCHES_LENGTH } from '../../../utils/default'; +export type IDimensionStyle = Omit; + interface IDimensionProps { id: string xStart: number @@ -8,7 +11,7 @@ interface IDimensionProps { xEnd: number yEnd: number text: string - color: string + style: IDimensionStyle scale?: number } @@ -28,8 +31,9 @@ function ApplyParametric(x0: number, t: number, vx: number): number { export function Dimension(props: IDimensionProps): JSX.Element { const scale = props.scale ?? 1; const style: React.CSSProperties = { - stroke: props.color, - strokeWidth: 2 / scale + stroke: props.style.color, + strokeWidth: (props.style.width ?? 2) / scale, + strokeDasharray: props.style.dashArray }; /// We need to find the points of the notches @@ -79,9 +83,11 @@ export function Dimension(props: IDimensionProps): JSX.Element { x2={endBottomX} y2={endBottomY} style={style}/> - @@ -37,8 +37,7 @@ function ActionByPosition( horizontalAction: (dim: number, ...params: any[]) => void, verticalAction: (dim: number, isRight: boolean, ...params: any[]) => void, params: any[] -): boolean { - let incrementDepthSymbols = false; +): void { positions.forEach((position: Position) => { const dim = dimMapped[position]; switch (position) { @@ -50,12 +49,10 @@ function ActionByPosition( } case Position.Down: case Position.Up: - incrementDepthSymbols = true; horizontalAction(dim, ...params); break; } }); - return incrementDepthSymbols; } /** @@ -74,7 +71,6 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps): if (!SHOW_SELF_DIMENSIONS) { return []; } - let startDepthSymbols: number = 0; for (const { container, depth, currentTransform } of it) { const offset = (DIMENSION_MARGIN * (depth + 1)) / scale; @@ -84,7 +80,7 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps): const containerRightDim = rightDim + offset; const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim]; if (SHOW_SELF_DIMENSIONS && container.properties.dimensionOptions.selfDimensions.positions.length > 0) { - const incrementDepthSymbol = ActionByPosition( + ActionByPosition( dimMapped, container.properties.dimensionOptions.selfDimensions.positions, AddHorizontalSelfDimension, @@ -93,14 +89,13 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps): container, currentTransform, dimensions, - scale, - container.properties.dimensionOptions.selfDimensions.color] + scale + ] ); - if (incrementDepthSymbol) { startDepthSymbols++; } } if (SHOW_SELF_MARGINS_DIMENSIONS && container.properties.dimensionOptions.selfMarginsDimensions.positions.length > 0) { - const incrementDepthSymbol = ActionByPosition( + ActionByPosition( dimMapped, container.properties.dimensionOptions.selfMarginsDimensions.positions, AddHorizontalSelfMarginsDimension, @@ -109,14 +104,13 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps): container, currentTransform, dimensions, - scale, - container.properties.dimensionOptions.selfMarginsDimensions.color] + scale + ] ); - if (incrementDepthSymbol) { startDepthSymbols++; } } if (SHOW_BORROWER_DIMENSIONS && container.properties.dimensionOptions.dimensionWithMarks.positions.length > 0) { - const incrementDepthSymbol = ActionByPosition( + ActionByPosition( dimMapped, container.properties.dimensionOptions.dimensionWithMarks.positions, @@ -128,14 +122,13 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps): depth, currentTransform, dimensions, - scale, - container.properties.dimensionOptions.dimensionWithMarks.color] + scale + ] ); - if (incrementDepthSymbol) { startDepthSymbols++; } } if (SHOW_CHILDREN_DIMENSIONS && container.properties.dimensionOptions.childrenDimensions.positions.length > 0 && container.children.length >= 2) { - const incrementDepthSymbol = ActionByPosition( + ActionByPosition( dimMapped, container.properties.dimensionOptions.childrenDimensions.positions, AddHorizontalChildrenDimension, @@ -145,28 +138,32 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps): container, currentTransform, dimensions, - scale, - container.properties.dimensionOptions.childrenDimensions.color] + scale + ] ); - - if (incrementDepthSymbol) { startDepthSymbols++; } } } + let startDepthSymbols: number = 0; for (const symbol of symbols) { if (symbol[1].showDimension) { startDepthSymbols++; - AddHorizontalSymbolDimension(symbol[1], dimensions, scale, 'black', startDepthSymbols); + AddHorizontalSymbolDimension( + symbol[1], + dimensions, + scale, + startDepthSymbols + ); } } return dimensions; } -function AddHorizontalSymbolDimension(symbol: ISymbolModel, +function AddHorizontalSymbolDimension( + symbol: ISymbolModel, dimensions: React.ReactNode[], scale: number, - color: string, depth: number ): void { const width = symbol.x + (symbol.width / 2); @@ -177,6 +174,11 @@ function AddHorizontalSymbolDimension(symbol: ISymbolModel, const text = width .toFixed(0) .toString(); + + // TODO: Put this in default.ts + const defaultDimensionSymbolStyle: IDimensionStyle = { + color: 'black' + }; dimensions.push( + style={defaultDimensionSymbolStyle}/> ); } } @@ -213,10 +215,10 @@ function AddHorizontalChildrenDimension( container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], - scale: number, - color: string + scale: number ): void { 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 lastChild = FindContainerById(containers, lastChildId); @@ -266,7 +268,7 @@ function AddHorizontalChildrenDimension( yEnd={yDim} text={textChildren} scale={scale} - color={color}/>); + style={style}/>); } function AddVerticalChildrenDimension( @@ -276,10 +278,10 @@ function AddVerticalChildrenDimension( container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], - scale: number, - color: string + scale: number ): void { 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 lastChild = FindContainerById(containers, lastChildId); @@ -334,7 +336,7 @@ function AddVerticalChildrenDimension( yEnd={yChildrenEnd + offset} text={textChildren} scale={scale} - color={color} + style={style} />); } @@ -345,9 +347,9 @@ function AddHorizontalBorrowerDimension( depth: number, currentTransform: [number, number], dimensions: React.ReactNode[], - scale: number, - color: string + scale: number ): void { + const style = container.properties.dimensionOptions.dimensionWithMarks; const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform); const marks = []; // list of vertical lines for the dimension for (const { @@ -392,7 +394,7 @@ function AddHorizontalBorrowerDimension( yEnd={yDim} text={value.toFixed(0)} scale={scale} - color={color}/>); + style={style}/>); count++; } } @@ -405,9 +407,9 @@ function AddVerticalBorrowerDimension( depth: number, currentTransform: [number, number], dimensions: React.ReactNode[], - scale: number, - color: string + scale: number ): void { + const style = container.properties.dimensionOptions.dimensionWithMarks; const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform); const marks = []; // list of vertical lines for the dimension for (const { @@ -457,7 +459,7 @@ function AddVerticalBorrowerDimension( yEnd={next} text={value.toFixed(0)} scale={scale} - color={color}/>); + style={style}/>); count++; } } @@ -468,9 +470,9 @@ function AddVerticalSelfDimension( container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], - scale: number, - color: string + scale: number ): void { + const style = container.properties.dimensionOptions.selfDimensions; const height = container.properties.height; const idVert = `dim-x${xDim.toFixed(0)}-${container.properties.id}`; let yStart = container.properties.y + currentTransform[1] + height; @@ -493,7 +495,7 @@ function AddVerticalSelfDimension( yEnd={yEnd} text={textVert} scale={scale} - color={color}/> + style={style}/> ); } @@ -502,9 +504,9 @@ function AddHorizontalSelfDimension( container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], - scale: number, - color: string + scale: number ): void { + const style = container.properties.dimensionOptions.selfDimensions; const width = container.properties.width; const id = `dim-y${yDim.toFixed(0)}-${container.properties.id}`; const xStart = container.properties.x + currentTransform[0]; @@ -522,7 +524,7 @@ function AddHorizontalSelfDimension( yEnd={yDim} text={text} scale={scale} - color={color}/> + style={style}/> ); } @@ -531,9 +533,9 @@ function AddHorizontalSelfMarginsDimension( container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], - scale: number, - color: string + scale: number ): void { + const style = container.properties.dimensionOptions.selfMarginsDimensions; const left = container.properties.margin.left; if (left != null) { const id = `dim-y-margin-left${yDim.toFixed(0)}-${container.properties.id}`; @@ -552,7 +554,7 @@ function AddHorizontalSelfMarginsDimension( yEnd={yDim} text={text} scale={scale} - color={color}/> + style={style}/> ); } @@ -574,7 +576,7 @@ function AddHorizontalSelfMarginsDimension( yEnd={yDim} text={text} scale={scale} - color={color}/> + style={style}/> ); } } @@ -585,9 +587,9 @@ function AddVerticalSelfMarginDimension( container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], - scale: number, - color: string + scale: number ): void { + const style = container.properties.dimensionOptions.selfMarginsDimensions; const top = container.properties.margin.top; if (top != null) { const idVert = `dim-x-margin-top${xDim.toFixed(0)}-${container.properties.id}`; @@ -611,7 +613,7 @@ function AddVerticalSelfMarginDimension( yEnd={yEnd} text={textVert} scale={scale} - color={color}/> + style={style}/> ); } const bottom = container.properties.margin.bottom; @@ -637,7 +639,7 @@ function AddVerticalSelfMarginDimension( yEnd={yEnd} text={textVert} scale={scale} - color={color}/> + style={style}/> ); } } diff --git a/src/Components/SVG/Elements/Selector/Selector.scss b/src/Components/SVG/Elements/Selector.scss similarity index 100% rename from src/Components/SVG/Elements/Selector/Selector.scss rename to src/Components/SVG/Elements/Selector.scss diff --git a/src/Components/SVG/Elements/Selector/Selector.tsx b/src/Components/SVG/Elements/Selector/Selector.tsx index c7baf92..1a8fb3e 100644 --- a/src/Components/SVG/Elements/Selector/Selector.tsx +++ b/src/Components/SVG/Elements/Selector/Selector.tsx @@ -1,38 +1,18 @@ -import './Selector.scss'; +import '../Selector.scss'; import * as React from 'react'; -import { IContainerModel } from '../../../../Interfaces/IContainerModel'; import { SHOW_SELECTOR_TEXT } from '../../../../utils/default'; -import { GetAbsolutePosition } from '../../../../utils/itertools'; -import { RemoveMargin } from '../../../../utils/svg'; interface ISelectorProps { - containers: Map - selected?: IContainerModel - scale?: number + text: string + x: number + y: number + width: number + height: number + scale: number + style?: React.CSSProperties } -export function Selector(props: ISelectorProps): JSX.Element { - if (props.selected === undefined || props.selected === null) { - return ( - - - ); - } - - 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 - )); - +export function Selector({ text, x, y, width, height, scale, style: overrideStyle }: ISelectorProps): JSX.Element { const xText = x + width / 2; const yText = y + height / 2; @@ -43,7 +23,8 @@ export function Selector(props: ISelectorProps): JSX.Element { transitionProperty: 'all', transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', transitionDuration: '150ms', - animation: 'fadein 750ms ease-in alternate infinite' + animation: 'fadein 750ms ease-in alternate infinite', + ...overrideStyle }; return ( @@ -65,7 +46,7 @@ export function Selector(props: ISelectorProps): JSX.Element { transformBox: 'fill-box' }} > - {props.selected.properties.displayedText} + { text } : null} diff --git a/src/Components/SVG/Elements/SelectorContainer/SelectorContainer.tsx b/src/Components/SVG/Elements/SelectorContainer/SelectorContainer.tsx new file mode 100644 index 0000000..c2ad3e2 --- /dev/null +++ b/src/Components/SVG/Elements/SelectorContainer/SelectorContainer.tsx @@ -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 + selected?: IContainerModel + scale?: number +} + +export function SelectorContainer(props: ISelectorContainerProps): JSX.Element { + if (props.selected === undefined || props.selected === null) { + return ( + + + ); + } + + 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 ( + + ); +} diff --git a/src/Components/SVG/Elements/SelectorSymbol/SelectorSymbol.tsx b/src/Components/SVG/Elements/SelectorSymbol/SelectorSymbol.tsx new file mode 100644 index 0000000..01133fb --- /dev/null +++ b/src/Components/SVG/Elements/SelectorSymbol/SelectorSymbol.tsx @@ -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 + selected?: ISymbolModel + scale?: number +} + +export function SelectorSymbol(props: ISelectorSymbolProps): JSX.Element { + if (props.selected === undefined || props.selected === null) { + return ( + + + ); + } + + 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 ( + + ); +} diff --git a/src/Components/SVG/SVG.tsx b/src/Components/SVG/SVG.tsx index cdb11de..4b49213 100644 --- a/src/Components/SVG/SVG.tsx +++ b/src/Components/SVG/SVG.tsx @@ -1,13 +1,13 @@ 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 { IContainerModel } from '../../Interfaces/IContainerModel'; -import { Selector } from './Elements/Selector/Selector'; -import { DepthDimensionLayer } from './Elements/DepthDimensionLayer'; -import { MAX_FRAMERATE, SHOW_DIMENSIONS_PER_DEPTH } from '../../utils/default'; +import { SelectorContainer } from './Elements/SelectorContainer/SelectorContainer'; +import { MAX_FRAMERATE } from '../../utils/default'; import { SymbolLayer } from './Elements/SymbolLayer'; -import { ISymbolModel } from '../../Interfaces/ISymbolModel'; +import { type ISymbolModel } from '../../Interfaces/ISymbolModel'; import { DimensionLayer } from './Elements/DimensionLayer'; +import { SelectorSymbol } from './Elements/SelectorSymbol/SelectorSymbol'; interface ISVGProps { className?: string @@ -17,11 +17,19 @@ interface ISVGProps { height: number containers: Map children: IContainerModel - selected?: IContainerModel + selectedContainer?: IContainerModel symbols: Map + selectedSymbol?: ISymbolModel + selectorMode: SelectorMode selectContainer: (containerId: string) => void } +export enum SelectorMode { + Nothing, + Containers, + Symbols +} + export const ID = 'svg'; export function SVG(props: ISVGProps): JSX.Element { @@ -49,8 +57,7 @@ export function SVG(props: ISVGProps): JSX.Element { xmlns }; - let children: React.ReactNode | React.ReactNode[] = []; - children = ; + function Selector(): JSX.Element { + switch (props.selectorMode) { + case SelectorMode.Containers: + return ; + case SelectorMode.Symbols: + return ; + default: + return <>; + } + } + return (
{children} - {SHOW_DIMENSIONS_PER_DEPTH - ? - : null} - {/* leave this at the end so it can be removed during the svg export */} +
diff --git a/src/Components/SymbolsList/SymbolsSidebar.tsx b/src/Components/SymbolsList/SymbolsSidebar.tsx index a43e8ae..300dcf9 100644 --- a/src/Components/SymbolsList/SymbolsSidebar.tsx +++ b/src/Components/SymbolsList/SymbolsSidebar.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import useSize from '@react-hook/size'; import { FixedSizeList as List } from 'react-window'; -import { ISymbolModel } from '../../Interfaces/ISymbolModel'; +import { type ISymbolModel } from '../../Interfaces/ISymbolModel'; import { SymbolProperties } from '../SymbolProperties/SymbolProperties'; import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar'; import { Text } from '../Text/Text'; @@ -23,28 +23,7 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element { const [showProperties, setShowProperties] = useState(props.isExpanded); // Render const symbols = [...props.symbols.values()]; - function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element { - const symbol = symbols[index]; - const key = symbol.id; - const text = symbol.displayedText; - const selectedClass: string = props.selectedSymbolId !== '' && - props.selectedSymbolId === symbol.id - ? 'border-l-4 bg-slate-400/60 hover:bg-slate-400' - : 'bg-slate-300/60 hover:bg-slate-300'; - return ( - - ); - } const selectedSymbol = props.symbols.get(props.selectedSymbolId); return (
@@ -71,4 +50,27 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
); + + function Row({ index, style }: { index: number, style: React.CSSProperties }): JSX.Element { + const symbol = symbols[index]; + const key = symbol.id; + const text = symbol.displayedText; + const selectedClass: string = props.selectedSymbolId !== '' && + props.selectedSymbolId === symbol.id + ? 'border-l-4 bg-slate-400/60 hover:bg-slate-400' + : 'bg-slate-300/60 hover:bg-slate-300'; + + return ( + + ); + } } diff --git a/src/Components/UI/UI.tsx b/src/Components/UI/UI.tsx index f85b63c..840f1dd 100644 --- a/src/Components/UI/UI.tsx +++ b/src/Components/UI/UI.tsx @@ -4,26 +4,29 @@ import { History } from '../History/History'; import { Bar, BAR_WIDTH } from '../Bar/Bar'; import { Symbols } from '../Symbols/Symbols'; import { SymbolsSidebar } from '../SymbolsList/SymbolsSidebar'; -import { PropertyType } from '../../Enums/PropertyType'; +import { type PropertyType } from '../../Enums/PropertyType'; import { Messages } from '../Messages/Messages'; import { Sidebar } from '../Sidebar/Sidebar'; import { Components } from '../Components/Components'; import { Viewer } from '../Viewer/Viewer'; import { Settings } from '../Settings/Settings'; -import { IMessage } from '../../Interfaces/IMessage'; +import { type IMessage } from '../../Interfaces/IMessage'; import { DISABLE_API } from '../../utils/default'; import { UseWorker, UseAsync } from './UseWorker'; import { FindContainerById } from '../../utils/itertools'; -import { IEditorState } from '../../Interfaces/IEditorState'; +import { type IEditorState } from '../../Interfaces/IEditorState'; import { GetCurrentHistoryState } from '../Editor/Editor'; import { Text } from '../Text/Text'; +import { IReplaceContainer } from '../../Interfaces/IReplaceContainer'; +import { Dispatch } from 'react'; export interface IUIProps { editorState: IEditorState + replaceContainer: IReplaceContainer selectContainer: (containerId: string) => void deleteContainer: (containerId: string) => void onPropertyChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => void - addContainer: (type: string) => void + addOrReplaceContainer: (type: string) => void addContainerAt: (index: number, type: string, parent: string) => void addSymbol: (type: string) => void onSymbolPropertyChange: (key: string, value: string | number | boolean) => void @@ -32,6 +35,8 @@ export interface IUIProps { saveEditorAsJSON: () => void saveEditorAsSVG: () => void loadState: (move: number) => void + setReplaceContainer: Dispatch> + } export enum SidebarType { @@ -59,7 +64,7 @@ function UseSetOrToggleSidebar( }; } -export function UI({ editorState, ...methods }: IUIProps): JSX.Element { +export function UI({ editorState, replaceContainer, setReplaceContainer, ...methods }: IUIProps): JSX.Element { const [selectedSidebar, setSelectedSidebar] = React.useState(SidebarType.Components); const [messages, setMessages] = React.useState([]); @@ -96,16 +101,19 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element { } const selectedContainer = FindContainerById(current.containers, current.selectedContainerId); + const selectedSymbol = current.symbols.get(current.selectedSymbolId); switch (selectedSidebar) { case SidebarType.Components: leftSidebarTitle = Text({ textId: '@Components' }); + leftChildren = ; + buttonOnClick={methods.addOrReplaceContainer} + replaceContainer={replaceContainer} + setReplaceContainer={setReplaceContainer}/>; rightSidebarTitle = Text({ textId: '@Elements' }); rightChildren = setOrToggleSidebar(SidebarType.ComponentsExpanded) } + onExpandChange={() => { setOrToggleSidebar(SidebarType.ComponentsExpanded); } } />; break; case SidebarType.ComponentsExpanded: @@ -125,8 +133,9 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element { selectedContainer={selectedContainer} componentOptions={configuration.AvailableContainers} categories={configuration.Categories} - buttonOnClick={methods.addContainer} - />; + buttonOnClick={methods.addOrReplaceContainer} + replaceContainer={replaceContainer} + setReplaceContainer={setReplaceContainer}/>; rightSidebarTitle = Text({ textId: '@Elements' }); rightChildren = setOrToggleSidebar(SidebarType.Components) } + onExpandChange={() => { setOrToggleSidebar(SidebarType.Components); } } />; break; case SidebarType.Symbols: @@ -153,7 +162,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element { onPropertyChange={methods.onSymbolPropertyChange} selectSymbol={methods.selectSymbol} isExpanded ={false} - onExpandChange={() => setOrToggleSidebar(SidebarType.SymbolsExpanded) } + onExpandChange={() => { setOrToggleSidebar(SidebarType.SymbolsExpanded); } } />; break; case SidebarType.SymbolsExpanded: @@ -169,7 +178,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element { onPropertyChange={methods.onSymbolPropertyChange} selectSymbol={methods.selectSymbol} isExpanded ={true} - onExpandChange={() => setOrToggleSidebar(SidebarType.Symbols)} + onExpandChange={() => { setOrToggleSidebar(SidebarType.Symbols); }} />; break; @@ -187,7 +196,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element { leftChildren = setMessages([])} + clearMessage={() => { setMessages([]); }} />; break; @@ -242,11 +251,16 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element { isLeftSidebarOpenClasses.add('left-sidebar-single'); } + 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 ( <> { rightChildren } diff --git a/src/Components/Viewer/Viewer.tsx b/src/Components/Viewer/Viewer.tsx index cdf630e..a6092c4 100644 --- a/src/Components/Viewer/Viewer.tsx +++ b/src/Components/Viewer/Viewer.tsx @@ -1,23 +1,27 @@ import * as React from 'react'; -import { IContainerModel } from '../../Interfaces/IContainerModel'; -import { IHistoryState } from '../../Interfaces/IHistoryState'; -import { IPoint } from '../../Interfaces/IPoint'; +import { type IContainerModel } from '../../Interfaces/IContainerModel'; +import { type IHistoryState } from '../../Interfaces/IHistoryState'; +import { type IPoint } from '../../Interfaces/IPoint'; import { DIMENSION_MARGIN, USE_EXPERIMENTAL_CANVAS_API } from '../../utils/default'; import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools'; import { BAR_WIDTH } from '../Bar/Bar'; import { Canvas } from '../Canvas/Canvas'; import { AddDimensions } from '../Canvas/DimensionLayer'; import { RenderSelector } from '../Canvas/Selector'; -import { SVG } from '../SVG/SVG'; +import { SelectorMode, SVG } from '../SVG/SVG'; import { RenderSymbol } from '../Canvas/Symbol'; import { useState } from 'react'; +import { type ISymbolModel } from '../../Interfaces/ISymbolModel'; interface IViewerProps { - className?: string + className: string current: IHistoryState selectedContainer: IContainerModel | undefined selectContainer: (containerId: string) => void + selectedSymbol: ISymbolModel | undefined margin: number + isComponentsOpen: boolean + isSymbolsOpen: boolean } export function Viewer({ @@ -25,7 +29,10 @@ export function Viewer({ current, selectedContainer, selectContainer, - margin + selectedSymbol, + margin, + isComponentsOpen, + isSymbolsOpen }: IViewerProps): JSX.Element { function computeWidth(margin: number): number { return window.innerWidth - (window.innerWidth < 768 ? BAR_WIDTH : margin); @@ -64,6 +71,13 @@ export function Viewer({ return <>; } + let selectorMode = SelectorMode.Nothing; + if (isComponentsOpen) { + selectorMode = SelectorMode.Containers; + } else if (isSymbolsOpen) { + selectorMode = SelectorMode.Symbols; + } + if (USE_EXPERIMENTAL_CANVAS_API) { function Draw(ctx: CanvasRenderingContext2D, frameCount: number, scale: number, translatePos: IPoint): void { if (mainContainer === undefined) { @@ -119,7 +133,7 @@ export function Viewer({ return ( @@ -134,11 +148,12 @@ export function Viewer({ width={mainContainer.properties.width} height={mainContainer.properties.height} containers={current.containers} - selected={selectedContainer} + selectedContainer={selectedContainer} symbols={current.symbols} + selectedSymbol={selectedSymbol} + selectorMode={selectorMode} selectContainer={selectContainer} > - {mainContainer} ); diff --git a/src/Enums/PropertyType.ts b/src/Enums/PropertyType.ts index edd78ff..f57d2b9 100644 --- a/src/Enums/PropertyType.ts +++ b/src/Enums/PropertyType.ts @@ -27,6 +27,6 @@ export enum PropertyType { SelfMarginDimension, ChildrenDimensions, DimensionWithMarks, - DimensionOptions + DimensionOptions } diff --git a/src/Interfaces/IAvailableContainer.ts b/src/Interfaces/IAvailableContainer.ts index dab4e59..d655ab2 100644 --- a/src/Interfaces/IAvailableContainer.ts +++ b/src/Interfaces/IAvailableContainer.ts @@ -1,12 +1,12 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { AddMethod } from '../Enums/AddMethod'; -import { PositionReference } from '../Enums/PositionReference'; -import { IAction } from './IAction'; -import { IMargin } from './IMargin'; -import { Orientation } from '../Enums/Orientation'; -import { IKeyValue } from './IKeyValue'; -import { IStyle } from './IStyle'; -import { IDimensions } from './IDimensions'; +import { type AddMethod } from '../Enums/AddMethod'; +import { type PositionReference } from '../Enums/PositionReference'; +import { type IAction } from './IAction'; +import { type IMargin } from './IMargin'; +import { type Orientation } from '../Enums/Orientation'; +import { type IKeyValue } from './IKeyValue'; +import { type IStyle } from './IStyle'; +import { type IDimensions } from './IDimensions'; /** Model of available container used in application configuration */ export interface IAvailableContainer { diff --git a/src/Interfaces/IDimensionOptions.ts b/src/Interfaces/IDimensionOptions.ts index 4c44adf..a12865a 100644 --- a/src/Interfaces/IDimensionOptions.ts +++ b/src/Interfaces/IDimensionOptions.ts @@ -1,10 +1,17 @@ -import { Position } from '../Enums/Position'; +import { type Position } from '../Enums/Position'; export interface IDimensionOptions { positions: Position[] + /** - * Stroke color - */ - color: string + * Stroke color + */ + color?: string + + /** stroke-width */ + width?: number + + /** stroke-dasharray */ + dashArray?: string } diff --git a/src/Interfaces/IReplaceContainer.ts b/src/Interfaces/IReplaceContainer.ts new file mode 100644 index 0000000..c1c8a71 --- /dev/null +++ b/src/Interfaces/IReplaceContainer.ts @@ -0,0 +1,5 @@ +export interface IReplaceContainer { + id: string | undefined + isReplacing: boolean + category: string | undefined +} diff --git a/src/Interfaces/ISymbolModel.ts b/src/Interfaces/ISymbolModel.ts index 3a71290..ca0604b 100644 --- a/src/Interfaces/ISymbolModel.ts +++ b/src/Interfaces/ISymbolModel.ts @@ -1,4 +1,4 @@ -import { IAvailableSymbol } from './IAvailableSymbol'; +import { type IAvailableSymbol } from './IAvailableSymbol'; export interface ISymbolModel { /** Identifier */ diff --git a/src/Translations/translation.en.json b/src/Translations/translation.en.json index b9d78ed..65d7410 100644 --- a/src/Translations/translation.en.json +++ b/src/Translations/translation.en.json @@ -59,11 +59,14 @@ "@ContainerAlignmentInput": "Alignment", "@ContainerAlignWithSymbol": "Align to symbol", "@ContainerDimensions": "Dimensions", - "@ContainerShowDimension": "Show Dimension", - "@ContainerShowChildrenDimension": "Show surrounding dimension of children", + "@ContainerShowDimension": "Show dimensions", + "@ContainerShowChildrenDimension": "Show surrounding dimensions of children", "@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", + "@StyleStrokeColor": "Stroke Color", + "@StyleStrokeDashArray": "Stroke Dash Array", "@StyleStroke": "Stroke", "@StyleStrokeOpacity": "Stroke Opacity", "@StyleStrokeWidth": "Stroke Width", diff --git a/src/Translations/translation.fr.json b/src/Translations/translation.fr.json index 3d71f2c..3d2ce91 100644 --- a/src/Translations/translation.fr.json +++ b/src/Translations/translation.fr.json @@ -63,7 +63,10 @@ "@ContainerShowChildrenDimension": "Afficher les cotations englobante des enfants", "@ContainerMarkPosition": "Marquer la position pour les parents", "@ContainerShowDimensionWithMarks": "Afficher les cotations avec les enfants marqués", + "@ContainerShowMarginsDimension": "Afficher les cotations des marges", "@ContainerStyle": "Style", + "@StyleStrokeColor": "Couleur du tracé", + "@StyleStrokeDashArray": "Tableau de traits", "@StyleStroke": "Tracé", "@StyleStrokeOpacity": "Opacité du tracé", "@StyleStrokeWidth": "Epaisseur du tracé", diff --git a/src/utils/default.ts b/src/utils/default.ts index 632c12a..5977edd 100644 --- a/src/utils/default.ts +++ b/src/utils/default.ts @@ -1,13 +1,14 @@ import { PositionReference } from '../Enums/PositionReference'; -import { IAvailableContainer } from '../Interfaces/IAvailableContainer'; -import { IAvailableSymbol } from '../Interfaces/IAvailableSymbol'; -import { IConfiguration } from '../Interfaces/IConfiguration'; -import { ContainerModel, IContainerModel } from '../Interfaces/IContainerModel'; -import { IContainerProperties } from '../Interfaces/IContainerProperties'; -import { IEditorState } from '../Interfaces/IEditorState'; -import { ISymbolModel } from '../Interfaces/ISymbolModel'; +import { type IAvailableContainer } from '../Interfaces/IAvailableContainer'; +import { type IAvailableSymbol } from '../Interfaces/IAvailableSymbol'; +import { type IConfiguration } from '../Interfaces/IConfiguration'; +import { ContainerModel, type IContainerModel } from '../Interfaces/IContainerModel'; +import { type IContainerProperties } from '../Interfaces/IContainerProperties'; +import { type IEditorState } from '../Interfaces/IEditorState'; +import { type ISymbolModel } from '../Interfaces/ISymbolModel'; import { Orientation } from '../Enums/Orientation'; import { AppState } from '../Enums/AppState'; +import { type IDimensionOptions } from '../Interfaces/IDimensionOptions'; /// EDITOR DEFAULTS /// @@ -65,7 +66,6 @@ export const SHOW_SELF_DIMENSIONS = true; export const SHOW_SELF_MARGINS_DIMENSIONS = true; export const SHOW_CHILDREN_DIMENSIONS = true; export const SHOW_BORROWER_DIMENSIONS = true; -export const SHOW_DIMENSIONS_PER_DEPTH = false; export const DIMENSION_MARGIN = 50; export const SYMBOL_MARGIN = 25; export const NOTCHES_LENGTH = 10; @@ -187,6 +187,12 @@ const DEFAULT_CONTAINER_STYLE = { strokeWidth: 2 }; +export const DEFAULT_DIMENSION_OPTION: IDimensionOptions = { + positions: [], + color: '#000000', + width: 2 +}; + /** * Default Main container properties */ @@ -211,23 +217,11 @@ export const DEFAULT_MAINCONTAINER_PROPS: IContainerProperties = { positionReference: PositionReference.TopLeft, hideChildrenInTreeview: false, dimensionOptions: { - childrenDimensions: { - color: '#000000', - positions: [] - }, - selfDimensions: { - color: '#000000', - positions: [] - }, - selfMarginsDimensions: { - color: '#000000', - positions: [] - }, + childrenDimensions: clone(DEFAULT_DIMENSION_OPTION), + selfDimensions: clone(DEFAULT_DIMENSION_OPTION), + selfMarginsDimensions: clone(DEFAULT_DIMENSION_OPTION), markPosition: [], - dimensionWithMarks: { - color: '#000000', - positions: [] - } + dimensionWithMarks: clone(DEFAULT_DIMENSION_OPTION) }, warning: '', style: DEFAULT_CONTAINER_STYLE @@ -276,20 +270,20 @@ export function GetDefaultContainerProps(type: string, hideChildrenInTreeview: containerConfig.HideChildrenInTreeview ?? false, dimensionOptions: { childrenDimensions: { - color: containerConfig.DimensionOptions?.childrenDimensions.color ?? '#000000', + ...containerConfig.DimensionOptions?.selfDimensions, positions: containerConfig.DimensionOptions?.childrenDimensions.positions ?? [] }, selfDimensions: { - color: containerConfig.DimensionOptions?.selfDimensions.color ?? '#000000', + ...containerConfig.DimensionOptions?.selfDimensions, positions: containerConfig.DimensionOptions?.selfDimensions.positions ?? [] }, selfMarginsDimensions: { - color: containerConfig.DimensionOptions?.selfMarginsDimensions.color ?? '#000000', + ...containerConfig.DimensionOptions?.selfMarginsDimensions, positions: containerConfig.DimensionOptions?.selfMarginsDimensions.positions ?? [] }, markPosition: containerConfig.DimensionOptions?.markPosition ?? [], dimensionWithMarks: { - color: containerConfig.DimensionOptions?.dimensionWithMarks.color ?? '#000000', + ...containerConfig.DimensionOptions?.dimensionWithMarks, positions: containerConfig.DimensionOptions?.dimensionWithMarks.positions ?? [] } }, @@ -317,3 +311,10 @@ export function GetDefaultSymbolModel(name: string, showDimension: false }; } + +/** + * Macro function for JSON.parse(JSON.stringify(obj)) + */ +function clone(object: T): T { + return JSON.parse(JSON.stringify(object)); +}