diff --git a/taxonomy-editor-frontend/package-lock.json b/taxonomy-editor-frontend/package-lock.json index caea5ab5..352cd9e5 100644 --- a/taxonomy-editor-frontend/package-lock.json +++ b/taxonomy-editor-frontend/package-lock.json @@ -49,6 +49,7 @@ "openapi-typescript-codegen": "^0.27.0", "prettier": "2.8.2", "vite": "^5.1.0", + "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^4.3.1" } }, @@ -8703,7 +8704,6 @@ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", "dev": true, - "peer": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -14606,7 +14606,6 @@ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", "dev": true, - "peer": true, "dependencies": { "tslib": "^2.0.3" } @@ -14998,7 +14997,6 @@ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", "dev": true, - "peer": true, "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -19963,6 +19961,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -20476,8 +20484,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "dev": true, - "peer": true + "dev": true }, "node_modules/svgo": { "version": "1.3.2", @@ -20975,8 +20982,7 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "peer": true + "dev": true }, "node_modules/tsutils": { "version": "3.21.0", @@ -21440,6 +21446,332 @@ } } }, + "node_modules/vite-plugin-svgr": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz", + "integrity": "sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.5", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": "^2.6.0 || 3 || 4 || 5" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/vite-plugin-svgr/node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/vite-plugin-svgr/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/vite-plugin-svgr/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vite-plugin-svgr/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/vite-plugin-svgr/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/vite-plugin-svgr/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/vite-plugin-svgr/node_modules/typescript": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "dev": true, + "optional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/vite-tsconfig-paths": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.1.tgz", @@ -28823,7 +29155,6 @@ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", "dev": true, - "peer": true, "requires": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -33244,7 +33575,6 @@ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", "dev": true, - "peer": true, "requires": { "tslib": "^2.0.3" } @@ -33544,7 +33874,6 @@ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", "dev": true, - "peer": true, "requires": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -37131,6 +37460,16 @@ } } }, + "snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -37524,8 +37863,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "dev": true, - "peer": true + "dev": true }, "svgo": { "version": "1.3.2", @@ -37913,8 +38251,7 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "peer": true + "dev": true }, "tsutils": { "version": "3.21.0", @@ -38245,6 +38582,184 @@ } } }, + "vite-plugin-svgr": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz", + "integrity": "sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.5", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "dependencies": { + "@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + } + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "requires": {} + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "requires": {} + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "requires": {} + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "requires": {} + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "requires": {} + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "requires": {} + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "requires": {} + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "requires": {} + }, + "@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + } + }, + "@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "requires": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "requires": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + } + }, + "@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "requires": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "requires": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + } + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "typescript": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "dev": true, + "optional": true, + "peer": true + } + } + }, "vite-tsconfig-paths": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.1.tgz", diff --git a/taxonomy-editor-frontend/package.json b/taxonomy-editor-frontend/package.json index 9273ed4e..321d2611 100644 --- a/taxonomy-editor-frontend/package.json +++ b/taxonomy-editor-frontend/package.json @@ -54,6 +54,7 @@ "openapi-typescript-codegen": "^0.27.0", "prettier": "2.8.2", "vite": "^5.1.0", + "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^4.3.1" }, "lint-staged": { diff --git a/taxonomy-editor-frontend/src/assets/icons/pushpin-line-grey.svg b/taxonomy-editor-frontend/src/assets/icons/pushpin-line-grey.svg new file mode 100644 index 00000000..e5cc0bbf --- /dev/null +++ b/taxonomy-editor-frontend/src/assets/icons/pushpin-line-grey.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/taxonomy-editor-frontend/src/pages/project/editentry/LanguageSelectionDialog.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/LanguageSelectionDialog.tsx index 8296e0cc..0158a931 100644 --- a/taxonomy-editor-frontend/src/pages/project/editentry/LanguageSelectionDialog.tsx +++ b/taxonomy-editor-frontend/src/pages/project/editentry/LanguageSelectionDialog.tsx @@ -1,18 +1,13 @@ -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import ISO6391 from "iso-639-1"; import { - OutlinedInput, - InputLabel, - MenuItem, - FormControl, - ListItemText, - Select, - Checkbox, + Autocomplete, DialogTitle, DialogContent, DialogActions, Button, + TextField, } from "@mui/material"; type Props = { @@ -28,56 +23,50 @@ const LanguageSelectionDialog = ({ handleDialogConfirm, shownLanguageCodes, }: Props) => { - const [newShownLanguageCodes, setNewShownLanguageCodes] = useState([ - ...shownLanguageCodes, - ]); + const [newLanguageCodes, setNewLanguageCodes] = useState([]); + const inputRef = useRef(null); + + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, []); return ( <> - Select shown languages + Show another language - - Languages - - + ISO6391.getName(langCode))} + onChange={(_event, newValue: string[]) => { + setNewLanguageCodes( + newValue.map((langName) => ISO6391.getCode(langName)) + ); + }} + options={ISO6391.getAllNames() + .sort() + .filter( + (languageName) => + !shownLanguageCodes.includes(ISO6391.getCode(languageName)) && + !newLanguageCodes.includes(ISO6391.getCode(languageName)) && + languageName !== ISO6391.getName(mainLanguageCode) + )} + getOptionLabel={(option) => option} + renderInput={(params) => ( + + )} + /> diff --git a/taxonomy-editor-frontend/src/pages/project/editentry/ListTranslations.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/ListTranslations.tsx index 03e898c6..d09a5fbb 100644 --- a/taxonomy-editor-frontend/src/pages/project/editentry/ListTranslations.tsx +++ b/taxonomy-editor-frontend/src/pages/project/editentry/ListTranslations.tsx @@ -6,7 +6,11 @@ import { Box, Dialog, Checkbox, + IconButton, + Tooltip, } from "@mui/material"; +import VisibilityOffOutlinedIcon from "@mui/icons-material/VisibilityOffOutlined"; +import PushPinOutlinedIcon from "@/assets/icons/pushpin-line-grey.svg?react"; import LanguageSelectionDialog from "./LanguageSelectionDialog"; import { useMemo, useEffect, useState } from "react"; import ISO6391 from "iso-639-1"; @@ -14,6 +18,30 @@ import { TranslationTags } from "./TranslationTags"; const SHOWN_LANGUAGES_KEY = "shownLanguages"; +const getLanguageName = (languageCode: string): string => { + if (languageCode === "xx") { + return "Fallback translations"; + } + const languageName = ISO6391.getName(languageCode); + if (languageName === "") { + return languageCode; + } + return languageName; +}; + +const sortByLanguageName = (lcA: any, lcB: any): number => { + const languageNameA = getLanguageName(lcA); + const languageNameB = getLanguageName(lcB); + + if (languageNameA < languageNameB) { + return -1; + } else if (languageNameA > languageNameB) { + return 1; + } else { + return 0; + } +}; + /** * Sub-component for rendering translation of an "entry" */ @@ -25,26 +53,8 @@ export const ListTranslations = ({ }) => { const [isDialogOpen, setIsDialogOpen] = useState(false); // Used for Dialog component const [shownLanguageCodes, setShownLanguageCodes] = useState([]); // Used for storing LCs that are shown in the interface - - // Helper functions for Dialog component - const handleClose = () => { - setIsDialogOpen(false); - }; - const handleOpen = () => { - setIsDialogOpen(true); - }; - - const handleXxLanguage = () => { - if (shownLanguageCodes.includes("xx")) { - const newShownLanguagesCodes = shownLanguageCodes.filter( - (lang) => lang !== "xx" - ); - setShownLanguageCodes(newShownLanguagesCodes); - } else { - const newShownLanguagesCodes = ["xx", ...shownLanguageCodes]; - setShownLanguageCodes(newShownLanguagesCodes); - } - }; + const [showExistingTranslations, setShowExistingTranslations] = + useState(false); const xxLanguageExists = useMemo(() => { const exists = @@ -54,15 +64,47 @@ export const ListTranslations = ({ return exists; }, [nodeObject]); - const handleDialogConfirm = (newShownLanguageCodes: string[]) => { + const handleLanguagePin = (languageCode: string) => { + let newShownLanguageCodes: string[]; + if (shownLanguageCodes.includes(languageCode)) { + newShownLanguageCodes = shownLanguageCodes.filter( + (langCode) => langCode !== languageCode + ); + } else { + newShownLanguageCodes = [...shownLanguageCodes, languageCode]; + newShownLanguageCodes.sort(sortByLanguageName); + } localStorage.setItem( SHOWN_LANGUAGES_KEY, JSON.stringify(newShownLanguageCodes) ); + setShownLanguageCodes(newShownLanguageCodes); + }; - if (xxLanguageExists && !newShownLanguageCodes.includes("xx")) { - newShownLanguageCodes = ["xx", ...newShownLanguageCodes]; - } + // Helper functions for Dialog component + const handleClose = () => { + setIsDialogOpen(false); + }; + const handleOpen = () => { + setIsDialogOpen(true); + }; + + const handleDialogConfirm = (newLanguageCodes: string[]) => { + let newShownLanguageCodes = [...shownLanguageCodes]; + newLanguageCodes.forEach((languageCode) => { + if (shownLanguageCodes.includes(languageCode)) { + newShownLanguageCodes = newShownLanguageCodes.filter( + (langCode) => langCode !== languageCode + ); + } else { + newShownLanguageCodes.push(languageCode); + newShownLanguageCodes.sort(sortByLanguageName); + } + }); + localStorage.setItem( + SHOWN_LANGUAGES_KEY, + JSON.stringify(newShownLanguageCodes) + ); setShownLanguageCodes(newShownLanguageCodes); setIsDialogOpen(false); @@ -114,29 +156,44 @@ export const ListTranslations = ({ }; }; - const languagesToShow: string[] = shownLanguageCodes.filter( + let languagesToShow: string[]; + languagesToShow = shownLanguageCodes.filter( (languageCode) => languageCode !== nodeObject.main_language ); + if (shownLanguageCodes.includes("xx")) { + languagesToShow = languagesToShow.filter( + (languageCode) => languageCode !== "xx" + ); + languagesToShow.unshift("xx"); + } languagesToShow.unshift(nodeObject.main_language); - const numberOfLanguagesShownMessage = - "(" + - languagesToShow.length + - " language" + - (languagesToShow.length === 1 ? "" : "s") + - " shown)"; + if (showExistingTranslations) { + languagesToShow.push( + ...Object.keys(nodeObject) + .filter( + (key) => + key.startsWith("tags_") && + !key.startsWith("tags_ids_") && + (nodeObject[key]?.length > 0 || originalNodeObject[key]?.length > 0) + ) + .map((key) => key.slice(5)) + .filter((languageCode) => !languagesToShow.includes(languageCode)) + .sort(sortByLanguageName) + ); + } const hasFirstTranslationChanged: boolean[] = languagesToShow.map( (language: string) => + originalNodeObject[`tags_${language}`]?.[0] && + nodeObject[`tags_${language}`]?.[0] && nodeObject[`tags_${language}`]?.[0] !== - originalNodeObject[`tags_${language}`]?.[0] + originalNodeObject[`tags_${language}`]?.[0] ); const shownLanguagesInfo = languagesToShow.map((languageCode: string) => { const languageName = - (languageCode === "xx" - ? "All languages" - : ISO6391.getName(languageCode)) + + getLanguageName(languageCode) + (languageCode === nodeObject.main_language ? " (main language)" : ""); const alertMessage = "Changing the first translation will modify " + @@ -151,58 +208,104 @@ export const ListTranslations = ({ {/* Title */} - + Translations - - {xxLanguageExists ? ( - // if "xx" words exist, the "All language" is always displayed, the user can't choose - <> - - - All languages - - - ) : ( - <> - - All languages - - )} + {/* if "xx" words exist, the "Fallback translations" are always + displayed, the user can't choose */} + handleLanguagePin("xx")} + /> + + Show fallback translations + + { + setShowExistingTranslations(!showExistingTranslations); + }} + /> + Show all existing translations + {!["en", "xx"].includes(nodeObject.main_language) && ( + + English or Fallback translations is not the main language for this + entry. Please consider changing it to adhere to the prevailing + convention. + + )} + {/* Render translation tags for each language to show */} {shownLanguagesInfo.map( - ({ languageCode, languageName, alertMessage }) => ( - - - {languageName} - - {hasFirstTranslationChanged[ - languagesToShow.indexOf(languageCode) - ] && ( - - {alertMessage} - - )} - - { - - } + ({ languageCode, languageName, alertMessage }) => { + const isLanguageSelected = shownLanguageCodes.includes(languageCode); + return ( + + + + {languageName} + + + {languageCode !== "xx" && ( + + handleLanguagePin(languageCode)} + > + {!isLanguageSelected ? ( + + ) : ( + + )} + + + )} + + {hasFirstTranslationChanged[ + languagesToShow.indexOf(languageCode) + ] && ( + + {alertMessage} + + )} + + { + + } + - - ) + ); + } )} + + {/* Dialog box for adding translations */} +/// interface ImportMetaEnv { readonly VITE_APP_API_URL: string; diff --git a/taxonomy-editor-frontend/vite.config.ts b/taxonomy-editor-frontend/vite.config.ts index ce3d05b4..8a72a8b3 100644 --- a/taxonomy-editor-frontend/vite.config.ts +++ b/taxonomy-editor-frontend/vite.config.ts @@ -1,12 +1,13 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import viteTsconfigPaths from "vite-tsconfig-paths"; +import svgr from "vite-plugin-svgr"; export default defineConfig({ build: { outDir: "build", }, - plugins: [react(), viteTsconfigPaths()], + plugins: [react(), viteTsconfigPaths(), svgr()], server: { port: parseInt(process.env.VITE_SERVER_PORT || "3000"), host: process.env.VITE_SERVER_HOST || "localhost",