diff --git a/.drone.yml b/.drone.yml index 18d98d2..e3b1f91 100644 --- a/.drone.yml +++ b/.drone.yml @@ -6,9 +6,11 @@ steps: - name: test image: node:16 commands: + - curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7 - node ./test-server/node-http.js & - - npm install - - npm test + - pnpm install + - pnpm run test:nowatch + - pnpm run build --- kind: pipeline @@ -18,6 +20,8 @@ steps: - name: test image: node commands: + - curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7 - node ./test-server/node-http.js & - - npm install - - npm test \ No newline at end of file + - pnpm install + - pnpm run test:nowatch + - pnpm run build \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 86a1866..b72be75 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -5,7 +5,7 @@ module.exports = { }, extends: [ 'plugin:react/recommended', - 'standard' + 'standard-with-typescript' ], parser: '@typescript-eslint/parser', parserOptions: { @@ -13,7 +13,8 @@ module.exports = { jsx: true }, ecmaVersion: 'latest', - sourceType: 'module' + sourceType: 'module', + project: './tsconfig.json' }, plugins: [ 'react', @@ -21,9 +22,11 @@ module.exports = { ], rules: { 'space-before-function-paren': ['error', 'never'], + '@typescript-eslint/space-before-function-paren': ['error', 'never'], indent: ['warn', 2, { SwitchCase: 1 }], - semi: ['warn', 'always'], + semi: 'off', + '@typescript-eslint/semi': ['warn', 'always'], 'no-unused-vars': 'off', - '@typescript-eslint/no-unused-vars': 'error' + '@typescript-eslint/no-unused-vars': 'error', } }; diff --git a/README.md b/README.md index 7fa3b5c..635d01b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # SVG Layout Designer React +[![Build Status](https://drone.siklos-chaneru.duckdns.org/api/badges/Siklos/svg-layout-designer-react/status.svg)](https://drone.siklos-chaneru.duckdns.org/Siklos/svg-layout-designer-react) + +[![Build Status](https://drone.siklos-chaneru.duckdns.org/api/badges/Siklos/svg-layout-designer-react/status.svg?ref=refs/heads/dev)](https://drone.siklos-chaneru.duckdns.org/Siklos/svg-layout-designer-react) + An svg layout designer. # Getting Started Requierements : - NodeJS -- NPM +- npm +- pnpm (optional but recommanded unless you prefer having a huge `node_modules` directory) # Developping diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..5c52dac --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,57 @@ +# Node.js with React +# Build a Node.js project that uses React. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- master +- dev +- dev* + +pool: + vmImage: ubuntu-latest + +variables: + pnpm_config_cache: $(Pipeline.Workspace)/.pnpm-store + +steps: +- task: Cache@2 + inputs: + key: 'pnpm | "$(Agent.OS)" | pnpm-lock.yaml' + path: $(pnpm_config_cache) + displayName: Cache pnpm + +- script: | + curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7 + pnpm config set store-dir $(pnpm_config_cache) + displayName: "Setup pnpm" + +- task: NodeTool@0 + inputs: + versionSpec: '16.x' + displayName: 'Install Node.js 16.x LTS' + +- script: | + node --version + node ./test-server/node-http.js & + jobs + pnpm i + pnpm run test:nowatch + pnpm run build + kill -2 %1 2>/dev/null + displayName: 'Test on Node.js 16.x LTS' + +- task: NodeTool@0 + inputs: + versionSpec: '>=18.7.0' + displayName: 'Install Node.js Latest' + +- script: | + node --version + node ./test-server/node-http.js & + jobs + pnpm i + pnpm run test:nowatch + pnpm run build + kill -2 %1 2>/dev/null + displayName: 'Test on Node.js 18.x Latest' \ No newline at end of file diff --git a/docs/CICD.md b/docs/CICD.md new file mode 100644 index 0000000..7c3543f --- /dev/null +++ b/docs/CICD.md @@ -0,0 +1,12 @@ +# Azure Pipelines + +This project uses Azure Pipelines to runs automatic tests. + +Its `azure-pipelines.yml` configuration file can be found at the root project folder. + + +# Drone.io + +Due to the limitations of Azure Pipelines (limited free usage, no parallel, no dockerhub...), it might be more useful to use Drone.io. + +Its config file can be found in `.drone.yml`. \ No newline at end of file diff --git a/docs/Dependencies.md b/docs/Dependencies.md new file mode 100644 index 0000000..626458e --- /dev/null +++ b/docs/Dependencies.md @@ -0,0 +1,73 @@ +# Dependencies + +This document briefly explains the different dependencies located in the `package.json`. + +This document will not explain how to use them. You can read their documentation for that and the codebase have exemples for references. + + +# [React](https://reactjs.org/) + +Main framework to build the js application. + +It depends on Vite in order to build the project. + +Others dependencies: +- [react-dom](https://reactjs.org/docs/react-dom.html): library used to inject the app to `#root` html element. +- [react-svg-pan-zoom](https://www.npmjs.com/package/react-svg-pan-zoom): component that offers pan + zoom to a svg element + + +# [Vite](https://vitejs.dev/) + +Vite is the main tool to develop the react application. + +Its uses the following files to configure the project : +- `vite.config.ts` +- `.env*` +- `src/vite-env.d.ts` + +Others dependencies: +- @vitejs/plugin-react + + +# [Tailwind CSS](https://tailwindcss.com/) + +CSS framework designed around constraints with utility classes in order to reduce dead css code. + +Its uses the following files to configure the project : +- `src/index.scss` +- `tailwind.config.cjs` +- `postcss.config.cjs` + +Other dependencies: +- postcss +- sass +- autoprefixer + + +# [Heroicons](https://heroicons.com/) + +SVG Icons that can be used as JSX elements with Tailwind CSS + + +# Testing + +- [Vitest](https://vitest.dev/) +- [Testing Library](https://testing-library.com/) +- [jsdom](https://github.com/jsdom/jsdom) + + +# [eslint](https://eslint.org/) + +A Linter. Used for error checking, syntax checking and code style enforcing. + +Currently using `standard-with-typescript` with a few modification. + +See the `.eslintrc.cjs` for more informations. + +Other dependencies: +- typescript-eslint/eslint-plugin +- typescript-eslint/parser +- eslint-plugin-import +- eslint-plugin-n +- eslint-plugin-promise +- eslint-plugin-react diff --git a/docs/Project_Structure.md b/docs/Project_Structure.md new file mode 100644 index 0000000..890daf8 --- /dev/null +++ b/docs/Project_Structure.md @@ -0,0 +1,38 @@ +# Project Structure + +The project is structured this way + +``` +. +├── docs Documentation folder +├── public Public folder in which the index.html +│ import its resources +├── src Source folder for the react app +│ ├── assets Assets folder in which the react app +│ │ import its resources +│ ├── Components Components folder +│ ├── Enums Enums folder +│ ├── Interfaces Interface (+ types folder) +│ ├── test Setup folder for the tests +│ ├── tests Other tests + resources +│ ├── utils Utilities folder +│ ├── index.scss Tailwind CSS extends +│ ├── main.tsx Entrypoint for App injection +│ └── vite-env.d.ts Types for .env files +├── test-server Tests servers to test the API +│ ├── http.js Test server for bun.sh +│ └── node-http.js Test server for Node.js +├── azure-pipelines.yml Azure Pipelines YAML config file +├── index.html HTML Page +├── package-lock.json Describe the node_modules tree for npm +├── package.json Node.JS config file +├── pnpm-lock.yaml Describe the node_modules tree for pnpm +├── postcss.config.cjs Postcss config file for SCSS processing +├── README.md +├── tailwind.config.cjs Tailwind CSS config file +├── tsconfig.json Typescript config file +├── tsconfig.node.json Typescript config file for Node modules +├── vite.config.ts Vite config file +└── vitest.config.ts Vitest config file +``` + diff --git a/package-lock.json b/package-lock.json index 83e5fcb..285432c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "autoprefixer": "^10.4.8", "eslint": "^8.20.0", "eslint-config-standard": "^17.0.0", + "eslint-config-standard-with-typescript": "^22.0.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-n": "^15.2.4", "eslint-plugin-promise": "^6.0.0", @@ -2697,6 +2698,24 @@ "eslint-plugin-promise": "^6.0.0" } }, + "node_modules/eslint-config-standard-with-typescript": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-22.0.0.tgz", + "integrity": "sha512-VA36U7UlFpwULvkdnh6MQj5GAV2Q+tT68ALLAwJP0ZuNXU2m0wX07uxX4qyLRdHgSzH4QJ73CveKBuSOYvh7vQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint-config-standard": "17.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0", + "typescript": "*" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", @@ -8093,6 +8112,16 @@ "dev": true, "requires": {} }, + "eslint-config-standard-with-typescript": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-22.0.0.tgz", + "integrity": "sha512-VA36U7UlFpwULvkdnh6MQj5GAV2Q+tT68ALLAwJP0ZuNXU2m0wX07uxX4qyLRdHgSzH4QJ73CveKBuSOYvh7vQ==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "^5.0.0", + "eslint-config-standard": "17.0.0" + } + }, "eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", diff --git a/package.json b/package.json index 149d76f..148f302 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "preview": "vite preview", "test": "vitest", "test:ui": "vitest --ui", + "test:nowatch": "vitest run", "coverage": "vitest run coverage" }, "dependencies": { @@ -19,6 +20,7 @@ "react-svg-pan-zoom": "^3.11.0" }, "devDependencies": { + "@testing-library/dom": "^8.16.1", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^14.4.1", @@ -32,6 +34,7 @@ "autoprefixer": "^10.4.8", "eslint": "^8.20.0", "eslint-config-standard": "^17.0.0", + "eslint-config-standard-with-typescript": "^22.0.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-n": "^15.2.4", "eslint-plugin-promise": "^6.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..f9bce2d --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3607 @@ +lockfileVersion: 5.4 + +specifiers: + '@heroicons/react': ^1.0.6 + '@testing-library/dom': ^8.16.1 + '@testing-library/jest-dom': ^5.16.4 + '@testing-library/react': ^13.3.0 + '@testing-library/user-event': ^14.4.1 + '@types/react': ^18.0.15 + '@types/react-dom': ^18.0.6 + '@types/react-svg-pan-zoom': ^3.3.5 + '@typescript-eslint/eslint-plugin': ^5.31.0 + '@typescript-eslint/parser': ^5.31.0 + '@vitejs/plugin-react': ^2.0.0 + '@vitest/ui': ^0.20.3 + autoprefixer: ^10.4.8 + eslint: ^8.20.0 + eslint-config-standard: ^17.0.0 + eslint-config-standard-with-typescript: ^22.0.0 + eslint-plugin-import: ^2.26.0 + eslint-plugin-n: ^15.2.4 + eslint-plugin-promise: ^6.0.0 + eslint-plugin-react: ^7.30.1 + framer-motion: ^6.5.1 + jsdom: ^20.0.0 + postcss: ^8.4.14 + react: ^18.2.0 + react-dom: ^18.2.0 + react-svg-pan-zoom: ^3.11.0 + sass: ^1.54.0 + tailwindcss: ^3.1.7 + typescript: ^4.6.4 + vite: ^3.0.0 + vitest: ^0.20.3 + +dependencies: + '@heroicons/react': 1.0.6_react@18.2.0 + framer-motion: 6.5.1_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-svg-pan-zoom: 3.11.0_react@18.2.0 + +devDependencies: + '@testing-library/dom': 8.16.1 + '@testing-library/jest-dom': 5.16.5 + '@testing-library/react': 13.3.0_biqbaboplfbrettd7655fr4n2y + '@testing-library/user-event': 14.4.2_znfriv3ismgf3ybh2woqwlpfea + '@types/react': 18.0.17 + '@types/react-dom': 18.0.6 + '@types/react-svg-pan-zoom': 3.3.5 + '@typescript-eslint/eslint-plugin': 5.32.0_iosr3hrei2tubxveewluhu5lhy + '@typescript-eslint/parser': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq + '@vitejs/plugin-react': 2.0.0_vite@3.0.4 + '@vitest/ui': 0.20.3 + autoprefixer: 10.4.8_postcss@8.4.16 + eslint: 8.21.0 + eslint-config-standard: 17.0.0_dfwa53o44x4e5xhsfv5mvfhk5a + eslint-config-standard-with-typescript: 22.0.0_mfupvx5msz6are6ggwiepter3m + eslint-plugin-import: 2.26.0_wuikv5nqgdfyng42xxm7lklfmi + eslint-plugin-n: 15.2.4_eslint@8.21.0 + eslint-plugin-promise: 6.0.0_eslint@8.21.0 + eslint-plugin-react: 7.30.1_eslint@8.21.0 + jsdom: 20.0.0 + postcss: 8.4.16 + sass: 1.54.3 + tailwindcss: 3.1.8 + typescript: 4.7.4 + vite: 3.0.4_sass@1.54.3 + vitest: 0.20.3_hymhw3vkyr5yfvzfskw3x5v26q + +packages: + + /@adobe/css-tools/4.0.1: + resolution: {integrity: sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==} + dev: true + + /@ampproject/remapping/2.2.0: + resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.1.1 + '@jridgewell/trace-mapping': 0.3.14 + dev: true + + /@babel/code-frame/7.18.6: + resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/compat-data/7.18.8: + resolution: {integrity: sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core/7.18.10: + resolution: {integrity: sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.18.12 + '@babel/helper-compilation-targets': 7.18.9_@babel+core@7.18.10 + '@babel/helper-module-transforms': 7.18.9 + '@babel/helpers': 7.18.9 + '@babel/parser': 7.18.11 + '@babel/template': 7.18.10 + '@babel/traverse': 7.18.11 + '@babel/types': 7.18.10 + convert-source-map: 1.8.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator/7.18.12: + resolution: {integrity: sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + '@jridgewell/gen-mapping': 0.3.2 + jsesc: 2.5.2 + dev: true + + /@babel/helper-annotate-as-pure/7.18.6: + resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + dev: true + + /@babel/helper-compilation-targets/7.18.9_@babel+core@7.18.10: + resolution: {integrity: sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.18.8 + '@babel/core': 7.18.10 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.3 + semver: 6.3.0 + dev: true + + /@babel/helper-environment-visitor/7.18.9: + resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name/7.18.9: + resolution: {integrity: sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.18.10 + '@babel/types': 7.18.10 + dev: true + + /@babel/helper-hoist-variables/7.18.6: + resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + dev: true + + /@babel/helper-module-imports/7.18.6: + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + dev: true + + /@babel/helper-module-transforms/7.18.9: + resolution: {integrity: sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-simple-access': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.18.6 + '@babel/template': 7.18.10 + '@babel/traverse': 7.18.11 + '@babel/types': 7.18.10 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-plugin-utils/7.18.9: + resolution: {integrity: sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access/7.18.6: + resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + dev: true + + /@babel/helper-split-export-declaration/7.18.6: + resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + dev: true + + /@babel/helper-string-parser/7.18.10: + resolution: {integrity: sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier/7.18.6: + resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option/7.18.6: + resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers/7.18.9: + resolution: {integrity: sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.18.10 + '@babel/traverse': 7.18.11 + '@babel/types': 7.18.10 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight/7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.18.6 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser/7.18.11: + resolution: {integrity: sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.18.10 + dev: true + + /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.18.10: + resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.10 + '@babel/helper-plugin-utils': 7.18.9 + dev: true + + /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.18.10: + resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.10 + '@babel/plugin-transform-react-jsx': 7.18.10_@babel+core@7.18.10 + dev: true + + /@babel/plugin-transform-react-jsx-self/7.18.6_@babel+core@7.18.10: + resolution: {integrity: sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.10 + '@babel/helper-plugin-utils': 7.18.9 + dev: true + + /@babel/plugin-transform-react-jsx-source/7.18.6_@babel+core@7.18.10: + resolution: {integrity: sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.10 + '@babel/helper-plugin-utils': 7.18.9 + dev: true + + /@babel/plugin-transform-react-jsx/7.18.10_@babel+core@7.18.10: + resolution: {integrity: sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.10 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.18.10 + '@babel/types': 7.18.10 + dev: true + + /@babel/runtime/7.18.9: + resolution: {integrity: sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.9 + dev: true + + /@babel/template/7.18.10: + resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/parser': 7.18.11 + '@babel/types': 7.18.10 + dev: true + + /@babel/traverse/7.18.11: + resolution: {integrity: sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.18.12 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.18.9 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.18.11 + '@babel/types': 7.18.10 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types/7.18.10: + resolution: {integrity: sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.18.10 + '@babel/helper-validator-identifier': 7.18.6 + to-fast-properties: 2.0.0 + 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: + resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@eslint/eslintrc/1.3.0: + resolution: {integrity: sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.3.3 + globals: 13.17.0 + ignore: 5.2.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@heroicons/react/1.0.6_react@18.2.0: + resolution: {integrity: sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ==} + peerDependencies: + react: '>= 16' + dependencies: + react: 18.2.0 + dev: false + + /@humanwhocodes/config-array/0.10.4: + resolution: {integrity: sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/gitignore-to-minimatch/1.0.2: + resolution: {integrity: sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==} + dev: true + + /@humanwhocodes/object-schema/1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@jest/schemas/28.1.3: + resolution: {integrity: sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@sinclair/typebox': 0.24.27 + dev: true + + /@jridgewell/gen-mapping/0.1.1: + resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@jridgewell/gen-mapping/0.3.2: + resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/trace-mapping': 0.3.14 + dev: true + + /@jridgewell/resolve-uri/3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array/1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec/1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@jridgewell/trace-mapping/0.3.14: + resolution: {integrity: sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + 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: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat/2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk/1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.13.0 + dev: true + + /@polka/url/1.0.0-next.21: + resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} + dev: true + + /@sinclair/typebox/0.24.27: + resolution: {integrity: sha512-K7C7IlQ3zLePEZleUN21ceBA2aLcMnLHTLph8QWk1JK37L90obdpY+QGY8bXMKxf1ht1Z0MNewvXxWv0oGDYFg==} + dev: true + + /@testing-library/dom/8.16.1: + resolution: {integrity: sha512-XEV2mBxgv6DKjL3+U3WEUzBgT2CjYksoXGlLrrJXYP8OvRfGkBonvelkorazpFlp8tkEecO06r43vN4DIEyegQ==} + engines: {node: '>=12'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/runtime': 7.18.9 + '@types/aria-query': 4.2.2 + aria-query: 5.0.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.14 + lz-string: 1.4.4 + pretty-format: 27.5.1 + dev: true + + /@testing-library/jest-dom/5.16.5: + resolution: {integrity: sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==} + engines: {node: '>=8', npm: '>=6', yarn: '>=1'} + dependencies: + '@adobe/css-tools': 4.0.1 + '@babel/runtime': 7.18.9 + '@types/testing-library__jest-dom': 5.14.5 + aria-query: 5.0.0 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.5.14 + lodash: 4.17.21 + redent: 3.0.0 + dev: true + + /@testing-library/react/13.3.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-DB79aA426+deFgGSjnf5grczDPiL4taK3hFaa+M5q7q20Kcve9eQottOG5kZ74KEr55v0tU2CQormSSDK87zYQ==} + engines: {node: '>=12'} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + '@babel/runtime': 7.18.9 + '@testing-library/dom': 8.16.1 + '@types/react-dom': 18.0.6 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: true + + /@testing-library/user-event/14.4.2_znfriv3ismgf3ybh2woqwlpfea: + resolution: {integrity: sha512-1gVTWtueNimveOjcm2ApFCnCTeky7WqY3EX31/GRKLWyCd+HfH+Gd2l1J8go9FpDNe+0Mx8X4zbQHTg0WWNJwg==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + dependencies: + '@testing-library/dom': 8.16.1 + dev: true + + /@tootallnate/once/2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: true + + /@types/aria-query/4.2.2: + resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} + dev: true + + /@types/chai-subset/1.3.3: + resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} + dependencies: + '@types/chai': 4.3.3 + dev: true + + /@types/chai/4.3.3: + resolution: {integrity: sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==} + dev: true + + /@types/jest/28.1.6: + resolution: {integrity: sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ==} + dependencies: + jest-matcher-utils: 28.1.3 + pretty-format: 28.1.3 + dev: true + + /@types/json-schema/7.0.11: + resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + dev: true + + /@types/json5/0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + + /@types/node/18.6.4: + resolution: {integrity: sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==} + dev: true + + /@types/prop-types/15.7.5: + resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + dev: true + + /@types/react-dom/18.0.6: + resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==} + dependencies: + '@types/react': 18.0.17 + dev: true + + /@types/react-svg-pan-zoom/3.3.5: + resolution: {integrity: sha512-W8GRFCDy7raSDr5OXGjSyvX5KmdWlIQfv0NLa1jfAYVUO4ClVbgorWeAAom7nY3Pl+4h9blXE1Bnu2CW1iMEvQ==} + dependencies: + '@types/react': 18.0.17 + dev: true + + /@types/react/18.0.17: + resolution: {integrity: sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.2 + csstype: 3.1.0 + dev: true + + /@types/scheduler/0.16.2: + resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} + dev: true + + /@types/testing-library__jest-dom/5.14.5: + resolution: {integrity: sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==} + dependencies: + '@types/jest': 28.1.6 + dev: true + + /@typescript-eslint/eslint-plugin/5.32.0_iosr3hrei2tubxveewluhu5lhy: + resolution: {integrity: sha512-CHLuz5Uz7bHP2WgVlvoZGhf0BvFakBJKAD/43Ty0emn4wXWv5k01ND0C0fHcl/Im8Td2y/7h44E9pca9qAu2ew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/parser': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/scope-manager': 5.32.0 + '@typescript-eslint/type-utils': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/utils': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq + debug: 4.3.4 + eslint: 8.21.0 + functional-red-black-tree: 1.0.1 + ignore: 5.2.0 + regexpp: 3.2.0 + semver: 7.3.7 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser/5.32.0_qugx7qdu5zevzvxaiqyxfiwquq: + resolution: {integrity: sha512-IxRtsehdGV9GFQ35IGm5oKKR2OGcazUoiNBxhRV160iF9FoyuXxjY+rIqs1gfnd+4eL98OjeGnMpE7RF/NBb3A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.32.0 + '@typescript-eslint/types': 5.32.0 + '@typescript-eslint/typescript-estree': 5.32.0_typescript@4.7.4 + debug: 4.3.4 + eslint: 8.21.0 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager/5.32.0: + resolution: {integrity: sha512-KyAE+tUON0D7tNz92p1uetRqVJiiAkeluvwvZOqBmW9z2XApmk5WSMV9FrzOroAcVxJZB3GfUwVKr98Dr/OjOg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.32.0 + '@typescript-eslint/visitor-keys': 5.32.0 + dev: true + + /@typescript-eslint/type-utils/5.32.0_qugx7qdu5zevzvxaiqyxfiwquq: + resolution: {integrity: sha512-0gSsIhFDduBz3QcHJIp3qRCvVYbqzHg8D6bHFsDMrm0rURYDj+skBK2zmYebdCp+4nrd9VWd13egvhYFJj/wZg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/utils': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq + debug: 4.3.4 + eslint: 8.21.0 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types/5.32.0: + resolution: {integrity: sha512-EBUKs68DOcT/EjGfzywp+f8wG9Zw6gj6BjWu7KV/IYllqKJFPlZlLSYw/PTvVyiRw50t6wVbgv4p9uE2h6sZrQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree/5.32.0_typescript@4.7.4: + resolution: {integrity: sha512-ZVAUkvPk3ITGtCLU5J4atCw9RTxK+SRc6hXqLtllC2sGSeMFWN+YwbiJR9CFrSFJ3w4SJfcWtDwNb/DmUIHdhg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.32.0 + '@typescript-eslint/visitor-keys': 5.32.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.7 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils/5.32.0_qugx7qdu5zevzvxaiqyxfiwquq: + resolution: {integrity: sha512-W7lYIAI5Zlc5K082dGR27Fczjb3Q57ECcXefKU/f0ajM5ToM0P+N9NmJWip8GmGu/g6QISNT+K6KYB+iSHjXCQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@typescript-eslint/scope-manager': 5.32.0 + '@typescript-eslint/types': 5.32.0 + '@typescript-eslint/typescript-estree': 5.32.0_typescript@4.7.4 + eslint: 8.21.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@8.21.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys/5.32.0: + resolution: {integrity: sha512-S54xOHZgfThiZ38/ZGTgB2rqx51CMJ5MCfVT2IplK4Q7hgzGfe0nLzLCcenDnc/cSjP568hdeKfeDcBgqNHD/g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.32.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /@vitejs/plugin-react/2.0.0_vite@3.0.4: + resolution: {integrity: sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==} + engines: {node: '>=14.18.0'} + peerDependencies: + vite: ^3.0.0 + dependencies: + '@babel/core': 7.18.10 + '@babel/plugin-transform-react-jsx': 7.18.10_@babel+core@7.18.10 + '@babel/plugin-transform-react-jsx-development': 7.18.6_@babel+core@7.18.10 + '@babel/plugin-transform-react-jsx-self': 7.18.6_@babel+core@7.18.10 + '@babel/plugin-transform-react-jsx-source': 7.18.6_@babel+core@7.18.10 + magic-string: 0.26.2 + react-refresh: 0.14.0 + vite: 3.0.4_sass@1.54.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@vitest/ui/0.20.3: + resolution: {integrity: sha512-Rlg+y3PtE5IcGPVmViF/BXM7euY7LG0yjfIvXKlF0L3OnNSVS8+esgLlAhaYftSJXtcunqa/cYXiQ+qFVTaBGw==} + dependencies: + sirv: 2.0.2 + dev: true + + /abab/2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + dev: true + + /acorn-globals/6.0.0: + resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==} + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + dev: true + + /acorn-jsx/5.3.2_acorn@8.8.0: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.8.0 + dev: true + + /acorn-node/1.8.2: + resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + xtend: 4.0.2 + dev: true + + /acorn-walk/7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn/7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /acorn/8.8.0: + resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /agent-base/6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /ajv/6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex/5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles/3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles/4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles/5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /anymatch/3.1.2: + resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /arg/5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: true + + /argparse/2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /aria-query/5.0.0: + resolution: {integrity: sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==} + engines: {node: '>=6.0'} + dev: true + + /array-includes/3.1.5: + resolution: {integrity: sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + get-intrinsic: 1.1.2 + is-string: 1.0.7 + dev: true + + /array-union/2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flat/1.3.0: + resolution: {integrity: sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.flatmap/1.3.0: + resolution: {integrity: sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + es-shim-unscopables: 1.0.0 + dev: true + + /assertion-error/1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + + /asynckit/0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: true + + /autoprefixer/10.4.8_postcss@8.4.16: + resolution: {integrity: sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.3 + caniuse-lite: 1.0.30001374 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.16 + postcss-value-parser: 4.2.0 + dev: true + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browser-process-hrtime/1.0.0: + resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} + dev: true + + /browserslist/4.21.3: + resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001374 + electron-to-chromium: 1.4.211 + node-releases: 2.0.6 + update-browserslist-db: 1.0.5_browserslist@4.21.3 + dev: true + + /builtins/5.0.1: + resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} + dependencies: + semver: 7.3.7 + dev: true + + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.1.2 + dev: true + + /callsites/3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase-css/2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: true + + /caniuse-lite/1.0.30001374: + resolution: {integrity: sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==} + dev: true + + /chai/4.3.6: + resolution: {integrity: sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.2 + deep-eql: 3.0.1 + get-func-name: 2.0.0 + loupe: 2.3.4 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + + /chalk/2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk/3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chalk/4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /check-error/1.0.2: + resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + dev: true + + /chokidar/3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.2 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /color-convert/1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert/2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name/1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name/1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /combined-stream/1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: true + + /concat-map/0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /convert-source-map/1.8.0: + resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /cross-spawn/7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /css.escape/1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + dev: true + + /cssesc/3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /cssom/0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + dev: true + + /cssom/0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + dev: true + + /cssstyle/2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + dependencies: + cssom: 0.3.8 + dev: true + + /csstype/3.1.0: + resolution: {integrity: sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==} + dev: true + + /data-urls/3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + dev: true + + /debug/2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: true + + /debug/3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug/4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /decimal.js/10.3.1: + resolution: {integrity: sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==} + dev: true + + /deep-eql/3.0.1: + resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==} + engines: {node: '>=0.12'} + dependencies: + type-detect: 4.0.8 + dev: true + + /deep-is/0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /define-properties/1.1.4: + resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /defined/1.0.0: + resolution: {integrity: sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==} + dev: true + + /delayed-stream/1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: true + + /detective/5.2.1: + resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} + engines: {node: '>=0.8.0'} + hasBin: true + dependencies: + acorn-node: 1.8.2 + defined: 1.0.0 + minimist: 1.2.6 + dev: true + + /didyoumean/1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: true + + /diff-sequences/28.1.1: + resolution: {integrity: sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dev: true + + /dir-glob/3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /dlv/1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: true + + /doctrine/2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine/3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-accessibility-api/0.5.14: + resolution: {integrity: sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==} + dev: true + + /domexception/4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + dependencies: + webidl-conversions: 7.0.0 + dev: true + + /electron-to-chromium/1.4.211: + resolution: {integrity: sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==} + dev: true + + /entities/4.3.1: + resolution: {integrity: sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==} + engines: {node: '>=0.12'} + dev: true + + /es-abstract/1.20.1: + resolution: {integrity: sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + es-to-primitive: 1.2.1 + function-bind: 1.1.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.1.2 + get-symbol-description: 1.0.0 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-symbols: 1.0.3 + internal-slot: 1.0.3 + is-callable: 1.2.4 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-weakref: 1.0.2 + object-inspect: 1.12.2 + object-keys: 1.1.1 + object.assign: 4.1.3 + regexp.prototype.flags: 1.4.3 + string.prototype.trimend: 1.0.5 + string.prototype.trimstart: 1.0.5 + unbox-primitive: 1.0.2 + dev: true + + /es-shim-unscopables/1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + dependencies: + has: 1.0.3 + dev: true + + /es-to-primitive/1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.4 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild-android-64/0.14.54: + resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-android-arm64/0.14.54: + resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-64/0.14.54: + resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-arm64/0.14.54: + resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-64/0.14.54: + resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-arm64/0.14.54: + resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-32/0.14.54: + resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-64/0.14.54: + resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm/0.14.54: + resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm64/0.14.54: + resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-mips64le/0.14.54: + resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-ppc64le/0.14.54: + resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-riscv64/0.14.54: + resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-s390x/0.14.54: + resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-netbsd-64/0.14.54: + resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-openbsd-64/0.14.54: + resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-sunos-64/0.14.54: + resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-32/0.14.54: + resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-64/0.14.54: + resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-arm64/0.14.54: + resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild/0.14.54: + resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/linux-loong64': 0.14.54 + esbuild-android-64: 0.14.54 + esbuild-android-arm64: 0.14.54 + esbuild-darwin-64: 0.14.54 + esbuild-darwin-arm64: 0.14.54 + esbuild-freebsd-64: 0.14.54 + esbuild-freebsd-arm64: 0.14.54 + esbuild-linux-32: 0.14.54 + esbuild-linux-64: 0.14.54 + esbuild-linux-arm: 0.14.54 + esbuild-linux-arm64: 0.14.54 + esbuild-linux-mips64le: 0.14.54 + esbuild-linux-ppc64le: 0.14.54 + esbuild-linux-riscv64: 0.14.54 + esbuild-linux-s390x: 0.14.54 + esbuild-netbsd-64: 0.14.54 + esbuild-openbsd-64: 0.14.54 + esbuild-sunos-64: 0.14.54 + esbuild-windows-32: 0.14.54 + esbuild-windows-64: 0.14.54 + esbuild-windows-arm64: 0.14.54 + dev: true + + /escalade/3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp/1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp/4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /escodegen/2.0.0: + resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + optionalDependencies: + source-map: 0.6.1 + dev: true + + /eslint-config-standard-with-typescript/22.0.0_mfupvx5msz6are6ggwiepter3m: + resolution: {integrity: sha512-VA36U7UlFpwULvkdnh6MQj5GAV2Q+tT68ALLAwJP0ZuNXU2m0wX07uxX4qyLRdHgSzH4QJ73CveKBuSOYvh7vQ==} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: ^15.0.0 + eslint-plugin-promise: ^6.0.0 + typescript: '*' + dependencies: + '@typescript-eslint/eslint-plugin': 5.32.0_iosr3hrei2tubxveewluhu5lhy + '@typescript-eslint/parser': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq + eslint: 8.21.0 + eslint-config-standard: 17.0.0_dfwa53o44x4e5xhsfv5mvfhk5a + eslint-plugin-import: 2.26.0_wuikv5nqgdfyng42xxm7lklfmi + eslint-plugin-n: 15.2.4_eslint@8.21.0 + eslint-plugin-promise: 6.0.0_eslint@8.21.0 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-config-standard/17.0.0_dfwa53o44x4e5xhsfv5mvfhk5a: + resolution: {integrity: sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==} + peerDependencies: + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: ^15.0.0 + eslint-plugin-promise: ^6.0.0 + dependencies: + eslint: 8.21.0 + eslint-plugin-import: 2.26.0_wuikv5nqgdfyng42xxm7lklfmi + eslint-plugin-n: 15.2.4_eslint@8.21.0 + eslint-plugin-promise: 6.0.0_eslint@8.21.0 + dev: true + + /eslint-import-resolver-node/0.3.6: + resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} + dependencies: + debug: 3.2.7 + resolve: 1.22.1 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils/2.7.3_gjpiwexkhexdr4bbgrtzf23bg4: + resolution: {integrity: sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq + debug: 3.2.7 + eslint-import-resolver-node: 0.3.6 + find-up: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-es/4.1.0_eslint@8.21.0: + resolution: {integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=4.19.1' + dependencies: + eslint: 8.21.0 + eslint-utils: 2.1.0 + regexpp: 3.2.0 + dev: true + + /eslint-plugin-import/2.26.0_wuikv5nqgdfyng42xxm7lklfmi: + resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq + array-includes: 3.1.5 + array.prototype.flat: 1.3.0 + debug: 2.6.9 + doctrine: 2.1.0 + eslint: 8.21.0 + eslint-import-resolver-node: 0.3.6 + eslint-module-utils: 2.7.3_gjpiwexkhexdr4bbgrtzf23bg4 + has: 1.0.3 + is-core-module: 2.10.0 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.values: 1.1.5 + resolve: 1.22.1 + tsconfig-paths: 3.14.1 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-n/15.2.4_eslint@8.21.0: + resolution: {integrity: sha512-tjnVMv2fiXYMnuiIFI8QMtyUFI42SckEEWvi8h68SWGWshfqO6SSCASy24dGMGAiy7NUk6DZt90DM0iNUsmQ5w==} + engines: {node: '>=12.22.0'} + peerDependencies: + eslint: '>=7.0.0' + dependencies: + builtins: 5.0.1 + eslint: 8.21.0 + eslint-plugin-es: 4.1.0_eslint@8.21.0 + eslint-utils: 3.0.0_eslint@8.21.0 + ignore: 5.2.0 + is-core-module: 2.10.0 + minimatch: 3.1.2 + resolve: 1.22.1 + semver: 7.3.7 + dev: true + + /eslint-plugin-promise/6.0.0_eslint@8.21.0: + resolution: {integrity: sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.21.0 + dev: true + + /eslint-plugin-react/7.30.1_eslint@8.21.0: + resolution: {integrity: sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.5 + array.prototype.flatmap: 1.3.0 + doctrine: 2.1.0 + eslint: 8.21.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.2 + minimatch: 3.1.2 + object.entries: 1.1.5 + object.fromentries: 2.0.5 + object.hasown: 1.1.1 + object.values: 1.1.5 + prop-types: 15.8.1 + resolve: 2.0.0-next.4 + semver: 6.3.0 + string.prototype.matchall: 4.0.7 + dev: true + + /eslint-scope/5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope/7.1.1: + resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils/2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + dependencies: + eslint-visitor-keys: 1.3.0 + dev: true + + /eslint-utils/3.0.0_eslint@8.21.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.21.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys/1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + dev: true + + /eslint-visitor-keys/2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys/3.3.0: + resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint/8.21.0: + resolution: {integrity: sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint/eslintrc': 1.3.0 + '@humanwhocodes/config-array': 0.10.4 + '@humanwhocodes/gitignore-to-minimatch': 1.0.2 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-utils: 3.0.0_eslint@8.21.0 + eslint-visitor-keys: 3.3.0 + espree: 9.3.3 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + functional-red-black-tree: 1.0.1 + glob-parent: 6.0.2 + globals: 13.17.0 + globby: 11.1.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.0 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + v8-compile-cache: 2.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree/9.3.3: + resolution: {integrity: sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.8.0 + acorn-jsx: 5.3.2_acorn@8.8.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /esprima/4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /esquery/1.4.0: + resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse/4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse/4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse/5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils/2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal/3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob/3.2.11: + resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify/2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein/2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq/1.13.0: + resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache/6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up/2.1.0: + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} + engines: {node: '>=4'} + dependencies: + locate-path: 2.0.0 + dev: true + + /find-up/5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache/3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.6 + rimraf: 3.0.2 + dev: true + + /flatted/3.2.6: + resolution: {integrity: sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==} + dev: true + + /form-data/4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + + /fraction.js/4.2.0: + resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + 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: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /function.prototype.name/1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + functions-have-names: 1.2.3 + dev: true + + /functional-red-black-tree/1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + dev: true + + /functions-have-names/1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /gensync/1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-func-name/2.0.0: + resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + dev: true + + /get-intrinsic/1.1.2: + resolution: {integrity: sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + dev: true + + /get-symbol-description/1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.2 + dev: true + + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent/6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob/7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals/11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals/13.17.0: + resolution: {integrity: sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby/11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.11 + ignore: 5.2.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /grapheme-splitter/1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /has-bigints/1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag/3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag/4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors/1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.1.2 + dev: true + + /has-symbols/1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag/1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /hey-listen/1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + dev: false + + /html-encoding-sniffer/3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + dependencies: + whatwg-encoding: 2.0.0 + dev: true + + /http-proxy-agent/5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /https-proxy-agent/5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /iconv-lite/0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /ignore/5.2.0: + resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} + engines: {node: '>= 4'} + dev: true + + /immutable/4.1.0: + resolution: {integrity: sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==} + dev: true + + /import-fresh/3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash/0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string/4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight/1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /internal-slot/1.0.3: + resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.1.2 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /is-bigint/1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-boolean-object/1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable/1.2.4: + resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module/2.10.0: + resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==} + dependencies: + has: 1.0.3 + dev: true + + /is-date-object/1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-extglob/2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob/4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-negative-zero/2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object/1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-potential-custom-element-name/1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: true + + /is-regex/1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-shared-array-buffer/1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-string/1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol/1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-weakref/1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /isexe/2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /jest-diff/28.1.3: + resolution: {integrity: sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 28.1.1 + jest-get-type: 28.0.2 + pretty-format: 28.1.3 + dev: true + + /jest-get-type/28.0.2: + resolution: {integrity: sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dev: true + + /jest-matcher-utils/28.1.3: + resolution: {integrity: sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 28.1.3 + jest-get-type: 28.0.2 + pretty-format: 28.1.3 + dev: true + + /js-tokens/4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml/4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsdom/20.0.0: + resolution: {integrity: sha512-x4a6CKCgx00uCmP+QakBDFXwjAJ69IkkIWHmtmjd3wvXPcdOS44hfX2vqkOQrVrq8l9DhNNADZRXaCEWvgXtVA==} + engines: {node: '>=14'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + abab: 2.0.6 + acorn: 8.8.0 + acorn-globals: 6.0.0 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.3.1 + domexception: 4.0.0 + escodegen: 2.0.0 + form-data: 4.0.0 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.1 + parse5: 7.0.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.0.0 + w3c-hr-time: 1.0.2 + w3c-xmlserializer: 3.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + ws: 8.8.1 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + /jsesc/2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-schema-traverse/0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify/1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5/1.0.1: + resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} + hasBin: true + dependencies: + minimist: 1.2.6 + dev: true + + /json5/2.2.1: + resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsx-ast-utils/3.3.2: + resolution: {integrity: sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.5 + object.assign: 4.1.3 + dev: true + + /levn/0.3.0: + resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + type-check: 0.3.2 + dev: true + + /levn/0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig/2.0.6: + resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} + engines: {node: '>=10'} + dev: true + + /local-pkg/0.4.2: + resolution: {integrity: sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==} + engines: {node: '>=14'} + dev: true + + /locate-path/2.0.0: + resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} + engines: {node: '>=4'} + dependencies: + p-locate: 2.0.0 + path-exists: 3.0.0 + dev: true + + /locate-path/6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge/4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash/4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /loose-envify/1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + + /loupe/2.3.4: + resolution: {integrity: sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==} + dependencies: + get-func-name: 2.0.0 + dev: true + + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /lz-string/1.4.4: + resolution: {integrity: sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==} + hasBin: true + dev: true + + /magic-string/0.26.2: + resolution: {integrity: sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==} + engines: {node: '>=12'} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch/4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime-db/1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: true + + /mime-types/2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: true + + /min-indent/1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch/3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist/1.2.6: + resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} + dev: true + + /mrmime/1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + dev: true + + /ms/2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: true + + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms/2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /nanoid/3.3.4: + resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare/1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /node-releases/2.0.6: + resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} + dev: true + + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-range/0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + + /nwsapi/2.2.1: + resolution: {integrity: sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==} + dev: true + + /object-assign/4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + /object-hash/3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: true + + /object-inspect/1.12.2: + resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} + dev: true + + /object-keys/1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign/4.1.3: + resolution: {integrity: sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.entries/1.1.5: + resolution: {integrity: sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + dev: true + + /object.fromentries/2.0.5: + resolution: {integrity: sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + dev: true + + /object.hasown/1.1.1: + resolution: {integrity: sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==} + dependencies: + define-properties: 1.1.4 + es-abstract: 1.20.1 + dev: true + + /object.values/1.1.5: + resolution: {integrity: sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + dev: true + + /once/1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator/0.8.3: + resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.3.0 + prelude-ls: 1.1.2 + type-check: 0.3.2 + word-wrap: 1.2.3 + dev: true + + /optionator/0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.3 + dev: true + + /p-limit/1.3.0: + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} + dependencies: + p-try: 1.0.0 + dev: true + + /p-limit/3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate/2.0.0: + resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} + engines: {node: '>=4'} + dependencies: + p-limit: 1.3.0 + dev: true + + /p-locate/5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-try/1.0.0: + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} + engines: {node: '>=4'} + dev: true + + /parent-module/1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse5/7.0.0: + resolution: {integrity: sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==} + dependencies: + entities: 4.3.1 + dev: true + + /path-exists/3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + dev: true + + /path-exists/4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute/1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key/3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type/4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pathval/1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /picocolors/1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch/2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pify/2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + 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: + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.16 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.1 + dev: true + + /postcss-js/4.0.0_postcss@8.4.16: + resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.3.3 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.16 + dev: true + + /postcss-load-config/3.1.4_postcss@8.4.16: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.0.6 + postcss: 8.4.16 + yaml: 1.10.2 + dev: true + + /postcss-nested/5.0.6_postcss@8.4.16: + resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.16 + postcss-selector-parser: 6.0.10 + dev: true + + /postcss-selector-parser/6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-value-parser/4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + + /postcss/8.4.16: + resolution: {integrity: sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls/1.1.2: + resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} + engines: {node: '>= 0.8.0'} + dev: true + + /prelude-ls/1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /pretty-format/27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + dev: true + + /pretty-format/28.1.3: + resolution: {integrity: sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/schemas': 28.1.3 + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /prop-types/15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + /psl/1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + dev: true + + /punycode/2.1.1: + resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + engines: {node: '>=6'} + dev: true + + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /quick-lru/5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true + + /react-dom/18.2.0_react@18.2.0: + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + + /react-is/16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + /react-is/17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + dev: true + + /react-is/18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /react-refresh/0.14.0: + resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} + engines: {node: '>=0.10.0'} + dev: true + + /react-svg-pan-zoom/3.11.0_react@18.2.0: + resolution: {integrity: sha512-xK2tpfp4YksHOfyMZH5zXP52ARLSBgkoJgWNJmJ1B+6O1tkuf23TQp7Q4m9GG5IRSK5KWO0JEGEWlNYG9+iiug==} + peerDependencies: + react: '>=17.0.0' + dependencies: + prop-types: 15.8.1 + react: 18.2.0 + transformation-matrix: 2.12.0 + dev: false + + /react/18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + + /read-cache/1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: true + + /readdirp/3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /redent/3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + dev: true + + /regenerator-runtime/0.13.9: + resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} + dev: true + + /regexp.prototype.flags/1.4.3: + resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + functions-have-names: 1.2.3 + dev: true + + /regexpp/3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /resolve-from/4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve/1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.10.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /resolve/2.0.0-next.4: + resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + hasBin: true + dependencies: + is-core-module: 2.10.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify/1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf/3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup/2.77.2: + resolution: {integrity: sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /run-parallel/1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-buffer/5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /safer-buffer/2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true + + /sass/1.54.3: + resolution: {integrity: sha512-fLodey5Qd41Pxp/Tk7Al97sViYwF/TazRc5t6E65O7JOk4XF8pzwIW7CvCxYVOfJFFI/1x5+elDyBIixrp+zrw==} + engines: {node: '>=12.0.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + immutable: 4.1.0 + source-map-js: 1.0.2 + dev: true + + /saxes/6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + dependencies: + xmlchars: 2.2.0 + dev: true + + /scheduler/0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + + /semver/6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: true + + /semver/7.3.7: + resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /shebang-command/2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex/3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.2 + object-inspect: 1.12.2 + dev: true + + /sirv/2.0.2: + resolution: {integrity: sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.21 + mrmime: 1.0.1 + totalist: 3.0.0 + dev: true + + /slash/3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-js/1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map/0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: true + optional: true + + /sourcemap-codec/1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + dev: true + + /string.prototype.matchall/4.0.7: + resolution: {integrity: sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + get-intrinsic: 1.1.2 + has-symbols: 1.0.3 + internal-slot: 1.0.3 + regexp.prototype.flags: 1.4.3 + side-channel: 1.0.4 + dev: true + + /string.prototype.trimend/1.0.5: + resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + dev: true + + /string.prototype.trimstart/1.0.5: + resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + dev: true + + /strip-ansi/6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-bom/3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-indent/3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments/3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + 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: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color/7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag/1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /symbol-tree/3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: true + + /tailwindcss/3.1.8: + resolution: {integrity: sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g==} + engines: {node: '>=12.13.0'} + hasBin: true + dependencies: + arg: 5.0.2 + chokidar: 3.5.3 + color-name: 1.1.4 + detective: 5.2.1 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.2.11 + glob-parent: 6.0.2 + is-glob: 4.0.3 + lilconfig: 2.0.6 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.16 + postcss-import: 14.1.0_postcss@8.4.16 + postcss-js: 4.0.0_postcss@8.4.16 + postcss-load-config: 3.1.4_postcss@8.4.16 + postcss-nested: 5.0.6_postcss@8.4.16 + postcss-selector-parser: 6.0.10 + postcss-value-parser: 4.2.0 + quick-lru: 5.1.1 + resolve: 1.22.1 + transitivePeerDependencies: + - ts-node + dev: true + + /text-table/0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /tinypool/0.2.4: + resolution: {integrity: sha512-Vs3rhkUH6Qq1t5bqtb816oT+HeJTXfwt2cbPH17sWHIYKTotQIFPk3tf2fgqRrVyMDVOc1EnPgzIxfIulXVzwQ==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy/1.0.0: + resolution: {integrity: sha512-FI5B2QdODQYDRjfuLF+OrJ8bjWRMCXokQPcwKm0W3IzcbUmBNv536cQc7eXGoAuXphZwgx1DFbqImwzz08Fnhw==} + engines: {node: '>=14.0.0'} + dev: true + + /to-fast-properties/2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /totalist/3.0.0: + resolution: {integrity: sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==} + engines: {node: '>=6'} + dev: true + + /tough-cookie/4.0.0: + resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==} + engines: {node: '>=6'} + dependencies: + psl: 1.9.0 + punycode: 2.1.1 + universalify: 0.1.2 + dev: true + + /tr46/3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + dependencies: + punycode: 2.1.1 + dev: true + + /transformation-matrix/2.12.0: + resolution: {integrity: sha512-BbzXM7el7rNwIr1s87m8tcffH5qgY+HYROLn3BStRU9Y6vYTL37YZKadfNPEvGbP813iA1h8qflo4pa2TomkyQ==} + dev: false + + /tsconfig-paths/3.14.1: + resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.1 + minimist: 1.2.6 + strip-bom: 3.0.0 + dev: true + + /tslib/1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib/2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + dev: false + + /tsutils/3.21.0_typescript@4.7.4: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.7.4 + dev: true + + /type-check/0.3.2: + resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + dev: true + + /type-check/0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect/4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest/0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typescript/4.7.4: + resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /unbox-primitive/1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /universalify/0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: true + + /update-browserslist-db/1.0.5_browserslist@4.21.3: + resolution: {integrity: sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.3 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js/4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.1.1 + dev: true + + /util-deprecate/1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /v8-compile-cache/2.3.0: + resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} + dev: true + + /vite/3.0.4_sass@1.54.3: + resolution: {integrity: sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + less: '*' + sass: '*' + stylus: '*' + terser: ^5.4.0 + peerDependenciesMeta: + less: + optional: true + sass: + optional: true + stylus: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.14.54 + postcss: 8.4.16 + resolve: 1.22.1 + rollup: 2.77.2 + sass: 1.54.3 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vitest/0.20.3_hymhw3vkyr5yfvzfskw3x5v26q: + resolution: {integrity: sha512-cXMjTbZxBBUUuIF3PUzEGPLJWtIMeURBDXVxckSHpk7xss4JxkiiWh5cnIlfGyfJne2Ii3QpbiRuFL5dMJtljw==} + engines: {node: '>=v14.16.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + c8: '*' + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + c8: + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/chai': 4.3.3 + '@types/chai-subset': 1.3.3 + '@types/node': 18.6.4 + '@vitest/ui': 0.20.3 + chai: 4.3.6 + debug: 4.3.4 + jsdom: 20.0.0 + local-pkg: 0.4.2 + tinypool: 0.2.4 + tinyspy: 1.0.0 + vite: 3.0.4_sass@1.54.3 + transitivePeerDependencies: + - less + - sass + - stylus + - supports-color + - terser + dev: true + + /w3c-hr-time/1.0.2: + resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} + dependencies: + browser-process-hrtime: 1.0.0 + dev: true + + /w3c-xmlserializer/3.0.0: + resolution: {integrity: sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==} + engines: {node: '>=12'} + dependencies: + xml-name-validator: 4.0.0 + dev: true + + /webidl-conversions/7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: true + + /whatwg-encoding/2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + dependencies: + iconv-lite: 0.6.3 + dev: true + + /whatwg-mimetype/3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + dev: true + + /whatwg-url/11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: true + + /which-boxed-primitive/1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which/2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /word-wrap/1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /ws/8.8.1: + resolution: {integrity: sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + + /xml-name-validator/4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + dev: true + + /xmlchars/2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: true + + /xtend/4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml/1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + + /yocto-queue/0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 2b8c7fd..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,206 +0,0 @@ -import * as React from 'react'; -import './App.scss'; -import { MainMenu } from './Components/MainMenu/MainMenu'; -import { ContainerModel, findContainerById, IContainerModel, MakeIterator } from './Interfaces/ContainerModel'; -import Editor, { IEditorState } from './Editor'; -import { AvailableContainer } from './Interfaces/AvailableContainer'; -import { Configuration } from './Interfaces/Configuration'; - -export interface IHistoryState { - MainContainer: IContainerModel | null, - SelectedContainer: IContainerModel | null, - SelectedContainerId: string, - TypeCounters: Record -} - -interface IAppProps { -} - -interface IAppState { - configuration: Configuration, - history: IHistoryState[], - historyCurrentStep: number, - isLoaded: boolean -} - -export class App extends React.Component { - public state: IAppState; - - constructor(props: IAppProps) { - super(props); - this.state = { - configuration: { - AvailableContainers: [], - AvailableSymbols: [], - MainContainer: {} as AvailableContainer - }, - history: [], - historyCurrentStep: 0, - isLoaded: false - }; - } - - componentDidMount() { - const queryString = window.location.search; - const urlParams = new URLSearchParams(queryString); - const state = urlParams.get('state'); - - if (state === null) { - return; - } - - fetch(state) - .then((response) => response.json()) - .then((data: IEditorState) => { - this.LoadState(data); - }); - } - - public NewEditor() { - // Fetch the configuration from the API - fetchConfiguration().then((configuration: Configuration) => { - // Set the main container from the given properties of the API - const MainContainer = new ContainerModel( - null, - { - id: 'main', - parentId: 'null', - x: 0, - y: 0, - width: configuration.MainContainer.Width, - height: configuration.MainContainer.Height, - fillOpacity: 0, - stroke: 'black' - } - ); - - // Save the configuration and the new MainContainer - // and default the selected container to it - this.setState({ - configuration, - history: - [ - { - MainContainer, - SelectedContainer: MainContainer, - TypeCounters: {} - } - ], - historyCurrentStep: 0, - isLoaded: true - } as IAppState); - }); - } - - public LoadEditor(files: FileList | null) { - if (files === null) { - return; - } - const file = files[0]; - const reader = new FileReader(); - reader.addEventListener('load', () => { - const result = reader.result as string; - const editorState: IEditorState = JSON.parse(result); - - this.LoadState(editorState); - }); - reader.readAsText(file); - } - - private LoadState(editorState: IEditorState) { - Revive(editorState); - - this.setState({ - configuration: editorState.configuration, - history: editorState.history, - historyCurrentStep: editorState.historyCurrentStep, - isLoaded: true - } as IAppState); - } - - public render() { - if (this.state.isLoaded) { - return ( -
- -
- ); - } else { - return ( -
- this.NewEditor()} - loadEditor={(files: FileList | null) => this.LoadEditor(files)} - /> -
- ); - } - } -} - -/** - * Fetch the configuration from the API - * @returns {Configation} The model of the configuration for the application - */ -export async function fetchConfiguration(): Promise { - const url = `${import.meta.env.VITE_API_URL}`; - // The test library cannot use the Fetch API - // @ts-ignore - if (window.fetch) { - return await fetch(url, { - method: 'POST' - }) - .then((response) => - response.json() - ) as Configuration; - } - return new Promise((resolve) => { - const xhr = new XMLHttpRequest(); - xhr.open('POST', url, true); - xhr.onreadystatechange = function() { // Call a function when the state changes. - if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { - resolve(JSON.parse(this.responseText)); - } - }; - xhr.send(); - }); -} - -/** - * Revive the Editor state - * by setting the containers references to their parent - * @param editorState Editor state - */ -function Revive(editorState: IEditorState): void { - const history = editorState.history; - for (const state of history) { - if (state.MainContainer === null || state.MainContainer === undefined) { - continue; - } - - const it = MakeIterator(state.MainContainer); - for (const container of it) { - const parentId = container.properties.parentId; - if (parentId === null) { - container.parent = null; - continue; - } - const parent = findContainerById(state.MainContainer, parentId); - if (parent === undefined) { - continue; - } - container.parent = parent; - } - - const selected = findContainerById(state.MainContainer, state.SelectedContainerId); - if (selected === undefined) { - state.SelectedContainer = null; - continue; - } - state.SelectedContainer = selected; - } -} diff --git a/src/test/api.test.tsx b/src/Components/API/api.test.tsx similarity index 70% rename from src/test/api.test.tsx rename to src/Components/API/api.test.tsx index 19db6cc..55d63b1 100644 --- a/src/test/api.test.tsx +++ b/src/Components/API/api.test.tsx @@ -1,13 +1,13 @@ -import { describe, test, expect } from 'vitest'; -import { fetchConfiguration } from '../App'; +import { describe, it, expect } from 'vitest'; +import { fetchConfiguration } from './api'; -describe('API test', () => { - test('Load environment', () => { +describe.concurrent('API test', () => { + it('Load environment', () => { const url = import.meta.env.VITE_API_URL; expect(url).toBe('http://localhost:5000'); }); - test('Fetch configuration', async() => { + it('Fetch configuration', async() => { const configuration = await fetchConfiguration(); expect(configuration.MainContainer).toBeDefined(); expect(configuration.MainContainer.Height).toBeGreaterThan(0); diff --git a/src/Components/API/api.ts b/src/Components/API/api.ts new file mode 100644 index 0000000..e113cc9 --- /dev/null +++ b/src/Components/API/api.ts @@ -0,0 +1,30 @@ +import { Configuration } from '../../Interfaces/Configuration'; + +/** + * Fetch the configuration from the API + * @returns {Configation} The model of the configuration for the application + */ +export async function fetchConfiguration(): Promise { + const url = `${import.meta.env.VITE_API_URL}`; + // The test library cannot use the Fetch API + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + if (window.fetch) { + return await fetch(url, { + method: 'POST' + }) + .then(async(response) => + await response.json() + ) as Configuration; + } + return await new Promise((resolve) => { + const xhr = new XMLHttpRequest(); + xhr.open('POST', url, true); + xhr.onreadystatechange = function() { // Call a function when the state changes. + if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { + resolve(JSON.parse(this.responseText)); + } + }; + xhr.send(); + }); +} diff --git a/src/App.scss b/src/Components/App/App.scss similarity index 100% rename from src/App.scss rename to src/Components/App/App.scss diff --git a/src/Components/App/App.tsx b/src/Components/App/App.tsx new file mode 100644 index 0000000..fa725cf --- /dev/null +++ b/src/Components/App/App.tsx @@ -0,0 +1,79 @@ +import React, { useEffect, useState } from 'react'; +import './App.scss'; +import { MainMenu } from '../MainMenu/MainMenu'; +import { ContainerModel } from '../../Interfaces/ContainerModel'; +import Editor, { IEditorState } from '../Editor/Editor'; +import { LoadState } from './Load'; +import { LoadEditor, NewEditor } from './MenuActions'; +import { DEFAULT_CONFIG, DEFAULT_MAINCONTAINER_PROPS } from '../../utils/default'; + +// App will never have props +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface IAppProps { +} + +export const App: React.FunctionComponent = (props) => { + const [isLoaded, setLoaded] = useState(false); + + const defaultMainContainer = new ContainerModel( + null, + DEFAULT_MAINCONTAINER_PROPS + ); + + const [editorState, setEditorState] = useState({ + configuration: DEFAULT_CONFIG, + history: [{ + MainContainer: defaultMainContainer, + SelectedContainer: defaultMainContainer, + SelectedContainerId: defaultMainContainer.properties.id, + TypeCounters: {} + }], + historyCurrentStep: 0 + }); + + useEffect(() => { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const state = urlParams.get('state'); + + if (state === null) { + return; + } + + fetch(state) + .then( + async(response) => await response.json(), + (error) => { throw new Error(error); } + ) + .then((data: IEditorState) => { + LoadState(data, setEditorState, setLoaded); + }, (error) => { throw new Error(error); }); + }); + + if (isLoaded) { + return ( +
+ +
+ ); + } + + return ( +
+ NewEditor( + setEditorState, setLoaded + )} + loadEditor={(files: FileList | null) => LoadEditor( + files, + setEditorState, + setLoaded + )} + /> +
+ ); +}; diff --git a/src/Components/App/Load.ts b/src/Components/App/Load.ts new file mode 100644 index 0000000..b5459c7 --- /dev/null +++ b/src/Components/App/Load.ts @@ -0,0 +1,13 @@ +import { Dispatch, SetStateAction } from 'react'; +import { Revive } from '../../utils/saveload'; +import { IEditorState } from '../Editor/Editor'; + +export function LoadState( + editorState: IEditorState, + setEditorState: Dispatch>, + setLoaded: Dispatch> +): void { + Revive(editorState); + setEditorState(editorState); + setLoaded(true); +} diff --git a/src/Components/App/MenuActions.ts b/src/Components/App/MenuActions.ts new file mode 100644 index 0000000..ce0cd7d --- /dev/null +++ b/src/Components/App/MenuActions.ts @@ -0,0 +1,71 @@ +import { Dispatch, SetStateAction } from 'react'; +import { Configuration } from '../../Interfaces/Configuration'; +import { ContainerModel } from '../../Interfaces/ContainerModel'; +import { fetchConfiguration } from '../API/api'; +import { IEditorState } from '../Editor/Editor'; +import { LoadState } from './Load'; + +export function NewEditor( + setEditorState: Dispatch>, + setLoaded: Dispatch> +): void { + // Fetch the configuration from the API + fetchConfiguration() + .then((configuration: Configuration) => { + // Set the main container from the given properties of the API + const MainContainer = new ContainerModel( + null, + { + id: 'main', + parentId: 'null', + x: 0, + y: 0, + width: configuration.MainContainer.Width, + height: configuration.MainContainer.Height, + fillOpacity: 0, + stroke: 'black' + } + ); + + // Save the configuration and the new MainContainer + // and default the selected container to it + const editorState: IEditorState = { + configuration, + history: + [ + { + MainContainer, + SelectedContainer: MainContainer, + SelectedContainerId: MainContainer.properties.id, + TypeCounters: {} + } + ], + historyCurrentStep: 0 + }; + setEditorState(editorState); + setLoaded(true); + }, (error) => { + // TODO: Implement an alert component + console.warn('[NewEditor] Could not fetch resource from API. Using default.', error); + setLoaded(true); + }); +} + +export function LoadEditor( + files: FileList | null, + setEditorState: Dispatch>, + setLoaded: Dispatch> +): void { + if (files === null) { + return; + } + const file = files[0]; + const reader = new FileReader(); + reader.addEventListener('load', () => { + const result = reader.result as string; + const editorState: IEditorState = JSON.parse(result); + + LoadState(editorState, setEditorState, setLoaded); + }); + reader.readAsText(file); +} diff --git a/src/Components/Bar/Bar.tsx b/src/Components/Bar/Bar.tsx new file mode 100644 index 0000000..1738a3f --- /dev/null +++ b/src/Components/Bar/Bar.tsx @@ -0,0 +1,39 @@ +import { ClockIcon, CubeIcon, MapIcon } from '@heroicons/react/outline'; +import * as React from 'react'; +import { BarIcon } from './BarIcon'; + +interface IBarProps { + isSidebarOpen: boolean + isElementsSidebarOpen: boolean + isHistoryOpen: boolean + ToggleSidebar: () => void + ToggleElementsSidebar: () => void + ToggleTimeline: () => void +} + +export const BAR_WIDTH = 64; // 4rem + +export const Bar: React.FC = (props) => { + return ( +
+ props.ToggleSidebar()}> + + + props.ToggleElementsSidebar()}> + + + props.ToggleTimeline()}> + + +
+ ); +}; diff --git a/src/Components/Bar/BarIcon.tsx b/src/Components/Bar/BarIcon.tsx new file mode 100644 index 0000000..5afaae7 --- /dev/null +++ b/src/Components/Bar/BarIcon.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; + +interface IBarIconProps { + title: string + children: React.ReactElement + isActive: boolean + onClick: () => void +} + +export const BarIcon: React.FC = (props) => { + const isActiveClasses = props.isActive ? 'border-l-4 border-blue-500 bg-slate-200' : ''; + return ( + + ); +}; diff --git a/src/Components/Editor/ContainerOperations.ts b/src/Components/Editor/ContainerOperations.ts new file mode 100644 index 0000000..d9bdea5 --- /dev/null +++ b/src/Components/Editor/ContainerOperations.ts @@ -0,0 +1,269 @@ +import { Dispatch, SetStateAction } from 'react'; +import { HistoryState } from "../../Interfaces/HistoryState"; +import { Configuration } from '../../Interfaces/Configuration'; +import { ContainerModel, IContainerModel } from '../../Interfaces/ContainerModel'; +import { findContainerById } from '../../utils/itertools'; +import { getCurrentHistory } from './Editor'; + +/** + * Select a container + * @param container Selected container + */ +export function SelectContainer( + container: ContainerModel, + fullHistory: HistoryState[], + historyCurrentStep: number, + setHistory: Dispatch>, + setHistoryCurrentStep: Dispatch> +): void { + const history = getCurrentHistory(fullHistory, historyCurrentStep); + const current = history[history.length - 1]; + + if (current.MainContainer === null) { + throw new Error('[SelectContainer] Tried to select a container while there is no main container!'); + } + + const mainContainerClone = structuredClone(current.MainContainer); + const SelectedContainer = findContainerById(mainContainerClone, container.properties.id); + + if (SelectedContainer === undefined) { + throw new Error('[SelectContainer] Cannot find container among children of main container!'); + } + + setHistory(history.concat([{ + MainContainer: mainContainerClone, + TypeCounters: Object.assign({}, current.TypeCounters), + SelectedContainer, + SelectedContainerId: SelectedContainer.properties.id + }])); + setHistoryCurrentStep(history.length); +} + +export function DeleteContainer( + containerId: string, + fullHistory: HistoryState[], + historyCurrentStep: number, + setHistory: Dispatch>, + setHistoryCurrentStep: Dispatch> +): void { + const history = getCurrentHistory(fullHistory, historyCurrentStep); + const current = history[historyCurrentStep]; + + if (current.MainContainer === null) { + throw new Error('[DeleteContainer] Error: Tried to delete a container without a main container'); + } + + const mainContainerClone: IContainerModel = structuredClone(current.MainContainer); + const container = findContainerById(mainContainerClone, containerId); + + if (container === undefined) { + throw new Error(`[DeleteContainer] Tried to delete a container that is not present in the main container: ${containerId}`); + } + + if (container === mainContainerClone) { + // TODO: Implement alert + throw new Error('[DeleteContainer] Tried to delete the main container! Deleting the main container is not allowed !'); + } + + if (container === null || container === undefined) { + throw new Error('[OnPropertyChange] Container model was not found among children of the main container!'); + } + + if (container.parent != null) { + const index = container.parent.children.indexOf(container); + if (index > -1) { + container.parent.children.splice(index, 1); + } + } + + setHistory(history.concat([{ + SelectedContainer: null, + SelectedContainerId: '', + MainContainer: mainContainerClone, + TypeCounters: Object.assign({}, current.TypeCounters) + }])); + setHistoryCurrentStep(history.length); +} + +/** + * Add a new container to a selected container + * @param type The type of container + * @returns void + */ +export function AddContainerToSelectedContainer( + type: string, + configuration: Configuration, + fullHistory: HistoryState[], + historyCurrentStep: number, + setHistory: Dispatch>, + setHistoryCurrentStep: Dispatch> +): void { + const history = getCurrentHistory(fullHistory, historyCurrentStep); + const current = history[history.length - 1]; + + if (current.SelectedContainer === null || + current.SelectedContainer === undefined) { + return; + } + + const parent = current.SelectedContainer; + AddContainer( + parent.children.length, + type, + parent.properties.id, + configuration, + fullHistory, + historyCurrentStep, + setHistory, + setHistoryCurrentStep + ); +} + +export function AddContainer( + index: number, + type: string, + parentId: string, + configuration: Configuration, + fullHistory: HistoryState[], + historyCurrentStep: number, + setHistory: Dispatch>, + setHistoryCurrentStep: Dispatch> +): void { + const history = getCurrentHistory(fullHistory, historyCurrentStep); + const current = history[history.length - 1]; + + if (current.MainContainer === null || + current.MainContainer === undefined) { + return; + } + + // Get the preset properties from the API + const properties = configuration.AvailableContainers + .find(option => option.Type === type); + + if (properties === undefined) { + throw new Error(`[AddContainer] Object type not found. Found: ${type}`); + } + + // Set the counter of the object type in order to assign an unique id + const newCounters = Object.assign({}, current.TypeCounters); + if (newCounters[type] === null || + newCounters[type] === undefined) { + newCounters[type] = 0; + } else { + newCounters[type]++; + } + const count = newCounters[type]; + + // Create maincontainer model + const clone: IContainerModel = structuredClone(current.MainContainer); + + // Find the parent + const parentClone: IContainerModel | undefined = findContainerById( + clone, parentId + ); + + if (parentClone === null || parentClone === undefined) { + throw new Error('[AddContainer] Container model was not found among children of the main container!'); + } + + let x = 0; + if (index !== 0) { + const lastChild: IContainerModel | undefined = parentClone.children.at(index - 1); + if (lastChild !== undefined) { + x = lastChild.properties.x + Number(lastChild.properties.width); + } + } + + // Create the container + const newContainer = new ContainerModel( + parentClone, + { + id: `${type}-${count}`, + parentId: parentClone.properties.id, + x, + y: 0, + width: properties?.Width, + height: parentClone.properties.height, + ...properties.Style + }, + [], + { + type + } + ); + + // And push it the the parent children + if (index === parentClone.children.length) { + parentClone.children.push(newContainer); + } else { + parentClone.children.splice(index, 0, newContainer); + } + + // Update the state + setHistory(history.concat([{ + MainContainer: clone, + TypeCounters: newCounters, + SelectedContainer: parentClone, + SelectedContainerId: parentClone.properties.id + }])); + setHistoryCurrentStep(history.length); +} + +/** + * 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, + fullHistory: HistoryState[], + historyCurrentStep: number, + setHistory: Dispatch>, + setHistoryCurrentStep: Dispatch> +): 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 (current.MainContainer === null || + current.MainContainer === undefined) { + throw new Error('[OnPropertyChange] Property was changed before the main container was added'); + } + + if (parent === null) { + const selectedContainerClone: IContainerModel = structuredClone(current.SelectedContainer); + (selectedContainerClone.properties as any)[key] = value; + setHistory(history.concat([{ + SelectedContainer: selectedContainerClone, + SelectedContainerId: selectedContainerClone.properties.id, + MainContainer: selectedContainerClone, + 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; + + setHistory(history.concat([{ + SelectedContainer: container, + SelectedContainerId: container.properties.id, + MainContainer: mainContainerClone, + TypeCounters: Object.assign({}, current.TypeCounters) + }])); + setHistoryCurrentStep(history.length); +} diff --git a/src/Editor.scss b/src/Components/Editor/Editor.scss similarity index 100% rename from src/Editor.scss rename to src/Components/Editor/Editor.scss diff --git a/src/Components/Editor/Editor.tsx b/src/Components/Editor/Editor.tsx new file mode 100644 index 0000000..cf67ed7 --- /dev/null +++ b/src/Components/Editor/Editor.tsx @@ -0,0 +1,115 @@ +import React from 'react'; +import './Editor.scss'; +import { Configuration } from '../../Interfaces/Configuration'; +import { SVG } from '../SVG/SVG'; +import { HistoryState } from '../../Interfaces/HistoryState'; +import { UI } from '../UI/UI'; +import { SelectContainer, DeleteContainer, OnPropertyChange, AddContainerToSelectedContainer, AddContainer } from './ContainerOperations'; +import { SaveEditorAsJSON, SaveEditorAsSVG } from './Save'; +import { onKeyDown } from './Shortcuts'; + +interface IEditorProps { + configuration: Configuration + history: HistoryState[] + historyCurrentStep: number +} + +export interface IEditorState { + history: HistoryState[] + historyCurrentStep: number + configuration: Configuration +} + +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 = (props) => { + const [history, setHistory] = React.useState([...props.history]); + const [historyCurrentStep, setHistoryCurrentStep] = React.useState(0); + + React.useEffect(() => { + window.addEventListener('keyup', (event) => onKeyDown( + event, + history, + historyCurrentStep, + setHistoryCurrentStep + )); + + return () => { + window.removeEventListener('keyup', (event) => onKeyDown( + event, + history, + historyCurrentStep, + setHistoryCurrentStep + )); + }; + }); + + const configuration = props.configuration; + const current = getCurrentHistoryState(history, historyCurrentStep); + return ( +
+ SelectContainer( + container, + history, + historyCurrentStep, + setHistory, + setHistoryCurrentStep + )} + DeleteContainer={(containerId: string) => DeleteContainer( + containerId, + history, + historyCurrentStep, + setHistory, + setHistoryCurrentStep + )} + OnPropertyChange={(key, value) => OnPropertyChange( + key, value, + history, + historyCurrentStep, + setHistory, + setHistoryCurrentStep + )} + AddContainerToSelectedContainer={(type) => AddContainerToSelectedContainer( + type, + configuration, + history, + historyCurrentStep, + setHistory, + setHistoryCurrentStep + )} + AddContainer={(index, type, parentId) => AddContainer( + index, + type, + parentId, + configuration, + history, + historyCurrentStep, + setHistory, + setHistoryCurrentStep + )} + SaveEditorAsJSON={() => SaveEditorAsJSON( + history, + historyCurrentStep, + configuration + )} + SaveEditorAsSVG={() => SaveEditorAsSVG()} + LoadState={(move) => setHistoryCurrentStep(move)} + /> + + { current.MainContainer } + +
+ ); +}; + +export default Editor; diff --git a/src/Components/Editor/Save.ts b/src/Components/Editor/Save.ts new file mode 100644 index 0000000..91cee2f --- /dev/null +++ b/src/Components/Editor/Save.ts @@ -0,0 +1,41 @@ +import { HistoryState } from "../../Interfaces/HistoryState"; +import { Configuration } from '../../Interfaces/Configuration'; +import { getCircularReplacer } from '../../utils/saveload'; +import { ID } from '../SVG/SVG'; +import { IEditorState } from './Editor'; + +export function SaveEditorAsJSON( + history: HistoryState[], + historyCurrentStep: number, + configuration: Configuration +): void { + const exportName = 'state'; + const spaces = import.meta.env.DEV ? 4 : 0; + const editorState: IEditorState = { + history, + historyCurrentStep, + configuration + }; + const data = JSON.stringify(editorState, getCircularReplacer(), spaces); + const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(data)}`; + 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 { + const svgWrapper = document.getElementById(ID) as HTMLElement; + const svg = svgWrapper.querySelector('svg') as SVGSVGElement; + const preface = '\r\n'; + const svgBlob = new Blob([preface, svg.outerHTML], { type: 'image/svg+xml;charset=utf-8' }); + const svgUrl = URL.createObjectURL(svgBlob); + const downloadLink = document.createElement('a'); + downloadLink.href = svgUrl; + downloadLink.download = 'newesttree.svg'; + document.body.appendChild(downloadLink); + downloadLink.click(); + document.body.removeChild(downloadLink); +} diff --git a/src/Components/Editor/Shortcuts.ts b/src/Components/Editor/Shortcuts.ts new file mode 100644 index 0000000..00b449e --- /dev/null +++ b/src/Components/Editor/Shortcuts.ts @@ -0,0 +1,23 @@ +import { Dispatch, SetStateAction } from 'react'; +import { HistoryState } from "../../Interfaces/HistoryState"; + +export function onKeyDown( + event: KeyboardEvent, + history: HistoryState[], + historyCurrentStep: number, + setHistoryCurrentStep: Dispatch> +): void { + event.preventDefault(); + if (event.isComposing || event.keyCode === 229) { + return; + } + if (event.key === 'z' && + event.ctrlKey && + historyCurrentStep > 0) { + setHistoryCurrentStep(historyCurrentStep - 1); + } else if (event.key === 'y' && + event.ctrlKey && + historyCurrentStep < history.length - 1) { + setHistoryCurrentStep(historyCurrentStep + 1); + } +} diff --git a/src/Components/ElementsSidebar/ElementsSidebar.test.tsx b/src/Components/ElementsSidebar/ElementsSidebar.test.tsx new file mode 100644 index 0000000..230949d --- /dev/null +++ b/src/Components/ElementsSidebar/ElementsSidebar.test.tsx @@ -0,0 +1,231 @@ +import { describe, expect, it, vi } from 'vitest'; +import * as React from 'react'; +import { fireEvent, render, screen } from '../../utils/test-utils'; +import { ElementsSidebar } from './ElementsSidebar'; +import { IContainerModel } from '../../Interfaces/ContainerModel'; + +describe.concurrent('Elements sidebar', () => { + it('With a MainContainer', () => { + render( {}} + SelectContainer={() => {}} + DeleteContainer={() => {}} + AddContainer={() => {}} + />); + + expect(screen.getByText(/Elements/i)); + expect(screen.queryByText('id')).toBeNull(); + expect(screen.getByText(/main/i)); + }); + + it('With a selected MainContainer', () => { + const MainContainer = { + children: [], + parent: null, + properties: { + id: 'main', + parentId: '', + x: 0, + y: 0, + width: 2000, + height: 100 + }, + userData: {} + }; + + const { container } = render( {}} + SelectContainer={() => {}} + DeleteContainer={() => {}} + AddContainer={() => {}} + />); + + expect(screen.getByText(/Elements/i)); + expect(screen.getByText(/main/i)); + expect(screen.queryByText('id')).toBeDefined(); + expect(screen.queryByText('parentId')).toBeDefined(); + expect(screen.queryByText('x')).toBeDefined(); + expect(screen.queryByText('y')).toBeDefined(); + expect(screen.queryByText('width')).toBeDefined(); + expect(screen.queryByText('height')).toBeDefined(); + const propertyId = container.querySelector('#property-id'); + const propertyParentId = container.querySelector('#property-parentId'); + const propertyX = container.querySelector('#property-x'); + const propertyY = container.querySelector('#property-y'); + const propertyWidth = container.querySelector('#property-width'); + const propertyHeight = container.querySelector('#property-height'); + expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString()); + expect(propertyParentId).toBeDefined(); + expect((propertyParentId as HTMLInputElement).value).toBe(''); + expect(propertyX).toBeDefined(); + expect((propertyX as HTMLInputElement).value).toBe(MainContainer.properties.x.toString()); + expect(propertyY).toBeDefined(); + expect((propertyY as HTMLInputElement).value).toBe(MainContainer.properties.y.toString()); + expect(propertyWidth).toBeDefined(); + expect((propertyWidth as HTMLInputElement).value).toBe(MainContainer.properties.width.toString()); + expect(propertyHeight).toBeDefined(); + expect((propertyHeight as HTMLInputElement).value).toBe(MainContainer.properties.height.toString()); + }); + + it('With multiple containers', () => { + const children: IContainerModel[] = []; + const MainContainer = { + children, + parent: null, + properties: { + id: 'main', + parentId: '', + x: 0, + y: 0, + width: 2000, + height: 100 + }, + userData: {} + }; + + children.push( + { + children: [], + parent: MainContainer, + properties: { + id: 'child-1', + parentId: 'main', + x: 0, + y: 0, + width: 0, + height: 0 + }, + userData: {} + } + ); + + children.push( + { + children: [], + parent: MainContainer, + properties: { + id: 'child-2', + parentId: 'main', + x: 0, + y: 0, + width: 0, + height: 0 + }, + userData: {} + } + ); + + render( {}} + SelectContainer={() => {}} + DeleteContainer={() => {}} + AddContainer={() => {}} + />); + + expect(screen.getByText(/Elements/i)); + expect(screen.queryByText('id')).toBeDefined(); + expect(screen.getByText(/main/i)); + expect(screen.getByText(/child-1/i)); + expect(screen.getByText(/child-2/i)); + }); + + it('With multiple containers, change selection', () => { + const children: IContainerModel[] = []; + const MainContainer: IContainerModel = { + children, + parent: null, + properties: { + id: 'main', + parentId: '', + x: 0, + y: 0, + width: 2000, + height: 100 + }, + userData: {} + }; + + const child1Model: IContainerModel = { + children: [], + parent: MainContainer, + properties: { + id: 'child-1', + parentId: 'main', + x: 0, + y: 0, + width: 0, + height: 0 + }, + userData: {} + }; + children.push(child1Model); + + let SelectedContainer = MainContainer; + const selectContainer = vi.fn((container: IContainerModel) => { + SelectedContainer = container; + }); + + const { container, rerender } = render( {}} + SelectContainer={selectContainer} + DeleteContainer={() => {}} + AddContainer={() => {}} + />); + + expect(screen.getByText(/Elements/i)); + expect(screen.queryByText('id')).toBeDefined(); + expect(screen.getByText(/main/i)); + const child1 = screen.getByText(/child-1/i); + expect(child1); + const propertyId = container.querySelector('#property-id'); + const propertyParentId = container.querySelector('#property-parentId'); + expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString()); + expect((propertyParentId as HTMLInputElement).value).toBe(''); + + fireEvent.click(child1); + + rerender( {}} + SelectContainer={selectContainer} + DeleteContainer={() => {}} + AddContainer={() => {}} + />); + + expect((propertyId as HTMLInputElement).value === 'main').toBeFalsy(); + expect((propertyParentId as HTMLInputElement).value === '').toBeFalsy(); + expect((propertyId as HTMLInputElement).value).toBe(child1Model.properties.id.toString()); + expect((propertyParentId as HTMLInputElement).value).toBe(child1Model.properties.parentId?.toString()); + }); +}); diff --git a/src/Components/ElementsSidebar/ElementsSidebar.tsx b/src/Components/ElementsSidebar/ElementsSidebar.tsx index c4ea51a..f449960 100644 --- a/src/Components/ElementsSidebar/ElementsSidebar.tsx +++ b/src/Components/ElementsSidebar/ElementsSidebar.tsx @@ -1,81 +1,149 @@ import * as React from 'react'; import { motion } from 'framer-motion'; import { Properties } from '../Properties/Properties'; -import { IContainerModel, getDepth, MakeIterator } from '../../Interfaces/ContainerModel'; +import { IContainerModel } from '../../Interfaces/ContainerModel'; +import { getDepth, MakeIterator } from '../../utils/itertools'; +import { Menu } from '../Menu/Menu'; +import { MenuItem } from '../Menu/MenuItem'; +import { handleDragLeave, handleDragOver, handleLeftClick, handleOnDrop, handleRightClick } from './MouseEventHandlers'; +import { Point } from '../../Interfaces/Point'; interface IElementsSidebarProps { - MainContainer: IContainerModel | null, - isOpen: boolean, + MainContainer: IContainerModel + isOpen: boolean isHistoryOpen: boolean - SelectedContainer: IContainerModel | null, - onClick: () => void, - onPropertyChange: (key: string, value: string) => void, - selectContainer: (container: IContainerModel) => void + SelectedContainer: IContainerModel | null + onPropertyChange: (key: string, value: string) => void + SelectContainer: (container: IContainerModel) => void + DeleteContainer: (containerid: string) => void + AddContainer: (index: number, type: string, parent: string) => void } -export class ElementsSidebar extends React.PureComponent { - public iterateChilds(handleContainer: (container: IContainerModel) => void): React.ReactNode { - if (!this.props.MainContainer) { - return null; - } +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'; - const it = MakeIterator(this.props.MainContainer); - for (const container of it) { - handleContainer(container as IContainerModel); - } + containerRows.push( + handleOnDrop(event, props.MainContainer, props.AddContainer)} + onDragOver={(event) => handleDragOver(event, props.MainContainer)} + onDragLeave={(event) => handleDragLeave(event)} + onClick={() => props.SelectContainer(container)} + > + { text } + + ); +}; + +export const ElementsSidebar: React.FC = (props: IElementsSidebarProps): JSX.Element => { + // States + const [isContextMenuOpen, setIsContextMenuOpen] = React.useState(false); + const [onClickContainerId, setOnClickContainerId] = React.useState(''); + const [contextMenuPosition, setContextMenuPosition] = React.useState({ + x: 0, + y: 0 + }); + + const elementRef = React.useRef(null); + + // Event listeners + React.useEffect(() => { + elementRef.current?.addEventListener( + 'contextmenu', + (event) => handleRightClick( + event, + setIsContextMenuOpen, + setOnClickContainerId, + setContextMenuPosition + )); + + window.addEventListener( + 'click', + (event) => handleLeftClick( + isContextMenuOpen, + setIsContextMenuOpen, + setOnClickContainerId + )); + + return () => { + elementRef.current?.addEventListener( + 'contextmenu', + (event) => handleRightClick( + event, + setIsContextMenuOpen, + setOnClickContainerId, + setContextMenuPosition + )); + + window.removeEventListener( + 'click', + (event) => handleLeftClick( + isContextMenuOpen, + setIsContextMenuOpen, + setOnClickContainerId + )); + }; + }, []); + + // Render + let isOpenClasses = '-right-64'; + if (props.isOpen) { + isOpenClasses = props.isHistoryOpen + ? 'right-64' + : 'right-0'; } - public render() { - let isOpenClasses = '-right-64'; - if (this.props.isOpen) { - isOpenClasses = this.props.isHistoryOpen - ? 'right-64' - : 'right-0'; - } + const containerRows: React.ReactNode[] = []; - const containerRows: React.ReactNode[] = []; - this.iterateChilds((container: IContainerModel) => { - const depth: number = getDepth(container); - const key = container.properties.id.toString(); - const text = '|\t'.repeat(depth) + key; - const selectedClass: string = this.props.SelectedContainer !== undefined && - this.props.SelectedContainer !== null && - this.props.SelectedContainer.properties.id === container.properties.id - ? 'bg-blue-500 hover:bg-blue-600' - : 'bg-slate-400 hover:bg-slate-600'; - containerRows.push( - this.props.selectContainer(container)}> - { text } - - ); - }); - - return ( -
- -
- Elements -
-
- { containerRows } -
- -
+ const it = MakeIterator(props.MainContainer); + for (const container of it) { + createRows( + container, + props, + containerRows ); } -} + + return ( +
+
+ Elements +
+
+ { containerRows } +
+ + props.DeleteContainer(onClickContainerId)} /> + + +
+ ); +}; diff --git a/src/Components/ElementsSidebar/MouseEventHandlers.ts b/src/Components/ElementsSidebar/MouseEventHandlers.ts new file mode 100644 index 0000000..53fb14e --- /dev/null +++ b/src/Components/ElementsSidebar/MouseEventHandlers.ts @@ -0,0 +1,137 @@ +import { IContainerModel } from '../../Interfaces/ContainerModel'; +import { Point } from '../../Interfaces/Point'; +import { findContainerById } from '../../utils/itertools'; + +export function handleRightClick( + event: MouseEvent, + setIsContextMenuOpen: React.Dispatch>, + setOnClickContainerId: React.Dispatch>, + setContextMenuPosition: React.Dispatch> +): void { + event.preventDefault(); + + if (!(event.target instanceof HTMLButtonElement)) { + setIsContextMenuOpen(false); + setOnClickContainerId(''); + return; + } + + const contextMenuPosition: Point = { x: event.pageX, y: event.pageY }; + setIsContextMenuOpen(true); + setOnClickContainerId(event.target.id); + setContextMenuPosition(contextMenuPosition); +} + +export function handleLeftClick( + isContextMenuOpen: boolean, + setIsContextMenuOpen: React.Dispatch>, + setOnClickContainerId: React.Dispatch> +): void { + if (!isContextMenuOpen) { + return; + } + + setIsContextMenuOpen(false); + setOnClickContainerId(''); +} + +export function removeBorderClasses(target: HTMLButtonElement): void { + target.classList.remove('border-t-8'); + target.classList.remove('border-8'); + target.classList.remove('border-b-8'); +} + +export function handleDragLeave(event: React.DragEvent): void { + const target: HTMLButtonElement = event.target as HTMLButtonElement; + removeBorderClasses(target); +} + +export function handleDragOver( + event: React.DragEvent, + mainContainer: IContainerModel +): void { + event.preventDefault(); + const target: HTMLButtonElement = event.target as HTMLButtonElement; + const rect = target.getBoundingClientRect(); + const y = event.clientY - rect.top; // y position within the element. + + if (target.id === mainContainer.properties.id) { + target.classList.add('border-8'); + target.classList.remove('border-t-8'); + target.classList.remove('border-b-8'); + return; + } + + if (y < 12) { + target.classList.add('border-t-8'); + target.classList.remove('border-b-8'); + target.classList.remove('border-8'); + } else if (y < 24) { + target.classList.add('border-8'); + target.classList.remove('border-t-8'); + target.classList.remove('border-b-8'); + } else { + target.classList.add('border-b-8'); + target.classList.remove('border-8'); + target.classList.remove('border-t-8'); + } +} + +export function handleOnDrop( + event: React.DragEvent, + mainContainer: IContainerModel, + addContainer: (index: number, type: string, parent: string) => void +): void { + event.preventDefault(); + const type = event.dataTransfer.getData('type'); + const target: HTMLButtonElement = event.target as HTMLButtonElement; + removeBorderClasses(target); + + const targetContainer: IContainerModel | undefined = findContainerById( + mainContainer, + target.id + ); + + if (targetContainer === undefined) { + throw new Error('[handleOnDrop] Tried to drop onto a unknown container!'); + } + + if (targetContainer === mainContainer) { + // if the container is the root, only add type as child + addContainer( + targetContainer.children.length, + type, + targetContainer.properties.id); + return; + } + + if (targetContainer.parent === null || + targetContainer.parent === undefined) { + throw new Error('[handleDrop] Tried to drop into a child container without a parent!'); + } + + const rect = target.getBoundingClientRect(); + const y = event.clientY - rect.top; // y position within the element. + + // locate the hitboxes + if (y < 12) { + const index = targetContainer.parent.children.indexOf(targetContainer); + addContainer( + index, + type, + targetContainer.parent.properties.id + ); + } else if (y < 24) { + addContainer( + targetContainer.children.length, + type, + targetContainer.properties.id); + } else { + const index = targetContainer.parent.children.indexOf(targetContainer); + addContainer( + index + 1, + type, + targetContainer.parent.properties.id + ); + } +} diff --git a/src/Components/FloatingButton/FloatingButton.tsx b/src/Components/FloatingButton/FloatingButton.tsx index fb5fc1c..65f491e 100644 --- a/src/Components/FloatingButton/FloatingButton.tsx +++ b/src/Components/FloatingButton/FloatingButton.tsx @@ -1,10 +1,36 @@ import * as React from 'react'; +import { MenuIcon, XIcon } from '@heroicons/react/outline'; interface IFloatingButtonProps { + children: React.ReactNode[] | React.ReactNode + className: string } -const FloatingButton: React.FC = (props: IFloatingButtonProps) => { - return <>; +const toggleState = ( + isHidden: boolean, + setHidden: React.Dispatch> +): void => { + setHidden(!isHidden); }; -export default FloatingButton; +export const FloatingButton: React.FC = (props: IFloatingButtonProps) => { + const [isHidden, setHidden] = React.useState(true); + const buttonListClasses = isHidden ? 'invisible opacity-0' : 'visible opacity-100'; + const icon = isHidden + ? + : ; + + return ( +
+
+ { props.children } +
+ +
); +}; diff --git a/src/Components/History/History.tsx b/src/Components/History/History.tsx index e77a8c7..067a867 100644 --- a/src/Components/History/History.tsx +++ b/src/Components/History/History.tsx @@ -1,62 +1,56 @@ import * as React from 'react'; -import { IHistoryState } from '../../App'; +import { HistoryState } from "../../Interfaces/HistoryState"; interface IHistoryProps { - history: IHistoryState[], - historyCurrentStep: number, - isOpen: boolean, - onClick: () => void, + history: HistoryState[] + historyCurrentStep: number + isOpen: boolean jumpTo: (move: number) => void } -export class History extends React.PureComponent { - public render() { - const isOpenClasses = this.props.isOpen ? 'right-0' : '-right-64'; +export const History: React.FC = (props: IHistoryProps) => { + const isOpenClasses = props.isOpen ? 'right-0' : '-right-64'; - const states = this.props.history.map((step, move) => { - const desc = move - ? `Go to modification n°${move}` - : 'Go to the beginning'; + const states = props.history.map((step, move) => { + const desc = move > 0 + ? `Go to modification n°${move}` + : 'Go to the beginning'; - const isCurrent = move === this.props.historyCurrentStep; + const isCurrent = move === props.historyCurrentStep; - const selectedClass = isCurrent - ? 'bg-blue-500 hover:bg-blue-600' - : 'bg-slate-500 hover:bg-slate-700'; - - const isCurrentText = isCurrent - ? ' (current)' - : ''; - return ( - - - ); - }); - - // recent first - states.reverse(); + const selectedClass = isCurrent + ? 'bg-blue-500 hover:bg-blue-600' + : 'bg-slate-500 hover:bg-slate-700'; + const isCurrentText = isCurrent + ? ' (current)' + : ''; return ( -
- -
- History -
-
- { states } -
-
+ + ); - } -} + }); + + // recent first + states.reverse(); + + return ( +
+
+ Timeline +
+
+ { states } +
+
+ ); +}; diff --git a/src/Components/MainMenu/MainMenu.tsx b/src/Components/MainMenu/MainMenu.tsx index 92b19d5..1d84f9a 100644 --- a/src/Components/MainMenu/MainMenu.tsx +++ b/src/Components/MainMenu/MainMenu.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; interface IMainMenuProps { - newEditor: () => void; + newEditor: () => void loadEditor: (files: FileList | null) => void } @@ -36,18 +36,6 @@ export const MainMenu: React.FC = (props) => { "/> - {/* */} + ); +}; diff --git a/src/Components/Properties/Properties.test.tsx b/src/Components/Properties/Properties.test.tsx new file mode 100644 index 0000000..86a80e2 --- /dev/null +++ b/src/Components/Properties/Properties.test.tsx @@ -0,0 +1,82 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import * as React from 'react'; +import { expect, describe, it, vi } from 'vitest'; +import { Properties } from './Properties'; + +describe.concurrent('Properties', () => { + it('No properties', () => { + render( {}} + />); + + expect(screen.queryByText('id')).toBeNull(); + expect(screen.queryByText('parentId')).toBeNull(); + expect(screen.queryByText('x')).toBeNull(); + expect(screen.queryByText('y')).toBeNull(); + }); + + it('Some properties', () => { + const prop = { + id: 'stuff', + parentId: 'parentId', + x: 1, + y: 1 + }; + + const handleChange = vi.fn((key, value) => { + (prop as any)[key] = value; + }); + + const { container, rerender } = render(); + + expect(screen.queryByText('id')).toBeDefined(); + expect(screen.queryByText('parentId')).toBeDefined(); + expect(screen.queryByText('x')).toBeDefined(); + expect(screen.queryByText('y')).toBeDefined(); + + let propertyId = container.querySelector('#property-id'); + let propertyParentId = container.querySelector('#property-parentId'); + let propertyX = container.querySelector('#property-x'); + let propertyY = container.querySelector('#property-y'); + expect(propertyId).toBeDefined(); + expect((propertyId as HTMLInputElement).value).toBe('stuff'); + expect(propertyParentId).toBeDefined(); + expect((propertyParentId as HTMLInputElement).value).toBe('parentId'); + expect(propertyX).toBeDefined(); + expect((propertyX as HTMLInputElement).value).toBe('1'); + expect(propertyY).toBeDefined(); + expect((propertyY as HTMLInputElement).value).toBe('1'); + + fireEvent.change(propertyId as Element, { target: { value: 'stuffed' } }); + fireEvent.change(propertyParentId as Element, { target: { value: 'parentedId' } }); + fireEvent.change(propertyX as Element, { target: { value: '2' } }); + fireEvent.change(propertyY as Element, { target: { value: '2' } }); + expect(handleChange).toBeCalledTimes(4); + + expect(prop.id).toBe('stuffed'); + expect(prop.parentId).toBe('parentedId'); + expect(prop.x).toBe('2'); + expect(prop.y).toBe('2'); + rerender(); + + propertyId = container.querySelector('#property-id'); + propertyParentId = container.querySelector('#property-parentId'); + propertyX = container.querySelector('#property-x'); + propertyY = container.querySelector('#property-y'); + expect(propertyId).toBeDefined(); + expect((propertyId as HTMLInputElement).value).toBe('stuffed'); + expect(propertyParentId).toBeDefined(); + expect((propertyParentId as HTMLInputElement).value).toBe('parentedId'); + expect(propertyX).toBeDefined(); + expect((propertyX as HTMLInputElement).value).toBe('2'); + expect(propertyY).toBeDefined(); + expect((propertyY as HTMLInputElement).value).toBe('2'); + }); +}); diff --git a/src/Components/Properties/Properties.tsx b/src/Components/Properties/Properties.tsx index 65cfefd..ca2056a 100644 --- a/src/Components/Properties/Properties.tsx +++ b/src/Components/Properties/Properties.tsx @@ -2,51 +2,50 @@ import * as React from 'react'; import ContainerProperties from '../../Interfaces/Properties'; interface IPropertiesProps { - properties?: ContainerProperties, + properties?: ContainerProperties onChange: (key: string, value: string) => void } -export class Properties extends React.PureComponent { - public render() { - if (this.props.properties === undefined) { - return
; - } - - const groupInput: React.ReactNode[] = []; - Object - .entries(this.props.properties) - .forEach((pair) => this.handleProperties(pair, groupInput)); - - return ( -
- { groupInput } -
- ); +export const Properties: React.FC = (props: IPropertiesProps) => { + if (props.properties === undefined) { + return
; } - public handleProperties = ( - [key, value]: [string, string | number], - groupInput: React.ReactNode[] - ) => { - const id = `property-${key}`; - const type = 'text'; - const isDisabled = key === 'id' || key === 'parentId'; // hardcoded - groupInput.push( -
- - + { groupInput } +
+ ); +}; + +const handleProperties = ( + [key, value]: [string, string | number], + groupInput: React.ReactNode[], + onChange: (key: string, value: string) => void +): void => { + const id = `property-${key}`; + const type = 'text'; + const isDisabled = key === 'id' || key === 'parentId'; // hardcoded + groupInput.push( +
+ + this.props.onChange(key, event.target.value)} - disabled={isDisabled} - /> -
- ); - }; -} + type={type} + id={id} + value={value} + onChange={(event) => onChange(key, event.target.value)} + disabled={isDisabled} + /> + + ); +}; diff --git a/src/Components/SVG/Elements/Container.tsx b/src/Components/SVG/Elements/Container.tsx index a352bcd..02dea6f 100644 --- a/src/Components/SVG/Elements/Container.tsx +++ b/src/Components/SVG/Elements/Container.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { getDepth, IContainerModel } from '../../../Interfaces/ContainerModel'; +import { IContainerModel } from '../../../Interfaces/ContainerModel'; +import { getDepth } from '../../../utils/itertools'; import { Dimension } from './Dimension'; export interface IContainerProps { @@ -8,70 +9,68 @@ export interface IContainerProps { const GAP = 50; -export class Container extends React.PureComponent { - /** - * Render the container - * @returns Render the container - */ - public render(): React.ReactNode { - const containersElements = this.props.model.children.map(child => ); - const xText = Number(this.props.model.properties.width) / 2; - const yText = Number(this.props.model.properties.height) / 2; - const transform = `translate(${Number(this.props.model.properties.x)}, ${Number(this.props.model.properties.y)})`; +/** + * Render the container + * @returns Render the container + */ +export const Container: React.FC = (props: IContainerProps) => { + const containersElements = props.model.children.map(child => ); + const xText = Number(props.model.properties.width) / 2; + const yText = Number(props.model.properties.height) / 2; + const transform = `translate(${Number(props.model.properties.x)}, ${Number(props.model.properties.y)})`; - // g style - const defaultStyle = { - transitionProperty: 'all', - transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', - transitionDuration: '150ms' - } as React.CSSProperties; + // g style + const defaultStyle: React.CSSProperties = { + transitionProperty: 'all', + transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', + transitionDuration: '150ms' + }; - // Rect style - const style = Object.assign( - JSON.parse(JSON.stringify(defaultStyle)), - this.props.model.properties - ); - style.x = 0; - style.y = 0; - delete style.height; - delete style.width; + // Rect style + const style = Object.assign( + JSON.parse(JSON.stringify(defaultStyle)), + props.model.properties + ); + style.x = 0; + style.y = 0; + delete style.height; + delete style.width; - // Dimension props - const id = `dim-${this.props.model.properties.id}`; - const xStart: number = 0; - const xEnd = Number(this.props.model.properties.width); - const y = -(GAP * (getDepth(this.props.model) + 1)); - const strokeWidth = 1; - const text = (this.props.model.properties.width ?? 0).toString(); + // Dimension props + const id = `dim-${props.model.properties.id}`; + const xStart: number = 0; + const xEnd = Number(props.model.properties.width); + const y = -(GAP * (getDepth(props.model) + 1)); + const strokeWidth = 1; + const text = (props.model.properties.width ?? 0).toString(); - return ( - + + - - - - - {this.props.model.properties.id} - - { containersElements } - - ); - } -} + + + {props.model.properties.id} + + { containersElements } + + ); +}; diff --git a/src/Components/SVG/Elements/Dimension.tsx b/src/Components/SVG/Elements/Dimension.tsx index 96ab2c9..ec51873 100644 --- a/src/Components/SVG/Elements/Dimension.tsx +++ b/src/Components/SVG/Elements/Dimension.tsx @@ -1,52 +1,50 @@ import * as React from 'react'; interface IDimensionProps { - id: string; - xStart: number; - xEnd: number; - y: number; - text: string; - strokeWidth: number; + id: string + xStart: number + xEnd: number + y: number + text: string + strokeWidth: number } -export class Dimension extends React.PureComponent { - public render() { - const style = { - stroke: 'black' - } as React.CSSProperties; - return ( - - - - - - {this.props.text} - - - ); - } -} +export const Dimension: React.FC = (props: IDimensionProps) => { + const style: React.CSSProperties = { + stroke: 'black' + }; + return ( + + + + + + {props.text} + + + ); +}; diff --git a/src/Components/SVG/Elements/DimensionLayer.tsx b/src/Components/SVG/Elements/DimensionLayer.tsx index 3a3dee8..85f1a43 100644 --- a/src/Components/SVG/Elements/DimensionLayer.tsx +++ b/src/Components/SVG/Elements/DimensionLayer.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; -import { ContainerModel, getDepth, MakeIterator } from '../../../Interfaces/ContainerModel'; +import { ContainerModel } from '../../../Interfaces/ContainerModel'; +import { getDepth, MakeIterator } from '../../../utils/itertools'; import { Dimension } from './Dimension'; interface IDimensionLayerProps { - isHidden: boolean, - roots: ContainerModel | ContainerModel[] | null, + isHidden: boolean + roots: ContainerModel | ContainerModel[] | null } const GAP: number = 50; diff --git a/src/Components/SVG/Elements/Selector.tsx b/src/Components/SVG/Elements/Selector.tsx index de6d9a3..c5937e0 100644 --- a/src/Components/SVG/Elements/Selector.tsx +++ b/src/Components/SVG/Elements/Selector.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { IContainerModel, getAbsolutePosition } from '../../../Interfaces/ContainerModel'; +import { IContainerModel } from '../../../Interfaces/ContainerModel'; +import { getAbsolutePosition } from '../../../utils/itertools'; interface ISelectorProps { selected: IContainerModel | null @@ -18,7 +19,7 @@ export const Selector: React.FC = (props) => { props.selected.properties.width, props.selected.properties.height ]; - const style = { + const style: React.CSSProperties = { stroke: '#3B82F6', // tw blue-500 strokeWidth: 4, fillOpacity: 0, @@ -26,7 +27,7 @@ export const Selector: React.FC = (props) => { transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', transitionDuration: '150ms', animation: 'fadein 750ms ease-in alternate infinite' - } as React.CSSProperties; + }; return ( { - public state: ISVGState; - public svg: React.RefObject; +export const ID = 'svg'; - constructor(props: ISVGProps) { - super(props); - this.state = { - value: { - viewerWidth: window.innerWidth, - viewerHeight: window.innerHeight - } as Value, - tool: TOOL_PAN +function resizeViewBox( + setViewer: React.Dispatch> +): void { + setViewer({ + viewerWidth: window.innerWidth - BAR_WIDTH, + viewerHeight: window.innerHeight + }); +} + +export const SVG: React.FC = (props: ISVGProps) => { + const [viewer, setViewer] = React.useState({ + viewerWidth: window.innerWidth, + viewerHeight: window.innerHeight + }); + + React.useEffect(() => { + window.addEventListener('resize', () => resizeViewBox(setViewer)); + + return () => { + window.addEventListener('resize', () => resizeViewBox(setViewer)); }; - this.svg = React.createRef(); + }); + + const xmlns = ''; + const properties = { + width: props.width, + height: props.height, + xmlns + }; + + let children: React.ReactNode | React.ReactNode[] = []; + if (Array.isArray(props.children)) { + children = props.children.map(child => ); + } else if (props.children !== null) { + children = ; } - render() { - const xmlns = ''; - - const properties = { - width: this.props.width, - height: this.props.height, - xmlns - }; - - let children: React.ReactNode | React.ReactNode[] = []; - if (Array.isArray(this.props.children)) { - children = this.props.children.map(child => ); - } else if (this.props.children !== null) { - children = ; - } - - return ( - + this.setState({ value })} - tool={this.state.tool} onChangeTool={tool => this.setState({ tool })} miniatureProps={{ position: 'left', background: '#616264', - width: window.innerWidth - 12, + width: window.innerWidth - 12 - BAR_WIDTH, height: 120 }} > - + { children } - + - - ); - }; -} + + + ); +}; diff --git a/src/Components/Sidebar/Sidebar.test.tsx b/src/Components/Sidebar/Sidebar.test.tsx index c17f48e..886496e 100644 --- a/src/Components/Sidebar/Sidebar.test.tsx +++ b/src/Components/Sidebar/Sidebar.test.tsx @@ -1,19 +1,54 @@ import * as React from 'react'; -import { describe, test, expect } from 'vitest'; -import { render, screen } from '../../utils/test-utils'; -import Sidebar from './Sidebar'; +import { describe, it, expect, vi } from 'vitest'; +import { fireEvent, render, screen } from '../../utils/test-utils'; +import { Sidebar } from './Sidebar'; -describe('Sidebar test', () => { - test('Start empty', () => { +describe.concurrent('Sidebar', () => { + it('Start default', () => { render( {}} buttonOnClick={() => {}} /> ); + const stuff = screen.queryByText(/stuff/i); - expect(screen.getByText(/Components/i)).toBeDefined(); + expect(screen.getByText(/Components/i).classList.contains('left-0')).toBeDefined(); + expect(stuff).toBeNull(); + }); + + it('Start close', () => { + render( {}} + />); + + const stuff = screen.queryByText(/stuff/i); + expect(screen.getByText(/Components/i).classList.contains('-left-64')).toBeDefined(); + expect(stuff).toBeNull(); + }); + + it('With stuff', () => { + const Type = 'stuff'; + const handleButtonClick = vi.fn(); + render(); + const stuff = screen.getByText(/stuff/i); + + expect(stuff).toBeDefined(); + fireEvent.click(stuff); + expect(handleButtonClick).toHaveBeenCalledTimes(1); }); }); diff --git a/src/Components/Sidebar/Sidebar.tsx b/src/Components/Sidebar/Sidebar.tsx index dd34812..dc2f1a7 100644 --- a/src/Components/Sidebar/Sidebar.tsx +++ b/src/Components/Sidebar/Sidebar.tsx @@ -1,32 +1,44 @@ import * as React from 'react'; import { AvailableContainer } from '../../Interfaces/AvailableContainer'; +import { truncateString } from '../../utils/stringtools'; interface ISidebarProps { componentOptions: AvailableContainer[] - isOpen: boolean; - onClick: () => void; - buttonOnClick: (type: string) => void; + isOpen: boolean + buttonOnClick: (type: string) => void } -export default class Sidebar extends React.PureComponent { - public render() { - const listElements = this.props.componentOptions.map(componentOption => - - ); +function handleDragStart(event: React.DragEvent): void { + event.dataTransfer.setData('type', (event.target as HTMLButtonElement).id); +} - const isOpenClasses = this.props.isOpen ? 'left-0' : '-left-64'; - return ( -
- -
+export const Sidebar: React.FC = (props: ISidebarProps) => { + const listElements = props.componentOptions.map(componentOption => + + ); + + const isOpenClasses = props.isOpen ? 'left-16' : '-left-64'; + return ( +
+
Components -
+
+
{listElements}
- ); - } -} +
+ ); +}; diff --git a/src/Components/UI/UI.tsx b/src/Components/UI/UI.tsx new file mode 100644 index 0000000..7422471 --- /dev/null +++ b/src/Components/UI/UI.tsx @@ -0,0 +1,93 @@ +import * as React from 'react'; +import { ElementsSidebar } from '../ElementsSidebar/ElementsSidebar'; +import { Sidebar } from '../Sidebar/Sidebar'; +import { History } from '../History/History'; +import { AvailableContainer } from '../../Interfaces/AvailableContainer'; +import { ContainerModel } from '../../Interfaces/ContainerModel'; +import { HistoryState } from "../../Interfaces/HistoryState"; +import { PhotographIcon, UploadIcon } from '@heroicons/react/outline'; +import { FloatingButton } from '../FloatingButton/FloatingButton'; +import { Bar } from '../Bar/Bar'; + +interface IUIProps { + current: HistoryState + history: HistoryState[] + historyCurrentStep: number + AvailableContainers: AvailableContainer[] + SelectContainer: (container: ContainerModel) => void + DeleteContainer: (containerId: string) => void + OnPropertyChange: (key: string, value: string) => void + AddContainerToSelectedContainer: (type: string) => void + AddContainer: (index: number, type: string, parentId: string) => void + SaveEditorAsJSON: () => void + SaveEditorAsSVG: () => void + LoadState: (move: number) => void +} + +export const UI: React.FunctionComponent = (props: IUIProps) => { + const [isSidebarOpen, setIsSidebarOpen] = React.useState(true); + const [isElementsSidebarOpen, setIsElementsSidebarOpen] = React.useState(false); + const [isHistoryOpen, setIsHistoryOpen] = React.useState(false); + + let buttonRightOffsetClasses = 'right-12'; + if (isElementsSidebarOpen || isHistoryOpen) { + buttonRightOffsetClasses = 'right-72'; + } + if (isHistoryOpen && isElementsSidebarOpen) { + buttonRightOffsetClasses = 'right-[544px]'; + } + + return ( + <> + setIsElementsSidebarOpen(!isElementsSidebarOpen)} + ToggleSidebar={() => setIsSidebarOpen(!isSidebarOpen)} + ToggleTimeline={() => setIsHistoryOpen(!isHistoryOpen)} + /> + + props.AddContainerToSelectedContainer(type)} + /> + + + + + + + + + ); +}; + +export default UI; diff --git a/src/Editor.tsx b/src/Editor.tsx deleted file mode 100644 index 482b91a..0000000 --- a/src/Editor.tsx +++ /dev/null @@ -1,366 +0,0 @@ -import React from 'react'; -import { UploadIcon } from '@heroicons/react/outline'; -import './Editor.scss'; -import Sidebar from './Components/Sidebar/Sidebar'; -import { ElementsSidebar } from './Components/ElementsSidebar/ElementsSidebar'; -import { Configuration } from './Interfaces/Configuration'; -import { SVG } from './Components/SVG/SVG'; -import { History } from './Components/History/History'; -import { ContainerModel, findContainerById, IContainerModel, MakeIterator } from './Interfaces/ContainerModel'; -import Properties from './Interfaces/Properties'; -import { IHistoryState } from './App'; - -interface IEditorProps { - configuration: Configuration, - history: Array, - historyCurrentStep: number -} - -export interface IEditorState { - isSidebarOpen: boolean, - isElementsSidebarOpen: boolean, - isHistoryOpen: boolean, - history: Array, - historyCurrentStep: number, - // do not use it, use props.configuration - // only used for serialization purpose - configuration: Configuration -} - -class Editor extends React.Component { - public state: IEditorState; - - constructor(props: IEditorProps) { - super(props); - this.state = { - isSidebarOpen: true, - isElementsSidebarOpen: false, - isHistoryOpen: false, - configuration: Object.assign({}, props.configuration), - history: [...props.history], - historyCurrentStep: props.historyCurrentStep - } as IEditorState; - } - - public getCurrentHistory = (): IHistoryState[] => this.state.history.slice(0, this.state.historyCurrentStep + 1); - public getCurrentHistoryState = (): IHistoryState => this.state.history[this.state.historyCurrentStep]; - - /** - * Toggle the components sidebar - */ - public ToggleSidebar() { - this.setState({ - isSidebarOpen: !this.state.isSidebarOpen - } as IEditorState); - } - - /** - * Toggle the elements - */ - public ToggleElementsSidebar() { - this.setState({ - isElementsSidebarOpen: !this.state.isElementsSidebarOpen - } as IEditorState); - } - - /** - * Toggle the elements - */ - public ToggleHistory() { - this.setState({ - isHistoryOpen: !this.state.isHistoryOpen - } as IEditorState); - } - - /** - * Select a container - * @param container Selected container - */ - public SelectContainer(container: ContainerModel) { - const history = this.getCurrentHistory(); - const current = history[history.length - 1]; - - if (current.MainContainer === null) { - throw new Error('[SelectContainer] Tried to select a container while there is no main container!'); - } - - const mainContainerClone = structuredClone(current.MainContainer); - const SelectedContainer = findContainerById(mainContainerClone, container.properties.id); - - if (SelectedContainer === undefined) { - throw new Error('[SelectContainer] Cannot find container among children of main container!'); - } - - this.setState({ - history: history.concat([{ - MainContainer: mainContainerClone, - TypeCounters: Object.assign({}, current.TypeCounters), - SelectedContainer, - SelectedContainerId: SelectedContainer.properties.id - }]), - historyCurrentStep: history.length - } as IEditorState); - } - - /** - * Handled the property change event in the properties form - * @param key Property name - * @param value New value of the property - * @returns void - */ - public OnPropertyChange(key: string, value: string | number): void { - const history = this.getCurrentHistory(); - 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 (current.MainContainer === null || - current.MainContainer === undefined) { - throw new Error('[OnPropertyChange] Property was changed before the main container was added'); - } - - if (parent === null) { - const selectedContainerClone: IContainerModel = structuredClone(current.SelectedContainer); - (selectedContainerClone.properties as any)[key] = value; - this.setState({ - history: history.concat([{ - SelectedContainer: selectedContainerClone, - SelectedContainerId: selectedContainerClone.properties.id, - MainContainer: selectedContainerClone, - TypeCounters: Object.assign({}, current.TypeCounters) - }]), - historyCurrentStep: history.length - } as IEditorState); - 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; - - this.setState( - { - history: history.concat([{ - SelectedContainer: container, - SelectedContainerId: container.properties.id, - MainContainer: mainContainerClone, - TypeCounters: Object.assign({}, current.TypeCounters) - }]), - historyCurrentStep: history.length - } as IEditorState); - } - - /** - * Add a new container to a selected container - * @param type The type of container - * @returns void - */ - public AddContainer(type: string): void { - const history = this.getCurrentHistory(); - const current = history[history.length - 1]; - - if (current.SelectedContainer === null || - current.SelectedContainer === undefined) { - return; - } - - if (current.MainContainer === null || - current.MainContainer === undefined) { - return; - } - - // Get the preset properties from the API - const properties = this.props.configuration.AvailableContainers.find(option => option.Type === type); - - if (properties === undefined) { - throw new Error(`[AddContainer] Object type not found. Found: ${type}`); - } - - // Set the counter of the object type in order to assign an unique id - const newCounters = Object.assign({}, current.TypeCounters); - if (newCounters[type] === null || - newCounters[type] === undefined) { - newCounters[type] = 0; - } else { - newCounters[type]++; - } - const count = newCounters[type]; - - // Create maincontainer model - const structure: IContainerModel = structuredClone(current.MainContainer); - const clone = Object.assign(new ContainerModel(null, {} as Properties), structure); - - // Find the parent - const it = MakeIterator(clone); - let parent: ContainerModel | null = null; - for (const child of it) { - if (child.properties.id === current.SelectedContainer.properties.id) { - parent = child as ContainerModel; - break; - } - } - - if (parent === null) { - throw new Error('[OnPropertyChange] Container model was not found among children of the main container!'); - } - - let x = 0; - const lastChild: IContainerModel | undefined = parent.children.at(-1); - if (lastChild !== undefined) { - x = lastChild.properties.x + Number(lastChild.properties.width); - } - - // Create the container - const newContainer = new ContainerModel( - parent, - { - id: `${type}-${count}`, - parentId: parent.properties.id, - x, - y: 0, - width: properties?.Width, - height: parent.properties.height, - ...properties.Style - } as Properties, - [], - { - type - } - ); - - // And push it the the parent children - parent.children.push(newContainer); - - // Update the state - this.setState({ - history: history.concat([{ - MainContainer: clone, - TypeCounters: newCounters, - SelectedContainer: parent, - SelectedContainerId: parent.properties.id - }]), - historyCurrentStep: history.length - } as IEditorState); - } - - public jumpTo(move: number): void { - this.setState({ - historyCurrentStep: move - } as IEditorState); - } - - public SaveEditor() { - const exportName = 'state'; - const spaces = import.meta.env.DEV ? 4 : 0; - const data = JSON.stringify(this.state, getCircularReplacer(), spaces); - const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(data)}`; - 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(); - } - - /** - * Render the application - * @returns {JSX.Element} Rendered JSX element - */ - render() { - const current = this.getCurrentHistoryState(); - let buttonRightOffsetClasses = 'right-12'; - if (this.state.isElementsSidebarOpen || this.state.isHistoryOpen) { - buttonRightOffsetClasses = 'right-72'; - } - if (this.state.isHistoryOpen && this.state.isElementsSidebarOpen) { - buttonRightOffsetClasses = 'right-[544px]'; - } - return ( -
- this.ToggleSidebar()} - buttonOnClick={(type: string) => this.AddContainer(type)} - /> - - - this.ToggleElementsSidebar()} - onPropertyChange={(key: string, value: string) => this.OnPropertyChange(key, value)} - selectContainer={(container: ContainerModel) => this.SelectContainer(container)} - /> - - - this.ToggleHistory()} - jumpTo={(move) => { this.jumpTo(move); }} - /> - - - - { current.MainContainer } - - -
- ); - } -} - -const getCircularReplacer = () => { - const seen = new WeakSet(); - return (key: any, value: object | null) => { - if (key === 'parent') { - return; - } - - if (typeof value === 'object' && value !== null) { - if (seen.has(value)) { - return; - } - seen.add(value); - } - return value; - }; -}; - -export default Editor; diff --git a/src/Enums/AddingBehavior.ts b/src/Enums/AddingBehavior.ts index ddf722e..fb6ae67 100644 --- a/src/Enums/AddingBehavior.ts +++ b/src/Enums/AddingBehavior.ts @@ -1,4 +1,4 @@ export enum AddingBehavior { - InsertInto, - Replace + InsertInto, + Replace } diff --git a/src/Enums/XPositionReference.ts b/src/Enums/XPositionReference.ts index ec8108a..8571167 100644 --- a/src/Enums/XPositionReference.ts +++ b/src/Enums/XPositionReference.ts @@ -1,5 +1,5 @@ export enum XPositionReference { - Left, - Center, - Right + Left, + Center, + Right } diff --git a/src/Interfaces/Configuration.ts b/src/Interfaces/Configuration.ts index 2127299..f8d4854 100644 --- a/src/Interfaces/Configuration.ts +++ b/src/Interfaces/Configuration.ts @@ -3,7 +3,7 @@ import { AvailableSymbolModel } from './AvailableSymbol'; /** Model of configuration for the application to configure it */ export interface Configuration { - AvailableContainers: AvailableContainer[]; - AvailableSymbols: AvailableSymbolModel[]; - MainContainer: AvailableContainer; + AvailableContainers: AvailableContainer[] + AvailableSymbols: AvailableSymbolModel[] + MainContainer: AvailableContainer } diff --git a/src/Interfaces/ContainerModel.ts b/src/Interfaces/ContainerModel.ts index 787cf91..1c70ae3 100644 --- a/src/Interfaces/ContainerModel.ts +++ b/src/Interfaces/ContainerModel.ts @@ -1,9 +1,9 @@ import Properties from './Properties'; export interface IContainerModel { - children: IContainerModel[], - parent: IContainerModel | null, - properties: Properties, + children: IContainerModel[] + parent: IContainerModel | null + properties: Properties userData: Record } @@ -24,67 +24,3 @@ export class ContainerModel implements IContainerModel { this.userData = userData; } }; - -/** - * Returns a Generator iterating of over the children depth-first - */ -export function * MakeIterator(root: IContainerModel): Generator { - const queue: IContainerModel[] = [root]; - const visited = new Set(queue); - while (queue.length > 0) { - const container = queue.pop() as IContainerModel; - - yield container; - - // if this reverse() gets costly, replace it by a simple for - container.children.forEach((child) => { - if (visited.has(child)) { - return; - } - visited.add(child); - queue.push(child); - }); - } -} - -/** - * Returns the depth of the container - * @returns The depth of the container - */ -export function getDepth(parent: IContainerModel) { - let depth = 0; - - let current: IContainerModel | null = parent; - while (current != null) { - depth++; - current = current.parent; - } - - return depth; -} - -/** - * Returns the absolute position by iterating to the parent - * @returns The absolute position of the container - */ -export function getAbsolutePosition(container: IContainerModel): [number, number] { - let x = Number(container.properties.x); - let y = Number(container.properties.y); - let current = container.parent; - while (current != null) { - x += Number(current.properties.x); - y += Number(current.properties.y); - current = current.parent; - } - return [x, y]; -} - -export function findContainerById(root: IContainerModel, id: string): IContainerModel | undefined { - const it = MakeIterator(root); - for (const container of it) { - if (container.properties.id === id) { - return container; - } - } - return undefined; -} diff --git a/src/Interfaces/HistoryState.ts b/src/Interfaces/HistoryState.ts new file mode 100644 index 0000000..aa51638 --- /dev/null +++ b/src/Interfaces/HistoryState.ts @@ -0,0 +1,8 @@ +import { IContainerModel } from './ContainerModel'; + +export interface HistoryState { + MainContainer: IContainerModel + SelectedContainer: IContainerModel | null + SelectedContainerId: string + TypeCounters: Record +} diff --git a/src/Interfaces/Image.ts b/src/Interfaces/Image.ts index 723d04b..b839b09 100644 --- a/src/Interfaces/Image.ts +++ b/src/Interfaces/Image.ts @@ -1,7 +1,7 @@ /** Model of an image with multiple source */ export interface Image { - Name: string; - Url: string; - Base64Image: string; - Svg: string; + Name: string + Url: string + Base64Image: string + Svg: string } diff --git a/src/Interfaces/Point.ts b/src/Interfaces/Point.ts new file mode 100644 index 0000000..43fd673 --- /dev/null +++ b/src/Interfaces/Point.ts @@ -0,0 +1,4 @@ +export interface Point { + x: number + y: number +} diff --git a/src/Interfaces/Properties.ts b/src/Interfaces/Properties.ts index 79f4f47..ed185f2 100644 --- a/src/Interfaces/Properties.ts +++ b/src/Interfaces/Properties.ts @@ -1,8 +1,8 @@ import * as React from 'react'; export default interface Properties extends React.CSSProperties { - id: string, - parentId: string | null, - x: number, + id: string + parentId: string | null + x: number y: number } diff --git a/src/assets/fonts/RobotoFlex-Regular.ttf b/src/assets/fonts/RobotoFlex-Regular.ttf new file mode 100644 index 0000000..f857ae9 Binary files /dev/null and b/src/assets/fonts/RobotoFlex-Regular.ttf differ diff --git a/src/index.scss b/src/index.scss index c7e0d50..1bc3361 100644 --- a/src/index.scss +++ b/src/index.scss @@ -3,16 +3,49 @@ @tailwind utilities; @layer components { - .sidebar-row { - @apply p-6 w-full + .sidebar-title { + @apply p-6 font-bold } + + .sidebar-component { + @apply transition-all px-2 py-6 text-sm rounded-lg bg-slate-300/60 hover:bg-slate-300 + } + .elements-sidebar-row { @apply pl-6 pr-6 pt-2 pb-2 w-full } + .close-button { - @apply transition-all w-full h-auto p-4 flex + @apply transition-all w-full h-auto p-4 flex } + .mainmenu-btn { @apply transition-all bg-blue-100 hover:bg-blue-200 text-blue-700 text-lg font-semibold p-8 rounded-lg } + + .floating-btn { + @apply h-full w-full text-white align-middle items-center justify-center + } + + .bar-btn { + @apply h-16 w-full p-3 bg-slate-100 hover:bg-slate-200 + transition-all text-gray-700 hover:text-gray-600 + } + + .heroicon { + @apply h-full w-full align-middle items-center justify-center + } + + .sidebar-tooltip { + @apply absolute w-auto p-2 m-2 min-w-max left-14 + rounded-md shadow-md + text-gray-800 bg-slate-100 + dark:text-white dark:bg-gray-800 + text-xs font-bold + transition-all duration-100 scale-0 origin-left; + } + + .contextmenu-item { + @apply px-2 py-1 hover:bg-slate-300 text-left + } } \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 6893f34..5399af1 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,6 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import { App } from './App'; +import { App } from './Components/App/App'; import './index.scss'; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( diff --git a/src/utils/default.ts b/src/utils/default.ts new file mode 100644 index 0000000..60371fc --- /dev/null +++ b/src/utils/default.ts @@ -0,0 +1,37 @@ +import { Configuration } from '../Interfaces/Configuration'; +import Properties from '../Interfaces/Properties'; + +export const DEFAULT_CONFIG: Configuration = { + AvailableContainers: [ + { + Type: 'Container', + Width: 75, + Height: 100, + Style: { + fillOpacity: 0, + stroke: 'green' + } + } + ], + AvailableSymbols: [], + MainContainer: { + Type: 'Container', + Width: 2000, + Height: 100, + Style: { + fillOpacity: 0, + stroke: 'black' + } + } +}; + +export const DEFAULT_MAINCONTAINER_PROPS: Properties = { + id: 'main', + parentId: 'null', + x: 0, + y: 0, + width: DEFAULT_CONFIG.MainContainer.Width, + height: DEFAULT_CONFIG.MainContainer.Height, + fillOpacity: 0, + stroke: 'black' +}; diff --git a/src/utils/itertools.ts b/src/utils/itertools.ts new file mode 100644 index 0000000..67cd40c --- /dev/null +++ b/src/utils/itertools.ts @@ -0,0 +1,65 @@ +import { IContainerModel } from '../Interfaces/ContainerModel'; + +/** + * Returns a Generator iterating of over the children depth-first + */ +export function * MakeIterator(root: IContainerModel): Generator { + const queue: IContainerModel[] = [root]; + const visited = new Set(queue); + while (queue.length > 0) { + const container = queue.pop() as IContainerModel; + + yield container; + + for (let i = container.children.length - 1; i >= 0; i--) { + const child = container.children[i]; + if (visited.has(child)) { + return; + } + visited.add(child); + queue.push(child); + } + } +} + +/** + * Returns the depth of the container + * @returns The depth of the container + */ +export function getDepth(parent: IContainerModel): number { + let depth = 0; + + let current: IContainerModel | null = parent; + while (current != null) { + depth++; + current = current.parent; + } + + return depth; +} + +/** + * Returns the absolute position by iterating to the parent + * @returns The absolute position of the container + */ +export function getAbsolutePosition(container: IContainerModel): [number, number] { + let x = Number(container.properties.x); + let y = Number(container.properties.y); + let current = container.parent; + while (current != null) { + x += Number(current.properties.x); + y += Number(current.properties.y); + current = current.parent; + } + return [x, y]; +} + +export function findContainerById(root: IContainerModel, id: string): IContainerModel | undefined { + const it = MakeIterator(root); + for (const container of it) { + if (container.properties.id === id) { + return container; + } + } + return undefined; +} diff --git a/src/utils/saveload.ts b/src/utils/saveload.ts new file mode 100644 index 0000000..de8b835 --- /dev/null +++ b/src/utils/saveload.ts @@ -0,0 +1,54 @@ +import { findContainerById, MakeIterator } from './itertools'; +import { IEditorState } from '../Components/Editor/Editor'; + +/** + * Revive the Editor state + * by setting the containers references to their parent + * @param editorState Editor state + */ +export function Revive(editorState: IEditorState): void { + const history = editorState.history; + for (const state of history) { + if (state.MainContainer === null || state.MainContainer === undefined) { + continue; + } + + const it = MakeIterator(state.MainContainer); + for (const container of it) { + const parentId = container.properties.parentId; + if (parentId === null) { + container.parent = null; + continue; + } + const parent = findContainerById(state.MainContainer, parentId); + if (parent === undefined) { + continue; + } + container.parent = parent; + } + + const selected = findContainerById(state.MainContainer, state.SelectedContainerId); + if (selected === undefined) { + state.SelectedContainer = null; + continue; + } + state.SelectedContainer = selected; + } +} + +export const getCircularReplacer = (): (key: any, value: object | null) => object | null | undefined => { + const seen = new WeakSet(); + return (key: any, value: object | null) => { + if (key === 'parent') { + return; + } + + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { + return; + } + seen.add(value); + } + return value; + }; +}; diff --git a/src/utils/stringtools.ts b/src/utils/stringtools.ts new file mode 100644 index 0000000..349e34a --- /dev/null +++ b/src/utils/stringtools.ts @@ -0,0 +1,6 @@ +export function truncateString(str: string, num: number): string { + if (str.length <= num) { + return str; + } + return `${str.slice(0, num)}...`; +} diff --git a/src/utils/test-utils.tsx b/src/utils/test-utils.tsx index bb5dc8b..139850e 100644 --- a/src/utils/test-utils.tsx +++ b/src/utils/test-utils.tsx @@ -1,13 +1,13 @@ /* eslint-disable import/export */ import * as React from 'react'; -import { cleanup, render } from '@testing-library/react'; +import { cleanup, render, RenderResult } from '@testing-library/react'; import { afterEach } from 'vitest'; afterEach(() => { cleanup(); }); -const customRender = (ui: React.ReactElement, options = {}) => +const customRender = (ui: React.ReactElement, options = {}): RenderResult => render(ui, { // wrap provider(s) here if needed wrapper: ({ children }) => children,