Merge branch 'master' into eslint-fixes
This commit is contained in:
commit
4bbb916788
36 changed files with 1002 additions and 630 deletions
|
@ -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',
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
7
mprocs.yaml
Normal file
7
mprocs.yaml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
procs:
|
||||||
|
nvim:
|
||||||
|
shell: "nvim ."
|
||||||
|
vite:
|
||||||
|
shell: "npx vite"
|
||||||
|
test-server:
|
||||||
|
shell: "npx nodemon ./test-server/http.js"
|
|
@ -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",
|
||||||
|
@ -52,6 +52,8 @@
|
||||||
"eslint-plugin-react": "^7.32.2",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"jsdom": "^21.1.0",
|
"jsdom": "^21.1.0",
|
||||||
|
"mprocs": "^0.6.4",
|
||||||
|
"nodemon": "^2.0.20",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"sass": "^1.58.0",
|
"sass": "^1.58.0",
|
||||||
"tailwindcss": "^3.2.4",
|
"tailwindcss": "^3.2.4",
|
||||||
|
|
86
pnpm-lock.yaml
generated
86
pnpm-lock.yaml
generated
|
@ -28,6 +28,8 @@ specifiers:
|
||||||
eslint-plugin-react-hooks: ^4.6.0
|
eslint-plugin-react-hooks: ^4.6.0
|
||||||
interweave: ^13.0.0
|
interweave: ^13.0.0
|
||||||
jsdom: ^21.1.0
|
jsdom: ^21.1.0
|
||||||
|
mprocs: ^0.6.4
|
||||||
|
nodemon: ^2.0.20
|
||||||
postcss: ^8.4.21
|
postcss: ^8.4.21
|
||||||
react: ^18.2.0
|
react: ^18.2.0
|
||||||
react-dom: ^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: 7.32.2_eslint@8.33.0
|
||||||
eslint-plugin-react-hooks: 4.6.0_eslint@8.33.0
|
eslint-plugin-react-hooks: 4.6.0_eslint@8.33.0
|
||||||
jsdom: 21.1.0
|
jsdom: 21.1.0
|
||||||
|
mprocs: 0.6.4
|
||||||
|
nodemon: 2.0.20
|
||||||
postcss: 8.4.21
|
postcss: 8.4.21
|
||||||
sass: 1.58.0
|
sass: 1.58.0
|
||||||
tailwindcss: 3.2.4_postcss@8.4.21
|
tailwindcss: 3.2.4_postcss@8.4.21
|
||||||
|
@ -1172,6 +1176,10 @@ packages:
|
||||||
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
|
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/abbrev/1.1.1:
|
||||||
|
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/acorn-globals/7.0.1:
|
/acorn-globals/7.0.1:
|
||||||
resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==}
|
resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1600,6 +1608,18 @@ packages:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
dev: true
|
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:
|
/debug/4.3.4:
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
|
@ -2507,6 +2527,10 @@ packages:
|
||||||
safer-buffer: 2.1.2
|
safer-buffer: 2.1.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ignore-by-default/1.0.1:
|
||||||
|
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ignore/5.2.4:
|
/ignore/5.2.4:
|
||||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
@ -3010,6 +3034,12 @@ packages:
|
||||||
ufo: 1.0.1
|
ufo: 1.0.1
|
||||||
dev: true
|
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:
|
/mrmime/1.0.1:
|
||||||
resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==}
|
resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -3041,6 +3071,30 @@ packages:
|
||||||
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
|
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
|
||||||
dev: true
|
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:
|
/normalize-path/3.0.0:
|
||||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
@ -3352,6 +3406,10 @@ packages:
|
||||||
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
|
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pstree.remy/1.1.8:
|
||||||
|
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/punycode/2.3.0:
|
/punycode/2.3.0:
|
||||||
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -3549,11 +3607,21 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
|
|
||||||
|
/semver/5.7.1:
|
||||||
|
resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/semver/6.3.0:
|
/semver/6.3.0:
|
||||||
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
|
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/semver/7.0.0:
|
||||||
|
resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/semver/7.3.8:
|
/semver/7.3.8:
|
||||||
resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
|
resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -3586,6 +3654,13 @@ packages:
|
||||||
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||||
dev: true
|
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:
|
/sirv/2.0.2:
|
||||||
resolution: {integrity: sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==}
|
resolution: {integrity: sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
|
@ -3831,6 +3906,13 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
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:
|
/tough-cookie/4.1.2:
|
||||||
resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==}
|
resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -3926,6 +4008,10 @@ packages:
|
||||||
which-boxed-primitive: 1.0.2
|
which-boxed-primitive: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/undefsafe/2.0.5:
|
||||||
|
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/universalify/0.2.0:
|
/universalify/0.2.0:
|
||||||
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
||||||
engines: {node: '>= 4.0.0'}
|
engines: {node: '>= 4.0.0'}
|
||||||
|
|
|
@ -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: [
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { BarIcon } from './BarIcon';
|
||||||
import { Text } from '../Text/Text';
|
import { Text } from '../Text/Text';
|
||||||
|
|
||||||
interface IBarProps {
|
interface IBarProps {
|
||||||
|
className: string
|
||||||
isComponentsOpen: boolean
|
isComponentsOpen: boolean
|
||||||
isSymbolsOpen: boolean
|
isSymbolsOpen: boolean
|
||||||
isHistoryOpen: boolean
|
isHistoryOpen: boolean
|
||||||
|
@ -33,7 +34,7 @@ export const BAR_WIDTH = 64; // 4rem
|
||||||
|
|
||||||
export function Bar(props: IBarProps): JSX.Element {
|
export function Bar(props: IBarProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className='bar'>
|
<div className={`bar ${props.className}`}>
|
||||||
<BarIcon
|
<BarIcon
|
||||||
isActive={props.isComponentsOpen}
|
isActive={props.isComponentsOpen}
|
||||||
title={Text({ textId: '@Components' })}
|
title={Text({ textId: '@Components' })}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
|
import { EyeIcon, EyeSlashIcon, XCircleIcon } from '@heroicons/react/24/outline';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
||||||
import { ICategory } from '../../Interfaces/ICategory';
|
import { ICategory } from '../../Interfaces/ICategory';
|
||||||
|
@ -6,11 +6,15 @@ import { IContainerModel } from '../../Interfaces/IContainerModel';
|
||||||
import { TruncateString } from '../../utils/stringtools';
|
import { TruncateString } from '../../utils/stringtools';
|
||||||
import { Category } from '../Category/Category';
|
import { Category } from '../Category/Category';
|
||||||
import { Text } from '../Text/Text';
|
import { Text } from '../Text/Text';
|
||||||
|
import { IReplaceContainer } from '../../Interfaces/IReplaceContainer';
|
||||||
|
import { Dispatch } from 'react';
|
||||||
|
|
||||||
interface IComponentsProps {
|
interface IComponentsProps {
|
||||||
selectedContainer: IContainerModel | undefined
|
selectedContainer: IContainerModel | undefined
|
||||||
componentOptions: IAvailableContainer[]
|
componentOptions: IAvailableContainer[]
|
||||||
categories: ICategory[]
|
categories: ICategory[]
|
||||||
|
replaceContainer: IReplaceContainer
|
||||||
|
setReplaceContainer: Dispatch<React.SetStateAction<IReplaceContainer>>
|
||||||
buttonOnClick: (type: string) => void
|
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;
|
disabled = config.Blacklist?.find(type => type === componentOption.Type) !== undefined ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.replaceContainer.isReplacing && componentOption.Category !== props.replaceContainer.category) {
|
||||||
|
disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (disabled && hideDisabled) {
|
if (disabled && hideDisabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -96,6 +104,15 @@ export function Components(props: IComponentsProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className='h-full'>
|
<div className='h-full'>
|
||||||
<div className='hover:bg-slate-300 h-7 text-right pr-1 pl-1'>
|
<div className='hover:bg-slate-300 h-7 text-right pr-1 pl-1'>
|
||||||
|
{props.replaceContainer.isReplacing && <button
|
||||||
|
onClick={() => {
|
||||||
|
props.setReplaceContainer({ isReplacing: false, id: undefined, category: undefined });
|
||||||
|
}}
|
||||||
|
className='h-full hover:bg-slate-400 rounded-lg p-1'
|
||||||
|
>
|
||||||
|
<XCircleIcon className='heroicon'></XCircleIcon>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
<button
|
<button
|
||||||
onClick={() => { setHideDisabled(!hideDisabled); }}
|
onClick={() => { setHideDisabled(!hideDisabled); }}
|
||||||
className='h-full hover:bg-slate-400 rounded-lg p-1'
|
className='h-full hover:bg-slate-400 rounded-lg p-1'
|
||||||
|
@ -117,4 +134,4 @@ export function Components(props: IComponentsProps): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
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,
|
||||||
|
@ -46,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'
|
||||||
|
@ -117,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(
|
||||||
|
@ -127,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' })}
|
||||||
|
@ -140,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(
|
||||||
|
@ -150,7 +153,8 @@ export function ContainerForm(props: IContainerFormProps): JSX.Element {
|
||||||
),
|
),
|
||||||
props.properties.margin.top
|
props.properties.margin.top
|
||||||
)
|
)
|
||||||
)}/>
|
);
|
||||||
|
}}/>
|
||||||
</div>
|
</div>
|
||||||
</Category>
|
</Category>
|
||||||
|
|
||||||
|
@ -171,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' })}
|
||||||
|
@ -182,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`}
|
||||||
|
@ -193,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`}
|
||||||
|
@ -204,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' })}
|
||||||
|
@ -215,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
|
||||||
|
@ -227,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>
|
||||||
|
|
||||||
|
@ -247,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' })}
|
||||||
|
@ -257,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' })}
|
||||||
|
@ -267,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' })}
|
||||||
|
@ -277,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>
|
||||||
|
|
||||||
|
@ -295,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' })}
|
||||||
|
@ -304,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>
|
||||||
|
|
||||||
|
@ -334,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>
|
||||||
|
|
||||||
|
@ -354,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>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -375,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>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -396,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>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -417,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'>
|
||||||
|
@ -427,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>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
@ -459,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`}
|
||||||
|
@ -472,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`}
|
||||||
|
@ -482,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`}
|
||||||
|
@ -492,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`}
|
||||||
|
@ -505,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>
|
||||||
|
|
|
@ -5,6 +5,7 @@ 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 { ContainerProperties } 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', () => {
|
||||||
|
@ -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: []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ import Swal from 'sweetalert2';
|
||||||
import { PropertyType } from '../../../Enums/PropertyType';
|
import { PropertyType } from '../../../Enums/PropertyType';
|
||||||
import { TransformX, TransformY } from '../../../utils/svg';
|
import { TransformX, TransformY } from '../../../utils/svg';
|
||||||
import { Orientation } from '../../../Enums/Orientation';
|
import { Orientation } from '../../../Enums/Orientation';
|
||||||
|
import { AddContainers } from './AddContainer';
|
||||||
|
import { IConfiguration } from '../../../Interfaces/IConfiguration';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a container
|
* Select a container
|
||||||
|
@ -133,6 +135,58 @@ export function DeleteContainer(
|
||||||
return history;
|
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
|
* Returns the next container that will be selected
|
||||||
* after the selectedContainer is removed.
|
* after the selectedContainer is removed.
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { AddContainers } from './AddContainer';
|
||||||
import { DeleteContainer } from './ContainerOperations';
|
import { DeleteContainer } from './ContainerOperations';
|
||||||
import { DeleteSymbol } from './SymbolOperations';
|
import { DeleteSymbol } from './SymbolOperations';
|
||||||
import { Text } from '../../Text/Text';
|
import { Text } from '../../Text/Text';
|
||||||
|
import { IReplaceContainer } from '../../../Interfaces/IReplaceContainer';
|
||||||
|
|
||||||
export function InitActions(
|
export function InitActions(
|
||||||
menuActions: Map<string, IMenuAction[]>,
|
menuActions: Map<string, IMenuAction[]>,
|
||||||
|
@ -23,7 +24,8 @@ export function InitActions(
|
||||||
history: IHistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setNewHistory: (newHistory: IHistoryState[]) => void,
|
setNewHistory: (newHistory: IHistoryState[]) => void,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
|
||||||
|
setIsReplacingContainer: Dispatch<SetStateAction<IReplaceContainer>>
|
||||||
): void {
|
): void {
|
||||||
menuActions.set(
|
menuActions.set(
|
||||||
'',
|
'',
|
||||||
|
@ -56,9 +58,24 @@ export function InitActions(
|
||||||
menuActions.set(
|
menuActions.set(
|
||||||
'elements-sidebar-row',
|
'elements-sidebar-row',
|
||||||
[{
|
[{
|
||||||
|
text: Text({ textId: '@ReplaceByContainer' }),
|
||||||
|
title: Text({ textId: '@ReplaceByContainerTitle' }),
|
||||||
|
shortcut: '<kbd>R</kbd>',
|
||||||
|
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' }),
|
text: Text({ textId: '@DeleteContainer' }),
|
||||||
title: Text({ textId: '@DeleteContainerTitle' }),
|
title: Text({ textId: '@DeleteContainerTitle' }),
|
||||||
shortcut: '<kbd>Suppr</kbd>',
|
shortcut: '<kbd>Suppr</kbd>',
|
||||||
|
|
||||||
action: (target: HTMLElement) => {
|
action: (target: HTMLElement) => {
|
||||||
const id = target.id;
|
const id = target.id;
|
||||||
const newHistory = DeleteContainer(
|
const newHistory = DeleteContainer(
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -7,7 +7,8 @@ export function OnKey(
|
||||||
history: IHistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
|
||||||
deleteAction: () => void
|
deleteAction: () => void,
|
||||||
|
resetState: () => void
|
||||||
): void {
|
): void {
|
||||||
if (!ENABLE_SHORTCUTS) {
|
if (!ENABLE_SHORTCUTS) {
|
||||||
return;
|
return;
|
||||||
|
@ -27,5 +28,7 @@ export function OnKey(
|
||||||
setHistoryCurrentStep(historyCurrentStep + 1);
|
setHistoryCurrentStep(historyCurrentStep + 1);
|
||||||
} else if (event.key === 'Delete') {
|
} else if (event.key === 'Delete') {
|
||||||
deleteAction();
|
deleteAction();
|
||||||
|
} else if (event.key === 'Escape') {
|
||||||
|
resetState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 './Editor.scss';
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
import { type IConfiguration } from '../../Interfaces/IConfiguration';
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { type IHistoryState } from '../../Interfaces/IHistoryState';
|
||||||
import { UI } from '../UI/UI';
|
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 { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save';
|
||||||
import { OnKey } from './Actions/Shortcuts';
|
import { OnKey } from './Actions/Shortcuts';
|
||||||
import { UseCustomEvents, UseEditorListener } from '../../Events/EditorEvents';
|
import { UseCustomEvents, UseEditorListener } from '../../Events/EditorEvents';
|
||||||
|
@ -13,6 +13,7 @@ import { FindContainerById } from '../../utils/itertools';
|
||||||
import { Menu } from '../Menu/Menu';
|
import { Menu } from '../Menu/Menu';
|
||||||
import { InitActions } from './Actions/ContextMenuActions';
|
import { InitActions } from './Actions/ContextMenuActions';
|
||||||
import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer';
|
import { AddContainerToSelectedContainer, AddContainer } from './Actions/AddContainer';
|
||||||
|
import { type IReplaceContainer } from '../../Interfaces/IReplaceContainer';
|
||||||
|
|
||||||
interface IEditorProps {
|
interface IEditorProps {
|
||||||
root: Element | Document
|
root: Element | Document
|
||||||
|
@ -25,16 +26,18 @@ function UseShortcuts(
|
||||||
history: IHistoryState[],
|
history: IHistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>,
|
||||||
deleteAction: () => void
|
deleteAction: () => void,
|
||||||
|
resetState: () => void
|
||||||
): void {
|
): void {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function OnKeyUp(event: KeyboardEvent): void {
|
function OnKeyUp(event: KeyboardEvent): void {
|
||||||
return OnKey(
|
OnKey(
|
||||||
event,
|
event,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
setHistoryCurrentStep,
|
setHistoryCurrentStep,
|
||||||
deleteAction
|
deleteAction,
|
||||||
|
resetState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,13 +65,20 @@ function UseNewHistoryState(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function Editor(props: IEditorProps): JSX.Element {
|
export function Editor(props: IEditorProps): JSX.Element {
|
||||||
// States
|
// States
|
||||||
const [history, setHistory] = React.useState<IHistoryState[]>(structuredClone(props.history));
|
const [history, setHistory] = React.useState<IHistoryState[]>(structuredClone(props.history));
|
||||||
const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep);
|
const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep);
|
||||||
|
const [replaceContainer, setReplaceContainer] = React.useState<IReplaceContainer>({ isReplacing: false, id: undefined, category: undefined });
|
||||||
|
|
||||||
const editorRef = useRef<HTMLDivElement>(null);
|
const editorRef = useRef<HTMLDivElement>(null);
|
||||||
const setNewHistory = UseNewHistoryState(setHistory, setHistoryCurrentStep);
|
const setNewHistory = UseNewHistoryState(setHistory, setHistoryCurrentStep);
|
||||||
|
|
||||||
|
function ResetState(): void {
|
||||||
|
setReplaceContainer({ isReplacing: false, id: undefined, category: undefined });
|
||||||
|
}
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
UseShortcuts(
|
UseShortcuts(
|
||||||
history,
|
history,
|
||||||
|
@ -79,7 +89,8 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
setNewHistory(
|
setNewHistory(
|
||||||
DeleteContainer(current.selectedContainerId, history, historyCurrentStep)
|
DeleteContainer(current.selectedContainerId, history, historyCurrentStep)
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
ResetState
|
||||||
);
|
);
|
||||||
UseCustomEvents(
|
UseCustomEvents(
|
||||||
props.root,
|
props.root,
|
||||||
|
@ -104,7 +115,8 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
setNewHistory,
|
setNewHistory,
|
||||||
setHistoryCurrentStep
|
setHistoryCurrentStep,
|
||||||
|
setReplaceContainer
|
||||||
);
|
);
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
|
@ -113,37 +125,54 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
const selected = FindContainerById(current.containers, current.selectedContainerId);
|
const selected = FindContainerById(current.containers, current.selectedContainerId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={editorRef} className="Editor font-sans h-full">
|
<div ref={editorRef} className="Editor font-sans h-full ">
|
||||||
<UI
|
<UI
|
||||||
editorState={{
|
editorState={{
|
||||||
configuration: props.configuration,
|
configuration: props.configuration,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
}}
|
}}
|
||||||
selectContainer={(container) => setNewHistory(
|
replaceContainer={replaceContainer}
|
||||||
|
selectContainer={(container) => {
|
||||||
|
setNewHistory(
|
||||||
SelectContainer(
|
SelectContainer(
|
||||||
container,
|
container,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
))}
|
));
|
||||||
deleteContainer={(containerId: string) => setNewHistory(
|
}}
|
||||||
|
deleteContainer={(containerId: string) => {
|
||||||
|
setNewHistory(
|
||||||
DeleteContainer(
|
DeleteContainer(
|
||||||
containerId,
|
containerId,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
))}
|
));
|
||||||
onPropertyChange={(key, value, type) => setNewHistory(
|
}}
|
||||||
|
onPropertyChange={(key, value, type) => {
|
||||||
|
setNewHistory(
|
||||||
OnPropertyChange(
|
OnPropertyChange(
|
||||||
key, value, type,
|
key, value, type,
|
||||||
selected,
|
selected,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
))}
|
));
|
||||||
addContainer={(type) => {
|
}}
|
||||||
|
addOrReplaceContainer={(type) => {
|
||||||
if (selected === null || selected === undefined) {
|
if (selected === null || selected === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
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(
|
setNewHistory(AddContainerToSelectedContainer(
|
||||||
type,
|
type,
|
||||||
selected,
|
selected,
|
||||||
|
@ -151,8 +180,10 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
));
|
));
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
addContainerAt={(index, type, parent) => setNewHistory(
|
addContainerAt={(index, type, parent) => {
|
||||||
|
setNewHistory(
|
||||||
AddContainer(
|
AddContainer(
|
||||||
index,
|
index,
|
||||||
type,
|
type,
|
||||||
|
@ -161,39 +192,51 @@ export function Editor(props: IEditorProps): JSX.Element {
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
)
|
)
|
||||||
)}
|
);
|
||||||
addSymbol={(type) => setNewHistory(
|
}}
|
||||||
|
addSymbol={(type) => {
|
||||||
|
setNewHistory(
|
||||||
AddSymbol(
|
AddSymbol(
|
||||||
type,
|
type,
|
||||||
configuration,
|
configuration,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
))}
|
));
|
||||||
onSymbolPropertyChange={(key, value) => setNewHistory(
|
}}
|
||||||
|
onSymbolPropertyChange={(key, value) => {
|
||||||
|
setNewHistory(
|
||||||
OnSymbolPropertyChange(
|
OnSymbolPropertyChange(
|
||||||
key, value,
|
key, value,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
))}
|
));
|
||||||
selectSymbol={(symbolId) => setNewHistory(
|
}}
|
||||||
|
selectSymbol={(symbolId) => {
|
||||||
|
setNewHistory(
|
||||||
SelectSymbol(
|
SelectSymbol(
|
||||||
symbolId,
|
symbolId,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
))}
|
));
|
||||||
deleteSymbol={(symbolId) => setNewHistory(
|
}}
|
||||||
|
deleteSymbol={(symbolId) => {
|
||||||
|
setNewHistory(
|
||||||
DeleteSymbol(
|
DeleteSymbol(
|
||||||
symbolId,
|
symbolId,
|
||||||
history,
|
history,
|
||||||
historyCurrentStep
|
historyCurrentStep
|
||||||
))}
|
));
|
||||||
saveEditorAsJSON={() => SaveEditorAsJSON(
|
}}
|
||||||
|
saveEditorAsJSON={() => {
|
||||||
|
SaveEditorAsJSON(
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
configuration
|
configuration
|
||||||
)}
|
);
|
||||||
saveEditorAsSVG={() => SaveEditorAsSVG()}
|
}}
|
||||||
loadState={(move) => setHistoryCurrentStep(move)}
|
saveEditorAsSVG={() => { SaveEditorAsSVG(); }}
|
||||||
|
loadState={(move) => { setHistoryCurrentStep(move); }}
|
||||||
|
setReplaceContainer={setReplaceContainer}
|
||||||
/>
|
/>
|
||||||
<Menu
|
<Menu
|
||||||
getListener={() => editorRef.current}
|
getListener={() => editorRef.current}
|
||||||
|
|
|
@ -21,6 +21,7 @@ export interface IMenuAction {
|
||||||
|
|
||||||
/** function to be called on button click */
|
/** function to be called on button click */
|
||||||
action: (target: HTMLElement) => void
|
action: (target: HTMLElement) => void
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function UseMouseEvents(
|
function UseMouseEvents(
|
||||||
|
@ -139,7 +140,7 @@ function AddClassSpecificActions(
|
||||||
onClick={() => action.action(target)} />);
|
onClick={() => action.action(target)} />);
|
||||||
});
|
});
|
||||||
children.push(<hr key={`contextmenu-hr-${count}`} className='border-slate-400' />);
|
children.push(<hr key={`contextmenu-hr-${count}`} className='border-slate-400' />);
|
||||||
};
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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`
|
||||||
|
|
|
@ -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,8 +10,9 @@ import {
|
||||||
} from '../../../utils/default';
|
} from '../../../utils/default';
|
||||||
import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools';
|
import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools';
|
||||||
import { TransformX, TransformY } from '../../../utils/svg';
|
import { TransformX, TransformY } from '../../../utils/svg';
|
||||||
import { Dimension } from './Dimension';
|
import { Dimension, type IDimensionStyle } from './Dimension';
|
||||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
import { type IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||||
|
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
||||||
|
|
||||||
interface IDimensionLayerProps {
|
interface IDimensionLayerProps {
|
||||||
containers: Map<string, IContainerModel>
|
containers: Map<string, IContainerModel>
|
||||||
|
@ -37,8 +37,7 @@ function ActionByPosition(
|
||||||
horizontalAction: (dim: number, ...params: any[]) => void,
|
horizontalAction: (dim: number, ...params: any[]) => void,
|
||||||
verticalAction: (dim: number, isRight: boolean, ...params: any[]) => void,
|
verticalAction: (dim: number, isRight: boolean, ...params: any[]) => void,
|
||||||
params: any[]
|
params: any[]
|
||||||
): boolean {
|
): void {
|
||||||
let incrementDepthSymbols = false;
|
|
||||||
positions.forEach((position: Position) => {
|
positions.forEach((position: Position) => {
|
||||||
const dim = dimMapped[position];
|
const dim = dimMapped[position];
|
||||||
switch (position) {
|
switch (position) {
|
||||||
|
@ -50,12 +49,10 @@ function ActionByPosition(
|
||||||
}
|
}
|
||||||
case Position.Down:
|
case Position.Down:
|
||||||
case Position.Up:
|
case Position.Up:
|
||||||
incrementDepthSymbols = true;
|
|
||||||
horizontalAction(dim, ...params);
|
horizontalAction(dim, ...params);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return incrementDepthSymbols;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,7 +71,6 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps):
|
||||||
if (!SHOW_SELF_DIMENSIONS) {
|
if (!SHOW_SELF_DIMENSIONS) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
let startDepthSymbols: number = 0;
|
|
||||||
|
|
||||||
for (const { container, depth, currentTransform } of it) {
|
for (const { container, depth, currentTransform } of it) {
|
||||||
const offset = (DIMENSION_MARGIN * (depth + 1)) / scale;
|
const offset = (DIMENSION_MARGIN * (depth + 1)) / scale;
|
||||||
|
@ -84,7 +80,7 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps):
|
||||||
const containerRightDim = rightDim + offset;
|
const containerRightDim = rightDim + offset;
|
||||||
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
|
const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim];
|
||||||
if (SHOW_SELF_DIMENSIONS && container.properties.dimensionOptions.selfDimensions.positions.length > 0) {
|
if (SHOW_SELF_DIMENSIONS && container.properties.dimensionOptions.selfDimensions.positions.length > 0) {
|
||||||
const incrementDepthSymbol = ActionByPosition(
|
ActionByPosition(
|
||||||
dimMapped,
|
dimMapped,
|
||||||
container.properties.dimensionOptions.selfDimensions.positions,
|
container.properties.dimensionOptions.selfDimensions.positions,
|
||||||
AddHorizontalSelfDimension,
|
AddHorizontalSelfDimension,
|
||||||
|
@ -93,14 +89,13 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps):
|
||||||
container,
|
container,
|
||||||
currentTransform,
|
currentTransform,
|
||||||
dimensions,
|
dimensions,
|
||||||
scale,
|
scale
|
||||||
container.properties.dimensionOptions.selfDimensions.color]
|
]
|
||||||
);
|
);
|
||||||
if (incrementDepthSymbol) { startDepthSymbols++; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SHOW_SELF_MARGINS_DIMENSIONS && container.properties.dimensionOptions.selfMarginsDimensions.positions.length > 0) {
|
if (SHOW_SELF_MARGINS_DIMENSIONS && container.properties.dimensionOptions.selfMarginsDimensions.positions.length > 0) {
|
||||||
const incrementDepthSymbol = ActionByPosition(
|
ActionByPosition(
|
||||||
dimMapped,
|
dimMapped,
|
||||||
container.properties.dimensionOptions.selfMarginsDimensions.positions,
|
container.properties.dimensionOptions.selfMarginsDimensions.positions,
|
||||||
AddHorizontalSelfMarginsDimension,
|
AddHorizontalSelfMarginsDimension,
|
||||||
|
@ -109,14 +104,13 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps):
|
||||||
container,
|
container,
|
||||||
currentTransform,
|
currentTransform,
|
||||||
dimensions,
|
dimensions,
|
||||||
scale,
|
scale
|
||||||
container.properties.dimensionOptions.selfMarginsDimensions.color]
|
]
|
||||||
);
|
);
|
||||||
if (incrementDepthSymbol) { startDepthSymbols++; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SHOW_BORROWER_DIMENSIONS && container.properties.dimensionOptions.dimensionWithMarks.positions.length > 0) {
|
if (SHOW_BORROWER_DIMENSIONS && container.properties.dimensionOptions.dimensionWithMarks.positions.length > 0) {
|
||||||
const incrementDepthSymbol = ActionByPosition(
|
ActionByPosition(
|
||||||
dimMapped,
|
dimMapped,
|
||||||
container.properties.dimensionOptions.dimensionWithMarks.positions,
|
container.properties.dimensionOptions.dimensionWithMarks.positions,
|
||||||
|
|
||||||
|
@ -128,14 +122,13 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps):
|
||||||
depth,
|
depth,
|
||||||
currentTransform,
|
currentTransform,
|
||||||
dimensions,
|
dimensions,
|
||||||
scale,
|
scale
|
||||||
container.properties.dimensionOptions.dimensionWithMarks.color]
|
]
|
||||||
);
|
);
|
||||||
if (incrementDepthSymbol) { startDepthSymbols++; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SHOW_CHILDREN_DIMENSIONS && container.properties.dimensionOptions.childrenDimensions.positions.length > 0 && container.children.length >= 2) {
|
if (SHOW_CHILDREN_DIMENSIONS && container.properties.dimensionOptions.childrenDimensions.positions.length > 0 && container.children.length >= 2) {
|
||||||
const incrementDepthSymbol = ActionByPosition(
|
ActionByPosition(
|
||||||
dimMapped,
|
dimMapped,
|
||||||
container.properties.dimensionOptions.childrenDimensions.positions,
|
container.properties.dimensionOptions.childrenDimensions.positions,
|
||||||
AddHorizontalChildrenDimension,
|
AddHorizontalChildrenDimension,
|
||||||
|
@ -145,28 +138,32 @@ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps):
|
||||||
container,
|
container,
|
||||||
currentTransform,
|
currentTransform,
|
||||||
dimensions,
|
dimensions,
|
||||||
scale,
|
scale
|
||||||
container.properties.dimensionOptions.childrenDimensions.color]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (incrementDepthSymbol) { startDepthSymbols++; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let startDepthSymbols: number = 0;
|
||||||
for (const symbol of symbols) {
|
for (const symbol of symbols) {
|
||||||
if (symbol[1].showDimension) {
|
if (symbol[1].showDimension) {
|
||||||
startDepthSymbols++;
|
startDepthSymbols++;
|
||||||
AddHorizontalSymbolDimension(symbol[1], dimensions, scale, 'black', startDepthSymbols);
|
AddHorizontalSymbolDimension(
|
||||||
|
symbol[1],
|
||||||
|
dimensions,
|
||||||
|
scale,
|
||||||
|
startDepthSymbols
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dimensions;
|
return dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AddHorizontalSymbolDimension(symbol: ISymbolModel,
|
function AddHorizontalSymbolDimension(
|
||||||
|
symbol: ISymbolModel,
|
||||||
dimensions: React.ReactNode[],
|
dimensions: React.ReactNode[],
|
||||||
scale: number,
|
scale: number,
|
||||||
color: string,
|
|
||||||
depth: number
|
depth: number
|
||||||
): void {
|
): void {
|
||||||
const width = symbol.x + (symbol.width / 2);
|
const width = symbol.x + (symbol.width / 2);
|
||||||
|
@ -177,6 +174,11 @@ function AddHorizontalSymbolDimension(symbol: ISymbolModel,
|
||||||
const text = width
|
const text = width
|
||||||
.toFixed(0)
|
.toFixed(0)
|
||||||
.toString();
|
.toString();
|
||||||
|
|
||||||
|
// TODO: Put this in default.ts
|
||||||
|
const defaultDimensionSymbolStyle: IDimensionStyle = {
|
||||||
|
color: 'black'
|
||||||
|
};
|
||||||
dimensions.push(
|
dimensions.push(
|
||||||
<Dimension
|
<Dimension
|
||||||
key={id}
|
key={id}
|
||||||
|
@ -187,7 +189,7 @@ function AddHorizontalSymbolDimension(symbol: ISymbolModel,
|
||||||
yEnd={-offset}
|
yEnd={-offset}
|
||||||
text={text}
|
text={text}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
color={color}/>
|
style={defaultDimensionSymbolStyle}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,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);
|
||||||
|
@ -266,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(
|
||||||
|
@ -276,10 +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);
|
||||||
|
@ -334,7 +336,7 @@ function AddVerticalChildrenDimension(
|
||||||
yEnd={yChildrenEnd + offset}
|
yEnd={yChildrenEnd + offset}
|
||||||
text={textChildren}
|
text={textChildren}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
color={color}
|
style={style}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,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 {
|
||||||
|
@ -392,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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -405,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 {
|
||||||
|
@ -457,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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -468,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;
|
||||||
|
@ -493,7 +495,7 @@ function AddVerticalSelfDimension(
|
||||||
yEnd={yEnd}
|
yEnd={yEnd}
|
||||||
text={textVert}
|
text={textVert}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
color={color}/>
|
style={style}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,9 +504,9 @@ function AddHorizontalSelfDimension(
|
||||||
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];
|
||||||
|
@ -522,7 +524,7 @@ function AddHorizontalSelfDimension(
|
||||||
yEnd={yDim}
|
yEnd={yDim}
|
||||||
text={text}
|
text={text}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
color={color}/>
|
style={style}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,9 +533,9 @@ function AddHorizontalSelfMarginsDimension(
|
||||||
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}`;
|
||||||
|
@ -552,7 +554,7 @@ function AddHorizontalSelfMarginsDimension(
|
||||||
yEnd={yDim}
|
yEnd={yDim}
|
||||||
text={text}
|
text={text}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
color={color}/>
|
style={style}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,7 +576,7 @@ function AddHorizontalSelfMarginsDimension(
|
||||||
yEnd={yDim}
|
yEnd={yDim}
|
||||||
text={text}
|
text={text}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
color={color}/>
|
style={style}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -585,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}`;
|
||||||
|
@ -611,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;
|
||||||
|
@ -637,7 +639,7 @@ function AddVerticalSelfMarginDimension(
|
||||||
yEnd={yEnd}
|
yEnd={yEnd}
|
||||||
text={textVert}
|
text={textVert}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
color={color}/>
|
style={style}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -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}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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
|
|
||||||
? <DepthDimensionLayer containers={props.containers} scale={scale} roots={props.children} />
|
|
||||||
: null}
|
|
||||||
<DimensionLayer containers={props.containers} symbols={props.symbols} scale={scale} root={props.children} />
|
<DimensionLayer containers={props.containers} symbols={props.symbols} 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>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
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 { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
|
||||||
import { Text } from '../Text/Text';
|
import { Text } from '../Text/Text';
|
||||||
|
@ -23,28 +23,7 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
|
||||||
const [showProperties, setShowProperties] = useState(props.isExpanded);
|
const [showProperties, setShowProperties] = useState(props.isExpanded);
|
||||||
// Render
|
// Render
|
||||||
const symbols = [...props.symbols.values()];
|
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 (
|
|
||||||
<button type="button"
|
|
||||||
className={`w-full border-blue-500 symbols-sidebar-row whitespace-pre
|
|
||||||
text-left text-sm font-medium transition-all ${selectedClass}`}
|
|
||||||
id={key}
|
|
||||||
key={key}
|
|
||||||
style={style}
|
|
||||||
onClick={() => props.selectSymbol(key)}
|
|
||||||
>
|
|
||||||
{text}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const selectedSymbol = props.symbols.get(props.selectedSymbolId);
|
const selectedSymbol = props.symbols.get(props.selectedSymbolId);
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-row h-full w-full'>
|
<div className='flex flex-row h-full w-full'>
|
||||||
|
@ -71,4 +50,27 @@ export function SymbolsSidebar(props: ISymbolsSidebarProps): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<button type="button"
|
||||||
|
className={`w-full border-blue-500 symbols-sidebar-row whitespace-pre
|
||||||
|
text-left text-sm font-medium transition-all ${selectedClass}`}
|
||||||
|
id={key}
|
||||||
|
key={key}
|
||||||
|
style={style}
|
||||||
|
onClick={() => { props.selectSymbol(key); }}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,26 +4,29 @@ import { History } from '../History/History';
|
||||||
import { Bar, BAR_WIDTH } from '../Bar/Bar';
|
import { Bar, BAR_WIDTH } from '../Bar/Bar';
|
||||||
import { Symbols } from '../Symbols/Symbols';
|
import { Symbols } from '../Symbols/Symbols';
|
||||||
import { SymbolsSidebar } from '../SymbolsList/SymbolsSidebar';
|
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 { Dispatch } from 'react';
|
||||||
|
|
||||||
export interface IUIProps {
|
export interface IUIProps {
|
||||||
editorState: IEditorState
|
editorState: IEditorState
|
||||||
|
replaceContainer: IReplaceContainer
|
||||||
selectContainer: (containerId: string) => void
|
selectContainer: (containerId: string) => void
|
||||||
deleteContainer: (containerId: string) => void
|
deleteContainer: (containerId: string) => void
|
||||||
onPropertyChange: (key: string, value: string | number | boolean | number[], type?: PropertyType) => 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
|
addContainerAt: (index: number, type: string, parent: string) => void
|
||||||
addSymbol: (type: string) => void
|
addSymbol: (type: string) => void
|
||||||
onSymbolPropertyChange: (key: string, value: string | number | boolean) => void
|
onSymbolPropertyChange: (key: string, value: string | number | boolean) => void
|
||||||
|
@ -32,6 +35,8 @@ export interface IUIProps {
|
||||||
saveEditorAsJSON: () => void
|
saveEditorAsJSON: () => void
|
||||||
saveEditorAsSVG: () => void
|
saveEditorAsSVG: () => void
|
||||||
loadState: (move: number) => void
|
loadState: (move: number) => void
|
||||||
|
setReplaceContainer: Dispatch<React.SetStateAction<IReplaceContainer>>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SidebarType {
|
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>(SidebarType.Components);
|
const [selectedSidebar, setSelectedSidebar] = React.useState<SidebarType>(SidebarType.Components);
|
||||||
|
|
||||||
const [messages, setMessages] = React.useState<IMessage[]>([]);
|
const [messages, setMessages] = React.useState<IMessage[]>([]);
|
||||||
|
@ -96,16 +101,19 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
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:
|
||||||
leftSidebarTitle = Text({ textId: '@Components' });
|
leftSidebarTitle = Text({ textId: '@Components' });
|
||||||
|
|
||||||
leftChildren = <Components
|
leftChildren = <Components
|
||||||
selectedContainer={selectedContainer}
|
selectedContainer={selectedContainer}
|
||||||
componentOptions={configuration.AvailableContainers}
|
componentOptions={configuration.AvailableContainers}
|
||||||
categories={configuration.Categories}
|
categories={configuration.Categories}
|
||||||
buttonOnClick={methods.addContainer}
|
buttonOnClick={methods.addOrReplaceContainer}
|
||||||
/>;
|
replaceContainer={replaceContainer}
|
||||||
|
setReplaceContainer={setReplaceContainer}/>;
|
||||||
rightSidebarTitle = Text({ textId: '@Elements' });
|
rightSidebarTitle = Text({ textId: '@Elements' });
|
||||||
rightChildren = <ElementsSideBar
|
rightChildren = <ElementsSideBar
|
||||||
containers={current.containers}
|
containers={current.containers}
|
||||||
|
@ -116,7 +124,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
selectContainer={methods.selectContainer}
|
selectContainer={methods.selectContainer}
|
||||||
addContainer={methods.addContainerAt}
|
addContainer={methods.addContainerAt}
|
||||||
isExpanded ={false}
|
isExpanded ={false}
|
||||||
onExpandChange={() => setOrToggleSidebar(SidebarType.ComponentsExpanded) }
|
onExpandChange={() => { setOrToggleSidebar(SidebarType.ComponentsExpanded); } }
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
case SidebarType.ComponentsExpanded:
|
case SidebarType.ComponentsExpanded:
|
||||||
|
@ -125,8 +133,9 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
selectedContainer={selectedContainer}
|
selectedContainer={selectedContainer}
|
||||||
componentOptions={configuration.AvailableContainers}
|
componentOptions={configuration.AvailableContainers}
|
||||||
categories={configuration.Categories}
|
categories={configuration.Categories}
|
||||||
buttonOnClick={methods.addContainer}
|
buttonOnClick={methods.addOrReplaceContainer}
|
||||||
/>;
|
replaceContainer={replaceContainer}
|
||||||
|
setReplaceContainer={setReplaceContainer}/>;
|
||||||
rightSidebarTitle = Text({ textId: '@Elements' });
|
rightSidebarTitle = Text({ textId: '@Elements' });
|
||||||
rightChildren = <ElementsSideBar
|
rightChildren = <ElementsSideBar
|
||||||
containers={current.containers}
|
containers={current.containers}
|
||||||
|
@ -137,7 +146,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
selectContainer={methods.selectContainer}
|
selectContainer={methods.selectContainer}
|
||||||
addContainer={methods.addContainerAt}
|
addContainer={methods.addContainerAt}
|
||||||
isExpanded ={true}
|
isExpanded ={true}
|
||||||
onExpandChange={() => setOrToggleSidebar(SidebarType.Components) }
|
onExpandChange={() => { setOrToggleSidebar(SidebarType.Components); } }
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
case SidebarType.Symbols:
|
case SidebarType.Symbols:
|
||||||
|
@ -153,7 +162,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
onPropertyChange={methods.onSymbolPropertyChange}
|
onPropertyChange={methods.onSymbolPropertyChange}
|
||||||
selectSymbol={methods.selectSymbol}
|
selectSymbol={methods.selectSymbol}
|
||||||
isExpanded ={false}
|
isExpanded ={false}
|
||||||
onExpandChange={() => setOrToggleSidebar(SidebarType.SymbolsExpanded) }
|
onExpandChange={() => { setOrToggleSidebar(SidebarType.SymbolsExpanded); } }
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
case SidebarType.SymbolsExpanded:
|
case SidebarType.SymbolsExpanded:
|
||||||
|
@ -169,7 +178,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
onPropertyChange={methods.onSymbolPropertyChange}
|
onPropertyChange={methods.onSymbolPropertyChange}
|
||||||
selectSymbol={methods.selectSymbol}
|
selectSymbol={methods.selectSymbol}
|
||||||
isExpanded ={true}
|
isExpanded ={true}
|
||||||
onExpandChange={() => setOrToggleSidebar(SidebarType.Symbols)}
|
onExpandChange={() => { setOrToggleSidebar(SidebarType.Symbols); }}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -187,7 +196,7 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
leftChildren = <Messages
|
leftChildren = <Messages
|
||||||
historyState={current}
|
historyState={current}
|
||||||
messages={messages}
|
messages={messages}
|
||||||
clearMessage={() => setMessages([])}
|
clearMessage={() => { setMessages([]); }}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -242,11 +251,16 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
isLeftSidebarOpenClasses.add('left-sidebar-single');
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Bar
|
<Bar
|
||||||
isComponentsOpen={selectedSidebar === SidebarType.Components || selectedSidebar === SidebarType.ComponentsExpanded}
|
className={clickRestrictionsClasses}
|
||||||
isSymbolsOpen={selectedSidebar === SidebarType.Symbols || selectedSidebar === SidebarType.SymbolsExpanded}
|
isComponentsOpen={isComponentsOpen}
|
||||||
|
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}
|
||||||
|
@ -281,14 +295,17 @@ export function UI({ editorState, ...methods }: IUIProps): JSX.Element {
|
||||||
{ leftChildren }
|
{ leftChildren }
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<Viewer
|
<Viewer
|
||||||
className={`${[...viewerMarginClasses.values()].join(' ')} w-full h-full`}
|
className={`${clickRestrictionsClasses} ${[...viewerMarginClasses.values()].join(' ')} w-full h-full`}
|
||||||
current={current}
|
current={current}
|
||||||
|
isComponentsOpen={isComponentsOpen}
|
||||||
|
isSymbolsOpen={isSymbolsOpen}
|
||||||
selectedContainer={selectedContainer}
|
selectedContainer={selectedContainer}
|
||||||
selectContainer={methods.selectContainer}
|
selectContainer={methods.selectContainer}
|
||||||
|
selectedSymbol={selectedSymbol}
|
||||||
margin={marginSidebar}
|
margin={marginSidebar}
|
||||||
/>
|
/>
|
||||||
<Sidebar
|
<Sidebar
|
||||||
className={`right-sidebar ${isRightSidebarOpenClasses}`}
|
className={`right-sidebar ${isRightSidebarOpenClasses} ${clickRestrictionsClasses}`}
|
||||||
title={rightSidebarTitle}
|
title={rightSidebarTitle}
|
||||||
>
|
>
|
||||||
{ rightChildren }
|
{ rightChildren }
|
||||||
|
|
|
@ -1,23 +1,27 @@
|
||||||
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 { useState } from 'react';
|
||||||
|
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
|
||||||
|
|
||||||
interface IViewerProps {
|
interface IViewerProps {
|
||||||
className?: string
|
className: string
|
||||||
current: IHistoryState
|
current: IHistoryState
|
||||||
selectedContainer: IContainerModel | undefined
|
selectedContainer: IContainerModel | undefined
|
||||||
selectContainer: (containerId: string) => void
|
selectContainer: (containerId: string) => void
|
||||||
|
selectedSymbol: ISymbolModel | undefined
|
||||||
margin: number
|
margin: number
|
||||||
|
isComponentsOpen: boolean
|
||||||
|
isSymbolsOpen: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Viewer({
|
export function Viewer({
|
||||||
|
@ -25,7 +29,10 @@ export function Viewer({
|
||||||
current,
|
current,
|
||||||
selectedContainer,
|
selectedContainer,
|
||||||
selectContainer,
|
selectContainer,
|
||||||
margin
|
selectedSymbol,
|
||||||
|
margin,
|
||||||
|
isComponentsOpen,
|
||||||
|
isSymbolsOpen
|
||||||
}: IViewerProps): JSX.Element {
|
}: IViewerProps): JSX.Element {
|
||||||
function computeWidth(margin: number): number {
|
function computeWidth(margin: number): number {
|
||||||
return window.innerWidth - (window.innerWidth < 768 ? BAR_WIDTH : margin);
|
return window.innerWidth - (window.innerWidth < 768 ? BAR_WIDTH : margin);
|
||||||
|
@ -64,6 +71,13 @@ export function Viewer({
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let selectorMode = SelectorMode.Nothing;
|
||||||
|
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 {
|
||||||
if (mainContainer === undefined) {
|
if (mainContainer === undefined) {
|
||||||
|
@ -119,7 +133,7 @@ export function Viewer({
|
||||||
return (
|
return (
|
||||||
<Canvas
|
<Canvas
|
||||||
draw={Draw}
|
draw={Draw}
|
||||||
className='ml-16'
|
className={`ml-16 ${className}`}
|
||||||
width={window.innerWidth - BAR_WIDTH}
|
width={window.innerWidth - BAR_WIDTH}
|
||||||
height={window.innerHeight}
|
height={window.innerHeight}
|
||||||
/>
|
/>
|
||||||
|
@ -134,11 +148,12 @@ export function Viewer({
|
||||||
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}
|
||||||
</SVG>
|
</SVG>
|
||||||
);
|
);
|
||||||
|
|
|
@ -27,6 +27,6 @@ export enum PropertyType {
|
||||||
SelfMarginDimension,
|
SelfMarginDimension,
|
||||||
ChildrenDimensions,
|
ChildrenDimensions,
|
||||||
DimensionWithMarks,
|
DimensionWithMarks,
|
||||||
DimensionOptions
|
|
||||||
|
|
||||||
|
DimensionOptions
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
5
src/Interfaces/IReplaceContainer.ts
Normal file
5
src/Interfaces/IReplaceContainer.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export interface IReplaceContainer {
|
||||||
|
id: string | undefined
|
||||||
|
isReplacing: boolean
|
||||||
|
category: string | undefined
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { IAvailableSymbol } from './IAvailableSymbol';
|
import { type IAvailableSymbol } from './IAvailableSymbol';
|
||||||
|
|
||||||
export interface ISymbolModel {
|
export interface ISymbolModel {
|
||||||
/** Identifier */
|
/** Identifier */
|
||||||
|
|
|
@ -59,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",
|
||||||
|
|
|
@ -63,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é",
|
||||||
|
|
|
@ -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 ?? []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -317,3 +311,10 @@ export function GetDefaultSymbolModel(name: string,
|
||||||
showDimension: false
|
showDimension: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Macro function for JSON.parse(JSON.stringify(obj))
|
||||||
|
*/
|
||||||
|
function clone<T>(object: T): T {
|
||||||
|
return JSON.parse(JSON.stringify(object));
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue