Compare commits
No commits in common. "39e74c69f55204f140666d7d4619331bb0461e7c" and "7f7ec4b921f4e80c7258d3a712ee02423a254ced" have entirely different histories.
39e74c69f5
...
7f7ec4b921
61 changed files with 1135 additions and 1388 deletions
|
@ -19,7 +19,6 @@ module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
'only-warn',
|
'only-warn',
|
||||||
'react',
|
'react',
|
||||||
'react-hooks',
|
|
||||||
'@typescript-eslint'
|
'@typescript-eslint'
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
|
@ -30,7 +29,5 @@ module.exports = {
|
||||||
'@typescript-eslint/semi': ['warn', 'always'],
|
'@typescript-eslint/semi': ['warn', 'always'],
|
||||||
'no-unused-vars': 'off',
|
'no-unused-vars': 'off',
|
||||||
'@typescript-eslint/no-unused-vars': 'error',
|
'@typescript-eslint/no-unused-vars': 'error',
|
||||||
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
|
|
||||||
'react-hooks/exhaustive-deps': 'warn' // Checks effect dependencies
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -1 +0,0 @@
|
||||||
*.drawio filter=lfs diff=lfs merge=lfs -text
|
|
BIN
docs/ComponentStructure.drawio
(Stored with Git LFS)
BIN
docs/ComponentStructure.drawio
(Stored with Git LFS)
Binary file not shown.
|
@ -3,7 +3,6 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Vite + React + TS</title>
|
<title>Vite + React + TS</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
469
package-lock.json
generated
469
package-lock.json
generated
|
@ -9,20 +9,18 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@heroicons/react": "^1.0.6",
|
"@heroicons/react": "^1.0.6",
|
||||||
|
"framer-motion": "^6.5.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-svg-pan-zoom": "^3.11.0",
|
"react-svg-pan-zoom": "^3.11.0"
|
||||||
"react-window": "^1.8.7"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/dom": "^8.16.1",
|
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@testing-library/react": "^13.3.0",
|
"@testing-library/react": "^13.3.0",
|
||||||
"@testing-library/user-event": "^14.4.1",
|
"@testing-library/user-event": "^14.4.1",
|
||||||
"@types/react": "^18.0.15",
|
"@types/react": "^18.0.15",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/react-svg-pan-zoom": "^3.3.5",
|
"@types/react-svg-pan-zoom": "^3.3.5",
|
||||||
"@types/react-window": "^1.8.5",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.31.0",
|
"@typescript-eslint/eslint-plugin": "^5.31.0",
|
||||||
"@typescript-eslint/parser": "^5.31.0",
|
"@typescript-eslint/parser": "^5.31.0",
|
||||||
"@vitejs/plugin-react": "^2.0.0",
|
"@vitejs/plugin-react": "^2.0.0",
|
||||||
|
@ -33,10 +31,8 @@
|
||||||
"eslint-config-standard-with-typescript": "^22.0.0",
|
"eslint-config-standard-with-typescript": "^22.0.0",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-n": "^15.2.4",
|
"eslint-plugin-n": "^15.2.4",
|
||||||
"eslint-plugin-only-warn": "^1.0.3",
|
|
||||||
"eslint-plugin-promise": "^6.0.0",
|
"eslint-plugin-promise": "^6.0.0",
|
||||||
"eslint-plugin-react": "^7.30.1",
|
"eslint-plugin-react": "^7.30.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
|
||||||
"jsdom": "^20.0.0",
|
"jsdom": "^20.0.0",
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.14",
|
||||||
"sass": "^1.54.0",
|
"sass": "^1.54.0",
|
||||||
|
@ -425,6 +421,7 @@
|
||||||
"version": "7.18.9",
|
"version": "7.18.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
|
||||||
"integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
|
"integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"regenerator-runtime": "^0.13.4"
|
"regenerator-runtime": "^0.13.4"
|
||||||
},
|
},
|
||||||
|
@ -480,6 +477,21 @@
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@emotion/is-prop-valid": {
|
||||||
|
"version": "0.8.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
|
||||||
|
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/memoize": "0.7.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@emotion/memoize": {
|
||||||
|
"version": "0.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
||||||
|
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/@eslint/eslintrc": {
|
"node_modules/@eslint/eslintrc": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
|
||||||
|
@ -602,6 +614,89 @@
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@motionone/animation": {
|
||||||
|
"version": "10.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.13.1.tgz",
|
||||||
|
"integrity": "sha512-dxQ+1wWxL6iFHDy1uv6hhcPjIdOg36eDT56jN4LI7Z5HZRyLpq8x1t7JFQclo/IEIb+6Bk4atmyinGFdXVECuA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@motionone/easing": "^10.13.1",
|
||||||
|
"@motionone/types": "^10.13.0",
|
||||||
|
"@motionone/utils": "^10.13.1",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@motionone/animation/node_modules/tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@motionone/dom": {
|
||||||
|
"version": "10.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz",
|
||||||
|
"integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@motionone/animation": "^10.12.0",
|
||||||
|
"@motionone/generators": "^10.12.0",
|
||||||
|
"@motionone/types": "^10.12.0",
|
||||||
|
"@motionone/utils": "^10.12.0",
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@motionone/dom/node_modules/tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@motionone/easing": {
|
||||||
|
"version": "10.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.13.1.tgz",
|
||||||
|
"integrity": "sha512-INEsInHHDHVgx0dp5qlXi1lMXBqYicgLMMSn3zfGzaIvcaEbI1Uz8BoyNV4BiclTupG7RYIh+T6BU83ZcEe74g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@motionone/utils": "^10.13.1",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@motionone/easing/node_modules/tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@motionone/generators": {
|
||||||
|
"version": "10.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.13.1.tgz",
|
||||||
|
"integrity": "sha512-+HK5u2YcNJCckTTqfOLgSVcrWv2z1dVwrSZEMVJuAh0EnWEWGDJRvMBoPc0cFf/osbkA2Rq9bH2+vP0Ex/D8uw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@motionone/types": "^10.13.0",
|
||||||
|
"@motionone/utils": "^10.13.1",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@motionone/generators/node_modules/tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@motionone/types": {
|
||||||
|
"version": "10.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.13.0.tgz",
|
||||||
|
"integrity": "sha512-qegk4qg8U1N9ZwAJ187BG3TkZz1k9LP/pvNtCSlqdq/PMUDKlCFG4ZnjJ481P0IOH/vIw1OzIbKIuyg0A3rk9g=="
|
||||||
|
},
|
||||||
|
"node_modules/@motionone/utils": {
|
||||||
|
"version": "10.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.13.1.tgz",
|
||||||
|
"integrity": "sha512-TjDPTIppaf3ofBXQv4ZzAketJgN0sclALXfZ6mfrkjJkOy83mLls9744F+6S+VKCpBmvbZcBY4PQfrfhAfeMtA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@motionone/types": "^10.13.0",
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@motionone/utils/node_modules/tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
@ -650,9 +745,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "8.17.1",
|
"version": "8.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.16.0.tgz",
|
||||||
"integrity": "sha512-KnH2MnJUzmFNPW6RIKfd+zf2Wue8mEKX0M3cpX6aKl5ZXrJM1/c/Pc8c2xDNYQCnJO48Sm5ITbMXgqTr3h4jxQ==",
|
"integrity": "sha512-uxF4zmnLHHDlmW4l+0WDjcgLVwCvH+OVLpD8Dfp+Bjfz85prwxWGbwXgJdLtkgjD0qfOzkJF9SmA6YZPsMYX4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.10.4",
|
"@babel/code-frame": "^7.10.4",
|
||||||
|
@ -984,15 +1079,6 @@
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/react-window": {
|
|
||||||
"version": "1.8.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.5.tgz",
|
|
||||||
"integrity": "sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/react": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/scheduler": {
|
"node_modules/@types/scheduler": {
|
||||||
"version": "0.16.2",
|
"version": "0.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
|
@ -2793,15 +2879,6 @@
|
||||||
"eslint": ">=7.0.0"
|
"eslint": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-only-warn": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-only-warn/-/eslint-plugin-only-warn-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-XQOX/TfLoLw6h8ky51d29uUjXRTQHqBGXPylDEmy5fe/w7LIOnp8MA24b1OSMEn9BQoKow1q3g1kLe5/9uBTvw==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eslint-plugin-promise": {
|
"node_modules/eslint-plugin-promise": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz",
|
||||||
|
@ -2842,18 +2919,6 @@
|
||||||
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
|
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-react-hooks": {
|
|
||||||
"version": "4.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
|
|
||||||
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eslint-plugin-react/node_modules/doctrine": {
|
"node_modules/eslint-plugin-react/node_modules/doctrine": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
||||||
|
@ -3266,6 +3331,44 @@
|
||||||
"url": "https://www.patreon.com/infusion"
|
"url": "https://www.patreon.com/infusion"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@motionone/dom": "10.12.0",
|
||||||
|
"framesync": "6.0.1",
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"popmotion": "11.0.3",
|
||||||
|
"style-value-types": "5.0.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "^0.8.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": ">=16.8 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/framer-motion/node_modules/tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
},
|
||||||
|
"node_modules/framesync": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/framesync/node_modules/tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
@ -3503,6 +3606,11 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hey-listen": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="
|
||||||
|
},
|
||||||
"node_modules/html-encoding-sniffer": {
|
"node_modules/html-encoding-sniffer": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
||||||
|
@ -4320,11 +4428,6 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/memoize-one": {
|
|
||||||
"version": "5.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
|
||||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
|
||||||
},
|
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
@ -4733,6 +4836,22 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/popmotion": {
|
||||||
|
"version": "11.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz",
|
||||||
|
"integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==",
|
||||||
|
"dependencies": {
|
||||||
|
"framesync": "6.0.1",
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"style-value-types": "5.0.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/popmotion/node_modules/tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.14",
|
"version": "8.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
||||||
|
@ -5010,22 +5129,6 @@
|
||||||
"react": ">=17.0.0"
|
"react": ">=17.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-window": {
|
|
||||||
"version": "1.8.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.7.tgz",
|
|
||||||
"integrity": "sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.0.0",
|
|
||||||
"memoize-one": ">=3.1.1 <6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">8.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
|
|
||||||
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
|
@ -5063,7 +5166,8 @@
|
||||||
"node_modules/regenerator-runtime": {
|
"node_modules/regenerator-runtime": {
|
||||||
"version": "0.13.9",
|
"version": "0.13.9",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
||||||
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
|
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/regexp.prototype.flags": {
|
"node_modules/regexp.prototype.flags": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
|
@ -5432,6 +5536,20 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/style-value-types": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==",
|
||||||
|
"dependencies": {
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/style-value-types/node_modules/tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
},
|
||||||
"node_modules/supports-color": {
|
"node_modules/supports-color": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
@ -6301,6 +6419,7 @@
|
||||||
"version": "7.18.9",
|
"version": "7.18.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
|
||||||
"integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
|
"integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"regenerator-runtime": "^0.13.4"
|
"regenerator-runtime": "^0.13.4"
|
||||||
}
|
}
|
||||||
|
@ -6344,6 +6463,21 @@
|
||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"version": "0.8.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
|
||||||
|
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"@emotion/memoize": "0.7.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@emotion/memoize": {
|
||||||
|
"version": "0.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
||||||
|
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"@eslint/eslintrc": {
|
"@eslint/eslintrc": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
|
||||||
|
@ -6442,6 +6576,99 @@
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@motionone/animation": {
|
||||||
|
"version": "10.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.13.1.tgz",
|
||||||
|
"integrity": "sha512-dxQ+1wWxL6iFHDy1uv6hhcPjIdOg36eDT56jN4LI7Z5HZRyLpq8x1t7JFQclo/IEIb+6Bk4atmyinGFdXVECuA==",
|
||||||
|
"requires": {
|
||||||
|
"@motionone/easing": "^10.13.1",
|
||||||
|
"@motionone/types": "^10.13.0",
|
||||||
|
"@motionone/utils": "^10.13.1",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@motionone/dom": {
|
||||||
|
"version": "10.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz",
|
||||||
|
"integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==",
|
||||||
|
"requires": {
|
||||||
|
"@motionone/animation": "^10.12.0",
|
||||||
|
"@motionone/generators": "^10.12.0",
|
||||||
|
"@motionone/types": "^10.12.0",
|
||||||
|
"@motionone/utils": "^10.12.0",
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@motionone/easing": {
|
||||||
|
"version": "10.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.13.1.tgz",
|
||||||
|
"integrity": "sha512-INEsInHHDHVgx0dp5qlXi1lMXBqYicgLMMSn3zfGzaIvcaEbI1Uz8BoyNV4BiclTupG7RYIh+T6BU83ZcEe74g==",
|
||||||
|
"requires": {
|
||||||
|
"@motionone/utils": "^10.13.1",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@motionone/generators": {
|
||||||
|
"version": "10.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.13.1.tgz",
|
||||||
|
"integrity": "sha512-+HK5u2YcNJCckTTqfOLgSVcrWv2z1dVwrSZEMVJuAh0EnWEWGDJRvMBoPc0cFf/osbkA2Rq9bH2+vP0Ex/D8uw==",
|
||||||
|
"requires": {
|
||||||
|
"@motionone/types": "^10.13.0",
|
||||||
|
"@motionone/utils": "^10.13.1",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@motionone/types": {
|
||||||
|
"version": "10.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.13.0.tgz",
|
||||||
|
"integrity": "sha512-qegk4qg8U1N9ZwAJ187BG3TkZz1k9LP/pvNtCSlqdq/PMUDKlCFG4ZnjJ481P0IOH/vIw1OzIbKIuyg0A3rk9g=="
|
||||||
|
},
|
||||||
|
"@motionone/utils": {
|
||||||
|
"version": "10.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.13.1.tgz",
|
||||||
|
"integrity": "sha512-TjDPTIppaf3ofBXQv4ZzAketJgN0sclALXfZ6mfrkjJkOy83mLls9744F+6S+VKCpBmvbZcBY4PQfrfhAfeMtA==",
|
||||||
|
"requires": {
|
||||||
|
"@motionone/types": "^10.13.0",
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@nodelib/fs.scandir": {
|
"@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
@ -6481,9 +6708,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@testing-library/dom": {
|
"@testing-library/dom": {
|
||||||
"version": "8.17.1",
|
"version": "8.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.16.0.tgz",
|
||||||
"integrity": "sha512-KnH2MnJUzmFNPW6RIKfd+zf2Wue8mEKX0M3cpX6aKl5ZXrJM1/c/Pc8c2xDNYQCnJO48Sm5ITbMXgqTr3h4jxQ==",
|
"integrity": "sha512-uxF4zmnLHHDlmW4l+0WDjcgLVwCvH+OVLpD8Dfp+Bjfz85prwxWGbwXgJdLtkgjD0qfOzkJF9SmA6YZPsMYX4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/code-frame": "^7.10.4",
|
"@babel/code-frame": "^7.10.4",
|
||||||
|
@ -6749,15 +6976,6 @@
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/react-window": {
|
|
||||||
"version": "1.8.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.5.tgz",
|
|
||||||
"integrity": "sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/react": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/scheduler": {
|
"@types/scheduler": {
|
||||||
"version": "0.16.2",
|
"version": "0.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
|
@ -8036,12 +8254,6 @@
|
||||||
"semver": "^7.3.7"
|
"semver": "^7.3.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-plugin-only-warn": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-only-warn/-/eslint-plugin-only-warn-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-XQOX/TfLoLw6h8ky51d29uUjXRTQHqBGXPylDEmy5fe/w7LIOnp8MA24b1OSMEn9BQoKow1q3g1kLe5/9uBTvw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"eslint-plugin-promise": {
|
"eslint-plugin-promise": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz",
|
||||||
|
@ -8099,13 +8311,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-plugin-react-hooks": {
|
|
||||||
"version": "4.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
|
|
||||||
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"eslint-scope": {
|
"eslint-scope": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||||
|
@ -8305,6 +8510,42 @@
|
||||||
"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
|
"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"framer-motion": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==",
|
||||||
|
"requires": {
|
||||||
|
"@emotion/is-prop-valid": "^0.8.2",
|
||||||
|
"@motionone/dom": "10.12.0",
|
||||||
|
"framesync": "6.0.1",
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"popmotion": "11.0.3",
|
||||||
|
"style-value-types": "5.0.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"framesync": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
@ -8469,6 +8710,11 @@
|
||||||
"has-symbols": "^1.0.2"
|
"has-symbols": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"hey-listen": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="
|
||||||
|
},
|
||||||
"html-encoding-sniffer": {
|
"html-encoding-sniffer": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
||||||
|
@ -9067,11 +9313,6 @@
|
||||||
"sourcemap-codec": "^1.4.8"
|
"sourcemap-codec": "^1.4.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"memoize-one": {
|
|
||||||
"version": "5.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
|
||||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
|
||||||
},
|
|
||||||
"merge2": {
|
"merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
@ -9369,6 +9610,24 @@
|
||||||
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"popmotion": {
|
||||||
|
"version": "11.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz",
|
||||||
|
"integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==",
|
||||||
|
"requires": {
|
||||||
|
"framesync": "6.0.1",
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"style-value-types": "5.0.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"postcss": {
|
"postcss": {
|
||||||
"version": "8.4.14",
|
"version": "8.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
||||||
|
@ -9537,15 +9796,6 @@
|
||||||
"transformation-matrix": "^2.11.1"
|
"transformation-matrix": "^2.11.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-window": {
|
|
||||||
"version": "1.8.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.7.tgz",
|
|
||||||
"integrity": "sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.0.0",
|
|
||||||
"memoize-one": ">=3.1.1 <6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"read-cache": {
|
"read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
|
@ -9577,7 +9827,8 @@
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.9",
|
"version": "0.13.9",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
||||||
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
|
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"regexp.prototype.flags": {
|
"regexp.prototype.flags": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
|
@ -9834,6 +10085,22 @@
|
||||||
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"style-value-types": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==",
|
||||||
|
"requires": {
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@heroicons/react": "^1.0.6",
|
"@heroicons/react": "^1.0.6",
|
||||||
|
"framer-motion": "^6.5.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-svg-pan-zoom": "^3.11.0",
|
"react-svg-pan-zoom": "^3.11.0"
|
||||||
"react-window": "^1.8.7"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/dom": "^8.16.1",
|
"@testing-library/dom": "^8.16.1",
|
||||||
|
@ -27,7 +27,6 @@
|
||||||
"@types/react": "^18.0.15",
|
"@types/react": "^18.0.15",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/react-svg-pan-zoom": "^3.3.5",
|
"@types/react-svg-pan-zoom": "^3.3.5",
|
||||||
"@types/react-window": "^1.8.5",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.31.0",
|
"@typescript-eslint/eslint-plugin": "^5.31.0",
|
||||||
"@typescript-eslint/parser": "^5.31.0",
|
"@typescript-eslint/parser": "^5.31.0",
|
||||||
"@vitejs/plugin-react": "^2.0.0",
|
"@vitejs/plugin-react": "^2.0.0",
|
||||||
|
@ -41,7 +40,6 @@
|
||||||
"eslint-plugin-only-warn": "^1.0.3",
|
"eslint-plugin-only-warn": "^1.0.3",
|
||||||
"eslint-plugin-promise": "^6.0.0",
|
"eslint-plugin-promise": "^6.0.0",
|
||||||
"eslint-plugin-react": "^7.30.1",
|
"eslint-plugin-react": "^7.30.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
|
||||||
"jsdom": "^20.0.0",
|
"jsdom": "^20.0.0",
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.14",
|
||||||
"sass": "^1.54.0",
|
"sass": "^1.54.0",
|
||||||
|
|
150
pnpm-lock.yaml
generated
150
pnpm-lock.yaml
generated
|
@ -9,7 +9,6 @@ specifiers:
|
||||||
'@types/react': ^18.0.15
|
'@types/react': ^18.0.15
|
||||||
'@types/react-dom': ^18.0.6
|
'@types/react-dom': ^18.0.6
|
||||||
'@types/react-svg-pan-zoom': ^3.3.5
|
'@types/react-svg-pan-zoom': ^3.3.5
|
||||||
'@types/react-window': ^1.8.5
|
|
||||||
'@typescript-eslint/eslint-plugin': ^5.31.0
|
'@typescript-eslint/eslint-plugin': ^5.31.0
|
||||||
'@typescript-eslint/parser': ^5.31.0
|
'@typescript-eslint/parser': ^5.31.0
|
||||||
'@vitejs/plugin-react': ^2.0.0
|
'@vitejs/plugin-react': ^2.0.0
|
||||||
|
@ -23,13 +22,12 @@ specifiers:
|
||||||
eslint-plugin-only-warn: ^1.0.3
|
eslint-plugin-only-warn: ^1.0.3
|
||||||
eslint-plugin-promise: ^6.0.0
|
eslint-plugin-promise: ^6.0.0
|
||||||
eslint-plugin-react: ^7.30.1
|
eslint-plugin-react: ^7.30.1
|
||||||
eslint-plugin-react-hooks: ^4.6.0
|
framer-motion: ^6.5.1
|
||||||
jsdom: ^20.0.0
|
jsdom: ^20.0.0
|
||||||
postcss: ^8.4.14
|
postcss: ^8.4.14
|
||||||
react: ^18.2.0
|
react: ^18.2.0
|
||||||
react-dom: ^18.2.0
|
react-dom: ^18.2.0
|
||||||
react-svg-pan-zoom: ^3.11.0
|
react-svg-pan-zoom: ^3.11.0
|
||||||
react-window: ^1.8.7
|
|
||||||
sass: ^1.54.0
|
sass: ^1.54.0
|
||||||
tailwindcss: ^3.1.7
|
tailwindcss: ^3.1.7
|
||||||
typescript: ^4.6.4
|
typescript: ^4.6.4
|
||||||
|
@ -38,10 +36,10 @@ specifiers:
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@heroicons/react': 1.0.6_react@18.2.0
|
'@heroicons/react': 1.0.6_react@18.2.0
|
||||||
|
framer-motion: 6.5.1_biqbaboplfbrettd7655fr4n2y
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
react-svg-pan-zoom: 3.11.0_react@18.2.0
|
react-svg-pan-zoom: 3.11.0_react@18.2.0
|
||||||
react-window: 1.8.7_biqbaboplfbrettd7655fr4n2y
|
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@testing-library/dom': 8.16.1
|
'@testing-library/dom': 8.16.1
|
||||||
|
@ -51,7 +49,6 @@ devDependencies:
|
||||||
'@types/react': 18.0.17
|
'@types/react': 18.0.17
|
||||||
'@types/react-dom': 18.0.6
|
'@types/react-dom': 18.0.6
|
||||||
'@types/react-svg-pan-zoom': 3.3.5
|
'@types/react-svg-pan-zoom': 3.3.5
|
||||||
'@types/react-window': 1.8.5
|
|
||||||
'@typescript-eslint/eslint-plugin': 5.32.0_iosr3hrei2tubxveewluhu5lhy
|
'@typescript-eslint/eslint-plugin': 5.32.0_iosr3hrei2tubxveewluhu5lhy
|
||||||
'@typescript-eslint/parser': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq
|
'@typescript-eslint/parser': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq
|
||||||
'@vitejs/plugin-react': 2.0.0_vite@3.0.4
|
'@vitejs/plugin-react': 2.0.0_vite@3.0.4
|
||||||
|
@ -65,7 +62,6 @@ devDependencies:
|
||||||
eslint-plugin-only-warn: 1.0.3
|
eslint-plugin-only-warn: 1.0.3
|
||||||
eslint-plugin-promise: 6.0.0_eslint@8.21.0
|
eslint-plugin-promise: 6.0.0_eslint@8.21.0
|
||||||
eslint-plugin-react: 7.30.1_eslint@8.21.0
|
eslint-plugin-react: 7.30.1_eslint@8.21.0
|
||||||
eslint-plugin-react-hooks: 4.6.0_eslint@8.21.0
|
|
||||||
jsdom: 20.0.0
|
jsdom: 20.0.0
|
||||||
postcss: 8.4.16
|
postcss: 8.4.16
|
||||||
sass: 1.54.3
|
sass: 1.54.3
|
||||||
|
@ -316,6 +312,7 @@ packages:
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime: 0.13.9
|
regenerator-runtime: 0.13.9
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/template/7.18.10:
|
/@babel/template/7.18.10:
|
||||||
resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==}
|
resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==}
|
||||||
|
@ -353,6 +350,19 @@ packages:
|
||||||
to-fast-properties: 2.0.0
|
to-fast-properties: 2.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@emotion/is-prop-valid/0.8.8:
|
||||||
|
resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==}
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
'@emotion/memoize': 0.7.4
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@emotion/memoize/0.7.4:
|
||||||
|
resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-loong64/0.14.54:
|
/@esbuild/linux-loong64/0.14.54:
|
||||||
resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==}
|
resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
@ -451,6 +461,53 @@ packages:
|
||||||
'@jridgewell/sourcemap-codec': 1.4.14
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@motionone/animation/10.13.2:
|
||||||
|
resolution: {integrity: sha512-YGWss58IR2X4lOjW89rv1Q+/Nq/QhfltaggI7i8sZTpKC1yUvM+XYDdvlRpWc6dk8LviMBrddBJAlLdbaqeRmw==}
|
||||||
|
dependencies:
|
||||||
|
'@motionone/easing': 10.13.2
|
||||||
|
'@motionone/types': 10.13.2
|
||||||
|
'@motionone/utils': 10.13.2
|
||||||
|
tslib: 2.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@motionone/dom/10.12.0:
|
||||||
|
resolution: {integrity: sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==}
|
||||||
|
dependencies:
|
||||||
|
'@motionone/animation': 10.13.2
|
||||||
|
'@motionone/generators': 10.13.2
|
||||||
|
'@motionone/types': 10.13.2
|
||||||
|
'@motionone/utils': 10.13.2
|
||||||
|
hey-listen: 1.0.8
|
||||||
|
tslib: 2.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@motionone/easing/10.13.2:
|
||||||
|
resolution: {integrity: sha512-3HqctS5NyDfDQ+8+cZqc3Pu7I6amFCt9zDUjcozHyFXHh4PKYHK4+GJDFjJIS8bCAF2BrJmpmduDQ2V7lFEYeQ==}
|
||||||
|
dependencies:
|
||||||
|
'@motionone/utils': 10.13.2
|
||||||
|
tslib: 2.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@motionone/generators/10.13.2:
|
||||||
|
resolution: {integrity: sha512-QMoXV1MXEEhR6D3dct/RMMS1FwJlAsW+kMPbFGzBA4NbweblgeYQCft9DcDAVpV9wIwD6qvlBG9u99sOXLfHiA==}
|
||||||
|
dependencies:
|
||||||
|
'@motionone/types': 10.13.2
|
||||||
|
'@motionone/utils': 10.13.2
|
||||||
|
tslib: 2.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@motionone/types/10.13.2:
|
||||||
|
resolution: {integrity: sha512-yYV4q5v5F0iADhab4wHfqaRJnM/eVtQLjUPhyEcS72aUz/xyOzi09GzD/Gu+K506BDfqn5eULIilUI77QNaqhw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@motionone/utils/10.13.2:
|
||||||
|
resolution: {integrity: sha512-6Lw5bDA/w7lrPmT/jYWQ76lkHlHs9fl2NZpJ22cVy1kKDdEH+Cl1U6hMTpdphO6VQktQ6v2APngag91WBKLqlA==}
|
||||||
|
dependencies:
|
||||||
|
'@motionone/types': 10.13.2
|
||||||
|
hey-listen: 1.0.8
|
||||||
|
tslib: 2.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@nodelib/fs.scandir/2.1.5:
|
/@nodelib/fs.scandir/2.1.5:
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
@ -586,12 +643,6 @@ packages:
|
||||||
'@types/react': 18.0.17
|
'@types/react': 18.0.17
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/react-window/1.8.5:
|
|
||||||
resolution: {integrity: sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==}
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 18.0.17
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/react/18.0.17:
|
/@types/react/18.0.17:
|
||||||
resolution: {integrity: sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ==}
|
resolution: {integrity: sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1689,15 +1740,6 @@ packages:
|
||||||
eslint: 8.21.0
|
eslint: 8.21.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-react-hooks/4.6.0_eslint@8.21.0:
|
|
||||||
resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
peerDependencies:
|
|
||||||
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
|
|
||||||
dependencies:
|
|
||||||
eslint: 8.21.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/eslint-plugin-react/7.30.1_eslint@8.21.0:
|
/eslint-plugin-react/7.30.1_eslint@8.21.0:
|
||||||
resolution: {integrity: sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==}
|
resolution: {integrity: sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
@ -1944,6 +1986,30 @@ packages:
|
||||||
resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
|
resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/framer-motion/6.5.1_biqbaboplfbrettd7655fr4n2y:
|
||||||
|
resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8 || ^17.0.0 || ^18.0.0'
|
||||||
|
react-dom: '>=16.8 || ^17.0.0 || ^18.0.0'
|
||||||
|
dependencies:
|
||||||
|
'@motionone/dom': 10.12.0
|
||||||
|
framesync: 6.0.1
|
||||||
|
hey-listen: 1.0.8
|
||||||
|
popmotion: 11.0.3
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0_react@18.2.0
|
||||||
|
style-value-types: 5.0.0
|
||||||
|
tslib: 2.4.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@emotion/is-prop-valid': 0.8.8
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/framesync/6.0.1:
|
||||||
|
resolution: {integrity: sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==}
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fs.realpath/1.0.0:
|
/fs.realpath/1.0.0:
|
||||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2095,6 +2161,10 @@ packages:
|
||||||
function-bind: 1.1.1
|
function-bind: 1.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/hey-listen/1.0.8:
|
||||||
|
resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/html-encoding-sniffer/3.0.0:
|
/html-encoding-sniffer/3.0.0:
|
||||||
resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
|
resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
@ -2479,10 +2549,6 @@ packages:
|
||||||
sourcemap-codec: 1.4.8
|
sourcemap-codec: 1.4.8
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/memoize-one/5.2.1:
|
|
||||||
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/merge2/1.4.1:
|
/merge2/1.4.1:
|
||||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
@ -2753,6 +2819,15 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/popmotion/11.0.3:
|
||||||
|
resolution: {integrity: sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==}
|
||||||
|
dependencies:
|
||||||
|
framesync: 6.0.1
|
||||||
|
hey-listen: 1.0.8
|
||||||
|
style-value-types: 5.0.0
|
||||||
|
tslib: 2.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/postcss-import/14.1.0_postcss@8.4.16:
|
/postcss-import/14.1.0_postcss@8.4.16:
|
||||||
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
|
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
|
@ -2912,19 +2987,6 @@ packages:
|
||||||
transformation-matrix: 2.12.0
|
transformation-matrix: 2.12.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-window/1.8.7_biqbaboplfbrettd7655fr4n2y:
|
|
||||||
resolution: {integrity: sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA==}
|
|
||||||
engines: {node: '>8.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
|
|
||||||
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.18.9
|
|
||||||
memoize-one: 5.2.1
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0_react@18.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react/18.2.0:
|
/react/18.2.0:
|
||||||
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
@ -2954,6 +3016,7 @@ packages:
|
||||||
|
|
||||||
/regenerator-runtime/0.13.9:
|
/regenerator-runtime/0.13.9:
|
||||||
resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==}
|
resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/regexp.prototype.flags/1.4.3:
|
/regexp.prototype.flags/1.4.3:
|
||||||
resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==}
|
resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==}
|
||||||
|
@ -3164,6 +3227,13 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/style-value-types/5.0.0:
|
||||||
|
resolution: {integrity: sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==}
|
||||||
|
dependencies:
|
||||||
|
hey-listen: 1.0.8
|
||||||
|
tslib: 2.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/supports-color/5.5.0:
|
/supports-color/5.5.0:
|
||||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
@ -3282,6 +3352,10 @@ packages:
|
||||||
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tslib/2.4.0:
|
||||||
|
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tsutils/3.21.0_typescript@4.7.4:
|
/tsutils/3.21.0_typescript@4.7.4:
|
||||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
65
public/Interfaces.d.ts
vendored
65
public/Interfaces.d.ts
vendored
|
@ -1,65 +0,0 @@
|
||||||
declare interface IHistoryState {
|
|
||||||
LastAction: string
|
|
||||||
MainContainer: IContainerModel
|
|
||||||
SelectedContainer: IContainerModel | null
|
|
||||||
SelectedContainerId: string
|
|
||||||
TypeCounters: Record<string, number>
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IAvailableContainer {
|
|
||||||
Type: string
|
|
||||||
Width: number
|
|
||||||
Height: number
|
|
||||||
XPositionReference?: XPositionReference
|
|
||||||
Style: React.CSSProperties
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IEditorState {
|
|
||||||
history: IHistoryState[]
|
|
||||||
historyCurrentStep: number
|
|
||||||
configuration: IConfiguration
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IConfiguration {
|
|
||||||
AvailableContainers: IAvailableContainer[]
|
|
||||||
AvailableSymbols: IAvailableSymbol[]
|
|
||||||
MainContainer: IAvailableContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IContainerModel {
|
|
||||||
children: IContainerModel[]
|
|
||||||
parent: IContainerModel | null
|
|
||||||
properties: IProperties
|
|
||||||
userData: Record<string, string | number>
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IProperties extends React.CSSProperties {
|
|
||||||
id: string
|
|
||||||
parentId: string | null
|
|
||||||
x: number
|
|
||||||
y: number
|
|
||||||
isRigidBody: boolean
|
|
||||||
XPositionReference?: XPositionReference
|
|
||||||
}
|
|
||||||
|
|
||||||
declare enum XPositionReference {
|
|
||||||
Left,
|
|
||||||
Center,
|
|
||||||
Right
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IAvailableSymbol {
|
|
||||||
Name: string
|
|
||||||
XPositionReference: XPositionReference
|
|
||||||
Image: IImage
|
|
||||||
Width: number
|
|
||||||
Height: number
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IImage {
|
|
||||||
Name: string
|
|
||||||
Url: string
|
|
||||||
Base64Image: string
|
|
||||||
Svg: string
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
html,
|
|
||||||
body,
|
|
||||||
#root {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
import { Configuration } from '../../Interfaces/Configuration';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the configuration from the API
|
* Fetch the configuration from the API
|
||||||
* @returns {Configation} The model of the configuration for the application
|
* @returns {Configation} The model of the configuration for the application
|
||||||
*/
|
*/
|
||||||
export async function fetchConfiguration(): Promise<IConfiguration> {
|
export async function fetchConfiguration(): Promise<Configuration> {
|
||||||
const url = `${import.meta.env.VITE_API_URL}`;
|
const url = `${import.meta.env.VITE_API_URL}`;
|
||||||
// The test library cannot use the Fetch API
|
// The test library cannot use the Fetch API
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
@ -15,7 +15,7 @@ export async function fetchConfiguration(): Promise<IConfiguration> {
|
||||||
})
|
})
|
||||||
.then(async(response) =>
|
.then(async(response) =>
|
||||||
await response.json()
|
await response.json()
|
||||||
) as IConfiguration;
|
) as Configuration;
|
||||||
}
|
}
|
||||||
return await new Promise((resolve) => {
|
return await new Promise((resolve) => {
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#root {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
import { MainMenu } from '../MainMenu/MainMenu';
|
import { MainMenu } from '../MainMenu/MainMenu';
|
||||||
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel } from '../../Interfaces/ContainerModel';
|
||||||
import Editor from '../Editor/Editor';
|
import Editor, { IEditorState } from '../Editor/Editor';
|
||||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
|
||||||
import { LoadState } from './Load';
|
import { LoadState } from './Load';
|
||||||
import { LoadEditor, NewEditor } from './MenuActions';
|
import { LoadEditor, NewEditor } from './MenuActions';
|
||||||
import { DEFAULT_CONFIG, DEFAULT_MAINCONTAINER_PROPS } from '../../utils/default';
|
import { DEFAULT_CONFIG, DEFAULT_MAINCONTAINER_PROPS } from '../../utils/default';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { Revive } from '../../utils/saveload';
|
import { Revive } from '../../utils/saveload';
|
||||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
import { IEditorState } from '../Editor/Editor';
|
||||||
|
|
||||||
export function LoadState(
|
export function LoadState(
|
||||||
editorState: IEditorState,
|
editorState: IEditorState,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
import { Configuration } from '../../Interfaces/Configuration';
|
||||||
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel } from '../../Interfaces/ContainerModel';
|
||||||
import { fetchConfiguration } from '../API/api';
|
import { fetchConfiguration } from '../API/api';
|
||||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
import { IEditorState } from '../Editor/Editor';
|
||||||
import { LoadState } from './Load';
|
import { LoadState } from './Load';
|
||||||
|
|
||||||
export function NewEditor(
|
export function NewEditor(
|
||||||
|
@ -11,7 +11,7 @@ export function NewEditor(
|
||||||
): void {
|
): void {
|
||||||
// Fetch the configuration from the API
|
// Fetch the configuration from the API
|
||||||
fetchConfiguration()
|
fetchConfiguration()
|
||||||
.then((configuration: IConfiguration) => {
|
.then((configuration: Configuration) => {
|
||||||
// Set the main container from the given properties of the API
|
// Set the main container from the given properties of the API
|
||||||
const MainContainer = new ContainerModel(
|
const MainContainer = new ContainerModel(
|
||||||
null,
|
null,
|
||||||
|
@ -23,7 +23,6 @@ export function NewEditor(
|
||||||
width: configuration.MainContainer.Width,
|
width: configuration.MainContainer.Width,
|
||||||
height: configuration.MainContainer.Height,
|
height: configuration.MainContainer.Height,
|
||||||
isRigidBody: false,
|
isRigidBody: false,
|
||||||
isAnchor: false,
|
|
||||||
fillOpacity: 0,
|
fillOpacity: 0,
|
||||||
stroke: 'black'
|
stroke: 'black'
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const BAR_WIDTH = 64; // 4rem
|
||||||
|
|
||||||
export const Bar: React.FC<IBarProps> = (props) => {
|
export const Bar: React.FC<IBarProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<div className='fixed z-20 flex flex-col top-0 left-0 h-full w-16 bg-slate-100'>
|
<div className='fixed z-20 flex flex-col top-0 left-0 h-screen w-16 bg-slate-100'>
|
||||||
<BarIcon
|
<BarIcon
|
||||||
isActive={props.isSidebarOpen}
|
isActive={props.isSidebarOpen}
|
||||||
title='Components'
|
title='Components'
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
/**
|
|
||||||
* @module AnchorBehavior
|
|
||||||
*
|
|
||||||
* An anchor is a container that takes physical priority in the representation :
|
|
||||||
* - It cannot be moved by other rigid siblings container
|
|
||||||
* - It cannot be resized by any other siblings container
|
|
||||||
* - It cannot overlap any other siblings rigid container :
|
|
||||||
* - overlapping container are shifted to the nearest available space/width
|
|
||||||
* - or resized when there is no available space left other than theirs
|
|
||||||
* - or lose their rigid body properties when there is no available space left)
|
|
||||||
* Meaning that:
|
|
||||||
* - Moving an anchor container will resize the width of an overlapping container
|
|
||||||
* or make them lose their property as a rigid body
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
|
||||||
import { constraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Impose the container position to its siblings
|
|
||||||
* Apply the following modification to the overlapping rigid body container :
|
|
||||||
* @param container Container to impose its position
|
|
||||||
*/
|
|
||||||
export function ImposePosition(container: IContainerModel): IContainerModel {
|
|
||||||
if (container.parent === undefined ||
|
|
||||||
container.parent === null) {
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rigidBodies = container.parent.children.filter(
|
|
||||||
child => child.properties.isRigidBody && !child.properties.isAnchor
|
|
||||||
);
|
|
||||||
|
|
||||||
const overlappingContainers = getOverlappingContainers(container, rigidBodies);
|
|
||||||
for (const overlappingContainer of overlappingContainers) {
|
|
||||||
constraintBodyInsideUnallocatedWidth(overlappingContainer);
|
|
||||||
}
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the overlapping containers with container
|
|
||||||
* @param container A container
|
|
||||||
* @param containers A list of containers
|
|
||||||
* @returns A list of overlapping containers
|
|
||||||
*/
|
|
||||||
function getOverlappingContainers(
|
|
||||||
container: IContainerModel,
|
|
||||||
containers: IContainerModel[]
|
|
||||||
): IContainerModel[] {
|
|
||||||
const min1 = container.properties.x;
|
|
||||||
const max1 = container.properties.x + Number(container.properties.width);
|
|
||||||
const overlappingContainers: IContainerModel[] = [];
|
|
||||||
for (const other of containers) {
|
|
||||||
if (other === container) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const min2 = other.properties.x;
|
|
||||||
const max2 = other.properties.x + Number(other.properties.width);
|
|
||||||
const isOverlapping = Math.min(max1, max2) - Math.max(min1, min2) > 0;
|
|
||||||
|
|
||||||
if (!isOverlapping) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
overlappingContainers.push(other);
|
|
||||||
}
|
|
||||||
return overlappingContainers;
|
|
||||||
}
|
|
|
@ -1,312 +0,0 @@
|
||||||
/**
|
|
||||||
* @module RigidBodyBehaviors
|
|
||||||
* Apply the following contraints to the `container` :
|
|
||||||
* - The container must be kept inside its parent
|
|
||||||
* - The container must find an unallocated space within the parent
|
|
||||||
* If the contraints fails, an error message will be returned
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
|
||||||
import { ISizePointer } from '../../../Interfaces/ISizePointer';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* "Transform the container into a rigid body"
|
|
||||||
* Apply the following contraints to the `container` :
|
|
||||||
* - The container must be kept inside its parent
|
|
||||||
* - The container must find an unallocated space within the parent
|
|
||||||
* If the contraints fails, an error message will be returned
|
|
||||||
* @param container Container to apply its rigid body properties
|
|
||||||
* @returns A rigid body container
|
|
||||||
*/
|
|
||||||
export function RecalculatePhysics(
|
|
||||||
container: IContainerModel
|
|
||||||
): IContainerModel {
|
|
||||||
container = constraintBodyInsideParent(container);
|
|
||||||
container = constraintBodyInsideUnallocatedWidth(container);
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Limit a rect inside a parent rect by applying the following rules :
|
|
||||||
* it cannot be bigger than the parent
|
|
||||||
* it cannot go out of bound
|
|
||||||
* Mutates and returns the container
|
|
||||||
* @param container
|
|
||||||
* @returns Updated container
|
|
||||||
*/
|
|
||||||
function constraintBodyInsideParent(
|
|
||||||
container: IContainerModel
|
|
||||||
): IContainerModel {
|
|
||||||
if (container.parent === null || container.parent === undefined) {
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentProperties = container.parent.properties;
|
|
||||||
const parentWidth = Number(parentProperties.width);
|
|
||||||
const parentHeight = Number(parentProperties.height);
|
|
||||||
|
|
||||||
return constraintBodyInsideSpace(container, 0, 0, parentWidth, parentHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Limit a container inside a rectangle
|
|
||||||
* Mutates and returns the container
|
|
||||||
* @param container A container
|
|
||||||
* @param x x of top left of the rectangle
|
|
||||||
* @param y y of top left of the rectangle
|
|
||||||
* @param width width of the rectangle
|
|
||||||
* @param height height of the rectangle
|
|
||||||
* @returns Updated container
|
|
||||||
*/
|
|
||||||
function constraintBodyInsideSpace(
|
|
||||||
container: IContainerModel,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
width: number,
|
|
||||||
height: number
|
|
||||||
): IContainerModel {
|
|
||||||
const containerProperties = container.properties;
|
|
||||||
const containerX = Number(containerProperties.x);
|
|
||||||
const containerY = Number(containerProperties.y);
|
|
||||||
const containerWidth = Number(containerProperties.width);
|
|
||||||
const containerHeight = Number(containerProperties.height);
|
|
||||||
|
|
||||||
// Check size bigger than parent
|
|
||||||
const isBodyLargerThanParent = containerWidth > width;
|
|
||||||
const isBodyTallerThanParentHeight = containerHeight > height;
|
|
||||||
if (isBodyLargerThanParent || isBodyTallerThanParentHeight) {
|
|
||||||
if (isBodyLargerThanParent) {
|
|
||||||
containerProperties.x = x;
|
|
||||||
containerProperties.width = width;
|
|
||||||
}
|
|
||||||
if (isBodyTallerThanParentHeight) {
|
|
||||||
containerProperties.y = y;
|
|
||||||
containerProperties.height = height;
|
|
||||||
}
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check horizontal out of bound
|
|
||||||
if (containerX < x) {
|
|
||||||
containerProperties.x = x;
|
|
||||||
}
|
|
||||||
if (containerX + containerWidth > x + width) {
|
|
||||||
containerProperties.x = x + width - containerWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check vertical out of bound
|
|
||||||
if (containerY < y) {
|
|
||||||
containerProperties.y = y;
|
|
||||||
}
|
|
||||||
if (containerY + containerHeight > y + height) {
|
|
||||||
containerProperties.y = y + height - containerHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constraint the container inside unallocated width/space of the parent container
|
|
||||||
* If there is no unalloacted width/space, an error will be thrown
|
|
||||||
* Mutates and returns the container
|
|
||||||
* @param container
|
|
||||||
* @returns Updated container
|
|
||||||
*/
|
|
||||||
export function constraintBodyInsideUnallocatedWidth(
|
|
||||||
container: IContainerModel
|
|
||||||
): IContainerModel {
|
|
||||||
if (container.parent === null) {
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the available spaces of the parent
|
|
||||||
const availableWidths = getAvailableWidths(container.parent, container);
|
|
||||||
const containerX = Number(container.properties.x);
|
|
||||||
const containerWidth = Number(container.properties.width);
|
|
||||||
|
|
||||||
// Check if there is still some space
|
|
||||||
if (availableWidths.length === 0) {
|
|
||||||
throw new Error(
|
|
||||||
'No available space found on the parent container. Try to free the parent a little before placing it inside.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const middle = containerX + containerWidth / 2;
|
|
||||||
// Sort the available width to find the space with the closest position
|
|
||||||
availableWidths.sort(
|
|
||||||
(width1, width2) => {
|
|
||||||
let compared1X = width1.x;
|
|
||||||
if (width1.x < containerX) {
|
|
||||||
compared1X = width1.x + width1.width - containerWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
let compared2X = width2.x;
|
|
||||||
if (width2.x < containerX) {
|
|
||||||
compared2X = width2.x + width2.width - containerWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.abs(compared1X - middle) - Math.abs(compared2X - middle);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if the container actually fit inside
|
|
||||||
// It will usually fit if it was alrady fitting
|
|
||||||
const availableWidthFound = availableWidths.find((width) =>
|
|
||||||
isFitting(container, width)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (availableWidthFound === undefined) {
|
|
||||||
// Otherwise, it is possible that it does not fit
|
|
||||||
// There is two way to reach this part of the code
|
|
||||||
// 1) Enable isRigidBody such as width > availableWidth.width
|
|
||||||
// 2) Resize a container such as width > availableWidth.width
|
|
||||||
|
|
||||||
// We want the container to fit automatically inside the available space
|
|
||||||
// even if it means to resize the container
|
|
||||||
// The end goal is that the code never show the error message no matter what action is done
|
|
||||||
// TODO: Actually give an option to not fit and show the error message shown below
|
|
||||||
const availableWidth = availableWidths[0];
|
|
||||||
container.properties.x = availableWidth.x;
|
|
||||||
container.properties.width = availableWidth.width;
|
|
||||||
// throw new Error('[constraintBodyInsideUnallocatedWidth] BIGERR: No available space found on the parent container, even though there is some.');
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
return constraintBodyInsideSpace(
|
|
||||||
container,
|
|
||||||
availableWidthFound.x,
|
|
||||||
0,
|
|
||||||
availableWidthFound.width,
|
|
||||||
Number(container.parent.properties.height)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the unallocated widths inside a container
|
|
||||||
* An allocated width is defined by its the widths of the children that are rigid bodies.
|
|
||||||
* An example of this allocation system is the disk space of an hard drive
|
|
||||||
* (except the fact that disk space is divided by block).
|
|
||||||
* @param container Container where to find an available width
|
|
||||||
* @param exception Container to exclude of the widths (since a container will be moved, it might need to be excluded)
|
|
||||||
* @returns {ISizePointer[]} Array of unallocated widths (x=position of the unallocated space, width=size of the allocated space)
|
|
||||||
*/
|
|
||||||
function getAvailableWidths(
|
|
||||||
container: IContainerModel,
|
|
||||||
exception: IContainerModel
|
|
||||||
): ISizePointer[] {
|
|
||||||
// Initialize the first size pointer
|
|
||||||
// which takes full width of the available space
|
|
||||||
const x = 0;
|
|
||||||
const width = Number(container.properties.width);
|
|
||||||
let unallocatedSpaces: ISizePointer[] = [{ x, width }];
|
|
||||||
|
|
||||||
// We will only uses containers that also are rigid or are anchors
|
|
||||||
const solidBodies = container.children.filter(
|
|
||||||
(child) => child.properties.isRigidBody || child.properties.isAnchor
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const child of solidBodies) {
|
|
||||||
// Ignore the exception
|
|
||||||
if (child === exception) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const childX = child.properties.x;
|
|
||||||
const childWidth = Number(child.properties.width);
|
|
||||||
|
|
||||||
// get the space of the child that is inside the parent
|
|
||||||
let newUnallocatedSpace: ISizePointer[] = [];
|
|
||||||
|
|
||||||
// We will iterate on a mutable variable in order to divide it
|
|
||||||
for (const unallocatedSpace of unallocatedSpaces) {
|
|
||||||
// In order to find unallocated space,
|
|
||||||
// We need to calculate the overlap between the two containers
|
|
||||||
// We only works with widths meaning in 1D (with lines)
|
|
||||||
const newUnallocatedWidths = getAvailableWidthsTwoLines(
|
|
||||||
unallocatedSpace.x,
|
|
||||||
unallocatedSpace.x + unallocatedSpace.width,
|
|
||||||
childX,
|
|
||||||
childX + childWidth
|
|
||||||
);
|
|
||||||
|
|
||||||
// Concat the new list of SizePointer pointing to availables spaces
|
|
||||||
newUnallocatedSpace = newUnallocatedSpace.concat(newUnallocatedWidths);
|
|
||||||
}
|
|
||||||
// Finally update the availables spaces found, loop again with it
|
|
||||||
unallocatedSpaces = newUnallocatedSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
return unallocatedSpaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the unallocated widths between two lines in 1D
|
|
||||||
* @param unalloctedSpaceLeft left of the first line
|
|
||||||
* @param unallocatedSpaceRight rigth of the first line
|
|
||||||
* @param rectLeft left of the second line
|
|
||||||
* @param rectRight right of the second line
|
|
||||||
* @returns Available widths
|
|
||||||
*/
|
|
||||||
function getAvailableWidthsTwoLines(
|
|
||||||
unalloctedSpaceLeft: number,
|
|
||||||
unallocatedSpaceRight: number,
|
|
||||||
rectLeft: number,
|
|
||||||
rectRight: number
|
|
||||||
): ISizePointer[] {
|
|
||||||
if (unallocatedSpaceRight < rectLeft ||
|
|
||||||
unalloctedSpaceLeft > rectRight
|
|
||||||
) {
|
|
||||||
// object 1 and 2 are not overlapping
|
|
||||||
return [{
|
|
||||||
x: unalloctedSpaceLeft,
|
|
||||||
width: unallocatedSpaceRight - unalloctedSpaceLeft
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rectLeft < unalloctedSpaceLeft && rectRight > unallocatedSpaceRight) {
|
|
||||||
// object 2 is overlapping full width
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unalloctedSpaceLeft >= rectLeft) {
|
|
||||||
// object 2 is partially overlapping on the left
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
x: rectRight,
|
|
||||||
width: unallocatedSpaceRight - rectRight
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rectRight >= unallocatedSpaceRight) {
|
|
||||||
// object 2 is partially overlapping on the right
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
x: unalloctedSpaceLeft,
|
|
||||||
width: rectRight - unalloctedSpaceLeft
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// object 2 is overlapping in the middle
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
x: unalloctedSpaceLeft,
|
|
||||||
width: rectLeft - unalloctedSpaceLeft
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: rectRight,
|
|
||||||
width: unallocatedSpaceRight - rectRight
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a container can fit inside a size space
|
|
||||||
* @param container Container to check
|
|
||||||
* @param sizePointer Size space to check
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const isFitting = (
|
|
||||||
container: IContainerModel,
|
|
||||||
sizePointer: ISizePointer
|
|
||||||
): boolean => Number(container.properties.width) <= sizePointer.width;
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { HistoryState } from '../../Interfaces/HistoryState';
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
import { Configuration } from '../../Interfaces/Configuration';
|
||||||
import { ContainerModel, IContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel, IContainerModel } from '../../Interfaces/ContainerModel';
|
||||||
import { findContainerById } from '../../utils/itertools';
|
import { findContainerById } from '../../utils/itertools';
|
||||||
import { getCurrentHistory } from './Editor';
|
import { getCurrentHistory } from './Editor';
|
||||||
import IProperties from '../../Interfaces/IProperties';
|
import { SizePointer } from '../../Interfaces/SizePointer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a container
|
* Select a container
|
||||||
|
@ -12,9 +12,9 @@ import IProperties from '../../Interfaces/IProperties';
|
||||||
*/
|
*/
|
||||||
export function SelectContainer(
|
export function SelectContainer(
|
||||||
container: ContainerModel,
|
container: ContainerModel,
|
||||||
fullHistory: IHistoryState[],
|
fullHistory: HistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
@ -27,30 +27,21 @@ export function SelectContainer(
|
||||||
throw new Error('[SelectContainer] Cannot find container among children of main container!');
|
throw new Error('[SelectContainer] Cannot find container among children of main container!');
|
||||||
}
|
}
|
||||||
|
|
||||||
history.push({
|
setHistory(history.concat([{
|
||||||
LastAction: `Select ${selectedContainer.properties.id}`,
|
LastAction: `Select container ${selectedContainer.properties.id}`,
|
||||||
MainContainer: mainContainerClone,
|
MainContainer: mainContainerClone,
|
||||||
SelectedContainer: selectedContainer,
|
SelectedContainer: selectedContainer,
|
||||||
SelectedContainerId: selectedContainer.properties.id,
|
SelectedContainerId: selectedContainer.properties.id,
|
||||||
TypeCounters: Object.assign({}, current.TypeCounters)
|
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||||
});
|
}]));
|
||||||
setHistory(history);
|
setHistoryCurrentStep(history.length);
|
||||||
setHistoryCurrentStep(history.length - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a container
|
|
||||||
* @param containerId containerId of the container to delete
|
|
||||||
* @param fullHistory History of the editor
|
|
||||||
* @param historyCurrentStep Current step
|
|
||||||
* @param setHistory State setter for History
|
|
||||||
* @param setHistoryCurrentStep State setter for current step
|
|
||||||
*/
|
|
||||||
export function DeleteContainer(
|
export function DeleteContainer(
|
||||||
containerId: string,
|
containerId: string,
|
||||||
fullHistory: IHistoryState[],
|
fullHistory: HistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
@ -63,58 +54,43 @@ export function DeleteContainer(
|
||||||
throw new Error(`[DeleteContainer] Tried to delete a container that is not present in the main container: ${containerId}`);
|
throw new Error(`[DeleteContainer] Tried to delete a container that is not present in the main container: ${containerId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container === mainContainerClone ||
|
if (container === mainContainerClone) {
|
||||||
container.parent === undefined ||
|
|
||||||
container.parent === null) {
|
|
||||||
// TODO: Implement alert
|
// TODO: Implement alert
|
||||||
throw new Error('[DeleteContainer] Tried to delete the main container! Deleting the main container is not allowed!');
|
throw new Error('[DeleteContainer] Tried to delete the main container! Deleting the main container is not allowed!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container === null || container === undefined) {
|
if (container === null || container === undefined) {
|
||||||
throw new Error('[DeleteContainer] Container model was not found among children of the main container!');
|
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = container.parent.children.indexOf(container);
|
if (container.parent != null) {
|
||||||
if (index > -1) {
|
const index = container.parent.children.indexOf(container);
|
||||||
container.parent.children.splice(index, 1);
|
if (index > -1) {
|
||||||
} else {
|
container.parent.children.splice(index, 1);
|
||||||
throw new Error('[DeleteContainer] Could not find container among parent\'s children');
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select the previous container
|
setHistory(history.concat([{
|
||||||
// or select the one above
|
LastAction: `Delete container ${containerId}`,
|
||||||
const SelectedContainer = findContainerById(mainContainerClone, current.SelectedContainerId) ??
|
|
||||||
container.parent.children.at(index - 1) ??
|
|
||||||
container.parent;
|
|
||||||
const SelectedContainerId = SelectedContainer.properties.id;
|
|
||||||
|
|
||||||
history.push({
|
|
||||||
LastAction: `Delete ${containerId}`,
|
|
||||||
MainContainer: mainContainerClone,
|
MainContainer: mainContainerClone,
|
||||||
SelectedContainer,
|
SelectedContainer: null,
|
||||||
SelectedContainerId,
|
SelectedContainerId: '',
|
||||||
TypeCounters: Object.assign({}, current.TypeCounters)
|
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||||
});
|
}]));
|
||||||
setHistory(history);
|
setHistoryCurrentStep(history.length);
|
||||||
setHistoryCurrentStep(history.length - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new container to a selected container
|
* Add a new container to a selected container
|
||||||
* @param type The type of container
|
* @param type The type of container
|
||||||
* @param configuration Configuration of the App
|
|
||||||
* @param fullHistory History of the editor
|
|
||||||
* @param historyCurrentStep Current step
|
|
||||||
* @param setHistory State setter for History
|
|
||||||
* @param setHistoryCurrentStep State setter for current step
|
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
export function AddContainerToSelectedContainer(
|
export function AddContainerToSelectedContainer(
|
||||||
type: string,
|
type: string,
|
||||||
configuration: IConfiguration,
|
configuration: Configuration,
|
||||||
fullHistory: IHistoryState[],
|
fullHistory: HistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
@ -138,26 +114,14 @@ export function AddContainerToSelectedContainer(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and add a new container at `index` in children of parent of `parentId`
|
|
||||||
* @param index Index where to insert to the new container
|
|
||||||
* @param type Type of container
|
|
||||||
* @param parentId Parent in which to insert the new container
|
|
||||||
* @param configuration Configuration of the app
|
|
||||||
* @param fullHistory History of the editor
|
|
||||||
* @param historyCurrentStep Current step
|
|
||||||
* @param setHistory State setter of History
|
|
||||||
* @param setHistoryCurrentStep State setter of the current step
|
|
||||||
* @returns void
|
|
||||||
*/
|
|
||||||
export function AddContainer(
|
export function AddContainer(
|
||||||
index: number,
|
index: number,
|
||||||
type: string,
|
type: string,
|
||||||
parentId: string,
|
parentId: string,
|
||||||
configuration: IConfiguration,
|
configuration: Configuration,
|
||||||
fullHistory: IHistoryState[],
|
fullHistory: HistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
@ -206,23 +170,19 @@ export function AddContainer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProperties: IProperties = {
|
|
||||||
id: `${type}-${count}`,
|
|
||||||
parentId: parentClone.properties.id,
|
|
||||||
x,
|
|
||||||
y: 0,
|
|
||||||
width: properties.Width,
|
|
||||||
height: parentClone.properties.height,
|
|
||||||
isRigidBody: false,
|
|
||||||
isAnchor: false,
|
|
||||||
XPositionReference: properties.XPositionReference,
|
|
||||||
...properties.Style
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the container
|
// Create the container
|
||||||
const newContainer = new ContainerModel(
|
const newContainer = new ContainerModel(
|
||||||
parentClone,
|
parentClone,
|
||||||
defaultProperties,
|
{
|
||||||
|
id: `${type}-${count}`,
|
||||||
|
parentId: parentClone.properties.id,
|
||||||
|
x,
|
||||||
|
y: 0,
|
||||||
|
width: properties?.Width,
|
||||||
|
height: parentClone.properties.height,
|
||||||
|
isRigidBody: false,
|
||||||
|
...properties.Style
|
||||||
|
},
|
||||||
[],
|
[],
|
||||||
{
|
{
|
||||||
type
|
type
|
||||||
|
@ -237,13 +197,278 @@ export function AddContainer(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the state
|
// Update the state
|
||||||
history.push({
|
setHistory(history.concat([{
|
||||||
LastAction: 'Add container',
|
LastAction: 'Add container',
|
||||||
MainContainer: clone,
|
MainContainer: clone,
|
||||||
SelectedContainer: parentClone,
|
SelectedContainer: parentClone,
|
||||||
SelectedContainerId: parentClone.properties.id,
|
SelectedContainerId: parentClone.properties.id,
|
||||||
TypeCounters: newCounters
|
TypeCounters: newCounters
|
||||||
});
|
}]));
|
||||||
setHistory(history);
|
setHistoryCurrentStep(history.length);
|
||||||
setHistoryCurrentStep(history.length - 1);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handled the property change event in the properties form
|
||||||
|
* @param key Property name
|
||||||
|
* @param value New value of the property
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
export function OnPropertyChange(
|
||||||
|
key: string,
|
||||||
|
value: string | number | boolean,
|
||||||
|
fullHistory: HistoryState[],
|
||||||
|
historyCurrentStep: number,
|
||||||
|
setHistory: Dispatch<SetStateAction<HistoryState[]>>,
|
||||||
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
|
): void {
|
||||||
|
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
||||||
|
const current = history[history.length - 1];
|
||||||
|
|
||||||
|
if (current.SelectedContainer === null ||
|
||||||
|
current.SelectedContainer === undefined) {
|
||||||
|
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent === null) {
|
||||||
|
const selectedContainerClone: IContainerModel = structuredClone(current.SelectedContainer);
|
||||||
|
(selectedContainerClone.properties as any)[key] = value;
|
||||||
|
setHistory(history.concat([{
|
||||||
|
LastAction: 'Change property of main',
|
||||||
|
MainContainer: selectedContainerClone,
|
||||||
|
SelectedContainer: selectedContainerClone,
|
||||||
|
SelectedContainerId: selectedContainerClone.properties.id,
|
||||||
|
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||||
|
}]));
|
||||||
|
setHistoryCurrentStep(history.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
||||||
|
const container: ContainerModel | undefined = findContainerById(mainContainerClone, current.SelectedContainer.properties.id);
|
||||||
|
|
||||||
|
if (container === null || container === undefined) {
|
||||||
|
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
||||||
|
}
|
||||||
|
|
||||||
|
(container.properties as any)[key] = value;
|
||||||
|
|
||||||
|
if (container.properties.isRigidBody) {
|
||||||
|
RecalculatePhysics(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
setHistory(history.concat([{
|
||||||
|
LastAction: `Change property of container ${container.properties.id}`,
|
||||||
|
MainContainer: mainContainerClone,
|
||||||
|
SelectedContainer: container,
|
||||||
|
SelectedContainerId: container.properties.id,
|
||||||
|
TypeCounters: Object.assign({}, current.TypeCounters)
|
||||||
|
}]));
|
||||||
|
setHistoryCurrentStep(history.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO put this in a different file
|
||||||
|
|
||||||
|
export function RecalculatePhysics(container: IContainerModel): IContainerModel {
|
||||||
|
container = constraintBodyInsideParent(container);
|
||||||
|
container = constraintBodyInsideUnallocatedWidth(container);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit a rect inside a parent rect by applying the following rules :
|
||||||
|
* it cannot be bigger than the parent
|
||||||
|
* it cannot go out of bound
|
||||||
|
* @param container
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function constraintBodyInsideParent(container: IContainerModel): IContainerModel {
|
||||||
|
if (container.parent === null || container.parent === undefined) {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentProperties = container.parent.properties;
|
||||||
|
const parentWidth = Number(parentProperties.width);
|
||||||
|
const parentHeight = Number(parentProperties.height);
|
||||||
|
|
||||||
|
return constraintBodyInsideSpace(container, 0, 0, parentWidth, parentHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
function constraintBodyInsideSpace(
|
||||||
|
container: IContainerModel,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
height: number
|
||||||
|
): IContainerModel {
|
||||||
|
const containerProperties = container.properties;
|
||||||
|
const containerX = Number(containerProperties.x);
|
||||||
|
const containerY = Number(containerProperties.y);
|
||||||
|
const containerWidth = Number(containerProperties.width);
|
||||||
|
const containerHeight = Number(containerProperties.height);
|
||||||
|
|
||||||
|
// Check size bigger than parent
|
||||||
|
const isBodyLargerThanParent = containerWidth > width;
|
||||||
|
const isBodyTallerThanParentHeight = containerHeight > height;
|
||||||
|
if (isBodyLargerThanParent || isBodyTallerThanParentHeight) {
|
||||||
|
if (isBodyLargerThanParent) {
|
||||||
|
containerProperties.x = x;
|
||||||
|
containerProperties.width = width;
|
||||||
|
}
|
||||||
|
if (isBodyTallerThanParentHeight) {
|
||||||
|
containerProperties.y = y;
|
||||||
|
containerProperties.height = height;
|
||||||
|
}
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check horizontal out of bound
|
||||||
|
if (containerX < x) {
|
||||||
|
containerProperties.x = x;
|
||||||
|
}
|
||||||
|
if (containerX + containerWidth > width) {
|
||||||
|
containerProperties.x = x + width - containerWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check vertical out of bound
|
||||||
|
if (containerY < y) {
|
||||||
|
containerProperties.y = y;
|
||||||
|
}
|
||||||
|
if (containerY + containerHeight > height) {
|
||||||
|
containerProperties.y = y + height - containerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unallocated widths inside a container
|
||||||
|
* An allocated width is defined by its the widths of the children that are rigid bodies.
|
||||||
|
* An example of this allocation system is the disk space
|
||||||
|
* (except the fact that disk space is divided by block).
|
||||||
|
* @param container
|
||||||
|
* @returns {SizePointer[]} Array of unallocated widths (x=position of the unallocated space, width=size of the allocated space)
|
||||||
|
*/
|
||||||
|
function getAvailableWidths(container: IContainerModel, exception: IContainerModel): SizePointer[] {
|
||||||
|
const x = 0;
|
||||||
|
const width = Number(container.properties.width);
|
||||||
|
let unallocatedSpaces: SizePointer[] = [{ x, width }];
|
||||||
|
|
||||||
|
const rigidBodies = container.children.filter(child => child.properties.isRigidBody);
|
||||||
|
for (const child of rigidBodies) {
|
||||||
|
if (child === exception) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the space of the child that is inside the parent
|
||||||
|
let newUnallocatedSpace: SizePointer[] = [];
|
||||||
|
for (const unallocatedSpace of unallocatedSpaces) {
|
||||||
|
const newUnallocatedWidths = getAvailableWidthsTwoLines(
|
||||||
|
unallocatedSpace.x,
|
||||||
|
unallocatedSpace.x + unallocatedSpace.width,
|
||||||
|
child.properties.x,
|
||||||
|
child.properties.x + Number(child.properties.width));
|
||||||
|
newUnallocatedSpace = newUnallocatedSpace.concat(newUnallocatedWidths);
|
||||||
|
}
|
||||||
|
unallocatedSpaces = newUnallocatedSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
return unallocatedSpaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unallocated widths between two lines in 1D
|
||||||
|
* @param min1 left of the first line
|
||||||
|
* @param max1 rigth of the first line
|
||||||
|
* @param min2 left of the second line
|
||||||
|
* @param max2 right of the second line
|
||||||
|
* @returns Available widths
|
||||||
|
*/
|
||||||
|
function getAvailableWidthsTwoLines(min1: number, max1: number, min2: number, max2: number): SizePointer[] {
|
||||||
|
if (min2 < min1 && max2 > max1) {
|
||||||
|
// object 2 is overlapping full width
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min1 >= min2) {
|
||||||
|
// object 2 is partially overlapping on the left
|
||||||
|
return [{
|
||||||
|
x: max2,
|
||||||
|
width: max1 - max2
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max2 >= max1) {
|
||||||
|
// object 2 is partially overlapping on the right
|
||||||
|
return [{
|
||||||
|
x: min2,
|
||||||
|
width: max2 - min1
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// object 2 is overlapping in the middle
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
x: min1,
|
||||||
|
width: min2 - min1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: min2,
|
||||||
|
width: max1 - max2
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param container
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function constraintBodyInsideUnallocatedWidth(container: IContainerModel): IContainerModel {
|
||||||
|
if (container.parent === null) {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
const availableWidths = getAvailableWidths(container.parent, container);
|
||||||
|
const containerX = Number(container.properties.x);
|
||||||
|
|
||||||
|
// Sort the available width
|
||||||
|
availableWidths
|
||||||
|
.sort((width1, width2) => Math.abs(width1.x - containerX) - Math.abs(width2.x - containerX));
|
||||||
|
|
||||||
|
if (availableWidths.length === 0) {
|
||||||
|
throw new Error('No available space found on the parent container. Try to free the parent a little before placing it inside.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const availableWidthFound = availableWidths.find(
|
||||||
|
width => isFitting(container, width)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (availableWidthFound === undefined) {
|
||||||
|
// There is two way to reach this part of the code
|
||||||
|
// 1) toggle the isRigidBody such as width > availableWidth.width
|
||||||
|
// 2) resize a container such as width > availableWidth.width
|
||||||
|
// We want the container to fit automatically inside the available space
|
||||||
|
// even if it means to resize the container
|
||||||
|
// The end goal is that the code never show the error message no matter what action is done
|
||||||
|
// TODO: Actually give an option to not fit and show the error message shown below
|
||||||
|
const availableWidth = availableWidths[0];
|
||||||
|
container.properties.x = availableWidth.x;
|
||||||
|
container.properties.width = availableWidth.width;
|
||||||
|
// throw new Error('[constraintBodyInsideUnallocatedWidth] BIGERR: No available space found on the parent container, even though there is some.');
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
return constraintBodyInsideSpace(
|
||||||
|
container,
|
||||||
|
availableWidthFound.x,
|
||||||
|
0,
|
||||||
|
availableWidthFound.width,
|
||||||
|
Number(container.parent.properties.height)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFitting(container: IContainerModel, sizePointer: SizePointer): boolean {
|
||||||
|
const containerWidth = Number(container.properties.width);
|
||||||
|
|
||||||
|
return containerWidth <= sizePointer.width;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,31 @@
|
||||||
import React, { useRef } from 'react';
|
import React from 'react';
|
||||||
import './Editor.scss';
|
import './Editor.scss';
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
import { Configuration } from '../../Interfaces/Configuration';
|
||||||
import { SVG } from '../SVG/SVG';
|
import { SVG } from '../SVG/SVG';
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { HistoryState } from '../../Interfaces/HistoryState';
|
||||||
import { UI } from '../UI/UI';
|
import { UI } from '../UI/UI';
|
||||||
import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, AddContainer } from './ContainerOperations';
|
import { SelectContainer, DeleteContainer, OnPropertyChange, AddContainerToSelectedContainer, AddContainer } from './ContainerOperations';
|
||||||
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Save';
|
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Save';
|
||||||
import { onKeyDown } from './Shortcuts';
|
import { onKeyDown } from './Shortcuts';
|
||||||
import { OnPropertyChange, OnPropertiesSubmit } from './PropertiesOperations';
|
|
||||||
import EditorEvents from '../../Events/EditorEvents';
|
|
||||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
|
||||||
import { MAX_HISTORY } from '../../utils/default';
|
|
||||||
|
|
||||||
interface IEditorProps {
|
interface IEditorProps {
|
||||||
configuration: IConfiguration
|
configuration: Configuration
|
||||||
history: IHistoryState[]
|
history: HistoryState[]
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getCurrentHistory = (history: IHistoryState[], historyCurrentStep: number): IHistoryState[] =>
|
export interface IEditorState {
|
||||||
history.slice(
|
history: HistoryState[]
|
||||||
Math.max(0, history.length - MAX_HISTORY), // change this to 0 for unlimited (not recommanded because of overflow)
|
historyCurrentStep: number
|
||||||
historyCurrentStep + 1
|
configuration: Configuration
|
||||||
);
|
}
|
||||||
|
|
||||||
export const getCurrentHistoryState = (history: IHistoryState[], historyCurrentStep: number): IHistoryState => history[historyCurrentStep];
|
export const getCurrentHistory = (history: HistoryState[], historyCurrentStep: number): HistoryState[] => history.slice(0, historyCurrentStep + 1);
|
||||||
|
export const getCurrentHistoryState = (history: HistoryState[], historyCurrentStep: number): HistoryState => history[historyCurrentStep];
|
||||||
|
|
||||||
const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
const [history, setHistory] = React.useState<IHistoryState[]>(structuredClone(props.history));
|
const [history, setHistory] = React.useState<HistoryState[]>(structuredClone(props.history));
|
||||||
const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep);
|
const [historyCurrentStep, setHistoryCurrentStep] = React.useState<number>(props.historyCurrentStep);
|
||||||
const editorRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const onKeyUp = (event: KeyboardEvent): void => onKeyDown(
|
const onKeyUp = (event: KeyboardEvent): void => onKeyDown(
|
||||||
|
@ -41,37 +37,15 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
|
|
||||||
window.addEventListener('keyup', onKeyUp);
|
window.addEventListener('keyup', onKeyUp);
|
||||||
|
|
||||||
const events = EditorEvents;
|
|
||||||
const editorState: IEditorState = {
|
|
||||||
history,
|
|
||||||
historyCurrentStep,
|
|
||||||
configuration: props.configuration
|
|
||||||
};
|
|
||||||
|
|
||||||
const funcs = new Map<string, () => void>();
|
|
||||||
for (const event of events) {
|
|
||||||
const func = (): void => event.func(editorState);
|
|
||||||
editorRef.current?.addEventListener(event.name, func);
|
|
||||||
funcs.set(event.name, func);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('keyup', onKeyUp);
|
window.removeEventListener('keyup', onKeyUp);
|
||||||
|
|
||||||
for (const event of events) {
|
|
||||||
const func = funcs.get(event.name);
|
|
||||||
if (func === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
editorRef.current?.removeEventListener(event.name, func);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const configuration = props.configuration;
|
const configuration = props.configuration;
|
||||||
const current = getCurrentHistoryState(history, historyCurrentStep);
|
const current = getCurrentHistoryState(history, historyCurrentStep);
|
||||||
return (
|
return (
|
||||||
<div ref={editorRef} className="Editor font-sans h-full">
|
<div className="App font-sans h-full">
|
||||||
<UI
|
<UI
|
||||||
current={current}
|
current={current}
|
||||||
history={history}
|
history={history}
|
||||||
|
@ -98,14 +72,6 @@ const Editor: React.FunctionComponent<IEditorProps> = (props) => {
|
||||||
setHistory,
|
setHistory,
|
||||||
setHistoryCurrentStep
|
setHistoryCurrentStep
|
||||||
)}
|
)}
|
||||||
OnPropertiesSubmit={(event, properties) => OnPropertiesSubmit(
|
|
||||||
event,
|
|
||||||
properties,
|
|
||||||
history,
|
|
||||||
historyCurrentStep,
|
|
||||||
setHistory,
|
|
||||||
setHistoryCurrentStep
|
|
||||||
)}
|
|
||||||
AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer(
|
AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer(
|
||||||
type,
|
type,
|
||||||
configuration,
|
configuration,
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
import { IContainerModel, ContainerModel } from '../../Interfaces/IContainerModel';
|
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
|
||||||
import IProperties from '../../Interfaces/IProperties';
|
|
||||||
import { findContainerById } from '../../utils/itertools';
|
|
||||||
import { getCurrentHistory } from './Editor';
|
|
||||||
import { RecalculatePhysics } from './Behaviors/RigidBodyBehaviors';
|
|
||||||
import { INPUT_TYPES } from '../Properties/PropertiesInputTypes';
|
|
||||||
import { ImposePosition } from './Behaviors/AnchorBehaviors';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handled the property change event in the properties form
|
|
||||||
* @param key Property name
|
|
||||||
* @param value New value of the property
|
|
||||||
* @returns void
|
|
||||||
*/
|
|
||||||
export function OnPropertyChange(
|
|
||||||
key: string,
|
|
||||||
value: string | number | boolean,
|
|
||||||
fullHistory: IHistoryState[],
|
|
||||||
historyCurrentStep: number,
|
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
|
||||||
): void {
|
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
|
||||||
const current = history[history.length - 1];
|
|
||||||
|
|
||||||
if (current.SelectedContainer === null ||
|
|
||||||
current.SelectedContainer === undefined) {
|
|
||||||
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
|
||||||
}
|
|
||||||
|
|
||||||
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
|
||||||
const container: ContainerModel | undefined = findContainerById(mainContainerClone, current.SelectedContainer.properties.id);
|
|
||||||
|
|
||||||
if (container === null || container === undefined) {
|
|
||||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (INPUT_TYPES[key] === 'number') {
|
|
||||||
(container.properties as any)[key] = Number(value);
|
|
||||||
} else {
|
|
||||||
(container.properties as any)[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container.properties.isAnchor) {
|
|
||||||
ImposePosition(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container.properties.isRigidBody) {
|
|
||||||
RecalculatePhysics(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
history.push({
|
|
||||||
LastAction: `Change ${key} of ${container.properties.id}`,
|
|
||||||
MainContainer: mainContainerClone,
|
|
||||||
SelectedContainer: container,
|
|
||||||
SelectedContainerId: container.properties.id,
|
|
||||||
TypeCounters: Object.assign({}, current.TypeCounters)
|
|
||||||
});
|
|
||||||
setHistory(history);
|
|
||||||
setHistoryCurrentStep(history.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handled the property change event in the properties form
|
|
||||||
* @param key Property name
|
|
||||||
* @param properties Properties of the selected container
|
|
||||||
* @returns void
|
|
||||||
*/
|
|
||||||
export function OnPropertiesSubmit(
|
|
||||||
event: React.SyntheticEvent<HTMLFormElement>,
|
|
||||||
properties: IProperties,
|
|
||||||
fullHistory: IHistoryState[],
|
|
||||||
historyCurrentStep: number,
|
|
||||||
setHistory: Dispatch<SetStateAction<IHistoryState[]>>,
|
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
|
||||||
): void {
|
|
||||||
event.preventDefault();
|
|
||||||
const history = getCurrentHistory(fullHistory, historyCurrentStep);
|
|
||||||
const current = history[history.length - 1];
|
|
||||||
|
|
||||||
if (current.SelectedContainer === null ||
|
|
||||||
current.SelectedContainer === undefined) {
|
|
||||||
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
|
|
||||||
}
|
|
||||||
|
|
||||||
const mainContainerClone: IContainerModel = structuredClone(current.MainContainer);
|
|
||||||
const container: ContainerModel | undefined = findContainerById(mainContainerClone, current.SelectedContainer.properties.id);
|
|
||||||
|
|
||||||
if (container === null || container === undefined) {
|
|
||||||
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const property in properties) {
|
|
||||||
const input = (event.target as HTMLFormElement).querySelector(`#${property}`);
|
|
||||||
if (input instanceof HTMLInputElement) {
|
|
||||||
(container.properties as any)[property] = input.value;
|
|
||||||
if (INPUT_TYPES[property] === 'number') {
|
|
||||||
(container.properties as any)[property] = Number(input.value);
|
|
||||||
} else {
|
|
||||||
(container.properties as any)[property] = input.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container.properties.isRigidBody) {
|
|
||||||
RecalculatePhysics(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
history.push({
|
|
||||||
LastAction: `Change properties of ${container.properties.id}`,
|
|
||||||
MainContainer: mainContainerClone,
|
|
||||||
SelectedContainer: container,
|
|
||||||
SelectedContainerId: container.properties.id,
|
|
||||||
TypeCounters: Object.assign({}, current.TypeCounters)
|
|
||||||
});
|
|
||||||
setHistory(history);
|
|
||||||
setHistoryCurrentStep(history.length - 1);
|
|
||||||
}
|
|
|
@ -1,40 +1,29 @@
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { HistoryState } from "../../Interfaces/HistoryState";
|
||||||
import { IConfiguration } from '../../Interfaces/IConfiguration';
|
import { Configuration } from '../../Interfaces/Configuration';
|
||||||
import { getCircularReplacer } from '../../utils/saveload';
|
import { getCircularReplacer } from '../../utils/saveload';
|
||||||
import { ID } from '../SVG/SVG';
|
import { ID } from '../SVG/SVG';
|
||||||
import { IEditorState } from '../../Interfaces/IEditorState';
|
import { IEditorState } from './Editor';
|
||||||
import Worker from '../../workers/worker?worker';
|
|
||||||
|
|
||||||
export function SaveEditorAsJSON(
|
export function SaveEditorAsJSON(
|
||||||
history: IHistoryState[],
|
history: HistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
configuration: IConfiguration
|
configuration: Configuration
|
||||||
): void {
|
): void {
|
||||||
const exportName = 'state.json';
|
const exportName = 'state';
|
||||||
const spaces = import.meta.env.DEV ? 4 : 0;
|
const spaces = import.meta.env.DEV ? 4 : 0;
|
||||||
const editorState: IEditorState = {
|
const editorState: IEditorState = {
|
||||||
history,
|
history,
|
||||||
historyCurrentStep,
|
historyCurrentStep,
|
||||||
configuration
|
configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
||||||
if (window.Worker) {
|
|
||||||
// use webworker for the stringify to avoid freezing
|
|
||||||
const myWorker = new Worker();
|
|
||||||
myWorker.postMessage({ editorState, spaces });
|
|
||||||
myWorker.onmessage = (event) => {
|
|
||||||
const data = event.data;
|
|
||||||
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(data)}`;
|
|
||||||
createDownloadNode(exportName, dataStr);
|
|
||||||
myWorker.terminate();
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = JSON.stringify(editorState, getCircularReplacer(), spaces);
|
const data = JSON.stringify(editorState, getCircularReplacer(), spaces);
|
||||||
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(data)}`;
|
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(data)}`;
|
||||||
createDownloadNode(exportName, dataStr);
|
const downloadAnchorNode = document.createElement('a');
|
||||||
|
downloadAnchorNode.setAttribute('href', dataStr);
|
||||||
|
downloadAnchorNode.setAttribute('download', `${exportName}.json`);
|
||||||
|
document.body.appendChild(downloadAnchorNode); // required for firefox
|
||||||
|
downloadAnchorNode.click();
|
||||||
|
downloadAnchorNode.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SaveEditorAsSVG(): void {
|
export function SaveEditorAsSVG(): void {
|
||||||
|
@ -43,14 +32,10 @@ export function SaveEditorAsSVG(): void {
|
||||||
const preface = '<?xml version="1.0" standalone="no"?>\r\n';
|
const preface = '<?xml version="1.0" standalone="no"?>\r\n';
|
||||||
const svgBlob = new Blob([preface, svg.outerHTML], { type: 'image/svg+xml;charset=utf-8' });
|
const svgBlob = new Blob([preface, svg.outerHTML], { type: 'image/svg+xml;charset=utf-8' });
|
||||||
const svgUrl = URL.createObjectURL(svgBlob);
|
const svgUrl = URL.createObjectURL(svgBlob);
|
||||||
createDownloadNode('state.svg', svgUrl);
|
const downloadLink = document.createElement('a');
|
||||||
}
|
downloadLink.href = svgUrl;
|
||||||
|
downloadLink.download = 'newesttree.svg';
|
||||||
function createDownloadNode(filename: string, datastring: string) {
|
document.body.appendChild(downloadLink);
|
||||||
const downloadAnchorNode = document.createElement('a');
|
downloadLink.click();
|
||||||
downloadAnchorNode.href = datastring;
|
document.body.removeChild(downloadLink);
|
||||||
downloadAnchorNode.download = filename;
|
|
||||||
document.body.appendChild(downloadAnchorNode); // required for firefox
|
|
||||||
downloadAnchorNode.click();
|
|
||||||
downloadAnchorNode.remove();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { HistoryState } from '../../Interfaces/HistoryState';
|
||||||
|
|
||||||
export function onKeyDown(
|
export function onKeyDown(
|
||||||
event: KeyboardEvent,
|
event: KeyboardEvent,
|
||||||
history: IHistoryState[],
|
history: HistoryState[],
|
||||||
historyCurrentStep: number,
|
historyCurrentStep: number,
|
||||||
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
setHistoryCurrentStep: Dispatch<SetStateAction<number>>
|
||||||
): void {
|
): void {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { fireEvent, render, screen } from '../../utils/test-utils';
|
import { fireEvent, render, screen } from '../../utils/test-utils';
|
||||||
import { ElementsSidebar } from './ElementsSidebar';
|
import { ElementsSidebar } from './ElementsSidebar';
|
||||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
||||||
|
|
||||||
describe.concurrent('Elements sidebar', () => {
|
describe.concurrent('Elements sidebar', () => {
|
||||||
it('With a MainContainer', () => {
|
it('With a MainContainer', () => {
|
||||||
|
@ -17,8 +17,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 2000,
|
width: 2000,
|
||||||
height: 100,
|
height: 100,
|
||||||
isRigidBody: false,
|
isRigidBody: false
|
||||||
isAnchor: false
|
|
||||||
},
|
},
|
||||||
userData: {}
|
userData: {}
|
||||||
}}
|
}}
|
||||||
|
@ -26,7 +25,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={null}
|
SelectedContainer={null}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
OnPropertiesSubmit={() => {}}
|
|
||||||
SelectContainer={() => {}}
|
SelectContainer={() => {}}
|
||||||
DeleteContainer={() => {}}
|
DeleteContainer={() => {}}
|
||||||
AddContainer={() => {}}
|
AddContainer={() => {}}
|
||||||
|
@ -48,8 +46,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 2000,
|
width: 2000,
|
||||||
height: 100,
|
height: 100,
|
||||||
isRigidBody: false,
|
isRigidBody: false
|
||||||
isAnchor: false
|
|
||||||
},
|
},
|
||||||
userData: {}
|
userData: {}
|
||||||
};
|
};
|
||||||
|
@ -60,7 +57,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={MainContainer}
|
SelectedContainer={MainContainer}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
OnPropertiesSubmit={() => {}}
|
|
||||||
SelectContainer={() => {}}
|
SelectContainer={() => {}}
|
||||||
DeleteContainer={() => {}}
|
DeleteContainer={() => {}}
|
||||||
AddContainer={() => {}}
|
AddContainer={() => {}}
|
||||||
|
@ -74,12 +70,12 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
expect(screen.queryByText('y')).toBeDefined();
|
expect(screen.queryByText('y')).toBeDefined();
|
||||||
expect(screen.queryByText('width')).toBeDefined();
|
expect(screen.queryByText('width')).toBeDefined();
|
||||||
expect(screen.queryByText('height')).toBeDefined();
|
expect(screen.queryByText('height')).toBeDefined();
|
||||||
const propertyId = container.querySelector('#id');
|
const propertyId = container.querySelector('#property-id');
|
||||||
const propertyParentId = container.querySelector('#parentId');
|
const propertyParentId = container.querySelector('#property-parentId');
|
||||||
const propertyX = container.querySelector('#x');
|
const propertyX = container.querySelector('#property-x');
|
||||||
const propertyY = container.querySelector('#y');
|
const propertyY = container.querySelector('#property-y');
|
||||||
const propertyWidth = container.querySelector('#width');
|
const propertyWidth = container.querySelector('#property-width');
|
||||||
const propertyHeight = container.querySelector('#height');
|
const propertyHeight = container.querySelector('#property-height');
|
||||||
expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString());
|
expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString());
|
||||||
expect(propertyParentId).toBeDefined();
|
expect(propertyParentId).toBeDefined();
|
||||||
expect((propertyParentId as HTMLInputElement).value).toBe('');
|
expect((propertyParentId as HTMLInputElement).value).toBe('');
|
||||||
|
@ -105,8 +101,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 2000,
|
width: 2000,
|
||||||
height: 100,
|
height: 100,
|
||||||
isRigidBody: false,
|
isRigidBody: false
|
||||||
isAnchor: false
|
|
||||||
},
|
},
|
||||||
userData: {}
|
userData: {}
|
||||||
};
|
};
|
||||||
|
@ -122,8 +117,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
isRigidBody: false,
|
isRigidBody: false
|
||||||
isAnchor: false
|
|
||||||
},
|
},
|
||||||
userData: {}
|
userData: {}
|
||||||
}
|
}
|
||||||
|
@ -140,8 +134,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
isRigidBody: false,
|
isRigidBody: false
|
||||||
isAnchor: false
|
|
||||||
},
|
},
|
||||||
userData: {}
|
userData: {}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +146,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={MainContainer}
|
SelectedContainer={MainContainer}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
OnPropertiesSubmit={() => {}}
|
|
||||||
SelectContainer={() => {}}
|
SelectContainer={() => {}}
|
||||||
DeleteContainer={() => {}}
|
DeleteContainer={() => {}}
|
||||||
AddContainer={() => {}}
|
AddContainer={() => {}}
|
||||||
|
@ -178,8 +170,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 2000,
|
width: 2000,
|
||||||
height: 100,
|
height: 100,
|
||||||
isRigidBody: false,
|
isRigidBody: false
|
||||||
isAnchor: false
|
|
||||||
},
|
},
|
||||||
userData: {}
|
userData: {}
|
||||||
};
|
};
|
||||||
|
@ -194,8 +185,7 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
isRigidBody: false,
|
isRigidBody: false
|
||||||
isAnchor: false
|
|
||||||
},
|
},
|
||||||
userData: {}
|
userData: {}
|
||||||
};
|
};
|
||||||
|
@ -212,7 +202,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={SelectedContainer}
|
SelectedContainer={SelectedContainer}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
OnPropertiesSubmit={() => {}}
|
|
||||||
SelectContainer={selectContainer}
|
SelectContainer={selectContainer}
|
||||||
DeleteContainer={() => {}}
|
DeleteContainer={() => {}}
|
||||||
AddContainer={() => {}}
|
AddContainer={() => {}}
|
||||||
|
@ -223,8 +212,8 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
expect(screen.getByText(/main/i));
|
expect(screen.getByText(/main/i));
|
||||||
const child1 = screen.getByText(/child-1/i);
|
const child1 = screen.getByText(/child-1/i);
|
||||||
expect(child1);
|
expect(child1);
|
||||||
const propertyId = container.querySelector('#id');
|
const propertyId = container.querySelector('#property-id');
|
||||||
const propertyParentId = container.querySelector('#parentId');
|
const propertyParentId = container.querySelector('#property-parentId');
|
||||||
expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString());
|
expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString());
|
||||||
expect((propertyParentId as HTMLInputElement).value).toBe('');
|
expect((propertyParentId as HTMLInputElement).value).toBe('');
|
||||||
|
|
||||||
|
@ -236,7 +225,6 @@ describe.concurrent('Elements sidebar', () => {
|
||||||
isHistoryOpen={false}
|
isHistoryOpen={false}
|
||||||
SelectedContainer={SelectedContainer}
|
SelectedContainer={SelectedContainer}
|
||||||
OnPropertyChange={() => {}}
|
OnPropertyChange={() => {}}
|
||||||
OnPropertiesSubmit={() => {}}
|
|
||||||
SelectContainer={selectContainer}
|
SelectContainer={selectContainer}
|
||||||
DeleteContainer={() => {}}
|
DeleteContainer={() => {}}
|
||||||
AddContainer={() => {}}
|
AddContainer={() => {}}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { FixedSizeList as List } from 'react-window';
|
import { motion } from 'framer-motion';
|
||||||
import { Properties } from '../Properties/Properties';
|
import { Properties } from '../Properties/Properties';
|
||||||
import ContainerProperties from '../../Interfaces/IProperties';
|
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
||||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
|
||||||
import { getDepth, MakeIterator } from '../../utils/itertools';
|
import { getDepth, MakeIterator } from '../../utils/itertools';
|
||||||
import { Menu } from '../Menu/Menu';
|
import { Menu } from '../Menu/Menu';
|
||||||
import { MenuItem } from '../Menu/MenuItem';
|
import { MenuItem } from '../Menu/MenuItem';
|
||||||
import { handleDragLeave, handleDragOver, handleLeftClick, handleOnDrop, handleRightClick } from './MouseEventHandlers';
|
import { handleDragLeave, handleDragOver, handleLeftClick, handleOnDrop, handleRightClick } from './MouseEventHandlers';
|
||||||
import { IPoint } from '../../Interfaces/IPoint';
|
import { Point } from '../../Interfaces/Point';
|
||||||
|
|
||||||
interface IElementsSidebarProps {
|
interface IElementsSidebarProps {
|
||||||
MainContainer: IContainerModel
|
MainContainer: IContainerModel
|
||||||
|
@ -15,17 +14,55 @@ interface IElementsSidebarProps {
|
||||||
isHistoryOpen: boolean
|
isHistoryOpen: boolean
|
||||||
SelectedContainer: IContainerModel | null
|
SelectedContainer: IContainerModel | null
|
||||||
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
||||||
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, properties: ContainerProperties) => void
|
|
||||||
SelectContainer: (container: IContainerModel) => void
|
SelectContainer: (container: IContainerModel) => void
|
||||||
DeleteContainer: (containerid: string) => void
|
DeleteContainer: (containerid: string) => void
|
||||||
AddContainer: (index: number, type: string, parent: string) => void
|
AddContainer: (index: number, type: string, parent: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createRows(
|
||||||
|
container: IContainerModel,
|
||||||
|
props: IElementsSidebarProps,
|
||||||
|
containerRows: React.ReactNode[]
|
||||||
|
): void {
|
||||||
|
const depth: number = getDepth(container);
|
||||||
|
const key = container.properties.id.toString();
|
||||||
|
const text = '|\t'.repeat(depth) + key;
|
||||||
|
const selectedClass: string = props.SelectedContainer !== undefined &&
|
||||||
|
props.SelectedContainer !== null &&
|
||||||
|
props.SelectedContainer.properties.id === container.properties.id
|
||||||
|
? 'border-l-4 bg-slate-400/60 hover:bg-slate-400'
|
||||||
|
: 'bg-slate-300/60 hover:bg-slate-300';
|
||||||
|
|
||||||
|
containerRows.push(
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 1.2 }}
|
||||||
|
initial={{ opacity: 0, scale: 0 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
transition={{
|
||||||
|
duration: 0.150
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
`w-full border-blue-500 elements-sidebar-row whitespace-pre
|
||||||
|
text-left text-sm font-medium transition-all ${selectedClass}`
|
||||||
|
}
|
||||||
|
id={key}
|
||||||
|
key={key}
|
||||||
|
onDrop={(event) => handleOnDrop(event, props.MainContainer, props.AddContainer)}
|
||||||
|
onDragOver={(event) => handleDragOver(event, props.MainContainer)}
|
||||||
|
onDragLeave={(event) => handleDragLeave(event)}
|
||||||
|
onClick={() => props.SelectContainer(container)}
|
||||||
|
>
|
||||||
|
{ text }
|
||||||
|
</motion.button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElementsSidebarProps): JSX.Element => {
|
export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElementsSidebarProps): JSX.Element => {
|
||||||
// States
|
// States
|
||||||
const [isContextMenuOpen, setIsContextMenuOpen] = React.useState<boolean>(false);
|
const [isContextMenuOpen, setIsContextMenuOpen] = React.useState<boolean>(false);
|
||||||
const [onClickContainerId, setOnClickContainerId] = React.useState<string>('');
|
const [onClickContainerId, setOnClickContainerId] = React.useState<string>('');
|
||||||
const [contextMenuPosition, setContextMenuPosition] = React.useState<IPoint>({
|
const [contextMenuPosition, setContextMenuPosition] = React.useState<Point>({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0
|
y: 0
|
||||||
});
|
});
|
||||||
|
@ -68,7 +105,7 @@ export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElement
|
||||||
onLeftClick
|
onLeftClick
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
let isOpenClasses = '-right-64';
|
let isOpenClasses = '-right-64';
|
||||||
|
@ -78,55 +115,24 @@ export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElement
|
||||||
: 'right-0';
|
: 'right-0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const containerRows: React.ReactNode[] = [];
|
||||||
|
|
||||||
const it = MakeIterator(props.MainContainer);
|
const it = MakeIterator(props.MainContainer);
|
||||||
const containers = [...it];
|
for (const container of it) {
|
||||||
const Row = ({ index, style }: {index: number, style: React.CSSProperties}): JSX.Element => {
|
createRows(
|
||||||
const container = containers[index];
|
container,
|
||||||
const depth: number = getDepth(container);
|
props,
|
||||||
const key = container.properties.id.toString();
|
containerRows
|
||||||
const text = '|\t'.repeat(depth) + key;
|
|
||||||
const selectedClass: string = props.SelectedContainer !== undefined &&
|
|
||||||
props.SelectedContainer !== null &&
|
|
||||||
props.SelectedContainer.properties.id === container.properties.id
|
|
||||||
? 'border-l-4 bg-slate-400/60 hover:bg-slate-400'
|
|
||||||
: 'bg-slate-300/60 hover:bg-slate-300';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className={
|
|
||||||
`w-full border-blue-500 elements-sidebar-row whitespace-pre
|
|
||||||
text-left text-sm font-medium transition-all ${selectedClass}`
|
|
||||||
}
|
|
||||||
id={key}
|
|
||||||
key={key}
|
|
||||||
style={style}
|
|
||||||
onDrop={(event) => handleOnDrop(event, props.MainContainer, props.AddContainer)}
|
|
||||||
onDragOver={(event) => handleDragOver(event, props.MainContainer)}
|
|
||||||
onDragLeave={(event) => handleDragLeave(event)}
|
|
||||||
onClick={() => props.SelectContainer(container)}
|
|
||||||
>
|
|
||||||
{ text }
|
|
||||||
</button>
|
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
const ROW_HEIGHT = 35;
|
|
||||||
const NUMBERS_OF_ROWS = 10;
|
|
||||||
return (
|
return (
|
||||||
<div className={`fixed flex flex-col bg-slate-100 text-gray-800 transition-all h-full w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
|
<div className={`fixed flex flex-col bg-slate-100 text-gray-800 transition-all h-screen w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
|
||||||
<div className='bg-slate-100 font-bold sidebar-title'>
|
<div className='bg-slate-100 font-bold sidebar-title'>
|
||||||
Elements
|
Elements
|
||||||
</div>
|
</div>
|
||||||
<div ref={elementRef} className='h-96 text-gray-800'>
|
<div ref={elementRef} className='overflow-y-auto overflow-x-hidden text-gray-800 flex-grow'>
|
||||||
<List
|
{ containerRows }
|
||||||
className='List'
|
|
||||||
itemCount={containers.length}
|
|
||||||
itemSize={35}
|
|
||||||
height={384}
|
|
||||||
width={256}
|
|
||||||
>
|
|
||||||
{ Row }
|
|
||||||
</List>
|
|
||||||
</div>
|
</div>
|
||||||
<Menu
|
<Menu
|
||||||
className='transition-opacity rounded bg-slate-200 py-1 drop-shadow-xl'
|
className='transition-opacity rounded bg-slate-200 py-1 drop-shadow-xl'
|
||||||
|
@ -139,11 +145,7 @@ export const ElementsSidebar: React.FC<IElementsSidebarProps> = (props: IElement
|
||||||
props.DeleteContainer(onClickContainerId);
|
props.DeleteContainer(onClickContainerId);
|
||||||
}} />
|
}} />
|
||||||
</Menu>
|
</Menu>
|
||||||
<Properties
|
<Properties properties={props.SelectedContainer?.properties} onChange={props.OnPropertyChange}></Properties>
|
||||||
properties={props.SelectedContainer?.properties}
|
|
||||||
onChange={props.OnPropertyChange}
|
|
||||||
onSubmit={props.OnPropertiesSubmit}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { IContainerModel } from '../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../Interfaces/ContainerModel';
|
||||||
import { IPoint } from '../../Interfaces/IPoint';
|
import { Point } from '../../Interfaces/Point';
|
||||||
import { findContainerById } from '../../utils/itertools';
|
import { findContainerById } from '../../utils/itertools';
|
||||||
|
|
||||||
export function handleRightClick(
|
export function handleRightClick(
|
||||||
event: MouseEvent,
|
event: MouseEvent,
|
||||||
setIsContextMenuOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
setIsContextMenuOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
||||||
setOnClickContainerId: React.Dispatch<React.SetStateAction<string>>,
|
setOnClickContainerId: React.Dispatch<React.SetStateAction<string>>,
|
||||||
setContextMenuPosition: React.Dispatch<React.SetStateAction<IPoint>>
|
setContextMenuPosition: React.Dispatch<React.SetStateAction<Point>>
|
||||||
): void {
|
): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ export function handleRightClick(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contextMenuPosition: IPoint = { x: event.pageX, y: event.pageY };
|
const contextMenuPosition: Point = { x: event.pageX, y: event.pageY };
|
||||||
setIsContextMenuOpen(true);
|
setIsContextMenuOpen(true);
|
||||||
setOnClickContainerId(event.target.id);
|
setOnClickContainerId(event.target.id);
|
||||||
setContextMenuPosition(contextMenuPosition);
|
setContextMenuPosition(contextMenuPosition);
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { FixedSizeList as List } from 'react-window';
|
import { HistoryState } from "../../Interfaces/HistoryState";
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
|
||||||
|
|
||||||
interface IHistoryProps {
|
interface IHistoryProps {
|
||||||
history: IHistoryState[]
|
history: HistoryState[]
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
jumpTo: (move: number) => void
|
jumpTo: (move: number) => void
|
||||||
|
@ -11,45 +10,47 @@ interface IHistoryProps {
|
||||||
|
|
||||||
export const History: React.FC<IHistoryProps> = (props: IHistoryProps) => {
|
export const History: React.FC<IHistoryProps> = (props: IHistoryProps) => {
|
||||||
const isOpenClasses = props.isOpen ? 'right-0' : '-right-64';
|
const isOpenClasses = props.isOpen ? 'right-0' : '-right-64';
|
||||||
const Row = ({ index, style }: {index: number, style: React.CSSProperties}): JSX.Element => {
|
|
||||||
const reversedIndex = (props.history.length - 1) - index;
|
|
||||||
const step = props.history[reversedIndex];
|
|
||||||
const desc = step.LastAction;
|
|
||||||
|
|
||||||
const selectedClass = reversedIndex === props.historyCurrentStep
|
const states = props.history.map((step, move) => {
|
||||||
|
const desc = move > 0
|
||||||
|
? `Go to modification n°${move}`
|
||||||
|
: 'Go to the beginning';
|
||||||
|
|
||||||
|
const isCurrent = move === props.historyCurrentStep;
|
||||||
|
|
||||||
|
const selectedClass = isCurrent
|
||||||
? 'bg-blue-500 hover:bg-blue-600'
|
? 'bg-blue-500 hover:bg-blue-600'
|
||||||
: 'bg-slate-500 hover:bg-slate-700';
|
: 'bg-slate-500 hover:bg-slate-700';
|
||||||
|
|
||||||
|
const isCurrentText = isCurrent
|
||||||
|
? ' (current)'
|
||||||
|
: '';
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<button
|
<button
|
||||||
key={reversedIndex}
|
key={move}
|
||||||
style={style}
|
onClick={() => props.jumpTo(move)}
|
||||||
onClick={() => props.jumpTo(reversedIndex)}
|
|
||||||
title={step.LastAction}
|
|
||||||
className={
|
className={
|
||||||
`w-full elements-sidebar-row whitespace-pre overflow-hidden
|
`w-full elements-sidebar-row whitespace-pre
|
||||||
text-left text-sm font-medium transition-all ${selectedClass}`
|
text-left text-sm font-medium transition-all ${selectedClass}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{desc}
|
{desc}{isCurrentText}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
|
// recent first
|
||||||
|
states.reverse();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`fixed flex flex-col bg-slate-300 text-white transition-all h-full w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
|
<div className={`fixed flex flex-col bg-slate-300 text-white transition-all h-screen w-64 overflow-y-auto z-20 ${isOpenClasses}`}>
|
||||||
<div className='bg-slate-600 font-bold sidebar-title'>
|
<div className='bg-slate-600 font-bold sidebar-title'>
|
||||||
Timeline
|
Timeline
|
||||||
</div>
|
</div>
|
||||||
<List
|
<div className='overflow-y-auto overflow-x-hidden text-slate-300 flex-grow divide-y divide-solid divide-slate-600'>
|
||||||
className='List overflow-x-hidden'
|
{ states }
|
||||||
itemCount={props.history.length}
|
</div>
|
||||||
itemSize={35}
|
|
||||||
height={window.innerHeight}
|
|
||||||
width={256}
|
|
||||||
>
|
|
||||||
{ Row }
|
|
||||||
</List>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,8 +38,13 @@ export const MainMenu: React.FC<IMainMenuProps> = (props) => {
|
||||||
</form>
|
</form>
|
||||||
<button
|
<button
|
||||||
onClick={() => setWindowState(WindowState.MAIN)}
|
onClick={() => setWindowState(WindowState.MAIN)}
|
||||||
className='normal-btn block
|
className='block text-sm
|
||||||
mt-8 '
|
mt-8 py-2 px-4
|
||||||
|
rounded-full border-0
|
||||||
|
font-semibold
|
||||||
|
transition-all
|
||||||
|
bg-blue-100 text-blue-700
|
||||||
|
hover:bg-blue-200'
|
||||||
>
|
>
|
||||||
Go back
|
Go back
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -8,7 +8,6 @@ describe.concurrent('Properties', () => {
|
||||||
render(<Properties
|
render(<Properties
|
||||||
properties={undefined}
|
properties={undefined}
|
||||||
onChange={() => {}}
|
onChange={() => {}}
|
||||||
onSubmit={() => {}}
|
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.queryByText('id')).toBeNull();
|
expect(screen.queryByText('id')).toBeNull();
|
||||||
|
@ -17,14 +16,13 @@ describe.concurrent('Properties', () => {
|
||||||
expect(screen.queryByText('y')).toBeNull();
|
expect(screen.queryByText('y')).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Some properties, change values with dynamic input', () => {
|
it('Some properties', () => {
|
||||||
const prop = {
|
const prop = {
|
||||||
id: 'stuff',
|
id: 'stuff',
|
||||||
parentId: 'parentId',
|
parentId: 'parentId',
|
||||||
x: 1,
|
x: 1,
|
||||||
y: 1,
|
y: 1,
|
||||||
isRigidBody: false,
|
isRigidBody: false
|
||||||
isAnchor: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = vi.fn((key, value) => {
|
const handleChange = vi.fn((key, value) => {
|
||||||
|
@ -34,7 +32,6 @@ describe.concurrent('Properties', () => {
|
||||||
const { container, rerender } = render(<Properties
|
const { container, rerender } = render(<Properties
|
||||||
properties={prop}
|
properties={prop}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onSubmit={() => {}}
|
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
expect(screen.queryByText('id')).toBeDefined();
|
expect(screen.queryByText('id')).toBeDefined();
|
||||||
|
@ -42,10 +39,10 @@ describe.concurrent('Properties', () => {
|
||||||
expect(screen.queryByText('x')).toBeDefined();
|
expect(screen.queryByText('x')).toBeDefined();
|
||||||
expect(screen.queryByText('y')).toBeDefined();
|
expect(screen.queryByText('y')).toBeDefined();
|
||||||
|
|
||||||
let propertyId = container.querySelector('#id');
|
let propertyId = container.querySelector('#property-id');
|
||||||
let propertyParentId = container.querySelector('#parentId');
|
let propertyParentId = container.querySelector('#property-parentId');
|
||||||
let propertyX = container.querySelector('#x');
|
let propertyX = container.querySelector('#property-x');
|
||||||
let propertyY = container.querySelector('#y');
|
let propertyY = container.querySelector('#property-y');
|
||||||
expect(propertyId).toBeDefined();
|
expect(propertyId).toBeDefined();
|
||||||
expect((propertyId as HTMLInputElement).value).toBe('stuff');
|
expect((propertyId as HTMLInputElement).value).toBe('stuff');
|
||||||
expect(propertyParentId).toBeDefined();
|
expect(propertyParentId).toBeDefined();
|
||||||
|
@ -68,13 +65,12 @@ describe.concurrent('Properties', () => {
|
||||||
rerender(<Properties
|
rerender(<Properties
|
||||||
properties={Object.assign({}, prop)}
|
properties={Object.assign({}, prop)}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onSubmit={() => {}}
|
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
propertyId = container.querySelector('#id');
|
propertyId = container.querySelector('#property-id');
|
||||||
propertyParentId = container.querySelector('#parentId');
|
propertyParentId = container.querySelector('#property-parentId');
|
||||||
propertyX = container.querySelector('#x');
|
propertyX = container.querySelector('#property-x');
|
||||||
propertyY = container.querySelector('#y');
|
propertyY = container.querySelector('#property-y');
|
||||||
expect(propertyId).toBeDefined();
|
expect(propertyId).toBeDefined();
|
||||||
expect((propertyId as HTMLInputElement).value).toBe('stuffed');
|
expect((propertyId as HTMLInputElement).value).toBe('stuffed');
|
||||||
expect(propertyParentId).toBeDefined();
|
expect(propertyParentId).toBeDefined();
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
import React, { useState } from 'react';
|
import * as React from 'react';
|
||||||
import ContainerProperties from '../../Interfaces/IProperties';
|
import ContainerProperties from '../../Interfaces/Properties';
|
||||||
import { ToggleButton } from '../ToggleButton/ToggleButton';
|
|
||||||
import { INPUT_TYPES } from './PropertiesInputTypes';
|
import { INPUT_TYPES } from './PropertiesInputTypes';
|
||||||
|
|
||||||
interface IPropertiesProps {
|
interface IPropertiesProps {
|
||||||
properties?: ContainerProperties
|
properties?: ContainerProperties
|
||||||
onChange: (key: string, value: string | number | boolean) => void
|
onChange: (key: string, value: string | number | boolean) => void
|
||||||
onSubmit: (event: React.FormEvent<HTMLFormElement>, properties: ContainerProperties) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps) => {
|
export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps) => {
|
||||||
const [isDynamicInput, setIsDynamicInput] = useState<boolean>(true);
|
|
||||||
|
|
||||||
if (props.properties === undefined) {
|
if (props.properties === undefined) {
|
||||||
return <div></div>;
|
return <div></div>;
|
||||||
}
|
}
|
||||||
|
@ -19,33 +15,11 @@ export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps)
|
||||||
const groupInput: React.ReactNode[] = [];
|
const groupInput: React.ReactNode[] = [];
|
||||||
Object
|
Object
|
||||||
.entries(props.properties)
|
.entries(props.properties)
|
||||||
.forEach((pair) => handleProperties(pair, groupInput, isDynamicInput, props.onChange));
|
.forEach((pair) => handleProperties(pair, groupInput, props.onChange));
|
||||||
|
|
||||||
const form = isDynamicInput
|
|
||||||
? <div className='grid grid-cols-2 gap-4'>
|
|
||||||
{ groupInput }
|
|
||||||
</div>
|
|
||||||
: <form
|
|
||||||
key={props.properties.id}
|
|
||||||
onSubmit={(event) => props.onSubmit(event, props.properties as ContainerProperties)}
|
|
||||||
>
|
|
||||||
<input type='submit' className='normal-btn block mx-auto mb-4 border-2 border-blue-400 cursor-pointer' value='Submit'/>
|
|
||||||
<div className='grid grid-cols-2 gap-y-4'>
|
|
||||||
{ groupInput }
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='h-3/5 p-3 bg-slate-200 overflow-y-auto'>
|
<div className='p-3 bg-slate-200 h-3/5 overflow-y-auto'>
|
||||||
<ToggleButton
|
{ groupInput }
|
||||||
id='isDynamic'
|
|
||||||
text='Dynamic update'
|
|
||||||
title='Enable dynamic svg update'
|
|
||||||
checked={isDynamicInput}
|
|
||||||
onChange={() => setIsDynamicInput(!isDynamicInput)}
|
|
||||||
/>
|
|
||||||
{ form }
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -53,7 +27,6 @@ export const Properties: React.FC<IPropertiesProps> = (props: IPropertiesProps)
|
||||||
const handleProperties = (
|
const handleProperties = (
|
||||||
[key, value]: [string, string | number],
|
[key, value]: [string, string | number],
|
||||||
groupInput: React.ReactNode[],
|
groupInput: React.ReactNode[],
|
||||||
isDynamicInput: boolean,
|
|
||||||
onChange: (key: string, value: string | number | boolean) => void
|
onChange: (key: string, value: string | number | boolean) => void
|
||||||
): void => {
|
): void => {
|
||||||
const id = `property-${key}`;
|
const id = `property-${key}`;
|
||||||
|
@ -69,48 +42,31 @@ const handleProperties = (
|
||||||
type = INPUT_TYPES[key];
|
type = INPUT_TYPES[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
const className = `
|
|
||||||
w-full
|
|
||||||
text-xs font-medium transition-all text-gray-800 mt-1 px-3 py-2
|
|
||||||
bg-white border-2 border-white rounded-lg placeholder-gray-800
|
|
||||||
focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500
|
|
||||||
disabled:bg-slate-300 disabled:text-gray-500 disabled:border-slate-300 disabled:shadow-none`;
|
|
||||||
const isDisabled = ['id', 'parentId'].includes(key);
|
const isDisabled = ['id', 'parentId'].includes(key);
|
||||||
const input = isDynamicInput
|
///
|
||||||
? <input
|
|
||||||
key={key}
|
|
||||||
id={key}
|
|
||||||
className={className}
|
|
||||||
type={type}
|
|
||||||
value={value}
|
|
||||||
checked={checked}
|
|
||||||
onChange={(event) => {
|
|
||||||
if (type === 'checkbox') {
|
|
||||||
onChange(key, event.target.checked);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onChange(key, event.target.value);
|
|
||||||
}}
|
|
||||||
disabled={isDisabled}
|
|
||||||
/>
|
|
||||||
: <input
|
|
||||||
key={key}
|
|
||||||
id={key}
|
|
||||||
className={className}
|
|
||||||
type={type}
|
|
||||||
defaultValue={value}
|
|
||||||
defaultChecked={checked}
|
|
||||||
disabled={isDisabled}
|
|
||||||
/>;
|
|
||||||
|
|
||||||
groupInput.push(
|
groupInput.push(
|
||||||
<label
|
<div key={id} className='mt-4'>
|
||||||
key={id}
|
<label className='text-sm font-medium text-gray-800' htmlFor={id}>{key}</label>
|
||||||
className='mt-4 text-xs font-medium text-gray-800'
|
<input
|
||||||
htmlFor={key}
|
className='text-base font-medium transition-all text-gray-800 mt-1 block w-full px-3 py-2
|
||||||
>
|
bg-white border-2 border-white rounded-lg placeholder-gray-800
|
||||||
{key}
|
focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500
|
||||||
</label>
|
disabled:bg-slate-300 disabled:text-gray-500 disabled:border-slate-300 disabled:shadow-none
|
||||||
|
'
|
||||||
|
type={type}
|
||||||
|
id={id}
|
||||||
|
value={value}
|
||||||
|
checked={checked}
|
||||||
|
onChange={(event) => {
|
||||||
|
if (type === 'checkbox') {
|
||||||
|
onChange(key, event.target.checked);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onChange(key, event.target.value);
|
||||||
|
}}
|
||||||
|
disabled={isDisabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
groupInput.push(input);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,5 @@ export const INPUT_TYPES: Record<string, string> = {
|
||||||
y: 'number',
|
y: 'number',
|
||||||
width: 'number',
|
width: 'number',
|
||||||
height: 'number',
|
height: 'number',
|
||||||
isRigidBody: 'checkbox',
|
isRigidBody: 'checkbox'
|
||||||
isAnchor: 'checkbox'
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { XPositionReference } from '../../../Enums/XPositionReference';
|
import { IContainerModel } from '../../../Interfaces/ContainerModel';
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
|
||||||
import { getDepth } from '../../../utils/itertools';
|
import { getDepth } from '../../../utils/itertools';
|
||||||
import { Dimension } from './Dimension';
|
import { Dimension } from './Dimension';
|
||||||
|
|
||||||
interface IContainerProps {
|
export interface IContainerProps {
|
||||||
model: IContainerModel
|
model: IContainerModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,14 +17,7 @@ export const Container: React.FC<IContainerProps> = (props: IContainerProps) =>
|
||||||
const containersElements = props.model.children.map(child => <Container key={`container-${child.properties.id}`} model={child} />);
|
const containersElements = props.model.children.map(child => <Container key={`container-${child.properties.id}`} model={child} />);
|
||||||
const xText = Number(props.model.properties.width) / 2;
|
const xText = Number(props.model.properties.width) / 2;
|
||||||
const yText = Number(props.model.properties.height) / 2;
|
const yText = Number(props.model.properties.height) / 2;
|
||||||
|
const transform = `translate(${Number(props.model.properties.x)}, ${Number(props.model.properties.y)})`;
|
||||||
const [transformedX, transformedY] = transformPosition(
|
|
||||||
Number(props.model.properties.x),
|
|
||||||
Number(props.model.properties.y),
|
|
||||||
Number(props.model.properties.width),
|
|
||||||
props.model.properties.XPositionReference
|
|
||||||
);
|
|
||||||
const transform = `translate(${transformedX}, ${transformedY})`;
|
|
||||||
|
|
||||||
// g style
|
// g style
|
||||||
const defaultStyle: React.CSSProperties = {
|
const defaultStyle: React.CSSProperties = {
|
||||||
|
@ -62,8 +54,7 @@ export const Container: React.FC<IContainerProps> = (props: IContainerProps) =>
|
||||||
id={id}
|
id={id}
|
||||||
xStart={xStart}
|
xStart={xStart}
|
||||||
xEnd={xEnd}
|
xEnd={xEnd}
|
||||||
yStart={y}
|
y={y}
|
||||||
yEnd={y}
|
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
text={text}
|
text={text}
|
||||||
/>
|
/>
|
||||||
|
@ -83,13 +74,3 @@ export const Container: React.FC<IContainerProps> = (props: IContainerProps) =>
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function transformPosition(x: number, y: number, width: number, xPositionReference = XPositionReference.Left): [number, number] {
|
|
||||||
let transformedX = x;
|
|
||||||
if (xPositionReference === XPositionReference.Center) {
|
|
||||||
transformedX -= width / 2;
|
|
||||||
} else if (xPositionReference === XPositionReference.Right) {
|
|
||||||
transformedX -= width;
|
|
||||||
}
|
|
||||||
return [transformedX, y];
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,83 +1,47 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { NOTCHES_LENGTH } from '../../../utils/default';
|
|
||||||
|
|
||||||
interface IDimensionProps {
|
interface IDimensionProps {
|
||||||
id: string
|
id: string
|
||||||
xStart: number
|
xStart: number
|
||||||
yStart: number
|
|
||||||
xEnd: number
|
xEnd: number
|
||||||
yEnd: number
|
y: number
|
||||||
text: string
|
text: string
|
||||||
strokeWidth: number
|
strokeWidth: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 2D Parametric function. Returns a new coordinate from the origin coordinate
|
|
||||||
* See for more details https://en.wikipedia.org/wiki/Parametric_equation.
|
|
||||||
* TL;DR a parametric function is a function with a parameter
|
|
||||||
* @param x0 Origin coordinate
|
|
||||||
* @param t The parameter
|
|
||||||
* @param vx Transform vector
|
|
||||||
* @returns Returns a new coordinate from the origin coordinate
|
|
||||||
*/
|
|
||||||
const applyParametric = (x0: number, t: number, vx: number): number => x0 + t * vx;
|
|
||||||
|
|
||||||
export const Dimension: React.FC<IDimensionProps> = (props: IDimensionProps) => {
|
export const Dimension: React.FC<IDimensionProps> = (props: IDimensionProps) => {
|
||||||
const style: React.CSSProperties = {
|
const style: React.CSSProperties = {
|
||||||
stroke: 'black'
|
stroke: 'black'
|
||||||
};
|
};
|
||||||
|
|
||||||
/// We need to find the points of the notches
|
|
||||||
// Get the vector of the line
|
|
||||||
const [deltaX, deltaY] = [(props.xEnd - props.xStart), (props.yEnd - props.yStart)];
|
|
||||||
|
|
||||||
// Get the unit vector
|
|
||||||
const norm = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
||||||
const [unitX, unitY] = [deltaX / norm, deltaY / norm];
|
|
||||||
|
|
||||||
// Get the perpandicular vector
|
|
||||||
const [perpVecX, perpVecY] = [unitY, -unitX];
|
|
||||||
|
|
||||||
// Use the parametric function to get the coordinates (x = x0 + t * v.x)
|
|
||||||
const startTopX = applyParametric(props.xStart, NOTCHES_LENGTH, perpVecX);
|
|
||||||
const startTopY = applyParametric(props.yStart, NOTCHES_LENGTH, perpVecY);
|
|
||||||
const startBottomX = applyParametric(props.xStart, -NOTCHES_LENGTH, perpVecX);
|
|
||||||
const startBottomY = applyParametric(props.yStart, -NOTCHES_LENGTH, perpVecY);
|
|
||||||
|
|
||||||
const endTopX = applyParametric(props.xEnd, NOTCHES_LENGTH, perpVecX);
|
|
||||||
const endTopY = applyParametric(props.yEnd, NOTCHES_LENGTH, perpVecY);
|
|
||||||
const endBottomX = applyParametric(props.xEnd, -NOTCHES_LENGTH, perpVecX);
|
|
||||||
const endBottomY = applyParametric(props.yEnd, -NOTCHES_LENGTH, perpVecY);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g key={props.id}>
|
<g key={props.id}>
|
||||||
<line
|
<line
|
||||||
x1={startTopX}
|
x1={props.xStart}
|
||||||
y1={startTopY}
|
y1={props.y - 4 * props.strokeWidth}
|
||||||
x2={startBottomX}
|
x2={props.xStart}
|
||||||
y2={startBottomY}
|
y2={props.y + 4 * props.strokeWidth}
|
||||||
strokeWidth={props.strokeWidth}
|
strokeWidth={props.strokeWidth}
|
||||||
style={style}
|
style={style}
|
||||||
/>
|
/>
|
||||||
<line
|
<line
|
||||||
x1={props.xStart}
|
x1={props.xStart}
|
||||||
y1={props.yStart}
|
y1={props.y}
|
||||||
x2={props.xEnd}
|
x2={props.xEnd}
|
||||||
y2={props.yEnd}
|
y2={props.y}
|
||||||
strokeWidth={props.strokeWidth}
|
strokeWidth={props.strokeWidth}
|
||||||
style={style}
|
style={style}
|
||||||
/>
|
/>
|
||||||
<line
|
<line
|
||||||
x1={endTopX}
|
x1={props.xEnd}
|
||||||
y1={endTopY}
|
y1={props.y - 4 * props.strokeWidth}
|
||||||
x2={endBottomX}
|
x2={props.xEnd}
|
||||||
y2={endBottomY}
|
y2={props.y + 4 * props.strokeWidth}
|
||||||
strokeWidth={props.strokeWidth}
|
strokeWidth={props.strokeWidth}
|
||||||
style={style}
|
style={style}
|
||||||
/>
|
/>
|
||||||
<text
|
<text
|
||||||
x={(props.xStart + props.xEnd) / 2}
|
x={(props.xStart + props.xEnd) / 2}
|
||||||
y={props.yStart}
|
y={props.y}
|
||||||
>
|
>
|
||||||
{props.text}
|
{props.text}
|
||||||
</text>
|
</text>
|
||||||
|
|
66
src/Components/SVG/Elements/DimensionLayer.tsx
Normal file
66
src/Components/SVG/Elements/DimensionLayer.tsx
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { ContainerModel } from '../../../Interfaces/ContainerModel';
|
||||||
|
import { getDepth, MakeIterator } from '../../../utils/itertools';
|
||||||
|
import { Dimension } from './Dimension';
|
||||||
|
|
||||||
|
interface IDimensionLayerProps {
|
||||||
|
isHidden: boolean
|
||||||
|
roots: ContainerModel | ContainerModel[] | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const GAP: number = 50;
|
||||||
|
|
||||||
|
const getDimensionsNodes = (root: ContainerModel): React.ReactNode[] => {
|
||||||
|
const it = MakeIterator(root);
|
||||||
|
const dimensions: React.ReactNode[] = [];
|
||||||
|
for (const container of it) {
|
||||||
|
// WARN: this might be dangerous later when using other units/rules
|
||||||
|
const width = Number(container.properties.width);
|
||||||
|
|
||||||
|
const id = `dim-${container.properties.id}`;
|
||||||
|
const xStart: number = container.properties.x;
|
||||||
|
const xEnd = xStart + width;
|
||||||
|
const y = -(GAP * (getDepth(container) + 1));
|
||||||
|
const strokeWidth = 1;
|
||||||
|
const text = width.toString();
|
||||||
|
dimensions.push(
|
||||||
|
<Dimension
|
||||||
|
id={id}
|
||||||
|
xStart={xStart}
|
||||||
|
xEnd={xEnd}
|
||||||
|
y={y}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
text={text}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return dimensions;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A layer containing all dimension
|
||||||
|
*
|
||||||
|
* @deprecated In order to avoid adding complexity
|
||||||
|
* with computing the position in a group hierarchy,
|
||||||
|
* use Dimension directly inside the Container,
|
||||||
|
* Currently it is glitched as
|
||||||
|
* it does not take parents into account,
|
||||||
|
* and will not work correctly
|
||||||
|
* @param props
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const DimensionLayer: React.FC<IDimensionLayerProps> = (props: IDimensionLayerProps) => {
|
||||||
|
let dimensions: React.ReactNode[] = [];
|
||||||
|
if (Array.isArray(props.roots)) {
|
||||||
|
props.roots.forEach(child => {
|
||||||
|
dimensions.concat(getDimensionsNodes(child));
|
||||||
|
});
|
||||||
|
} else if (props.roots !== null) {
|
||||||
|
dimensions = getDimensionsNodes(props.roots);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<g visibility={props.isHidden ? 'hidden' : 'visible'}>
|
||||||
|
{ dimensions }
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
import { IContainerModel } from '../../../Interfaces/ContainerModel';
|
||||||
import { getAbsolutePosition } from '../../../utils/itertools';
|
import { getAbsolutePosition } from '../../../utils/itertools';
|
||||||
|
|
||||||
interface ISelectorProps {
|
interface ISelectorProps {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { UncontrolledReactSVGPanZoom } from 'react-svg-pan-zoom';
|
import { UncontrolledReactSVGPanZoom } from 'react-svg-pan-zoom';
|
||||||
import { Container } from './Elements/Container';
|
import { Container } from './Elements/Container';
|
||||||
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel } from '../../Interfaces/ContainerModel';
|
||||||
import { Selector } from './Elements/Selector';
|
import { Selector } from './Elements/Selector';
|
||||||
import { BAR_WIDTH } from '../Bar/Bar';
|
import { BAR_WIDTH } from '../Bar/Bar';
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ function resizeViewBox(
|
||||||
|
|
||||||
export const SVG: React.FC<ISVGProps> = (props: ISVGProps) => {
|
export const SVG: React.FC<ISVGProps> = (props: ISVGProps) => {
|
||||||
const [viewer, setViewer] = React.useState<Viewer>({
|
const [viewer, setViewer] = React.useState<Viewer>({
|
||||||
viewerWidth: window.innerWidth - BAR_WIDTH,
|
viewerWidth: window.innerWidth,
|
||||||
viewerHeight: window.innerHeight
|
viewerHeight: window.innerHeight
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
import { AvailableContainer } from '../../Interfaces/AvailableContainer';
|
||||||
import { truncateString } from '../../utils/stringtools';
|
import { truncateString } from '../../utils/stringtools';
|
||||||
|
|
||||||
interface ISidebarProps {
|
interface ISidebarProps {
|
||||||
componentOptions: IAvailableContainer[]
|
componentOptions: AvailableContainer[]
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
buttonOnClick: (type: string) => void
|
buttonOnClick: (type: string) => void
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ export const Sidebar: React.FC<ISidebarProps> = (props: ISidebarProps) => {
|
||||||
const isOpenClasses = props.isOpen ? 'left-16' : '-left-64';
|
const isOpenClasses = props.isOpen ? 'left-16' : '-left-64';
|
||||||
return (
|
return (
|
||||||
<div className={`fixed z-10 bg-slate-200
|
<div className={`fixed z-10 bg-slate-200
|
||||||
text-gray-700 transition-all h-full w-64
|
text-gray-700 transition-all h-screen w-64
|
||||||
overflow-y-auto ${isOpenClasses}`}>
|
overflow-y-auto ${isOpenClasses}`}>
|
||||||
<div className='bg-slate-100 sidebar-title'>
|
<div className='bg-slate-100 sidebar-title'>
|
||||||
Components
|
Components
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
input:checked ~ .dot {
|
|
||||||
transform: translateX(100%);
|
|
||||||
}
|
|
||||||
input:checked ~ .line {
|
|
||||||
background-color: #3B82F6;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
import React, { FC } from 'react';
|
|
||||||
import './ToggleButton.scss';
|
|
||||||
|
|
||||||
interface IToggleButtonProps {
|
|
||||||
id: string
|
|
||||||
text: string
|
|
||||||
type?: TOGGLE_TYPE
|
|
||||||
title: string
|
|
||||||
checked: boolean
|
|
||||||
onChange: React.ChangeEventHandler<HTMLInputElement>
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum TOGGLE_TYPE {
|
|
||||||
MATERIAL,
|
|
||||||
IOS
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ToggleButton: FC<IToggleButtonProps> = (props) => {
|
|
||||||
const id = `toggle-${props.id}`;
|
|
||||||
const type = props.type ?? TOGGLE_TYPE.MATERIAL;
|
|
||||||
let classLine = 'line w-10 h-4 bg-gray-400 rounded-full shadow-inner';
|
|
||||||
let classDot = 'dot absolute w-6 h-6 bg-white rounded-full shadow -left-1 -top-1 transition';
|
|
||||||
if (type === TOGGLE_TYPE.IOS) {
|
|
||||||
classLine = 'line block bg-gray-600 w-14 h-8 rounded-full';
|
|
||||||
classDot = 'dot absolute left-1 top-1 bg-white w-6 h-6 rounded-full transition';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div title={props.title}>
|
|
||||||
<div className="flex items-center justify-center w-full mb-12">
|
|
||||||
<label
|
|
||||||
htmlFor={id}
|
|
||||||
className="flex items-center cursor-pointer"
|
|
||||||
>
|
|
||||||
<div className="relative">
|
|
||||||
<input
|
|
||||||
id={id}
|
|
||||||
type="checkbox"
|
|
||||||
onChange={props.onChange}
|
|
||||||
checked={props.checked}
|
|
||||||
className="sr-only" />
|
|
||||||
<div className={classLine}></div>
|
|
||||||
<div className={classDot}></div>
|
|
||||||
</div>
|
|
||||||
<div className="ml-3 text-gray-700 font-medium">
|
|
||||||
{ props.text }
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -2,23 +2,21 @@ import * as React from 'react';
|
||||||
import { ElementsSidebar } from '../ElementsSidebar/ElementsSidebar';
|
import { ElementsSidebar } from '../ElementsSidebar/ElementsSidebar';
|
||||||
import { Sidebar } from '../Sidebar/Sidebar';
|
import { Sidebar } from '../Sidebar/Sidebar';
|
||||||
import { History } from '../History/History';
|
import { History } from '../History/History';
|
||||||
import { IAvailableContainer } from '../../Interfaces/IAvailableContainer';
|
import { AvailableContainer } from '../../Interfaces/AvailableContainer';
|
||||||
import { ContainerModel } from '../../Interfaces/IContainerModel';
|
import { ContainerModel } from '../../Interfaces/ContainerModel';
|
||||||
import { IHistoryState } from '../../Interfaces/IHistoryState';
|
import { HistoryState } from '../../Interfaces/HistoryState';
|
||||||
import { PhotographIcon, UploadIcon } from '@heroicons/react/outline';
|
import { PhotographIcon, UploadIcon } from '@heroicons/react/outline';
|
||||||
import { FloatingButton } from '../FloatingButton/FloatingButton';
|
import { FloatingButton } from '../FloatingButton/FloatingButton';
|
||||||
import { Bar } from '../Bar/Bar';
|
import { Bar } from '../Bar/Bar';
|
||||||
import IProperties from '../../Interfaces/IProperties';
|
|
||||||
|
|
||||||
interface IUIProps {
|
interface IUIProps {
|
||||||
current: IHistoryState
|
current: HistoryState
|
||||||
history: IHistoryState[]
|
history: HistoryState[]
|
||||||
historyCurrentStep: number
|
historyCurrentStep: number
|
||||||
AvailableContainers: IAvailableContainer[]
|
AvailableContainers: AvailableContainer[]
|
||||||
SelectContainer: (container: ContainerModel) => void
|
SelectContainer: (container: ContainerModel) => void
|
||||||
DeleteContainer: (containerId: string) => void
|
DeleteContainer: (containerId: string) => void
|
||||||
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
OnPropertyChange: (key: string, value: string | number | boolean) => void
|
||||||
OnPropertiesSubmit: (event: React.FormEvent<HTMLFormElement>, properties: IProperties) => void
|
|
||||||
AddContainerToSelectedContainer: (type: string) => void
|
AddContainerToSelectedContainer: (type: string) => void
|
||||||
AddContainer: (index: number, type: string, parentId: string) => void
|
AddContainer: (index: number, type: string, parentId: string) => void
|
||||||
SaveEditorAsJSON: () => void
|
SaveEditorAsJSON: () => void
|
||||||
|
@ -61,7 +59,6 @@ export const UI: React.FunctionComponent<IUIProps> = (props: IUIProps) => {
|
||||||
isOpen={isElementsSidebarOpen}
|
isOpen={isElementsSidebarOpen}
|
||||||
isHistoryOpen={isHistoryOpen}
|
isHistoryOpen={isHistoryOpen}
|
||||||
OnPropertyChange={props.OnPropertyChange}
|
OnPropertyChange={props.OnPropertyChange}
|
||||||
OnPropertiesSubmit={props.OnPropertiesSubmit}
|
|
||||||
SelectContainer={props.SelectContainer}
|
SelectContainer={props.SelectContainer}
|
||||||
DeleteContainer={props.DeleteContainer}
|
DeleteContainer={props.DeleteContainer}
|
||||||
AddContainer={props.AddContainer}
|
AddContainer={props.AddContainer}
|
||||||
|
|
4
src/Enums/AddingBehavior.ts
Normal file
4
src/Enums/AddingBehavior.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export enum AddingBehavior {
|
||||||
|
InsertInto,
|
||||||
|
Replace
|
||||||
|
}
|
|
@ -1,26 +0,0 @@
|
||||||
import { IEditorState } from '../Interfaces/IEditorState';
|
|
||||||
import { IHistoryState } from '../Interfaces/IHistoryState';
|
|
||||||
|
|
||||||
const getEditorState = (editorState: IEditorState): void => {
|
|
||||||
const customEvent = new CustomEvent<IEditorState>('getEditorState', { detail: editorState });
|
|
||||||
document.dispatchEvent(customEvent);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCurrentHistoryState = (editorState: IEditorState): void => {
|
|
||||||
const customEvent = new CustomEvent<IHistoryState>(
|
|
||||||
'getCurrentHistoryState',
|
|
||||||
{ detail: editorState.history[editorState.historyCurrentStep] });
|
|
||||||
document.dispatchEvent(customEvent);
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface IEditorEvent {
|
|
||||||
name: string
|
|
||||||
func: (editorState: IEditorState) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const events: IEditorEvent[] = [
|
|
||||||
{ name: 'getEditorState', func: getEditorState },
|
|
||||||
{ name: 'getCurrentHistoryState', func: getCurrentHistoryState }
|
|
||||||
];
|
|
||||||
|
|
||||||
export default events;
|
|
|
@ -1,11 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { XPositionReference } from '../Enums/XPositionReference';
|
|
||||||
|
|
||||||
/** Model of available container used in application configuration */
|
/** Model of available container used in application configuration */
|
||||||
export interface IAvailableContainer {
|
export interface AvailableContainer {
|
||||||
Type: string
|
Type: string
|
||||||
Width: number
|
Width: number
|
||||||
Height: number
|
Height: number
|
||||||
XPositionReference?: XPositionReference
|
|
||||||
Style: React.CSSProperties
|
Style: React.CSSProperties
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
import { XPositionReference } from '../Enums/XPositionReference';
|
import { XPositionReference } from '../Enums/XPositionReference';
|
||||||
import { IImage } from './IImage';
|
import { Image } from './Image';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model of available symbol to configure the application */
|
* Model of available symbol to configure the application */
|
||||||
export interface IAvailableSymbol {
|
export interface AvailableSymbolModel {
|
||||||
Name: string
|
Name: string
|
||||||
XPositionReference: XPositionReference
|
XPositionReference: XPositionReference
|
||||||
Image: IImage
|
Image: Image
|
||||||
Width: number
|
Width: number
|
||||||
Height: number
|
Height: number
|
||||||
}
|
}
|
9
src/Interfaces/Configuration.ts
Normal file
9
src/Interfaces/Configuration.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { AvailableContainer } from './AvailableContainer';
|
||||||
|
import { AvailableSymbolModel } from './AvailableSymbol';
|
||||||
|
|
||||||
|
/** Model of configuration for the application to configure it */
|
||||||
|
export interface Configuration {
|
||||||
|
AvailableContainers: AvailableContainer[]
|
||||||
|
AvailableSymbols: AvailableSymbolModel[]
|
||||||
|
MainContainer: AvailableContainer
|
||||||
|
}
|
|
@ -1,21 +1,21 @@
|
||||||
import IProperties from './IProperties';
|
import Properties from './Properties';
|
||||||
|
|
||||||
export interface IContainerModel {
|
export interface IContainerModel {
|
||||||
children: IContainerModel[]
|
children: IContainerModel[]
|
||||||
parent: IContainerModel | null
|
parent: IContainerModel | null
|
||||||
properties: IProperties
|
properties: Properties
|
||||||
userData: Record<string, string | number>
|
userData: Record<string, string | number>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ContainerModel implements IContainerModel {
|
export class ContainerModel implements IContainerModel {
|
||||||
public children: IContainerModel[];
|
public children: IContainerModel[];
|
||||||
public parent: IContainerModel | null;
|
public parent: IContainerModel | null;
|
||||||
public properties: IProperties;
|
public properties: Properties;
|
||||||
public userData: Record<string, string | number>;
|
public userData: Record<string, string | number>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
parent: IContainerModel | null,
|
parent: IContainerModel | null,
|
||||||
properties: IProperties,
|
properties: Properties,
|
||||||
children: IContainerModel[] = [],
|
children: IContainerModel[] = [],
|
||||||
userData = {}) {
|
userData = {}) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
|
@ -1,6 +1,6 @@
|
||||||
import { IContainerModel } from './IContainerModel';
|
import { IContainerModel } from './ContainerModel';
|
||||||
|
|
||||||
export interface IHistoryState {
|
export interface HistoryState {
|
||||||
LastAction: string
|
LastAction: string
|
||||||
MainContainer: IContainerModel
|
MainContainer: IContainerModel
|
||||||
SelectedContainer: IContainerModel | null
|
SelectedContainer: IContainerModel | null
|
|
@ -1,9 +0,0 @@
|
||||||
import { IAvailableContainer } from './IAvailableContainer';
|
|
||||||
import { IAvailableSymbol } from './IAvailableSymbol';
|
|
||||||
|
|
||||||
/** Model of configuration for the application to configure it */
|
|
||||||
export interface IConfiguration {
|
|
||||||
AvailableContainers: IAvailableContainer[]
|
|
||||||
AvailableSymbols: IAvailableSymbol[]
|
|
||||||
MainContainer: IAvailableContainer
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
import { IConfiguration } from './IConfiguration';
|
|
||||||
import { IHistoryState } from './IHistoryState';
|
|
||||||
|
|
||||||
export interface IEditorState {
|
|
||||||
history: IHistoryState[]
|
|
||||||
historyCurrentStep: number
|
|
||||||
configuration: IConfiguration
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { XPositionReference } from '../Enums/XPositionReference';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Properties of a container
|
|
||||||
* @property id id of the container
|
|
||||||
* @property parentId id of the parent container
|
|
||||||
* @property x horizontal offset of the container
|
|
||||||
* @property y vertical offset of the container
|
|
||||||
* @property isRigidBody if true apply rigid body behaviors
|
|
||||||
* @property isAnchor if true apply anchor behaviors
|
|
||||||
*/
|
|
||||||
export default interface IProperties extends React.CSSProperties {
|
|
||||||
id: string
|
|
||||||
parentId: string | null
|
|
||||||
x: number
|
|
||||||
y: number
|
|
||||||
isRigidBody: boolean
|
|
||||||
isAnchor: boolean
|
|
||||||
XPositionReference?: XPositionReference
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
/**
|
|
||||||
* A SizePointer is a pointer in a 1 dimensional array of width/space
|
|
||||||
* x being the address where the pointer is pointing
|
|
||||||
* width being the overall (un)allocated space affected to the address
|
|
||||||
*/
|
|
||||||
export interface ISizePointer {
|
|
||||||
x: number
|
|
||||||
width: number
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/** Model of an image with multiple source */
|
/** Model of an image with multiple source */
|
||||||
export interface IImage {
|
export interface Image {
|
||||||
Name: string
|
Name: string
|
||||||
Url: string
|
Url: string
|
||||||
Base64Image: string
|
Base64Image: string
|
|
@ -1,4 +1,4 @@
|
||||||
export interface IPoint {
|
export interface Point {
|
||||||
x: number
|
x: number
|
||||||
y: number
|
y: number
|
||||||
}
|
}
|
9
src/Interfaces/Properties.ts
Normal file
9
src/Interfaces/Properties.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export default interface Properties extends React.CSSProperties {
|
||||||
|
id: string
|
||||||
|
parentId: string | null
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
isRigidBody: boolean
|
||||||
|
}
|
4
src/Interfaces/SizePointer.ts
Normal file
4
src/Interfaces/SizePointer.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export interface SizePointer {
|
||||||
|
x: number
|
||||||
|
width: number
|
||||||
|
}
|
|
@ -23,16 +23,6 @@
|
||||||
@apply transition-all bg-blue-100 hover:bg-blue-200 text-blue-700 text-lg font-semibold p-8 rounded-lg
|
@apply transition-all bg-blue-100 hover:bg-blue-200 text-blue-700 text-lg font-semibold p-8 rounded-lg
|
||||||
}
|
}
|
||||||
|
|
||||||
.normal-btn {
|
|
||||||
@apply text-sm
|
|
||||||
py-2 px-4
|
|
||||||
rounded-full border-0
|
|
||||||
font-semibold
|
|
||||||
transition-all
|
|
||||||
bg-blue-100 text-blue-700
|
|
||||||
hover:bg-blue-200
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-btn {
|
.floating-btn {
|
||||||
@apply h-full w-full text-white align-middle items-center justify-center
|
@apply h-full w-full text-white align-middle items-center justify-center
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { IConfiguration } from '../Interfaces/IConfiguration';
|
import { Configuration } from '../Interfaces/Configuration';
|
||||||
import IProperties from '../Interfaces/IProperties';
|
import Properties from '../Interfaces/Properties';
|
||||||
|
|
||||||
export const DEFAULT_CONFIG: IConfiguration = {
|
export const DEFAULT_CONFIG: Configuration = {
|
||||||
AvailableContainers: [
|
AvailableContainers: [
|
||||||
{
|
{
|
||||||
Type: 'Container',
|
Type: 'Container',
|
||||||
|
@ -25,19 +25,14 @@ export const DEFAULT_CONFIG: IConfiguration = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_MAINCONTAINER_PROPS: IProperties = {
|
export const DEFAULT_MAINCONTAINER_PROPS: Properties = {
|
||||||
id: 'main',
|
id: 'main',
|
||||||
parentId: 'null',
|
parentId: 'null',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
isRigidBody: false,
|
||||||
width: DEFAULT_CONFIG.MainContainer.Width,
|
width: DEFAULT_CONFIG.MainContainer.Width,
|
||||||
height: DEFAULT_CONFIG.MainContainer.Height,
|
height: DEFAULT_CONFIG.MainContainer.Height,
|
||||||
isRigidBody: false,
|
|
||||||
isAnchor: false,
|
|
||||||
fillOpacity: 0,
|
fillOpacity: 0,
|
||||||
stroke: 'black'
|
stroke: 'black'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NOTCHES_LENGTH = 4;
|
|
||||||
|
|
||||||
export const MAX_HISTORY = 200;
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { IContainerModel } from '../Interfaces/IContainerModel';
|
import { IContainerModel } from '../Interfaces/ContainerModel';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Generator iterating of over the children depth-first
|
* Returns a Generator iterating of over the children depth-first
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { findContainerById, MakeIterator } from './itertools';
|
import { findContainerById, MakeIterator } from './itertools';
|
||||||
import { IEditorState } from '../Interfaces/IEditorState';
|
import { IEditorState } from '../Components/Editor/Editor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revive the Editor state
|
* Revive the Editor state
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
onmessage = (e) => {
|
|
||||||
const data = JSON.stringify(e.data.editorState, getCircularReplacer(), e.data.spaces);
|
|
||||||
postMessage(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCircularReplacer = () => {
|
|
||||||
const seen = new WeakSet();
|
|
||||||
return (key, value) => {
|
|
||||||
if (key === 'parent') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'SelectedContainer') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === 'object' && value !== null) {
|
|
||||||
if (seen.has(value)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
seen.add(value);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -76,6 +76,9 @@ const GetSVGLayoutConfiguration = () => {
|
||||||
fillOpacity: 0,
|
fillOpacity: 0,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
stroke: 'blue',
|
stroke: 'blue',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
transformOrigin: 'center',
|
||||||
|
transformBox: 'fill-box'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": ["src", "src/workers"],
|
"include": ["src"],
|
||||||
"exclude": ["test-server"],
|
"exclude": ["test-server"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue