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 */}