diff --git a/react/package-lock.json b/react/package-lock.json index f4bf059d..cbf64a94 100644 --- a/react/package-lock.json +++ b/react/package-lock.json @@ -12,12 +12,16 @@ "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.0", "@changey/react-leaflet-markercluster": "^4.0.0-rc1", + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2", "@reduxjs/toolkit": "^1.8.4", "@tacc/core-components": "^0.0.3-beta.0", "@tacc/core-styles": "^2.24.1", "@turf/turf": "^6.5.0", "@types/geojson": "^7946.0.14", "@types/leaflet.markercluster": "^1.5.1", + "antd": "^5.21.6", "axios": "^1.6.2", "eslint-config-prettier": "^8.5.0", "eslint-plugin-jsx-a11y": "^6.6.1", @@ -81,6 +85,96 @@ "node": ">=6.0.0" } }, + "node_modules/@ant-design/colors": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.1.0.tgz", + "integrity": "sha512-MMoDGWn1y9LdQJQSHiCC20x3uZ3CwQnv9QMz6pCmJOrqdgM9YxsoVVY0wtrdXbmfSgnV0KNk6zi09NAhMR2jvg==", + "dependencies": { + "@ctrl/tinycolor": "^3.6.1" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.21.1.tgz", + "integrity": "sha512-tyWnlK+XH7Bumd0byfbCiZNK43HEubMoCcu9VxwsAwiHdHTgWa+tMN0/yvxa+e8EzuFP1WdUNNPclRpVtD33lg==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "classnames": "^2.3.1", + "csstype": "^3.1.3", + "rc-util": "^5.35.0", + "stylis": "^4.3.3" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.1.tgz", + "integrity": "sha512-2HAiyGGGnM0es40SxdszeQAU5iWp41wBIInq+ONTCKjlSKOrzQfnw4JDtB8IBmqE6tQaEKwmzTP2LGdt5DSwYQ==", + "dependencies": { + "@ant-design/cssinjs": "^1.21.0", + "@babel/runtime": "^7.23.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/fast-color": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz", + "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.5.1.tgz", + "integrity": "sha512-0UrM02MA2iDIgvLatWrj6YTCYe0F/cwXvVE0E2SqGrL7PZireQwgEKTKBisWpZyal5eXZLvuM98kju6YtYne8w==", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==" + }, + "node_modules/@ant-design/react-slick": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz", + "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==", + "dependencies": { + "@babel/runtime": "^7.10.4", + "classnames": "^2.2.5", + "json2mq": "^0.2.0", + "resize-observer-polyfill": "^1.5.1", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": ">=16.9.0" + } + }, "node_modules/@aw-web-design/x-default-browser": { "version": "1.4.126", "resolved": "https://registry.npmjs.org/@aw-web-design/x-default-browser/-/x-default-browser-1.4.126.tgz", @@ -233,9 +327,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -1982,9 +2076,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.0.4.tgz", - "integrity": "sha512-8/iCd8lH10gKNsq5detnbGWiFd6PXK2wB8wjE6fHNNhtqvshyMrIJgffwRcw6yl/gzGTH+N1i+KRhjqHxqYTmg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.0.tgz", + "integrity": "sha512-X69PmFOrjTZfN5ijxtI8hZ9kRADFSLrmmQ6hgDJ272Il049WGKpDY64KhrFm/7rbWve0z81QepawzjkKlqkNGw==", "funding": [ { "type": "github", @@ -2005,9 +2099,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.5.tgz", - "integrity": "sha512-4Wo8raj9YF3PnZ5iGrAl+BSsk2MYBOEUS/X4k1HL9mInhyCVftEG02MywdvelXlwZGUF2XTQ0qj9Jd398mhqrw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.6.tgz", + "integrity": "sha512-S/IjXqTHdpI4EtzGoNCHfqraXF37x12ZZHA1Lk7zoT5pm2lMjFuqhX/89L7dqX4CcMacKK+6ZCs5TmEGb/+wKw==", "funding": [ { "type": "github", @@ -2021,7 +2115,7 @@ "peer": true, "dependencies": { "@csstools/color-helpers": "^5.0.1", - "@csstools/css-calc": "^2.0.4" + "@csstools/css-calc": "^2.1.0" }, "engines": { "node": ">=18" @@ -2157,9 +2251,9 @@ } }, "node_modules/@csstools/postcss-color-function": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.5.tgz", - "integrity": "sha512-6dHr2NDsBMiZCPkGDi2qMfIbzV2kWV8Dh7SVb1FZGnN/r2TI4TSAkVF8rCG5L70yQZHMcQGB84yp8Zm+RGhoHA==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.6.tgz", + "integrity": "sha512-EcvXfC60cTIumzpsxWuvVjb7rsJEHPvqn3jeMEBUaE3JSc4FRuP7mEQ+1eicxWmIrs3FtzMH9gR3sgA5TH+ebQ==", "funding": [ { "type": "github", @@ -2172,7 +2266,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-color-parser": "^3.0.5", + "@csstools/css-color-parser": "^3.0.6", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/postcss-progressive-custom-properties": "^4.0.0", @@ -2186,9 +2280,9 @@ } }, "node_modules/@csstools/postcss-color-mix-function": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.5.tgz", - "integrity": "sha512-jgq0oGbit7TxWYP8y2hWWfV64xzcAgJk54PBYZ2fDrRgEDy1l5YMCrFawnn+5JETh/E1jjXPDFhFEYhwr3vA3g==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.6.tgz", + "integrity": "sha512-jVKdJn4+JkASYGhyPO+Wa5WXSx1+oUgaXb3JsjJn/BlrtFh5zjocCY7pwWi0nuP24V1fY7glQsxEYcYNy0dMFg==", "funding": [ { "type": "github", @@ -2201,7 +2295,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-color-parser": "^3.0.5", + "@csstools/css-color-parser": "^3.0.6", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/postcss-progressive-custom-properties": "^4.0.0", @@ -2270,9 +2364,9 @@ } }, "node_modules/@csstools/postcss-exponential-functions": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.4.tgz", - "integrity": "sha512-xmzFCGTkkLDs7q9vVaRGlnu8s51lRRJzHsaJ/nXmkQuyg0q7gh7rTbJ0bY5sSVet+KB7MTIxRXRUCl2tm7RODA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.5.tgz", + "integrity": "sha512-mi8R6dVfA2nDoKM3wcEi64I8vOYEgQVtVKCfmLHXupeLpACfGAided5ddMt5f+CnEodNu4DifuVwb0I6fQDGGQ==", "funding": [ { "type": "github", @@ -2285,7 +2379,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-calc": "^2.0.4", + "@csstools/css-calc": "^2.1.0", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" }, @@ -2323,9 +2417,9 @@ } }, "node_modules/@csstools/postcss-gamut-mapping": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.5.tgz", - "integrity": "sha512-VQDayRhC/Mg1fuo8/4F43La5aROgvVyqtCqdNyGvRKi6L1+zXfwQ583nImi7k/gn2GNJH82Bf9mutTuT1GtXzA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.6.tgz", + "integrity": "sha512-0ke7fmXfc8H+kysZz246yjirAH6JFhyX9GTlyRnM0exHO80XcA9zeJpy5pOp5zo/AZiC/q5Pf+Hw7Pd6/uAoYA==", "funding": [ { "type": "github", @@ -2338,7 +2432,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-color-parser": "^3.0.5", + "@csstools/css-color-parser": "^3.0.6", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" }, @@ -2350,9 +2444,9 @@ } }, "node_modules/@csstools/postcss-gradients-interpolation-method": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.5.tgz", - "integrity": "sha512-l3ShDdAt/szbyBh3Jz27MRFt5WPAbnVCMsU7Vs7EbBxJQNgVDrcu1APBB2nPagDJOyhI6/IahuW7nb6grWVTpA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.6.tgz", + "integrity": "sha512-Itrbx6SLUzsZ6Mz3VuOlxhbfuyLTogG5DwEF1V8dAi24iMuvQPIHd7Ti+pNDp7j6WixndJGZaoNR0f9VSzwuTg==", "funding": [ { "type": "github", @@ -2365,7 +2459,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-color-parser": "^3.0.5", + "@csstools/css-color-parser": "^3.0.6", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/postcss-progressive-custom-properties": "^4.0.0", @@ -2379,9 +2473,9 @@ } }, "node_modules/@csstools/postcss-hwb-function": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.5.tgz", - "integrity": "sha512-bPn/SQyiiYjWkwK2ykc7O9LliMR50YfUGukd6jQI2okHzB7NxNt/IS45tS1Muk7Hhf3B9Lbmg1Ofq36tBmM92Q==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.6.tgz", + "integrity": "sha512-927Pqy3a1uBP7U8sTfaNdZVB0mNXzIrJO/GZ8us9219q9n06gOqCdfZ0E6d1P66Fm0fYHvxfDbfcUuwAn5UwhQ==", "funding": [ { "type": "github", @@ -2394,7 +2488,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-color-parser": "^3.0.5", + "@csstools/css-color-parser": "^3.0.6", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/postcss-progressive-custom-properties": "^4.0.0", @@ -2663,9 +2757,9 @@ } }, "node_modules/@csstools/postcss-media-minmax": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.4.tgz", - "integrity": "sha512-zgdBOCI9aKoy5GK9tb/3ve0pl7vH0HJg7rfQEWT3TZiIKh7XEWucDSTSwnwgdgtgz50UxrOfbK+C59M+u2fE2Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.5.tgz", + "integrity": "sha512-sdh5i5GToZOIAiwhdntRWv77QDtsxP2r2gXW/WbLSCoLr00KTq/yiF1qlQ5XX2+lmiFa8rATKMcbwl3oXDMNew==", "funding": [ { "type": "github", @@ -2678,7 +2772,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-calc": "^2.0.4", + "@csstools/css-calc": "^2.1.0", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/media-query-list-parser": "^4.0.2" @@ -2769,9 +2863,9 @@ } }, "node_modules/@csstools/postcss-oklab-function": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.5.tgz", - "integrity": "sha512-19bsJQFyJNSEhpaVq0Mq1E0HDXfx8qMHa/bR1MaHr1UD4DWvM2/J6YXb9OVGS7eFl92Y3c84Yggn9uFv13vsiQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.6.tgz", + "integrity": "sha512-Hptoa0uX+XsNacFBCIQKTUBrFKDiplHan42X73EklG6XmQLG7/aIvxoNhvZ7PvOWMt67Pw3bIlUY2nD6p5vL8A==", "funding": [ { "type": "github", @@ -2784,7 +2878,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-color-parser": "^3.0.5", + "@csstools/css-color-parser": "^3.0.6", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/postcss-progressive-custom-properties": "^4.0.0", @@ -2822,10 +2916,37 @@ "postcss": "^8.4" } }, + "node_modules/@csstools/postcss-random-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-1.0.1.tgz", + "integrity": "sha512-Ab/tF8/RXktQlFwVhiC70UNfpFQRhtE5fQQoP2pO+KCPGLsLdWFiOuHgSRtBOqEshCVAzR4H6o38nhvRZq8deA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "peer": true, + "dependencies": { + "@csstools/css-calc": "^2.1.0", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.5.tgz", - "integrity": "sha512-5VrE4hAwv/ZpuL1Yo0ZGGFi1QPpIikp/rzz7LnpQ31ACQVRIA5/M9qZmJbRlZVsJ4bUFSQ3dq6kHSHrCt2uM6Q==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.6.tgz", + "integrity": "sha512-yxP618Xb+ji1I624jILaYM62uEmZcmbdmFoZHoaThw896sq0vU39kqTTF+ZNic9XyPtPMvq0vyvbgmHaszq8xg==", "funding": [ { "type": "github", @@ -2838,7 +2959,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-color-parser": "^3.0.5", + "@csstools/css-color-parser": "^3.0.6", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/postcss-progressive-custom-properties": "^4.0.0", @@ -2889,10 +3010,37 @@ "node": ">=4" } }, + "node_modules/@csstools/postcss-sign-functions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.0.0.tgz", + "integrity": "sha512-cUpr5W8eookBi5TiLSvx1HL6DFoTTgcj2pmiVNd63y2JHhvtpnJs3sfsFMmLhB42yTRS02tFPsNz3Q5zeN8ZVA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "peer": true, + "dependencies": { + "@csstools/css-calc": "^2.1.0", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.4.tgz", - "integrity": "sha512-JjShuWZkmIOT8EfI7lYjl7V5qM29LNDdnnSo5O7v/InJJHfeiQjtxyAaZzKGXzpkghPrCAcgLfJ+IyqTdXo7IA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.5.tgz", + "integrity": "sha512-G6SJ6hZJkhxo6UZojVlLo14MohH4J5J7z8CRBrxxUYy9JuZiIqUo5TBYyDGcE0PLdzpg63a7mHSJz3VD+gMwqw==", "funding": [ { "type": "github", @@ -2905,7 +3053,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-calc": "^2.0.4", + "@csstools/css-calc": "^2.1.0", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" }, @@ -2943,9 +3091,9 @@ } }, "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.4.tgz", - "integrity": "sha512-nn+gWTZZlSnwbyUtGQCnvBXIx1TX+HVStvIm3221dWGQvp47bB5giMBbuAK4a/UJGBbfDQhGKEbYq++WWM1i1A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.5.tgz", + "integrity": "sha512-/YQThYkt5MLvAmVu7zxjhceCYlKrYddK6LEmK5I4ojlS6BmO9u2yO4+xjXzu2+NPYmHSTtP4NFSamBCMmJ1NJA==", "funding": [ { "type": "github", @@ -2958,7 +3106,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-calc": "^2.0.4", + "@csstools/css-calc": "^2.1.0", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" }, @@ -3055,6 +3203,14 @@ "postcss": "^8.4" } }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "engines": { + "node": ">=10" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -3064,6 +3220,16 @@ "node": ">=10.0.0" } }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz", @@ -3573,6 +3739,48 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==" }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", + "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz", + "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz", + "integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -6483,6 +6691,146 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@rc-component/async-validator": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz", + "integrity": "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz", + "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==", + "dependencies": { + "@ant-design/fast-color": "^2.0.6", + "@babel/runtime": "^7.23.6", + "classnames": "^2.2.6", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz", + "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz", + "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz", + "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz", + "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.0.0.tgz", + "integrity": "sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==", + "dependencies": { + "@babel/runtime": "^7.24.7", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz", + "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==", + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/portal": "^1.0.0-9", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/trigger": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.5.tgz", + "integrity": "sha512-F1EJ4KjFpGAHAjuKvOyZB/6IZDkVx0bHl0M4fQM5wXcmm7lgTgVSSnR3bXwdmS6jOJGHOqfDxIJW3WUvwMIXhQ==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@rc-component/portal": "^1.1.0", + "classnames": "^2.3.2", + "rc-motion": "^2.0.0", + "rc-resize-observer": "^1.3.1", + "rc-util": "^5.38.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/@react-leaflet/core": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", @@ -6590,9 +6938,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.4.tgz", - "integrity": "sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.25.0.tgz", + "integrity": "sha512-CC/ZqFZwlAIbU1wUPisHyV/XRc5RydFrNLtgl3dGYskdwPZdt4HERtKm50a/+DtTlKeCq9IXFEWR+P6blwjqBA==", "cpu": [ "arm" ], @@ -6603,9 +6951,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.4.tgz", - "integrity": "sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.25.0.tgz", + "integrity": "sha512-/Y76tmLGUJqVBXXCfVS8Q8FJqYGhgH4wl4qTA24E9v/IJM0XvJCGQVSW1QZ4J+VURO9h8YCa28sTFacZXwK7Rg==", "cpu": [ "arm64" ], @@ -6616,9 +6964,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.4.tgz", - "integrity": "sha512-GmU/QgGtBTeraKyldC7cDVVvAJEOr3dFLKneez/n7BvX57UdhOqDsVwzU7UOnYA7AAOt+Xb26lk79PldDHgMIQ==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.25.0.tgz", + "integrity": "sha512-YVT6L3UrKTlC0FpCZd0MGA7NVdp7YNaEqkENbWQ7AOVOqd/7VzyHpgIpc1mIaxRAo1ZsJRH45fq8j4N63I/vvg==", "cpu": [ "arm64" ], @@ -6629,9 +6977,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.4.tgz", - "integrity": "sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.25.0.tgz", + "integrity": "sha512-ZRL+gexs3+ZmmWmGKEU43Bdn67kWnMeWXLFhcVv5Un8FQcx38yulHBA7XR2+KQdYIOtD0yZDWBCudmfj6lQJoA==", "cpu": [ "x64" ], @@ -6642,9 +6990,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.4.tgz", - "integrity": "sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.25.0.tgz", + "integrity": "sha512-xpEIXhiP27EAylEpreCozozsxWQ2TJbOLSivGfXhU4G1TBVEYtUPi2pOZBnvGXHyOdLAUUhPnJzH3ah5cqF01g==", "cpu": [ "arm64" ], @@ -6655,9 +7003,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.4.tgz", - "integrity": "sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.25.0.tgz", + "integrity": "sha512-sC5FsmZGlJv5dOcURrsnIK7ngc3Kirnx3as2XU9uER+zjfyqIjdcMVgzy4cOawhsssqzoAX19qmxgJ8a14Qrqw==", "cpu": [ "x64" ], @@ -6668,9 +7016,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.4.tgz", - "integrity": "sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.25.0.tgz", + "integrity": "sha512-uD/dbLSs1BEPzg564TpRAQ/YvTnCds2XxyOndAO8nJhaQcqQGFgv/DAVko/ZHap3boCvxnzYMa3mTkV/B/3SWA==", "cpu": [ "arm" ], @@ -6681,9 +7029,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.4.tgz", - "integrity": "sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.25.0.tgz", + "integrity": "sha512-ZVt/XkrDlQWegDWrwyC3l0OfAF7yeJUF4fq5RMS07YM72BlSfn2fQQ6lPyBNjt+YbczMguPiJoCfaQC2dnflpQ==", "cpu": [ "arm" ], @@ -6694,9 +7042,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.4.tgz", - "integrity": "sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.25.0.tgz", + "integrity": "sha512-qboZ+T0gHAW2kkSDPHxu7quaFaaBlynODXpBVnPxUgvWYaE84xgCKAPEYE+fSMd3Zv5PyFZR+L0tCdYCMAtG0A==", "cpu": [ "arm64" ], @@ -6707,9 +7055,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.4.tgz", - "integrity": "sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.25.0.tgz", + "integrity": "sha512-ndWTSEmAaKr88dBuogGH2NZaxe7u2rDoArsejNslugHZ+r44NfWiwjzizVS1nUOHo+n1Z6qV3X60rqE/HlISgw==", "cpu": [ "arm64" ], @@ -6720,9 +7068,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.4.tgz", - "integrity": "sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.25.0.tgz", + "integrity": "sha512-BVSQvVa2v5hKwJSy6X7W1fjDex6yZnNKy3Kx1JGimccHft6HV0THTwNtC2zawtNXKUu+S5CjXslilYdKBAadzA==", "cpu": [ "ppc64" ], @@ -6733,9 +7081,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.4.tgz", - "integrity": "sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.25.0.tgz", + "integrity": "sha512-G4hTREQrIdeV0PE2JruzI+vXdRnaK1pg64hemHq2v5fhv8C7WjVaeXc9P5i4Q5UC06d/L+zA0mszYIKl+wY8oA==", "cpu": [ "riscv64" ], @@ -6746,9 +7094,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.4.tgz", - "integrity": "sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.25.0.tgz", + "integrity": "sha512-9T/w0kQ+upxdkFL9zPVB6zy9vWW1deA3g8IauJxojN4bnz5FwSsUAD034KpXIVX5j5p/rn6XqumBMxfRkcHapQ==", "cpu": [ "s390x" ], @@ -6759,9 +7107,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.4.tgz", - "integrity": "sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.25.0.tgz", + "integrity": "sha512-ThcnU0EcMDn+J4B9LD++OgBYxZusuA7iemIIiz5yzEcFg04VZFzdFjuwPdlURmYPZw+fgVrFzj4CA64jSTG4Ig==", "cpu": [ "x64" ], @@ -6772,9 +7120,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.4.tgz", - "integrity": "sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.25.0.tgz", + "integrity": "sha512-zx71aY2oQxGxAT1JShfhNG79PnjYhMC6voAjzpu/xmMjDnKNf6Nl/xv7YaB/9SIa9jDYf8RBPWEnjcdlhlv1rQ==", "cpu": [ "x64" ], @@ -6785,9 +7133,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.4.tgz", - "integrity": "sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.25.0.tgz", + "integrity": "sha512-JT8tcjNocMs4CylWY/CxVLnv8e1lE7ff1fi6kbGocWwxDq9pj30IJ28Peb+Y8yiPNSF28oad42ApJB8oUkwGww==", "cpu": [ "arm64" ], @@ -6798,9 +7146,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.4.tgz", - "integrity": "sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.25.0.tgz", + "integrity": "sha512-dRLjLsO3dNOfSN6tjyVlG+Msm4IiZnGkuZ7G5NmpzwF9oOc582FZG05+UdfTbz5Jd4buK/wMb6UeHFhG18+OEg==", "cpu": [ "ia32" ], @@ -6811,9 +7159,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz", - "integrity": "sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.25.0.tgz", + "integrity": "sha512-/RqrIFtLB926frMhZD0a5oDa4eFIbyNEwLLloMTEjmqfwZWXywwVVOVmwTsuyhC9HKkVEZcOOi+KV4U9wmOdlg==", "cpu": [ "x64" ], @@ -9776,9 +10124,9 @@ } }, "node_modules/@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", "optional": true, "dependencies": { "@types/node": "*", @@ -10422,6 +10770,70 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/antd": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.22.0.tgz", + "integrity": "sha512-hZE8riK8+LWsXUnPpbluvBxjnwXRh34s1yIND7g5WUV/AVZtPjt81jOoXCbKw5SJ8ORIx2o7nlaa4PJ3Luwisg==", + "dependencies": { + "@ant-design/colors": "^7.1.0", + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/cssinjs-utils": "^1.1.1", + "@ant-design/icons": "^5.5.1", + "@ant-design/react-slick": "~1.1.2", + "@babel/runtime": "^7.25.7", + "@ctrl/tinycolor": "^3.6.1", + "@rc-component/color-picker": "~2.0.1", + "@rc-component/mutate-observer": "^1.1.0", + "@rc-component/qrcode": "~1.0.0", + "@rc-component/tour": "~1.15.1", + "@rc-component/trigger": "^2.2.5", + "classnames": "^2.5.1", + "copy-to-clipboard": "^3.3.3", + "dayjs": "^1.11.11", + "rc-cascader": "~3.30.0", + "rc-checkbox": "~3.3.0", + "rc-collapse": "~3.9.0", + "rc-dialog": "~9.6.0", + "rc-drawer": "~7.2.0", + "rc-dropdown": "~4.2.0", + "rc-field-form": "~2.5.0", + "rc-image": "~7.11.0", + "rc-input": "~1.6.3", + "rc-input-number": "~9.3.0", + "rc-mentions": "~2.17.0", + "rc-menu": "~9.16.0", + "rc-motion": "^2.9.3", + "rc-notification": "~5.6.2", + "rc-pagination": "~4.3.0", + "rc-picker": "~4.7.2", + "rc-progress": "~4.0.0", + "rc-rate": "~2.13.0", + "rc-resize-observer": "^1.4.0", + "rc-segmented": "~2.5.0", + "rc-select": "~14.16.3", + "rc-slider": "~11.1.7", + "rc-steps": "~6.0.1", + "rc-switch": "~4.1.0", + "rc-table": "~7.48.1", + "rc-tabs": "~15.4.0", + "rc-textarea": "~1.8.2", + "rc-tooltip": "~6.2.1", + "rc-tree": "~5.10.1", + "rc-tree-select": "~5.24.3", + "rc-upload": "~4.8.1", + "rc-util": "^5.43.0", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -10817,12 +11229,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", + "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { @@ -10842,11 +11254,11 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -11217,9 +11629,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001679", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz", - "integrity": "sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==", + "version": "1.0.30001680", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", + "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", "funding": [ { "type": "opencollective", @@ -11537,6 +11949,11 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "optional": true }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", + "integrity": "sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -11602,6 +12019,14 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "optional": true }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core-js-compat": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", @@ -11852,9 +12277,9 @@ "peer": true }, "node_modules/cssdb": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.1.2.tgz", - "integrity": "sha512-ba3HmHU/lxy9nfz/fQLA/Ul+/oSdSOXqoR53BDmRvXTfRbkGqHKqr2rSxADYMRF4uD8vZhMlCQ6c5TEfLLkkVA==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.2.1.tgz", + "integrity": "sha512-KwEPys7lNsC8OjASI8RrmwOYYDcm0JOW9zQhcV83ejYcQkirTEyeAGui8aO2F5PiS6SLpxuTzl6qlMElIdsgIg==", "funding": [ { "type": "opencollective", @@ -12089,6 +12514,11 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -12504,9 +12934,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.55", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz", - "integrity": "sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==" + "version": "1.5.56", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.56.tgz", + "integrity": "sha512-7lXb9dAvimCFdvUMTyucD4mnIndt/xhRKFAlky0CyFogdnNmdPQNoHI23msF/2V4mpTxMzgMdjK4+YRlFlRQZw==" }, "node_modules/emittery": { "version": "0.13.1", @@ -16543,6 +16973,14 @@ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "dependencies": { + "string-convert": "^0.2.0" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -17101,9 +17539,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/msw": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.2.tgz", - "integrity": "sha512-RdRgPvjfuzMIACkWv7VOVAeSRYMU3ofokLv1w0RsbFX960qnj/tFEyOFXY0G2GTUd9trA6rHuHciM/FKpBp6/A==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.4.tgz", + "integrity": "sha512-Pm4LmWQeytDsNCR+A7gt39XAdtH6zQb6jnIKRig0FlvYOn8eksn3s1nXxUfz5KYUjbckof7Z4p2ewzgffPoCbg==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -17229,9 +17667,9 @@ "optional": true }, "node_modules/nock": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.5.tgz", - "integrity": "sha512-XKYnqUrCwXC8DGG1xX4YH5yNIrlh9c065uaMZZHUoeUUINTOyt+x/G+ezYk0Ft6ExSREVIs+qBJDK503viTfFA==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", "dev": true, "dependencies": { "debug": "^4.1.0", @@ -18009,9 +18447,9 @@ } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "funding": [ { "type": "opencollective", @@ -18028,7 +18466,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -18211,9 +18649,9 @@ } }, "node_modules/postcss-color-functional-notation": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.5.tgz", - "integrity": "sha512-zW97tq5t2sSSSZQcIS4y6NDZj79zVv8hrBIJ4PSFZFmMBcjYqCt8sRXFGIYZohCpfFHmimMNqJje2Qd3qqMNdg==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.6.tgz", + "integrity": "sha512-wLXvm8RmLs14Z2nVpB4CWlnvaWPRcOZFltJSlcbYwSJ1EDZKsKDhPKIMecCnuU054KSmlmubkqczmm6qBPCBhA==", "funding": [ { "type": "github", @@ -18226,7 +18664,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-color-parser": "^3.0.5", + "@csstools/css-color-parser": "^3.0.6", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/postcss-progressive-custom-properties": "^4.0.0", @@ -18824,9 +19262,9 @@ } }, "node_modules/postcss-lab-function": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.5.tgz", - "integrity": "sha512-q2M8CfQbjHxbwv1GPAny05EVuj0WByUgq/OWKgpfbTHnMchtUqsVQgaW1mztjSZ4UPufwuTLB14fmFGsoTE/VQ==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.6.tgz", + "integrity": "sha512-HPwvsoK7C949vBZ+eMyvH2cQeMr3UREoHvbtra76/UhDuiViZH6pir+z71UaJQohd7VDSVUdR6TkWYKExEc9aQ==", "funding": [ { "type": "github", @@ -18839,7 +19277,7 @@ ], "peer": true, "dependencies": { - "@csstools/css-color-parser": "^3.0.5", + "@csstools/css-color-parser": "^3.0.6", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/postcss-progressive-custom-properties": "^4.0.0", @@ -19348,9 +19786,9 @@ } }, "node_modules/postcss-preset-env": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.0.9.tgz", - "integrity": "sha512-mpfJWMAW6szov+ifW9HpNUUZE3BoXoHc4CDzNQHdH2I4CwsqulQ3bpFNUR6zh4tg0BUcqM7UUAbzG4UTel8QYw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.1.0.tgz", + "integrity": "sha512-OfzbinZWpFcmuLB3mabsGa0NArzx5DVVtZ9G1k326iLvU7Jj9q/G3ihBu/Msi0mt96CjrM23HpbuEewDvT71KQ==", "funding": [ { "type": "github", @@ -19364,14 +19802,14 @@ "peer": true, "dependencies": { "@csstools/postcss-cascade-layers": "^5.0.1", - "@csstools/postcss-color-function": "^4.0.5", - "@csstools/postcss-color-mix-function": "^3.0.5", + "@csstools/postcss-color-function": "^4.0.6", + "@csstools/postcss-color-mix-function": "^3.0.6", "@csstools/postcss-content-alt-text": "^2.0.4", - "@csstools/postcss-exponential-functions": "^2.0.4", + "@csstools/postcss-exponential-functions": "^2.0.5", "@csstools/postcss-font-format-keywords": "^4.0.0", - "@csstools/postcss-gamut-mapping": "^2.0.5", - "@csstools/postcss-gradients-interpolation-method": "^5.0.5", - "@csstools/postcss-hwb-function": "^4.0.5", + "@csstools/postcss-gamut-mapping": "^2.0.6", + "@csstools/postcss-gradients-interpolation-method": "^5.0.6", + "@csstools/postcss-hwb-function": "^4.0.6", "@csstools/postcss-ic-unit": "^4.0.0", "@csstools/postcss-initial": "^2.0.0", "@csstools/postcss-is-pseudo-class": "^5.0.1", @@ -19381,27 +19819,29 @@ "@csstools/postcss-logical-overscroll-behavior": "^2.0.0", "@csstools/postcss-logical-resize": "^3.0.0", "@csstools/postcss-logical-viewport-units": "^3.0.3", - "@csstools/postcss-media-minmax": "^2.0.4", + "@csstools/postcss-media-minmax": "^2.0.5", "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.4", "@csstools/postcss-nested-calc": "^4.0.0", "@csstools/postcss-normalize-display-values": "^4.0.0", - "@csstools/postcss-oklab-function": "^4.0.5", + "@csstools/postcss-oklab-function": "^4.0.6", "@csstools/postcss-progressive-custom-properties": "^4.0.0", - "@csstools/postcss-relative-color-syntax": "^3.0.5", + "@csstools/postcss-random-function": "^1.0.0", + "@csstools/postcss-relative-color-syntax": "^3.0.6", "@csstools/postcss-scope-pseudo-class": "^4.0.1", - "@csstools/postcss-stepped-value-functions": "^4.0.4", + "@csstools/postcss-sign-functions": "^1.0.0", + "@csstools/postcss-stepped-value-functions": "^4.0.5", "@csstools/postcss-text-decoration-shorthand": "^4.0.1", - "@csstools/postcss-trigonometric-functions": "^4.0.4", + "@csstools/postcss-trigonometric-functions": "^4.0.5", "@csstools/postcss-unset-value": "^4.0.0", "autoprefixer": "^10.4.19", "browserslist": "^4.23.1", "css-blank-pseudo": "^7.0.1", "css-has-pseudo": "^7.0.1", "css-prefers-color-scheme": "^10.0.0", - "cssdb": "^8.1.2", + "cssdb": "^8.2.1", "postcss-attribute-case-insensitive": "^7.0.1", "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^7.0.5", + "postcss-color-functional-notation": "^7.0.6", "postcss-color-hex-alpha": "^10.0.0", "postcss-color-rebeccapurple": "^10.0.0", "postcss-custom-media": "^11.0.5", @@ -19414,7 +19854,7 @@ "postcss-font-variant": "^5.0.0", "postcss-gap-properties": "^6.0.0", "postcss-image-set-function": "^7.0.0", - "postcss-lab-function": "^7.0.5", + "postcss-lab-function": "^7.0.6", "postcss-logical": "^8.0.0", "postcss-nesting": "^13.0.1", "postcss-opacity-percentage": "^3.0.0", @@ -20025,6 +20465,582 @@ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==" }, + "node_modules/rc-cascader": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.30.0.tgz", + "integrity": "sha512-rrzSbk1Bdqbu+pDwiLCLHu72+lwX9BZ28+JKzoi0DWZ4N29QYFeip8Gctl33QVd2Xg3Rf14D3yAOG76ElJw16w==", + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "^2.3.1", + "rc-select": "~14.16.2", + "rc-tree": "~5.10.1", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-checkbox": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.3.0.tgz", + "integrity": "sha512-Ih3ZaAcoAiFKJjifzwsGiT/f/quIkxJoklW4yKGho14Olulwn8gN7hOBve0/WGDg5o/l/5mL0w7ff7/YGvefVw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.25.2" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-collapse": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz", + "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.3.4", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dialog": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz", + "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/portal": "^1.0.0-8", + "classnames": "^2.2.6", + "rc-motion": "^2.3.0", + "rc-util": "^5.21.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-drawer": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.2.0.tgz", + "integrity": "sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@rc-component/portal": "^1.1.1", + "classnames": "^2.2.6", + "rc-motion": "^2.6.1", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dropdown": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.0.tgz", + "integrity": "sha512-odM8Ove+gSh0zU27DUj5cG1gNKg7mLWBYzB5E4nNLrLwBmYEgYP43vHKDGOVZcJSVElQBI0+jTQgjnq0NfLjng==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-util": "^5.17.0" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/rc-field-form": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.5.0.tgz", + "integrity": "sha512-trTezNK0ThFQkKTy3KmsP6tY1TPMm0O5H/YviB+QLyfpdcMFuYs/aPn1uDbpdQupBd6dE6X73bzITcPS97zhJQ==", + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/async-validator": "^5.0.3", + "rc-util": "^5.32.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-image": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.11.0.tgz", + "integrity": "sha512-aZkTEZXqeqfPZtnSdNUnKQA0N/3MbgR7nUnZ+/4MfSFWPFHZau4p5r5ShaI0KPEMnNjv4kijSCFq/9wtJpwykw==", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/portal": "^1.0.2", + "classnames": "^2.2.6", + "rc-dialog": "~9.6.0", + "rc-motion": "^2.6.2", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-input": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.6.3.tgz", + "integrity": "sha512-wI4NzuqBS8vvKr8cljsvnTUqItMfG1QbJoxovCgL+DX4eVUcHIjVwharwevIxyy7H/jbLryh+K7ysnJr23aWIA==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.18.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-input-number": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.3.0.tgz", + "integrity": "sha512-JQ363ywqRyxwgVxpg2z2kja3CehTpYdqR7emJ/6yJjRdbvo+RvfE83fcpBCIJRq3zLp8SakmEXq60qzWyZ7Usw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/mini-decimal": "^1.0.1", + "classnames": "^2.2.5", + "rc-input": "~1.6.0", + "rc-util": "^5.40.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-mentions": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.17.0.tgz", + "integrity": "sha512-sfHy+qLvc+p8jx8GUsujZWXDOIlIimp6YQz7N5ONQ6bHsa2kyG+BLa5k2wuxgebBbH97is33wxiyq5UkiXRpHA==", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-input": "~1.6.0", + "rc-menu": "~9.16.0", + "rc-textarea": "~1.8.0", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-menu": { + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.0.tgz", + "integrity": "sha512-vAL0yqPkmXWk3+YKRkmIR8TYj3RVdEt3ptG2jCJXWNAvQbT0VJJdRyHZ7kG/l1JsZlB+VJq/VcYOo69VR4oD+w==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.0.0", + "classnames": "2.x", + "rc-motion": "^2.4.3", + "rc-overflow": "^1.3.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-motion": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.3.tgz", + "integrity": "sha512-rkW47ABVkic7WEB0EKJqzySpvDqwl60/tdkY7hWP7dYnh5pm0SzJpo54oW3TDUGXV5wfxXFmMkxrzRRbotQ0+w==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-notification": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.2.tgz", + "integrity": "sha512-Id4IYMoii3zzrG0lB0gD6dPgJx4Iu95Xu0BQrhHIbp7ZnAZbLqdqQ73aIWH0d0UFcElxwaKjnzNovTjo7kXz7g==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.9.0", + "rc-util": "^5.20.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-overflow": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.3.2.tgz", + "integrity": "sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.37.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-pagination": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-4.3.0.tgz", + "integrity": "sha512-UubEWA0ShnroQ1tDa291Fzw6kj0iOeF26IsUObxYTpimgj4/qPCWVFl18RLZE+0Up1IZg0IK4pMn6nB3mjvB7g==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-picker": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.7.2.tgz", + "integrity": "sha512-KShWVIdrncIKBZ1rm6E2s4Di9jlpcm38EYIZ472skXqKUz8YKlWQgewG5dT+HUjfon+tLs7dp5kmWUe0bW7Gqw==", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.1", + "rc-overflow": "^1.3.2", + "rc-resize-observer": "^1.4.0", + "rc-util": "^5.43.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/rc-progress": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz", + "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-util": "^5.16.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-rate": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.0.tgz", + "integrity": "sha512-oxvx1Q5k5wD30sjN5tqAyWTvJfLNNJn7Oq3IeS4HxWfAiC4BOXMITNAsw7u/fzdtO4MS8Ki8uRLOzcnEuoQiAw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-resize-observer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz", + "integrity": "sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q==", + "dependencies": { + "@babel/runtime": "^7.20.7", + "classnames": "^2.2.1", + "rc-util": "^5.38.0", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-segmented": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.5.0.tgz", + "integrity": "sha512-B28Fe3J9iUFOhFJET3RoXAPFJ2u47QvLSYcZWC4tFYNGPEjug5LAxEasZlA/PpAxhdOPqGWsGbSj7ftneukJnw==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-motion": "^2.4.4", + "rc-util": "^5.17.0" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-select": { + "version": "14.16.3", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.3.tgz", + "integrity": "sha512-51+j6s3fJJJXB7E+B6W1hM4Tjzv1B/Decooz9ilgegDBt3ZAth1b/xMwYCTrT5BbG2e53XACQsyDib2+3Ro1fg==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.1.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-overflow": "^1.3.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-slider": { + "version": "11.1.7", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.7.tgz", + "integrity": "sha512-ytYbZei81TX7otdC0QvoYD72XSlxvTihNth5OeZ6PMXyEDq/vHdWFulQmfDGyXK1NwKwSlKgpvINOa88uT5g2A==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-steps": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz", + "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==", + "dependencies": { + "@babel/runtime": "^7.16.7", + "classnames": "^2.2.3", + "rc-util": "^5.16.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-switch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz", + "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "classnames": "^2.2.1", + "rc-util": "^5.30.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-table": { + "version": "7.48.1", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.48.1.tgz", + "integrity": "sha512-Z4mDKjWg+xz/Ezdw6ivWcbqRpaJ0QfCORRoRrlrw65KSGZLK8OcTdacH22/fyGb8L4It/0/9qcMm8VrVAk/WBw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/context": "^1.4.0", + "classnames": "^2.2.5", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.41.0", + "rc-virtual-list": "^3.14.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tabs": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.4.0.tgz", + "integrity": "sha512-llKuyiAVqmXm2z7OrmhX5cNb2ueZaL8ZyA2P4R+6/72NYYcbEgOXibwHiQCFY2RiN3swXl53SIABi2CumUS02g==", + "dependencies": { + "@babel/runtime": "^7.11.2", + "classnames": "2.x", + "rc-dropdown": "~4.2.0", + "rc-menu": "~9.16.0", + "rc-motion": "^2.6.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.34.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-textarea": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.8.2.tgz", + "integrity": "sha512-UFAezAqltyR00a8Lf0IPAyTd29Jj9ee8wt8DqXyDMal7r/Cg/nDt3e1OOv3Th4W6mKaZijjgwuPXhAfVNTN8sw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-input": "~1.6.0", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tooltip": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.2.1.tgz", + "integrity": "sha512-rws0duD/3sHHsD905Nex7FvoUGy2UBQRhTkKxeEvr2FB+r21HsOxcDJI0TzyO8NHhnAA8ILr8pfbSBg5Jj5KBg==", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tree": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.10.1.tgz", + "integrity": "sha512-FPXb3tT/u39mgjr6JNlHaUTYfHkVGW56XaGDahDpEFLGsnPxGcVLNTjcqoQb/GNbSCycl7tD7EvIymwOTP0+Yw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-tree-select": { + "version": "5.24.4", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.24.4.tgz", + "integrity": "sha512-MzljkSkk7weKOcE853UtYlXB6uyUEzcEQhhpaCwE6jQPbmBUgGiRURuKWpYUnM/dXrwTTlCK969M6Pgjj35MLA==", + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "2.x", + "rc-select": "~14.16.2", + "rc-tree": "~5.10.1", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-upload": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.8.1.tgz", + "integrity": "sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "classnames": "^2.2.5", + "rc-util": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util": { + "version": "5.43.0", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.43.0.tgz", + "integrity": "sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/rc-virtual-list": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.15.0.tgz", + "integrity": "sha512-dF2YQztqrU3ijAeWOqscTshCEr7vpimzSqAVjO1AyAmaqcHulaXpnGR0ptK5PXfxTUy48VkJOiglMIxlkYGs0w==", + "dependencies": { + "@babel/runtime": "^7.20.0", + "classnames": "^2.2.6", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -20775,6 +21791,11 @@ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -21070,6 +22091,14 @@ "loose-envify": "^1.1.0" } }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -21376,6 +22405,11 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==" + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -21624,6 +22658,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz", + "integrity": "sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==" + }, "node_modules/sugarss": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-4.0.1.tgz", @@ -21811,6 +22850,14 @@ "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", "peer": true }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "engines": { + "node": ">=12.22" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -21871,11 +22918,16 @@ } }, "node_modules/tocbot": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.31.0.tgz", - "integrity": "sha512-Zd9tt6EQn2bvLSHIcug/Z1Sukyn/XJ62dMK9SjIbtHSDkI+Du40CmBvds6BedzXZe1sS1iPGl4Wup/k4cJkVhQ==", + "version": "4.32.2", + "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.32.2.tgz", + "integrity": "sha512-UbVZNXX79LUqMzsnSTwE/YF/PYc2pg3G77D/jcolHd6lmw+oklzfcLtHSsmWBhOf1wfWD1HfYzdjGQef1VcQgg==", "optional": true }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -22650,9 +23702,9 @@ } }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "devOptional": true, "dependencies": { "esbuild": "^0.21.3", @@ -23099,9 +24151,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.4.tgz", - "integrity": "sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.25.0.tgz", + "integrity": "sha512-uVbClXmR6wvx5R1M3Od4utyLUxrmOcEm3pAtMphn73Apq19PDtHpgZoEvqH2YnnaNUuvKmg2DgRd2Sqv+odyqg==", "devOptional": true, "dependencies": { "@types/estree": "1.0.6" @@ -23114,24 +24166,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.4", - "@rollup/rollup-android-arm64": "4.24.4", - "@rollup/rollup-darwin-arm64": "4.24.4", - "@rollup/rollup-darwin-x64": "4.24.4", - "@rollup/rollup-freebsd-arm64": "4.24.4", - "@rollup/rollup-freebsd-x64": "4.24.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.4", - "@rollup/rollup-linux-arm-musleabihf": "4.24.4", - "@rollup/rollup-linux-arm64-gnu": "4.24.4", - "@rollup/rollup-linux-arm64-musl": "4.24.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.4", - "@rollup/rollup-linux-riscv64-gnu": "4.24.4", - "@rollup/rollup-linux-s390x-gnu": "4.24.4", - "@rollup/rollup-linux-x64-gnu": "4.24.4", - "@rollup/rollup-linux-x64-musl": "4.24.4", - "@rollup/rollup-win32-arm64-msvc": "4.24.4", - "@rollup/rollup-win32-ia32-msvc": "4.24.4", - "@rollup/rollup-win32-x64-msvc": "4.24.4", + "@rollup/rollup-android-arm-eabi": "4.25.0", + "@rollup/rollup-android-arm64": "4.25.0", + "@rollup/rollup-darwin-arm64": "4.25.0", + "@rollup/rollup-darwin-x64": "4.25.0", + "@rollup/rollup-freebsd-arm64": "4.25.0", + "@rollup/rollup-freebsd-x64": "4.25.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.25.0", + "@rollup/rollup-linux-arm-musleabihf": "4.25.0", + "@rollup/rollup-linux-arm64-gnu": "4.25.0", + "@rollup/rollup-linux-arm64-musl": "4.25.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.25.0", + "@rollup/rollup-linux-riscv64-gnu": "4.25.0", + "@rollup/rollup-linux-s390x-gnu": "4.25.0", + "@rollup/rollup-linux-x64-gnu": "4.25.0", + "@rollup/rollup-linux-x64-musl": "4.25.0", + "@rollup/rollup-win32-arm64-msvc": "4.25.0", + "@rollup/rollup-win32-ia32-msvc": "4.25.0", + "@rollup/rollup-win32-x64-msvc": "4.25.0", "fsevents": "~2.3.2" } }, diff --git a/react/package.json b/react/package.json index c24d202e..c45e8e6d 100644 --- a/react/package.json +++ b/react/package.json @@ -36,12 +36,16 @@ "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.0", "@changey/react-leaflet-markercluster": "^4.0.0-rc1", + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2", "@reduxjs/toolkit": "^1.8.4", "@tacc/core-components": "^0.0.3-beta.0", "@tacc/core-styles": "^2.24.1", "@turf/turf": "^6.5.0", "@types/geojson": "^7946.0.14", "@types/leaflet.markercluster": "^1.5.1", + "antd": "^5.21.6", "axios": "^1.6.2", "eslint-config-prettier": "^8.5.0", "eslint-plugin-jsx-a11y": "^6.6.1", diff --git a/react/src/__fixtures__/featuresFixture.ts b/react/src/__fixtures__/featuresFixture.ts index 66c6b0a1..1cff53ec 100644 --- a/react/src/__fixtures__/featuresFixture.ts +++ b/react/src/__fixtures__/featuresFixture.ts @@ -1,10 +1,11 @@ import { Point } from 'geojson'; +import { FeatureCollection, AssetType } from '@hazmapper/types'; -export const featureCollection = { +export const featureCollection: FeatureCollection = { type: 'FeatureCollection', features: [ { - id: 2053334, + id: 1, project_id: 80, type: 'Feature', geometry: { @@ -18,15 +19,15 @@ export const featureCollection = { id: 2036568, path: '80/f7c2afa2-f184-40a7-80be-0874a7db755e.jpeg', uuid: 'f7c2afa2-f184-40a7-80be-0874a7db755e', - asset_type: 'image', - original_path: '/nathanf/image1.JPG', + asset_type: 'image' as AssetType, + original_path: '/foo/image1.JPG', original_name: null, - display_path: '/nathanf/image1.JPG', + display_path: '/foo/image1.JPG', }, ], }, { - id: 2053335, + id: 2, project_id: 80, type: 'Feature', geometry: { @@ -40,10 +41,54 @@ export const featureCollection = { id: 2036569, path: '80/5a3e7513-2740-4c0f-944d-7b497ceff2ea.jpeg', uuid: '5a3e7513-2740-4c0f-944d-7b497ceff2ea', - asset_type: 'image', - original_path: '/nathanf/image1.JPG', + asset_type: 'image' as AssetType, + original_path: '/foo/image2.JPG', + original_name: null, + display_path: '/foo/image2.JPG', + }, + ], + }, + ], +}; + +export const featureCollectionWithNestedPaths: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + id: 1, + project_id: 1, + type: 'Feature', + geometry: { type: 'Point', coordinates: [0, 0] } as Point, + properties: {}, + styles: {}, + assets: [ + { + id: 1, + display_path: 'folder1/file1.jpg', + path: '', + uuid: '', + asset_type: 'image' as AssetType, + original_path: '', + original_name: null, + }, + ], + }, + { + id: 2, + project_id: 1, + type: 'Feature', + geometry: { type: 'Point', coordinates: [0, 0] } as Point, + properties: {}, + styles: {}, + assets: [ + { + id: 2, + display_path: 'folder1/subfolder/file2.rq', + path: '', + uuid: '', + asset_type: 'questionnaire' as AssetType, + original_path: '', original_name: null, - display_path: '/nathanf/image1.JPG', }, ], }, diff --git a/react/src/components/AssetsPanel/AssetsPanel.module.css b/react/src/components/AssetsPanel/AssetsPanel.module.css index f6cd6ffe..10eb2a1b 100644 --- a/react/src/components/AssetsPanel/AssetsPanel.module.css +++ b/react/src/components/AssetsPanel/AssetsPanel.module.css @@ -3,4 +3,27 @@ flex-direction: column; width: 100%; height: 100%; + overflow: hidden; +} + +.topSection, +.bottomSection { + flex: 0 0 auto; + padding: 10px; + display: flex; + justify-content: center; + align-items: center; +} + +.middleSection { + flex: 1 1 auto; + overflow: hidden; + min-height: 0; + display: flex; + flex-direction: column; +} + +.middleSection > div { + flex: 1; + overflow: auto; } diff --git a/react/src/components/AssetsPanel/AssetsPanel.test.tsx b/react/src/components/AssetsPanel/AssetsPanel.test.tsx new file mode 100644 index 00000000..f39c6990 --- /dev/null +++ b/react/src/components/AssetsPanel/AssetsPanel.test.tsx @@ -0,0 +1,60 @@ +import React, { act } from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import AssetsPanel from './AssetsPanel'; +import { featureCollection } from '@hazmapper/__fixtures__/featuresFixture'; +import { useFeatures } from '@hazmapper/hooks'; + +jest.mock('@hazmapper/hooks', () => ({ + useFeatures: jest.fn(), +})); + +// Mock FeatureFileTree component since it's a complex component and tested elswhere +jest.mock('@hazmapper/components/FeatureFileTree', () => { + return function MockFeatureFileTree() { + return
FeatureFileTree Component
; + }; +}); + +describe('AssetsPanel', () => { + const defaultProps = { + featureCollection, + projectId: 1, + isPublic: false, + }; + + beforeEach(() => { + jest.clearAllMocks(); + + // Setup default mock implementation for useFeatures which is used for downloading geojson + (useFeatures as jest.Mock).mockReturnValue({ + isLoading: false, + refetch: jest.fn(), + }); + }); + + it('renders all main components', () => { + const { getByText } = render(); + + // Check for the presence of buttons + expect(getByText('Import from DesignSafe TODO/WG-387')).toBeDefined(); + expect(getByText('Export to GeoJSON')).toBeDefined(); + }); + + it('handles GeoJSON export correctly', async () => { + // Mock the useFeatures hook for download scenario + const mockRefetch = jest.fn(); + (useFeatures as jest.Mock).mockReturnValue({ + isLoading: false, + refetch: mockRefetch, + }); + + render(); + + // Click export button + await act(async () => { + fireEvent.click(screen.getByText('Export to GeoJSON')); + }); + // Verify refetch was called + expect(mockRefetch).toHaveBeenCalled(); + }); +}); diff --git a/react/src/components/AssetsPanel/AssetsPanel.tsx b/react/src/components/AssetsPanel/AssetsPanel.tsx index 0f0f949d..64989237 100644 --- a/react/src/components/AssetsPanel/AssetsPanel.tsx +++ b/react/src/components/AssetsPanel/AssetsPanel.tsx @@ -1,19 +1,95 @@ import React from 'react'; import styles from './AssetsPanel.module.css'; +import FeatureFileTree from '@hazmapper/components/FeatureFileTree'; +import { FeatureCollection } from '@hazmapper/types'; +import { Button } from '@tacc/core-components'; +import { useFeatures } from '@hazmapper/hooks'; + +interface DownloadFeaturesButtonProps { + projectId: number; + isPublic: boolean; +} + +const DownloadFeaturesButton: React.FC = ({ + projectId, + isPublic, +}) => { + const { isLoading: isDownloading, refetch: triggerDownload } = useFeatures({ + projectId, + isPublic, + assetTypes: [], // Empty array to get all features + options: { + enabled: false, // Only fetch when triggered by user clicking button + cacheTime: 0, + staleTime: 0, + onSuccess: (data: FeatureCollection) => { + // Create and trigger download + const blob = new Blob([JSON.stringify(data)], { + type: 'application/json', + }); + + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = `hazmapper.json`; + + document.body.appendChild(link); + link.click(); + + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + }, + }, + }); + + return ( + + ); +}; interface Props { + /** + * Features of map + */ + featureCollection: FeatureCollection; + /** * Whether or not the map project is public. */ isPublic: boolean; + + /** + * active project id + */ + projectId: number; } /** - * A component that displays a map project (a map and related data) + * A panel component that displays info on feature assets */ -const AssetsPanel: React.FC = ({ isPublic }) => { +const AssetsPanel: React.FC = ({ + isPublic, + featureCollection, + projectId, +}) => { return ( -
Assets Panel TODO, isPublic: {isPublic}
+
+
+ +
+
+ +
+
+ +
+
); }; diff --git a/react/src/components/DeleteMapModal/DeleteMapModal.test.tsx b/react/src/components/DeleteMapModal/DeleteMapModal.test.tsx index 7e68d5e9..d9fdf73b 100644 --- a/react/src/components/DeleteMapModal/DeleteMapModal.test.tsx +++ b/react/src/components/DeleteMapModal/DeleteMapModal.test.tsx @@ -1,7 +1,6 @@ -import React from 'react'; +import React, { act } from 'react'; import { render, screen } from '@testing-library/react'; import { BrowserRouter as Router } from 'react-router-dom'; -import { act } from 'react-dom/test-utils'; import { QueryClient, QueryClientProvider } from 'react-query'; import DeleteMapModal from './DeleteMapModal'; import { Provider } from 'react-redux'; diff --git a/react/src/components/FeatureFileTree/FeatureFileTree.module.css b/react/src/components/FeatureFileTree/FeatureFileTree.module.css new file mode 100644 index 00000000..4a5702b9 --- /dev/null +++ b/react/src/components/FeatureFileTree/FeatureFileTree.module.css @@ -0,0 +1,57 @@ +.root { + width: 100%; + overflow: visible; + + :global(.ant-tree .ant-tree-treenode) { + margin-bottom: 0px !important; /*needed to ensure calculation of row hight is right */ + } + + /* Remove switcher space */ + :global(.ant-tree-switcher) { + display: none; + } + + /* Hovering over non-selected items */ + :global(.ant-tree-node-content-wrapper:hover:not(.ant-tree-node-selected)) { + background-color: var(--global-color-accent--weak) !important; + } + + /* Selected items (both normal and hover state) */ /*TODO*/ + :global(.ant-tree-node-content-wrapper.ant-tree-node-selected) { + background-color: var(--global-color-accent--weak) !important; + } +} + +.featureFileTree { + height: 100%; + + :global(.ant-tree-node-content-wrapper) { + /*https://tacc-main.atlassian.net/browse/WG-390*/ + font-size: var(--global-font-family--small); + font-family: var(--global-font-family--sans); + max-width: var(--hazmapper-panel-width); /* fixed width of panel*/ + padding-left: 5px; + padding-right: 5px; + overflow: hidden; + } + + /* Adjust indent size */ + :global(.ant-tree-indent-unit) { + width: 8px; + } +} + +.treeNode { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; +} + +.fileName { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-left: 0.25em; +} diff --git a/react/src/components/FeatureFileTree/FeatureFileTree.test.tsx b/react/src/components/FeatureFileTree/FeatureFileTree.test.tsx new file mode 100644 index 00000000..acf52002 --- /dev/null +++ b/react/src/components/FeatureFileTree/FeatureFileTree.test.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import FeatureFileTree from './FeatureFileTree'; +import { featureCollection } from '@hazmapper/__fixtures__/featuresFixture'; +import { useDeleteFeature } from '@hazmapper/hooks'; + +// Mock the hooks +jest.mock('@hazmapper/hooks', () => ({ + useDeleteFeature: jest.fn(), +})); + +jest.mock('react-resize-detector', () => ({ + useResizeDetector: () => ({ + height: 500, + ref: jest.fn(), + }), +})); + +const renderWithRouter = (ui: React.ReactElement, { route = '/' } = {}) => { + return { + ...render({ui}), + }; +}; + +describe('FeatureFileTree', () => { + const defaultProps = { + featureCollection: featureCollection, + isPublic: false, + projectId: 1, + }; + + beforeEach(() => { + jest.clearAllMocks(); + + (useDeleteFeature as jest.Mock).mockImplementation(() => ({ + mutate: jest.fn(), + isLoading: false, + })); + }); + + it('renders feature list correctly', () => { + const { getByText } = renderWithRouter( + + ); + + expect(getByText('foo')).toBeDefined(); + expect(getByText('image1.JPG')).toBeDefined(); + expect(getByText('image2.JPG')).toBeDefined(); + }); + + it('handles feature deletion for non-public projects', () => { + const deleteFeatureMock = jest.fn(); + (useDeleteFeature as jest.Mock).mockImplementation(() => ({ + mutate: deleteFeatureMock, + isLoading: false, + })); + + const { getByTestId } = renderWithRouter( + , + { route: '/?selectedFeature=1' } + ); + + // Find and click delete button (as featured is selected) + const deleteButton = getByTestId('delete-feature-button'); + fireEvent.click(deleteButton); + + expect(deleteFeatureMock).toHaveBeenCalledWith({ + projectId: 1, + featureId: 1, + }); + }); + + it('does not show delete button for public projects', () => { + const { queryByTestId } = renderWithRouter( + , + { route: '/?selectedFeature=1' } + ); + + // Verify delete button is not present + const deleteButton = queryByTestId('delete-feature-button'); + expect(deleteButton).toBeNull(); + }); + + it('does not show delete button when no feature is selected', () => { + const { queryByTestId } = renderWithRouter( + + ); + + // Verify delete button is not present + const deleteButton = queryByTestId('delete-feature-button'); + expect(deleteButton).toBeNull(); + }); +}); diff --git a/react/src/components/FeatureFileTree/FeatureFileTree.tsx b/react/src/components/FeatureFileTree/FeatureFileTree.tsx new file mode 100644 index 00000000..1652597f --- /dev/null +++ b/react/src/components/FeatureFileTree/FeatureFileTree.tsx @@ -0,0 +1,207 @@ +import React, { useCallback, useState, useEffect } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { Tree } from 'antd'; +import type { DataNode } from 'antd/es/tree'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { + faFolderClosed, + faFolderOpen, +} from '@fortawesome/free-solid-svg-icons'; +import { useResizeDetector } from 'react-resize-detector'; +import { Button } from '@tacc/core-components'; +import { featureCollectionToFileNodeArray } from '@hazmapper/utils/featureTreeUtils'; +import { FeatureCollection, FeatureFileNode } from '@hazmapper/types'; +import { useDeleteFeature } from '@hazmapper/hooks'; +import { FeatureIcon } from '@hazmapper/components/FeatureIcon'; +import styles from './FeatureFileTree.module.css'; + +/* interface for combining antd tree node and our tree data */ +interface TreeDataNode extends DataNode { + title?: string; + key: string; + children?: TreeDataNode[]; + featureNode: FeatureFileNode; +} +interface FeatureFileTreeProps { + /** + * Features of map + */ + featureCollection: FeatureCollection; + + /** + * Whether or not the map project is public. + */ + isPublic: boolean; + + /** + * active project id + */ + projectId: number; +} + +/* + * A tree of feature files that correspond to the map's features + */ +const FeatureFileTree: React.FC = ({ + featureCollection, + isPublic, + projectId, +}) => { + const { mutate: deleteFeature, isLoading } = useDeleteFeature(); + const location = useLocation(); + const navigate = useNavigate(); + + const { height, ref } = useResizeDetector(); + + const [expanded, setExpanded] = useState([]); + const [treeData, setTreeData] = useState([]); + + const searchParams = new URLSearchParams(location.search); + const selectedFeature = searchParams.get('selectedFeature'); + + useEffect(() => { + const fileNodeArray = featureCollectionToFileNodeArray(featureCollection); + + const getDirectoryNodeIds = (nodes: FeatureFileNode[]): string[] => { + const directoryIds: string[] = []; + const stack = [...nodes]; + + while (stack.length > 0) { + const node = stack.pop(); + if (node && node.isDirectory) { + directoryIds.push(node.nodeId); + if (node.children) { + stack.push(...node.children); + } + } + } + + return directoryIds; + }; + + // Have all direcotories be in 'expanded' (i.e. everything is expanded) + const expandedDirectories = getDirectoryNodeIds(fileNodeArray); + + const convertToTreeNode = (node: FeatureFileNode) => ({ + title: node.name, + key: node.nodeId, + isLeaf: !node.isDirectory, + children: node.children?.map(convertToTreeNode), + featureNode: node, + }); + + // Convert features into Antd's DataNode (i.e. TreeDataNode) and our internal class FeatureFileNode + setTreeData(fileNodeArray.map(convertToTreeNode)); + setExpanded(expandedDirectories); + }, [featureCollection]); + + const handleExpand = (newExpandedKeys) => { + setExpanded(newExpandedKeys); + }; + + const handleDelete = useCallback( + (nodeId: string) => (e: React.MouseEvent) => { + e.stopPropagation(); + const featureId = parseInt(nodeId, 10); + + if (isNaN(featureId)) { + console.error('Invalid feature ID:', nodeId); + return; + } + + // Only proceed with deletion if projectId is valid + deleteFeature({ + projectId, + featureId, + }); + }, + [projectId, deleteFeature] + ); + + const titleRender = (node: TreeDataNode) => { + const featureNode = node.featureNode as FeatureFileNode; + const isSelected = selectedFeature === node.key && !featureNode.isDirectory; + const isExpanded = expanded.includes(node.key); + + const toggleNode = (e: React.MouseEvent | React.KeyboardEvent) => { + if (featureNode.isDirectory) { + e.stopPropagation(); + + // Toggle expanded state + const newExpanded = expanded.includes(node.key) + ? expanded.filter((k) => k !== node.key) + : [...expanded, node.key]; + setExpanded(newExpanded); + } else { + e.stopPropagation(); + + const newSearchParams = new URLSearchParams(searchParams); + if (selectedFeature === node.key) { + newSearchParams.delete('selectedFeature'); + } else { + newSearchParams.set('selectedFeature', node.key); + } + navigate({ search: newSearchParams.toString() }, { replace: true }); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' || e.key === ' ') { + toggleNode(e); + } + }; + + return ( +
+ {featureNode.isDirectory ? ( + + ) : ( + + )} + {featureNode.name} + {!isPublic && isSelected && ( +
+ ); + }; + + if (!treeData.length) return null; + + return ( +
+ +
+ ); +}; + +export default React.memo(FeatureFileTree); diff --git a/react/src/components/FeatureFileTree/index.tsx b/react/src/components/FeatureFileTree/index.tsx new file mode 100644 index 00000000..af0dde87 --- /dev/null +++ b/react/src/components/FeatureFileTree/index.tsx @@ -0,0 +1 @@ +export { default } from './FeatureFileTree'; diff --git a/react/src/components/FeatureIcon/FeatureIcon.module.css b/react/src/components/FeatureIcon/FeatureIcon.module.css new file mode 100644 index 00000000..9b936cfe --- /dev/null +++ b/react/src/components/FeatureIcon/FeatureIcon.module.css @@ -0,0 +1,3 @@ +.icon { + color: var(--global-color-accent--normal); +} diff --git a/react/src/components/FeatureIcon/FeatureIcon.tsx b/react/src/components/FeatureIcon/FeatureIcon.tsx new file mode 100644 index 00000000..e4bcaf66 --- /dev/null +++ b/react/src/components/FeatureIcon/FeatureIcon.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { IconDefinition } from '@fortawesome/fontawesome-svg-core'; +import { + faCameraRetro, + faVideo, + faClipboardList, + faMapMarkerAlt, + faDrawPolygon, + faCloud, + faBezierCurve, + faRoad, + faLayerGroup, + faQuestionCircle, +} from '@fortawesome/free-solid-svg-icons'; + +import { FeatureType, FeatureTypeNullable } from '@hazmapper/types'; +import styles from './FeatureIcon.module.css'; + +const featureTypeToIcon: Record = { + // Asset types + image: faCameraRetro, + video: faVideo, + questionnaire: faClipboardList, + point_cloud: faCloud /* https://tacc-main.atlassian.net/browse/WG-391 */, + streetview: faRoad, + + // Geometry types + Point: faMapMarkerAlt, + LineString: faBezierCurve, + Polygon: faDrawPolygon, + MultiPoint: faMapMarkerAlt, + MultiLineString: faBezierCurve, + MultiPolygon: faDrawPolygon, + GeometryCollection: faLayerGroup, + + // Collection type + collection: faLayerGroup, +}; + +interface Props { + featureType: FeatureTypeNullable; +} + +export const FeatureIcon: React.FC = ({ featureType }) => { + const icon = featureType ? featureTypeToIcon[featureType] : faQuestionCircle; + + return ; +}; diff --git a/react/src/components/FeatureIcon/index.tsx b/react/src/components/FeatureIcon/index.tsx new file mode 100644 index 00000000..29291495 --- /dev/null +++ b/react/src/components/FeatureIcon/index.tsx @@ -0,0 +1 @@ +export { FeatureIcon } from './FeatureIcon'; diff --git a/react/src/components/MapProjectNavBar/MapProjectNavBar.module.css b/react/src/components/MapProjectNavBar/MapProjectNavBar.module.css index a4182ed6..30ac3529 100644 --- a/react/src/components/MapProjectNavBar/MapProjectNavBar.module.css +++ b/react/src/components/MapProjectNavBar/MapProjectNavBar.module.css @@ -1,13 +1,25 @@ .root { height: 100%; - min-width: 150px; + width: var(--hazmapper-panel-navbar-width); + min-width: var(--hazmapper-panel-navbar-width); + overflow-y: auto; /* Allows scrolling */ padding-top: var(--global-space--section-top); } -.root > a > div { - padding-left: var(--global-space--section-left); +.root :global([class*='nav-content']) { + height: 70px; + min-height: 70px; +} + +.navItem { + height: 70px; + width: 75px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; } .image { - margin-right: 1rem; + width: 32px; } diff --git a/react/src/components/MapProjectNavBar/MapProjectNavBar.tsx b/react/src/components/MapProjectNavBar/MapProjectNavBar.tsx index 74729f36..9898e54a 100644 --- a/react/src/components/MapProjectNavBar/MapProjectNavBar.tsx +++ b/react/src/components/MapProjectNavBar/MapProjectNavBar.tsx @@ -27,7 +27,8 @@ const navItems: NavItem[] = [ }, { label: 'Point Clouds', - imagePath: pointCloudImage, + imagePath: + pointCloudImage /* https://tacc-main.atlassian.net/browse/WG-391 */, panel: Panel.PointClouds, showWhenPublic: false, }, @@ -89,14 +90,15 @@ const MapProjectNavBar: React.FC = ({ isPublic = false }) => { key={item.panel} to={to} active={activePanel === item.panel} + className={styles.navItem} > - {item.label} + {item.label} ); })} diff --git a/react/src/hazmapper.css b/react/src/hazmapper.css index 21078670..919c0f5c 100644 --- a/react/src/hazmapper.css +++ b/react/src/hazmapper.css @@ -6,5 +6,21 @@ :root { --global-color-accent--xx-light: blue; } - */ + +/* hazmapper globals */ +:root { + /* *** Map page variables *** */ + + /* Height of the map control toolbar */ + --hazmapper-control-bar-height: 27px; + + /* Height of the main navigation bar */ + --hazmapper-top-navbar-height: 40px; + + /* Width of panel navbar (i.e. icon navigation panel) */ + --hazmapper-panel-navbar-width: 75px; + + /* Width of expanded panels (assets, layers, filters etc) to the right of panel navbar */ + --hazmapper-panel-width: 230px; +} diff --git a/react/src/hooks/features/index.ts b/react/src/hooks/features/index.ts new file mode 100644 index 00000000..e738735c --- /dev/null +++ b/react/src/hooks/features/index.ts @@ -0,0 +1,2 @@ +export { useDeleteFeature } from './useDeleteFeature'; +export { useFeatures } from './useFeatures'; diff --git a/react/src/hooks/features/useDeleteFeature.ts b/react/src/hooks/features/useDeleteFeature.ts new file mode 100644 index 00000000..990a6cc1 --- /dev/null +++ b/react/src/hooks/features/useDeleteFeature.ts @@ -0,0 +1,22 @@ +import { useQueryClient } from 'react-query'; +import { useDeleteWithParams } from '@hazmapper/requests'; + +type DeleteFeatureParams = { + projectId: number; + featureId: number; +}; + +export function useDeleteFeature() { + const queryClient = useQueryClient(); + + return useDeleteWithParams({ + endpoint: ({ projectId, featureId }) => + `/projects/${projectId}/features/${featureId}/`, + options: { + onSuccess: () => { + // invalidate *any* feature listing query + queryClient.invalidateQueries(['activeProjectFeatures']); + }, + }, + }); +} diff --git a/react/src/hooks/features/useFeatures.ts b/react/src/hooks/features/useFeatures.ts index e032ef8e..2d69fc78 100644 --- a/react/src/hooks/features/useFeatures.ts +++ b/react/src/hooks/features/useFeatures.ts @@ -1,33 +1,39 @@ import { UseQueryResult } from 'react-query'; import { FeatureCollection } from '@hazmapper/types'; -import { useGet } from '../../requests'; +import { useGet } from '@hazmapper/requests'; interface UseFeaturesParams { - projectId?: number; + projectId: number; isPublic: boolean; - options: object; + assetTypes: string[]; + options?: object; } -const useFeatures = ({ +export const useFeatures = ({ projectId, isPublic, - options, assetTypes, -}: UseFeaturesParams & { - assetTypes?: string[]; -}): UseQueryResult => { + options = {}, +}: UseFeaturesParams): UseQueryResult => { const featuresRoute = isPublic ? 'public-projects' : 'projects'; let endpoint = `/${featuresRoute}/${projectId}/features/`; if (assetTypes?.length) { endpoint += `?assetType=${assetTypes.join(',')}`; } + /* Expensive to fetch and process so we only fetch when updated */ + const defaultQueryOptions = { + staleTime: Infinity, + cacheTime: Infinity, + refetchOnWindowFocus: false, + refetchOnMount: false, + refetchOnReconnect: false, + }; + const query = useGet({ endpoint, - key: ['features', { projectId, isPublic, assetTypes }], - options, + key: ['activeProjectFeatures', { projectId, isPublic, assetTypes }], + options: { ...defaultQueryOptions, ...options }, }); return query; }; - -export default useFeatures; diff --git a/react/src/hooks/index.ts b/react/src/hooks/index.ts index b1701009..1f770928 100644 --- a/react/src/hooks/index.ts +++ b/react/src/hooks/index.ts @@ -3,7 +3,7 @@ export { useProject, useProjectsWithDesignSafeInformation, } from './projects/useProjects'; -export { default as useFeatures } from './features/useFeatures'; +export * from './features/'; export { useTileServers } from './tileServers/useTileServers'; export { default as useSystems } from './systems/useSystems'; export * from './environment'; diff --git a/react/src/hooks/projects/useProjects.ts b/react/src/hooks/projects/useProjects.ts index d0b8ffd3..025cbc91 100644 --- a/react/src/hooks/projects/useProjects.ts +++ b/react/src/hooks/projects/useProjects.ts @@ -1,7 +1,11 @@ import { UseQueryResult, useQueryClient } from 'react-query'; import { useMemo } from 'react'; -import { Project, DesignSafeProjectCollection, ApiService } from '../../types'; -import { useGet, useDelete } from '../../requests'; +import { + Project, + DesignSafeProjectCollection, + ApiService, +} from '@hazmapper/types'; +import { useGet, useDelete } from '@hazmapper/requests'; export const useProjects = (): UseQueryResult => { const query = useGet({ @@ -32,10 +36,11 @@ export const useProject = ({ }); return query; }; + export const useDsProjects = (): UseQueryResult< DesignSafeProjectCollection | undefined > => { - const query = useGet({ + const query = useGet({ endpoint: `/api/projects/v2/`, key: ['projectsv2'], apiService: ApiService.DesignSafe, diff --git a/react/src/hooks/tileServers/useTileServers.ts b/react/src/hooks/tileServers/useTileServers.ts index f6ec511c..a5378ef9 100644 --- a/react/src/hooks/tileServers/useTileServers.ts +++ b/react/src/hooks/tileServers/useTileServers.ts @@ -5,13 +5,13 @@ import { TileServerLayer } from '@hazmapper/types'; interface UseTileServerParams { projectId?: number; isPublic: boolean; - options: object; + options?: object; } export const useTileServers = ({ projectId, isPublic, - options, + options = {}, }: UseTileServerParams): UseQueryResult => { const tileServersRoute = isPublic ? 'public-projects' : 'projects'; const endpoint = `/${tileServersRoute}/${projectId}/tile-servers/`; diff --git a/react/src/pages/MapProject/MapProject.module.css b/react/src/pages/MapProject/MapProject.module.css index 5a52b482..4abfc5a5 100644 --- a/react/src/pages/MapProject/MapProject.module.css +++ b/react/src/pages/MapProject/MapProject.module.css @@ -1,3 +1,11 @@ +.errorContainer { + display: flex; + align-items: center; + justify-content: center; + height: 100vh; + width: 100%; +} + .root { display: flex; flex-direction: column; @@ -8,33 +16,40 @@ .topNavbar { background-color: black; color: white; - height: 40px; + height: var(--hazmapper-top-navbar-height); + min-height: var(--hazmapper-top-navbar-height); display: flex; align-items: center; } .mapControlBar { background-color: white; - height: 27px; + height: var(--hazmapper-control-bar-height); + min-height: var(--hazmapper-control-bar-height); display: flex; align-items: center; padding: 0 10px; } .container { - height: 100vh; + height: calc( + 100vh - var(--hazmapper-top-navbar-height) - + var(--hazmapper-control-bar-height) + ); width: 100%; display: flex; flex: 1; } .panelContainer { - /* todo place into a PanelContainer and then the PanelNavigation where the buttons live either horizonal or vertical */ - width: 200px; - height: 95%; + width: var(--hazmapper-panel-width); + height: calc( + 100vh - var(--hazmapper-top-navbar-height) - + var(--hazmapper-control-bar-height) + ); background-color: var(--global-color-primary--xx-light); position: absolute; - left: 150px; + left: var(--hazmapper-panel-navbar-width); z-index: 5000; } diff --git a/react/src/pages/MapProject/MapProject.tsx b/react/src/pages/MapProject/MapProject.tsx index f9add1ad..8029ba16 100644 --- a/react/src/pages/MapProject/MapProject.tsx +++ b/react/src/pages/MapProject/MapProject.tsx @@ -11,8 +11,10 @@ import styles from './MapProject.module.css'; import MapProjectNavBar from '@hazmapper/components/MapProjectNavBar'; import Filters from '@hazmapper/components/FiltersPanel/Filter'; import { assetTypeOptions } from '@hazmapper/components/FiltersPanel/Filter'; +import { Project } from '@hazmapper/types'; +import { Message, LoadingSpinner } from '@tacc/core-components'; -interface Props { +interface MapProjectProps { /** * Whether or not the map project is public. * @default false @@ -21,10 +23,66 @@ interface Props { } /** - * A component that displays a map project (a map and related data) + * A component that displays a map project including initial loading/error components */ -const MapProject: React.FC = ({ isPublic = false }) => { +const MapProject: React.FC = ({ isPublic = false }) => { const { projectUUID } = useParams(); + + const { + data: activeProject, + isLoading, + error, + } = useProject({ + projectUUID, + isPublic, + options: { enabled: !!projectUUID }, + }); + + if (isLoading) { + /* TODO_REACT show error and improve spinner https://tacc-main.atlassian.net/browse/WG-260*/ + return ( +
+ +
+ ); + } + + if (error || !activeProject) { + /* TODO_REACT show error and improve spinner https://tacc-main.atlassian.net/browse/WG-260 + + * if no access, note why (missing project vs no access to project) + * if not logged in and project exists but they might have access, prompt to log in to see if accesable + */ + + return ( +
+ Error loading project +
+ ); + } + + return ; +}; + +interface LoadedMapProject { + /** + * Active project + */ + activeProject: Project; + + /** + * Whether or not the map project is public. + */ + isPublic; +} + +/** + * A component that displays a map project (a map and related data) + */ +const LoadedMapProject: React.FC = ({ + activeProject, + isPublic, +}) => { const [selectedAssetTypes, setSelectedAssetTypes] = useState( Object.keys(assetTypeOptions) ); @@ -49,28 +107,12 @@ const MapProject: React.FC = ({ isPublic = false }) => { ); const { - data: activeProject, - isLoading: isActiveProjectLoading, - error: activeProjectError, - } = useProject({ - projectUUID, - isPublic, - options: { enabled: !!projectUUID }, - }); - - const canFetchProjectFeaturesOrLayers = - !isActiveProjectLoading && !activeProjectError && !!activeProject; - - const { - data: featureCollection, + data: rawFeatureCollection, isLoading: isFeaturesLoading, error: featuresError, } = useFeatures({ - projectId: activeProject?.id, + projectId: activeProject.id, isPublic, - options: { - enabled: canFetchProjectFeaturesOrLayers, - }, assetTypes: formattedAssetTypes, }); @@ -79,11 +121,8 @@ const MapProject: React.FC = ({ isPublic = false }) => { isLoading: isTileServerLayersLoading, error: tileServerLayersError, } = useTileServers({ - projectId: activeProject?.id, + projectId: activeProject.id, isPublic, - options: { - enabled: canFetchProjectFeaturesOrLayers, - }, }); const location = useLocation(); @@ -91,15 +130,19 @@ const MapProject: React.FC = ({ isPublic = false }) => { const queryParams = new URLSearchParams(location.search); const activePanel = queryParams.get(queryPanelKey); - /* TODO_REACT show error and improve spinner https://tacc-main.atlassian.net/browse/WG-260*/ - const error = activeProjectError || featuresError || tileServerLayersError; + const error = featuresError || tileServerLayersError; if (error) { + /* TODO https://tacc-main.atlassian.net/browse/WG-260 */ console.error(error); } - const loading = - isActiveProjectLoading || isFeaturesLoading || isTileServerLayersLoading; + const loading = isFeaturesLoading || isTileServerLayersLoading; + + const featureCollection = rawFeatureCollection ?? { + type: 'FeatureCollection', + features: [], + }; return (
@@ -113,7 +156,11 @@ const MapProject: React.FC = ({ isPublic = false }) => { {activePanel && activePanel !== Panel.Manage && (
{activePanel === Panel.Assets && ( - + )} {activePanel === Panel.Filters && ( = ({ isPublic = false }) => {
diff --git a/react/src/requests.ts b/react/src/requests.ts index ee03e1a4..11dc937a 100644 --- a/react/src/requests.ts +++ b/react/src/requests.ts @@ -189,3 +189,40 @@ export function useDelete({ return useMutation(deleteUtil, options); } + +type UseDeleteWithParams = { + endpoint: string | ((variables: Variables) => string); + options?: Omit< + UseMutationOptions, + 'mutationFn' + >; + apiService?: ApiService; +}; + +export function useDeleteWithParams({ + endpoint, + options = {}, + apiService = ApiService.Geoapi, +}: UseDeleteWithParams) { + const client = axios; + const state = store.getState(); + const configuration = useAppConfiguration(); + + useEnsureAuthenticatedUserHasValidTapisToken(); + + const baseUrl = getBaseApiUrl(apiService, configuration); + const headers = getHeaders(apiService, state.auth); + + const deleteUtil = async (variables: Variables) => { + const finalEndpoint = + typeof endpoint === 'function' ? endpoint(variables) : endpoint; + + const response = await client.delete( + `${baseUrl}${finalEndpoint}`, + { headers } + ); + return response.data; + }; + + return useMutation(deleteUtil, options); +} diff --git a/react/src/types/feature.test.ts b/react/src/types/feature.test.ts new file mode 100644 index 00000000..8d96b0d3 --- /dev/null +++ b/react/src/types/feature.test.ts @@ -0,0 +1,24 @@ +import { featureCollection } from '@hazmapper/__fixtures__/featuresFixture'; +import { getFeatureType } from '@hazmapper/types/feature'; + +describe('FeatureClass', () => { + describe('featureType', () => { + it('should return asset_type when there is exactly one asset', () => { + const feature = featureCollection.features[0]; + expect(getFeatureType(feature)).toBe('image'); + }); + + it('should return "collection" when there are multiple assets', () => { + const feature = featureCollection.features[0]; + const feature2 = featureCollection.features[1]; + feature.assets.push(feature2.assets[0]); + expect(getFeatureType(feature)).toBe('collection'); + }); + + it('should return geometry type when there are no assets', () => { + const feature = featureCollection.features[0]; + feature.assets = []; + expect(getFeatureType(feature)).toBe('Point'); + }); + }); +}); diff --git a/react/src/types/feature.ts b/react/src/types/feature.ts index 46533508..4915926d 100644 --- a/react/src/types/feature.ts +++ b/react/src/types/feature.ts @@ -1,5 +1,21 @@ import { GeoJsonProperties, Geometry } from 'geojson'; +// Define asset types from GeoApi +export type AssetType = + | 'image' + | 'video' + | 'questionnaire' + | 'point_cloud' + | 'streetview'; + +// Define all possible geometry types from GeoJSON (e.g. "Point", "MultiPoint", "Polygon" etc) +export type GeoJSONGeometryType = Geometry['type']; + +// Combined feature type that includes all possibilities for a feature +export type FeatureType = AssetType | GeoJSONGeometryType | 'collection'; + +export type FeatureTypeNullable = FeatureType | undefined; + /** * Asset of a feature */ @@ -7,10 +23,7 @@ export interface Asset { id: number; path: string; uuid: string; - /** - * The type of asset, such as "image", "video", "point_cloud", or "streetview". - */ - asset_type: string; + asset_type: AssetType; original_path: string; original_name: string | null; display_path: string; @@ -50,34 +63,19 @@ export interface Feature { assets: Asset[]; } -export class FeatureClass implements Feature { - constructor( - public id: number, - public project_id: number, - public type: string, - public geometry: Geometry, - public properties: any, - public styles: any, - public assets: Asset[] - ) {} - - /** - * Returns the type of the feature based on its assets and geometry. - * If there are no assets, returns the type of the feature's geometry. - * If there is only one asset, returns the type of that asset (i.e. "image", "video", "point_cloud", or "streetview".) - * If there are multiple assets, returns "collection". - * If there are no assets or geometry, returns "unknown". - * - * @returns The type of the feature as a string. - */ - featureType(): string { - if (this.assets.length === 1) { - return this.assets[0].asset_type; - } else if (this.assets.length > 1) { - return 'collection'; - } else { - return this.geometry.type; - } +/** + * Returns the type of the feature based on its assets and geometry. + */ +export function getFeatureType(feature: Feature): FeatureType { + if (feature.assets.length === 1) { + /* If there is only one asset, returns the type of that asset (i.e. "image", "video", "point_cloud", or "streetview".)*/ + return feature.assets[0].asset_type; + } else if (feature.assets.length > 1) { + /* multiple assets */ + return 'collection'; + } else { + /* else we rely on geojson geometry type */ + return feature.geometry.type; } } @@ -94,3 +92,14 @@ export interface FeatureCollection { */ features: Feature[]; } + +/** + * Features/file abstraction for feature file tree representation + */ +export interface FeatureFileNode { + nodeId: string /* feature id if feature; path if directory node */; + name: string; + isDirectory: boolean; + featureType: FeatureTypeNullable; + children?: FeatureFileNode[]; +} diff --git a/react/src/types/index.ts b/react/src/types/index.ts index 8182d704..46ebc71e 100644 --- a/react/src/types/index.ts +++ b/react/src/types/index.ts @@ -1,10 +1,5 @@ export type { TileServerLayer } from './tileServerLayer'; -export type { - Asset, - Feature, - FeatureClass, - FeatureCollection, -} from './feature'; +export * from './feature'; export type { Project, DesignSafeProjectCollection, diff --git a/react/src/utils/featureTreeUtils.test.ts b/react/src/utils/featureTreeUtils.test.ts new file mode 100644 index 00000000..3ed2b5a2 --- /dev/null +++ b/react/src/utils/featureTreeUtils.test.ts @@ -0,0 +1,37 @@ +import { featureCollectionToFileNodeArray } from './featureTreeUtils'; +import { featureCollectionWithNestedPaths } from '@hazmapper/__fixtures__/featuresFixture'; + +describe('featureTreeUtils', () => { + describe('featureCollectionToFileNodeArray', () => { + it('should convert a feature collection to a tree structure', () => { + const mockFeatureCollection = featureCollectionWithNestedPaths; + + const result = featureCollectionToFileNodeArray(mockFeatureCollection); + + expect(result).toHaveLength(1); // One root node (folder1) + expect(result[0].name).toBe('folder1'); + expect(result[0].isDirectory).toBeTruthy(); + + // folder1/ contents + expect(result[0].children).toHaveLength(2); // file1.jpg and subfolder + //file1.jpg + expect(result[0].children?.[0].name).toBe('file1.jpg'); + expect(result[0].children?.[0].featureType).toBe('image'); + expect(result[0].children?.[0].nodeId).toBe('1'); + expect(result[0].children?.[0].isDirectory).toBeFalsy(); + // subfolder + expect(result[0].children?.[1].name).toBe('subfolder'); + expect(result[0].children?.[1].isDirectory).toBeTruthy(); + + // folder1/subfolder/ contents + expect(result[0].children?.[1].children?.[0].isDirectory).toBeFalsy(); + expect(result[0].children?.[1].children?.[0].name).toBe('file2.rq'); + expect(result[0].children?.[1].children?.[0].featureType).toBe( + 'questionnaire' + ); + expect(result[0].children?.[1].children?.[0].nodeId).toBe('2'); + }); + + // Add more test cases as needed + }); +}); diff --git a/react/src/utils/featureTreeUtils.ts b/react/src/utils/featureTreeUtils.ts new file mode 100644 index 00000000..d58b4400 --- /dev/null +++ b/react/src/utils/featureTreeUtils.ts @@ -0,0 +1,88 @@ +import { + FeatureCollection, + Feature, + FeatureType, + FeatureFileNode, + getFeatureType, +} from '../types'; + +function createFeatureFileNode( + nodeId: string, + name: string, + isDirectory: boolean, + featureType: FeatureType | undefined, + children?: FeatureFileNode[] +): FeatureFileNode { + return { + nodeId, + name, + isDirectory, + featureType, + ...(children && { children }), + }; +} + +function getFullPathFromFeature(feature: Feature): string { + const firstAsset = feature.assets[0]; + if (firstAsset?.display_path) { + return firstAsset.display_path; + } + if (firstAsset) { + return firstAsset.id.toString(); + } + return feature.id.toString(); +} + +/** + * Convert a feature collection to a tree of file nodes. + * + * This returns an array of only top-level nodes (which contain + * a node for all the features + their parent directories) + */ +export function featureCollectionToFileNodeArray( + featureCollection: FeatureCollection +): FeatureFileNode[] { + const rootNodes: FeatureFileNode[] = []; + const nodeMap: { [key: string]: FeatureFileNode } = {}; + + // Sort features to ensure consistent order + const sortedFeatures = featureCollection.features.sort((a, b) => + getFullPathFromFeature(a).localeCompare(getFullPathFromFeature(b)) + ); + + sortedFeatures.forEach((feature) => { + const nodePath = getFullPathFromFeature(feature); + const parts = nodePath.split('/'); + + let currentPath = ''; + let currentNode: FeatureFileNode | null = null; + + parts.forEach((part, index) => { + currentPath += (currentPath ? '/' : '') + part; + const isLast = index === parts.length - 1; + + if (!nodeMap[currentPath]) { + const newNode = createFeatureFileNode( + isLast ? feature.id.toString() : `DIR_${currentPath}` /* nodeId */, + part /* name */, + !isLast /* isDirectory */, + isLast + ? getFeatureType(feature) /*featureType*/ + : undefined /* or if dir, then undefined */ + ); + nodeMap[currentPath] = newNode; + + if (currentNode) { + if (!currentNode.children) currentNode.children = []; + currentNode.children.push(newNode); + } else { + rootNodes.push(newNode); + } + } + + currentNode = nodeMap[currentPath]; + }); + }); + + return rootNodes; +}