From 781018f731a3d973e5fb3496f4f6d0ebd3d4c433 Mon Sep 17 00:00:00 2001 From: Ben Hammond Date: Fri, 4 Oct 2024 11:06:36 -0600 Subject: [PATCH] Frontend: Fixes screenshot issues (#3700) --- cspell.config.json | 2 + frontend/package-lock.json | 554 ++++++------------ frontend/package.json | 4 +- frontend/src/cards/CardWrapper.tsx | 18 +- frontend/src/cards/DisparityBarChartCard.tsx | 6 - frontend/src/cards/MapCard.tsx | 9 +- frontend/src/cards/RateTrendsChartCard.tsx | 6 - frontend/src/cards/ShareTrendsChartCard.tsx | 6 - frontend/src/cards/SimpleBarChartCard.tsx | 6 - frontend/src/cards/TableCard.tsx | 6 - frontend/src/cards/UnknownsMapCard.tsx | 6 - frontend/src/cards/ui/AltTableView.tsx | 16 +- frontend/src/cards/ui/CardOptionsMenu.tsx | 10 +- .../src/cards/ui/DownloadCardImageButton.tsx | 6 +- .../src/cards/ui/DownloadCardImageHelpers.ts | 122 ++++ frontend/src/cards/ui/ExtremesListBox.tsx | 2 +- frontend/src/cards/ui/MultiMapDialog.tsx | 170 +++--- frontend/src/cards/ui/Sources.tsx | 1 - .../src/charts/trendsChart/FilterLegend.tsx | 6 +- frontend/src/charts/trendsChart/LineChart.tsx | 30 +- frontend/src/charts/trendsChart/helpers.ts | 28 +- frontend/src/pages/ui/SimpleSelect.tsx | 10 +- frontend/src/reports/ReportProviderSteps.ts | 4 + .../src/utils/hooks/useDownloadCardImage.tsx | 225 ------- frontend/src/utils/hooks/useStepObserver.ts | 1 + frontend/src/vite-env.d.ts | 3 - 26 files changed, 462 insertions(+), 795 deletions(-) create mode 100644 frontend/src/cards/ui/DownloadCardImageHelpers.ts delete mode 100644 frontend/src/utils/hooks/useDownloadCardImage.tsx diff --git a/cspell.config.json b/cspell.config.json index c576f3a037..c9b2fa87a6 100644 --- a/cspell.config.json +++ b/cspell.config.json @@ -100,6 +100,7 @@ "Belton", "Berhanu", "Bex", + "bgcolor", "biomejs", "bipoc", "BIPOC", @@ -184,6 +185,7 @@ "doi2", "doi3", "domcontentloaded", + "domtoimage", "Donrie", "dont", "DOTENV", diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 58d16e112d..814f45d6d0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -22,8 +22,8 @@ "biome": "^0.2.2", "d3": "^7.8.5", "data-forge": "^1.10.2", + "dom-to-image-more": "^3.4.5", "env-cmd": "^10.1.0", - "html2canvas": "^1.4.1", "jotai": "^2.10.0", "jotai-location": "^0.5.5", "just-debounce-it": "^3.2.0", @@ -42,7 +42,6 @@ "react-table": "^7.8.0", "react-vega": "^7.6.0", "seamless-scroll-polyfill": "^2.3.4", - "use-react-screenshot": "^4.0.0", "vega": "^5.30.0", "vega-lite": "^5.21.0", "vite": "^5.4.8", @@ -55,6 +54,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^16.0.1", "@types/d3": "^7.4.0", + "@types/dom-to-image": "^2.6.7", "@types/node": "^22.7.4", "@types/react": "^18.3.10", "@types/react-dom": "^18.3.0", @@ -688,9 +688,9 @@ "dev": true }, "node_modules/@cspell/dict-bash": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.4.tgz", - "integrity": "sha512-W/AHoQcJYn3Vn/tUiXX2+6D/bhfzdDshwcbQWv9TdiNlXP9P6UJjDKWbxyA5ogJCsR2D0X9Kx11oV8E58siGKQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.5.tgz", + "integrity": "sha512-YGim/h7E2U5HCCb2ckNufT6/yyWygt9nSZ5C7qw6oOD3bygbObqD1+rlPor1JW+YyO+3GwTIHE70uKEEU6VZYw==", "dev": true }, "node_modules/@cspell/dict-companies": { @@ -700,9 +700,9 @@ "dev": true }, "node_modules/@cspell/dict-cpp": { - "version": "5.1.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.1.16.tgz", - "integrity": "sha512-32fU5RkuOM55IRcxjByiSoKbjr+C4danDfYjHaQNRWdvjzJzci3fLDGA2wTXiclkgDODxGiV8LCTUwCz+3TNWA==", + "version": "5.1.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.1.17.tgz", + "integrity": "sha512-b+1SOTf0WkveOobmELlOzRXKtzBQIw87Vjm2UMwJ3GTo6Fnpws/NKnS4ZsBJWgzHU0RrqMC+wIW7yW+km/JOTQ==", "dev": true }, "node_modules/@cspell/dict-cryptocurrencies": { @@ -943,9 +943,9 @@ "dev": true }, "node_modules/@cspell/dict-ruby": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.3.tgz", - "integrity": "sha512-V1xzv9hN6u8r6SM4CkYdsxs4ov8gjXXo0Twfx5kWhLXbEVxTXDMt7ohLTqpy2XlF5mutixZdbHMeFiAww8v+Ug==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.4.tgz", + "integrity": "sha512-URw0jScj5pv8sKCVLNnde11qVCQR442rUpSd12u46Swl+5qBaSdnOUoCWQk419kd9/dpC6bB/3l4kOSY2fdYHw==", "dev": true }, "node_modules/@cspell/dict-rust": { @@ -961,9 +961,9 @@ "dev": true }, "node_modules/@cspell/dict-software-terms": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.1.4.tgz", - "integrity": "sha512-AHS25sYEzWze/aFglp9ODKSu+phjkuGx+OLwIcmOnvyn8axtSq5GCn9UqS4XG1/Qn0UG2Lgb4i5PJbZ0QNPNXQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.1.5.tgz", + "integrity": "sha512-txvSONYs7oT9z7qLVunSZUz9R4PRivGNyZvhHC8ysbWdWeCXo9q4R2kasuZE/YMpiPkfDLMfmd2huz4TRFercQ==", "dev": true }, "node_modules/@cspell/dict-sql": { @@ -1082,9 +1082,9 @@ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" }, "node_modules/@emotion/is-prop-valid": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.0.tgz", - "integrity": "sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", "dependencies": { "@emotion/memoize": "^0.9.0" } @@ -1118,14 +1118,14 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.1.tgz", - "integrity": "sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz", + "integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==", "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.0", + "@emotion/utils": "^1.4.1", "csstype": "^3.0.2" } }, @@ -1170,9 +1170,9 @@ } }, "node_modules/@emotion/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz", + "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==" }, "node_modules/@emotion/weak-memoize": { "version": "0.4.0", @@ -1952,9 +1952,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.16", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.16.tgz", - "integrity": "sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==", + "version": "7.2.17", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.17.tgz", + "integrity": "sha512-oyumoJgB6jDV8JFzRqjBo2daUuHpzDjoO/e3IrRhhHo/FxJlaVhET6mcNrKHUq2E+R+q3ql0qAtvQ4rfWHhAeQ==", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -2071,9 +2071,9 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", + "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", @@ -2092,9 +2092,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", - "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.23.0.tgz", + "integrity": "sha512-8OR+Ok3SGEMsAZispLx8jruuXw0HVF16k+ub2eNXKHDmdxL4cf9NlNpAzhlOhNyXzKDEJuFeq0nZm+XlNb1IFw==", "cpu": [ "arm" ], @@ -2104,9 +2104,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", - "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.23.0.tgz", + "integrity": "sha512-rEFtX1nP8gqmLmPZsXRMoLVNB5JBwOzIAk/XAcEPuKrPa2nPJ+DuGGpfQUR0XjRm8KjHfTZLpWbKXkA5BoFL3w==", "cpu": [ "arm64" ], @@ -2116,9 +2116,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", - "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.23.0.tgz", + "integrity": "sha512-ZbqlMkJRMMPeapfaU4drYHns7Q5MIxjM/QeOO62qQZGPh9XWziap+NF9fsqPHT0KzEL6HaPspC7sOwpgyA3J9g==", "cpu": [ "arm64" ], @@ -2128,9 +2128,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", - "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.23.0.tgz", + "integrity": "sha512-PfmgQp78xx5rBCgn2oYPQ1rQTtOaQCna0kRaBlc5w7RlA3TDGGo7m3XaptgitUZ54US9915i7KeVPHoy3/W8tA==", "cpu": [ "x64" ], @@ -2140,9 +2140,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", - "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.23.0.tgz", + "integrity": "sha512-WAeZfAAPus56eQgBioezXRRzArAjWJGjNo/M+BHZygUcs9EePIuGI1Wfc6U/Ki+tMW17FFGvhCfYnfcKPh18SA==", "cpu": [ "arm" ], @@ -2152,9 +2152,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", - "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.23.0.tgz", + "integrity": "sha512-v7PGcp1O5XKZxKX8phTXtmJDVpE20Ub1eF6w9iMmI3qrrPak6yR9/5eeq7ziLMrMTjppkkskXyxnmm00HdtXjA==", "cpu": [ "arm" ], @@ -2164,9 +2164,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", - "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.23.0.tgz", + "integrity": "sha512-nAbWsDZ9UkU6xQiXEyXBNHAKbzSAi95H3gTStJq9UGiS1v+YVXwRHcQOQEF/3CHuhX5BVhShKoeOf6Q/1M+Zhg==", "cpu": [ "arm64" ], @@ -2176,9 +2176,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", - "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.23.0.tgz", + "integrity": "sha512-5QT/Di5FbGNPaVw8hHO1wETunwkPuZBIu6W+5GNArlKHD9fkMHy7vS8zGHJk38oObXfWdsuLMogD4sBySLJ54g==", "cpu": [ "arm64" ], @@ -2188,9 +2188,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", - "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.23.0.tgz", + "integrity": "sha512-Sefl6vPyn5axzCsO13r1sHLcmPuiSOrKIImnq34CBurntcJ+lkQgAaTt/9JkgGmaZJ+OkaHmAJl4Bfd0DmdtOQ==", "cpu": [ "ppc64" ], @@ -2200,9 +2200,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", - "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.23.0.tgz", + "integrity": "sha512-o4QI2KU/QbP7ZExMse6ULotdV3oJUYMrdx3rBZCgUF3ur3gJPfe8Fuasn6tia16c5kZBBw0aTmaUygad6VB/hQ==", "cpu": [ "riscv64" ], @@ -2212,9 +2212,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", - "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.23.0.tgz", + "integrity": "sha512-+bxqx+V/D4FGrpXzPGKp/SEZIZ8cIW3K7wOtcJAoCrmXvzRtmdUhYNbgd+RztLzfDEfA2WtKj5F4tcbNPuqgeg==", "cpu": [ "s390x" ], @@ -2224,9 +2224,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", - "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.23.0.tgz", + "integrity": "sha512-I/eXsdVoCKtSgK9OwyQKPAfricWKUMNCwJKtatRYMmDo5N859tbO3UsBw5kT3dU1n6ZcM1JDzPRSGhAUkxfLxw==", "cpu": [ "x64" ], @@ -2236,9 +2236,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", - "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.23.0.tgz", + "integrity": "sha512-4ZoDZy5ShLbbe1KPSafbFh1vbl0asTVfkABC7eWqIs01+66ncM82YJxV2VtV3YVJTqq2P8HMx3DCoRSWB/N3rw==", "cpu": [ "x64" ], @@ -2248,9 +2248,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", - "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.23.0.tgz", + "integrity": "sha512-+5Ky8dhft4STaOEbZu3/NU4QIyYssKO+r1cD3FzuusA0vO5gso15on7qGzKdNXnc1gOrsgCqZjRw1w+zL4y4hQ==", "cpu": [ "arm64" ], @@ -2260,9 +2260,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", - "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.23.0.tgz", + "integrity": "sha512-0SPJk4cPZQhq9qA1UhIRumSE3+JJIBBjtlGl5PNC///BoaByckNZd53rOYD0glpTkYFBQSt7AkMeLVPfx65+BQ==", "cpu": [ "ia32" ], @@ -2272,9 +2272,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", - "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.23.0.tgz", + "integrity": "sha512-lqCK5GQC8fNo0+JvTSxcG7YB1UKYp8yrNLhsArlvPWN+16ovSZgoehlVHg6X0sSWPUkpjRBR5TuR12ZugowZ4g==", "cpu": [ "x64" ], @@ -3024,10 +3024,16 @@ "@types/d3-selection": "*" } }, + "node_modules/@types/dom-to-image": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@types/dom-to-image/-/dom-to-image-2.6.7.tgz", + "integrity": "sha512-me5VbCv+fcXozblWwG13krNBvuEOm6kA5xoa4RrjDJCNFOZSWR3/QLtOXimBHk1Fisq69Gx3JtOoXtg1N1tijg==", + "dev": true + }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "node_modules/@types/geojson": { "version": "7946.0.14", @@ -3066,9 +3072,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.12", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", - "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "version": "29.5.13", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", + "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -3116,9 +3122,9 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/react": { "version": "18.3.10", @@ -3382,11 +3388,11 @@ } }, "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/ansi-styles": { @@ -3523,14 +3529,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -3568,6 +3566,14 @@ "biome": "dist/index.js" } }, + "node_modules/biome/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/biome/node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", @@ -3675,9 +3681,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "funding": [ { "type": "opencollective", @@ -3693,8 +3699,8 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, @@ -3743,9 +3749,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001660", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", - "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", + "version": "1.0.30001664", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz", + "integrity": "sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==", "funding": [ { "type": "opencollective", @@ -3910,35 +3916,6 @@ "node": ">=12" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -4295,14 +4272,6 @@ "node": ">=10" } }, - "node_modules/css-line-break": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", - "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", - "dependencies": { - "utrie": "^1.0.2" - } - }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -4909,6 +4878,11 @@ "csstype": "^3.0.2" } }, + "node_modules/dom-to-image-more": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/dom-to-image-more/-/dom-to-image-more-3.4.5.tgz", + "integrity": "sha512-xLkG5idpEPgfuf5bZ82kbNnXq4U4PAB4wd7Zun7cEHrYvjolMtTQdIlheQURS8jLy07TGZeWqoTqGW6onZr0Tw==" + }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -4938,9 +4912,9 @@ "integrity": "sha512-SoRmbGStwNYHgKfjOrX2L0mUvp9bUVv0uPppZSOMAntEbcFtoC3MKF5b3T6HQPXKIV+QGY3xPO3JK5it5lVkuw==" }, "node_modules/electron-to-chromium": { - "version": "1.5.20", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.20.tgz", - "integrity": "sha512-74mdl6Fs1HHzK9SUX4CKFxAtAe3nUns48y79TskHNAG6fGOlLfyKA4j855x+0b5u8rWJIrlaG9tcTPstMlwjIw==" + "version": "1.5.30", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.30.tgz", + "integrity": "sha512-sXI35EBN4lYxzc/pIGorlymYNzDBOqkSlVRe6MkgBsW/hW1tpC/HDJ2fjG7XnjakzfLEuvdmux0Mjs6jHq4UOA==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -5416,6 +5390,14 @@ "node": ">=0.10.0" } }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -5469,18 +5451,6 @@ "node": ">=18" } }, - "node_modules/html2canvas": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", - "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", - "dependencies": { - "css-line-break": "^2.1.0", - "text-segmentation": "^1.0.3" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -5666,6 +5636,14 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -6636,9 +6614,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.12", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz", - "integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==", + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", + "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==", "dev": true }, "node_modules/object-assign": { @@ -6697,9 +6675,9 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, "node_modules/papaparse": { @@ -7068,16 +7046,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/pretty-format/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -7331,7 +7299,6 @@ "version": "6.26.2", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", - "license": "MIT", "dependencies": { "@remix-run/router": "1.19.2", "react-router": "6.26.2" @@ -7538,11 +7505,11 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, "node_modules/rollup": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", - "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.23.0.tgz", + "integrity": "sha512-vXB4IT9/KLDrS2WRXmY22sVB2wTsTwkpxjB8Q3mnakTENcYw3FRmfdYDy/acNmls+lHmDazgrRjK/yQ6hQAtwA==", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -7552,22 +7519,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.22.4", - "@rollup/rollup-android-arm64": "4.22.4", - "@rollup/rollup-darwin-arm64": "4.22.4", - "@rollup/rollup-darwin-x64": "4.22.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", - "@rollup/rollup-linux-arm-musleabihf": "4.22.4", - "@rollup/rollup-linux-arm64-gnu": "4.22.4", - "@rollup/rollup-linux-arm64-musl": "4.22.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", - "@rollup/rollup-linux-riscv64-gnu": "4.22.4", - "@rollup/rollup-linux-s390x-gnu": "4.22.4", - "@rollup/rollup-linux-x64-gnu": "4.22.4", - "@rollup/rollup-linux-x64-musl": "4.22.4", - "@rollup/rollup-win32-arm64-msvc": "4.22.4", - "@rollup/rollup-win32-ia32-msvc": "4.22.4", - "@rollup/rollup-win32-x64-msvc": "4.22.4", + "@rollup/rollup-android-arm-eabi": "4.23.0", + "@rollup/rollup-android-arm64": "4.23.0", + "@rollup/rollup-darwin-arm64": "4.23.0", + "@rollup/rollup-darwin-x64": "4.23.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.23.0", + "@rollup/rollup-linux-arm-musleabihf": "4.23.0", + "@rollup/rollup-linux-arm64-gnu": "4.23.0", + "@rollup/rollup-linux-arm64-musl": "4.23.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.23.0", + "@rollup/rollup-linux-riscv64-gnu": "4.23.0", + "@rollup/rollup-linux-s390x-gnu": "4.23.0", + "@rollup/rollup-linux-x64-gnu": "4.23.0", + "@rollup/rollup-linux-x64-musl": "4.23.0", + "@rollup/rollup-win32-arm64-msvc": "4.23.0", + "@rollup/rollup-win32-ia32-msvc": "4.23.0", + "@rollup/rollup-win32-x64-msvc": "4.23.0", "fsevents": "~2.3.2" } }, @@ -7797,12 +7764,10 @@ "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", "dev": true }, - "node_modules/string-width-cjs": { - "name": "string-width", + "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7812,29 +7777,37 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { + "node_modules/string-width/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7870,15 +7843,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi/node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -8065,14 +8029,6 @@ "node": ">=10.13.0" } }, - "node_modules/text-segmentation": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", - "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", - "dependencies": { - "utrie": "^1.0.2" - } - }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -8132,21 +8088,21 @@ } }, "node_modules/tldts": { - "version": "6.1.47", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.47.tgz", - "integrity": "sha512-R/K2tZ5MiY+mVrnSkNJkwqYT2vUv1lcT6wJvd2emGaMJ7PHUGRY4e3tUsdFCXgqxi2QgbHjL3yJgXCo40v9Hxw==", + "version": "6.1.48", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.48.tgz", + "integrity": "sha512-SPbnh1zaSzi/OsmHb1vrPNnYuwJbdWjwo5TbBYYMlTtH3/1DSb41t8bcSxkwDmmbG2q6VLPVvQc7Yf23T+1EEw==", "dev": true, "dependencies": { - "tldts-core": "^6.1.47" + "tldts-core": "^6.1.48" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.47", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.47.tgz", - "integrity": "sha512-6SWyFMnlst1fEt7GQVAAu16EGgFK0cLouH/2Mk6Ftlwhv3Ol40L0dlpGMcnnNiiOMyD2EV/aF3S+U2nKvvLvrA==", + "version": "6.1.48", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.48.tgz", + "integrity": "sha512-3gD9iKn/n2UuFH1uilBviK9gvTNT6iYwdqrj1Vr5mh8FuelvpRNaYVH4pNYqUgOGU4aAdL9X35eLuuj0gRsx+A==", "dev": true }, "node_modules/to-fast-properties": { @@ -8298,9 +8254,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "funding": [ { "type": "opencollective", @@ -8316,8 +8272,8 @@ } ], "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -8326,19 +8282,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/use-react-screenshot": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/use-react-screenshot/-/use-react-screenshot-4.0.0.tgz", - "integrity": "sha512-4UZIORp7iCklfNOS/dPJab9SPeGdS0nFyIi3qA1rfMyYf/em/KfodYhrOlSHAHWvfdeCrS67Jjk6H4M4oLYSWg==", - "engines": { - "node": ">=8", - "npm": ">=5" - }, - "peerDependencies": { - "html2canvas": "^1.3.3", - "react": "^18.2.0" - } - }, "node_modules/user-home": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", @@ -8356,14 +8299,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/utrie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", - "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", - "dependencies": { - "base64-arraybuffer": "^1.0.2" - } - }, "node_modules/vega": { "version": "5.30.0", "resolved": "https://registry.npmjs.org/vega/-/vega-5.30.0.tgz", @@ -9127,15 +9062,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -9169,29 +9095,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -9204,14 +9107,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -9242,27 +9137,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -9372,46 +9246,6 @@ "engines": { "node": ">=12" } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } } } } diff --git a/frontend/package.json b/frontend/package.json index aaecd5fcd9..a5885d860d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,8 +25,8 @@ "biome": "^0.2.2", "d3": "^7.8.5", "data-forge": "^1.10.2", + "dom-to-image-more": "^3.4.5", "env-cmd": "^10.1.0", - "html2canvas": "^1.4.1", "jotai": "^2.10.0", "jotai-location": "^0.5.5", "just-debounce-it": "^3.2.0", @@ -45,7 +45,6 @@ "react-table": "^7.8.0", "react-vega": "^7.6.0", "seamless-scroll-polyfill": "^2.3.4", - "use-react-screenshot": "^4.0.0", "vega": "^5.30.0", "vega-lite": "^5.21.0", "vite": "^5.4.8", @@ -58,6 +57,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^16.0.1", "@types/d3": "^7.4.0", + "@types/dom-to-image": "^2.6.7", "@types/node": "^22.7.4", "@types/react": "^18.3.10", "@types/react-dom": "^18.3.0", diff --git a/frontend/src/cards/CardWrapper.tsx b/frontend/src/cards/CardWrapper.tsx index 36a4627981..2d2ce761bc 100644 --- a/frontend/src/cards/CardWrapper.tsx +++ b/frontend/src/cards/CardWrapper.tsx @@ -7,11 +7,8 @@ import { WithMetadataAndMetrics } from '../data/react/WithLoadingOrErrorUI' import { Sources } from './ui/Sources' import type { MapOfDatasetMetadata } from '../data/utils/DatasetTypes' import type { ScrollableHashId } from '../utils/hooks/useStepObserver' -import { - type ElementHashIdHiddenOnScreenshot, - useDownloadCardImage, -} from '../utils/hooks/useDownloadCardImage' import CardOptionsMenu from './ui/CardOptionsMenu' +import { saveCardImage } from './ui/DownloadCardImageHelpers' function CardWrapper(props: { // prevent layout shift as component loads @@ -30,19 +27,11 @@ function CardWrapper(props: { isCensusNotAcs?: boolean scrollToHash: ScrollableHashId reportTitle: string - elementsToHide?: ElementHashIdHiddenOnScreenshot[] expanded?: boolean isCompareCard?: boolean className?: string hasIntersectionalAllCompareBar?: boolean }) { - const [screenshotTargetRef, downloadTargetScreenshot] = useDownloadCardImage( - props.downloadTitle, - props.elementsToHide, - props.scrollToHash, - props.expanded, - ) - const loadingComponent = (
+ saveCardImage(props.scrollToHash, props.downloadTitle) + } reportTitle={props.reportTitle} scrollToHash={props.scrollToHash} /> diff --git a/frontend/src/cards/DisparityBarChartCard.tsx b/frontend/src/cards/DisparityBarChartCard.tsx index 3151a4a46a..56b28afece 100644 --- a/frontend/src/cards/DisparityBarChartCard.tsx +++ b/frontend/src/cards/DisparityBarChartCard.tsx @@ -29,7 +29,6 @@ import type { ScrollableHashId } from '../utils/hooks/useStepObserver' import CAWPOverlappingRacesAlert from './ui/CAWPOverlappingRacesAlert' import ChartTitle from './ChartTitle' import { generateChartTitle, generateSubtitle } from '../charts/utils' -import type { ElementHashIdHiddenOnScreenshot } from '../utils/hooks/useDownloadCardImage' import HetNotice from '../styles/HetComponents/HetNotice' import { ALL_AHR_METRICS } from '../data/providers/AhrProvider' import { getMetricIdToConfigMap } from '../data/config/MetricConfigUtils' @@ -96,10 +95,6 @@ function DisparityBarChartCardWithKey(props: DisparityBarChartCardProps) { const HASH_ID: ScrollableHashId = 'population-vs-distribution' - const elementsToHide: ElementHashIdHiddenOnScreenshot[] = [ - '#card-options-menu', - ] - return ( {([queryResponse]) => { diff --git a/frontend/src/cards/MapCard.tsx b/frontend/src/cards/MapCard.tsx index 8c11150cce..0f1bd64375 100644 --- a/frontend/src/cards/MapCard.tsx +++ b/frontend/src/cards/MapCard.tsx @@ -65,7 +65,6 @@ import ChartTitle from './ChartTitle' import { useParamState } from '../utils/hooks/useParamState' import { POPULATION, SVI } from '../data/providers/GeoContextProvider' import { type CountColsMap, RATE_MAP_SCALE } from '../charts/mapGlobals' -import type { ElementHashIdHiddenOnScreenshot } from '../utils/hooks/useDownloadCardImage' import { PHRMA_METRICS } from '../data/providers/PhrmaProvider' import type { MadLibId } from '../utils/MadLibs' import { useIsBreakpointAndUp } from '../utils/hooks/useIsBreakpointAndUp' @@ -79,11 +78,6 @@ import { getSortArgs } from '../data/sorting/sortingUtils' const SIZE_OF_HIGHEST_LOWEST_GEOS_RATES_LIST = 5 const HASH_ID: ScrollableHashId = 'rate-map' -const elementsToHide: ElementHashIdHiddenOnScreenshot[] = [ - '#map-group-dropdown', - '#download-card-image-button', - '#card-options-menu', -] interface MapCardProps { className?: string @@ -277,7 +271,6 @@ function MapCardWithKey(props: MapCardProps) { minHeight={preloadHeight} scrollToHash={HASH_ID} reportTitle={props.reportTitle} - elementsToHide={elementsToHide} expanded={extremesMode} isCompareCard={props.isCompareCard} className={props.className} @@ -488,7 +481,7 @@ function MapCardWithKey(props: MapCardProps) { /> {!mapQueryResponse.dataIsMissing() && !hideGroupDropdown && ( -
+
diff --git a/frontend/src/cards/ShareTrendsChartCard.tsx b/frontend/src/cards/ShareTrendsChartCard.tsx index 1937155365..d0046239e6 100644 --- a/frontend/src/cards/ShareTrendsChartCard.tsx +++ b/frontend/src/cards/ShareTrendsChartCard.tsx @@ -35,7 +35,6 @@ import { generateChartTitle, generateSubtitle } from '../charts/utils' import { HIV_METRICS } from '../data/providers/HivProvider' import Hiv2020Alert from './ui/Hiv2020Alert' import ChartTitle from './ChartTitle' -import type { ElementHashIdHiddenOnScreenshot } from '../utils/hooks/useDownloadCardImage' import HetNotice from '../styles/HetComponents/HetNotice' /* minimize layout shift */ @@ -112,10 +111,6 @@ export default function ShareTrendsChartCard(props: ShareTrendsChartCardProps) { if (!inequityQuery || !metricConfigInequitable?.metricId) return <> - const elementsToHide: ElementHashIdHiddenOnScreenshot[] = [ - '#card-options-menu', - ] - const queries = [inequityQuery] pctShareQuery && queries.push(pctShareQuery) @@ -126,7 +121,6 @@ export default function ShareTrendsChartCard(props: ShareTrendsChartCardProps) { minHeight={PRELOAD_HEIGHT} scrollToHash={HASH_ID} reportTitle={props.reportTitle} - elementsToHide={elementsToHide} expanded={a11yTableExpanded} className={props.className} > diff --git a/frontend/src/cards/SimpleBarChartCard.tsx b/frontend/src/cards/SimpleBarChartCard.tsx index 8e0fe30250..f0228752d1 100644 --- a/frontend/src/cards/SimpleBarChartCard.tsx +++ b/frontend/src/cards/SimpleBarChartCard.tsx @@ -27,7 +27,6 @@ import { DATATYPES_NEEDING_13PLUS, GENDER_METRICS, } from '../data/providers/HivProvider' -import type { ElementHashIdHiddenOnScreenshot } from '../utils/hooks/useDownloadCardImage' import { GUN_VIOLENCE_DATATYPES } from '../data/providers/GunViolenceProvider' import LawEnforcementAlert from './ui/LawEnforcementAlert' import { isPctType } from '../data/config/MetricConfigUtils' @@ -113,10 +112,6 @@ function SimpleBarChartCardWithKey(props: SimpleBarChartCardProps) { const HASH_ID: ScrollableHashId = 'rate-chart' - const elementsToHide: ElementHashIdHiddenOnScreenshot[] = [ - '#card-options-menu', - ] - const rateComparisonConfig = rateConfig?.rateComparisonMetricForAlls if (rateComparisonConfig) { @@ -143,7 +138,6 @@ function SimpleBarChartCardWithKey(props: SimpleBarChartCardProps) { minHeight={PRELOAD_HEIGHT} scrollToHash={HASH_ID} reportTitle={props.reportTitle} - elementsToHide={elementsToHide} className={props.className} hasIntersectionalAllCompareBar={rateComparisonConfig !== undefined} > diff --git a/frontend/src/cards/TableCard.tsx b/frontend/src/cards/TableCard.tsx index 671bfa91a6..cc2cec547a 100644 --- a/frontend/src/cards/TableCard.tsx +++ b/frontend/src/cards/TableCard.tsx @@ -25,7 +25,6 @@ import { GENDER_METRICS, } from '../data/providers/HivProvider' import GenderDataShortAlert from './ui/GenderDataShortAlert' -import type { ElementHashIdHiddenOnScreenshot } from '../utils/hooks/useDownloadCardImage' import type { CountColsMap } from '../charts/mapGlobals' import HetNotice from '../styles/HetComponents/HetNotice' import { generateSubtitle } from '../charts/utils' @@ -105,10 +104,6 @@ export default function TableCard(props: TableCardProps) { const HASH_ID: ScrollableHashId = 'data-table' - const elementsToHide: ElementHashIdHiddenOnScreenshot[] = [ - '#card-options-menu', - ] - const subtitle = generateSubtitle( ALL, props.demographicType, @@ -124,7 +119,6 @@ export default function TableCard(props: TableCardProps) { queries={[query]} scrollToHash={HASH_ID} reportTitle={props.reportTitle} - elementsToHide={elementsToHide} className={props.className} > {([queryResponse]) => { diff --git a/frontend/src/cards/UnknownsMapCard.tsx b/frontend/src/cards/UnknownsMapCard.tsx index ba899f50af..ae1b09d5d2 100644 --- a/frontend/src/cards/UnknownsMapCard.tsx +++ b/frontend/src/cards/UnknownsMapCard.tsx @@ -24,7 +24,6 @@ import type { ScrollableHashId } from '../utils/hooks/useStepObserver' import TerritoryCircles from './ui/TerritoryCircles' import ChartTitle from './ChartTitle' import { generateChartTitle, generateSubtitle } from '../charts/utils' -import type { ElementHashIdHiddenOnScreenshot } from '../utils/hooks/useDownloadCardImage' import { unknownMapConfig } from '../charts/mapGlobals' import { useIsBreakpointAndUp } from '../utils/hooks/useIsBreakpointAndUp' import HetNotice from '../styles/HetComponents/HetNotice' @@ -113,10 +112,6 @@ function UnknownsMapCardWithKey(props: UnknownsMapCardProps) { const HASH_ID: ScrollableHashId = 'unknown-demographic-map' - const elementsToHide: ElementHashIdHiddenOnScreenshot[] = [ - '#card-options-menu', - ] - return ( {([mapQueryResponse, alertQueryResponse], metadata, geoData) => { // MOST of the items rendered in the card refer to the unknowns at the CHILD geo level, diff --git a/frontend/src/cards/ui/AltTableView.tsx b/frontend/src/cards/ui/AltTableView.tsx index 3ff295b96d..6341eb9b20 100644 --- a/frontend/src/cards/ui/AltTableView.tsx +++ b/frontend/src/cards/ui/AltTableView.tsx @@ -65,17 +65,21 @@ export default function AltTableView(props: AltTableViewProps) { const earliestTimePeriod: string = accessibleData[accessibleData.length - 1][TIME_PERIOD_LABEL] + const safeLabel = props.tableCaption.replaceAll(' ', '-') + + const uniqueId = `${safeLabel}-${ + props.isCompareCard + ? ALT_TABLE_VIEW_2_PARAM_KEY + : ALT_TABLE_VIEW_1_PARAM_KEY + }` + return ( window.dispatchEvent(new Event('resize'))} - className='mt-4 mx-2 rounded-md bg-listboxColor text-left' - id={ - props.isCompareCard - ? ALT_TABLE_VIEW_2_PARAM_KEY - : ALT_TABLE_VIEW_1_PARAM_KEY - } + className='mt-4 mx-2 rounded-md bg-listboxColor text-left hide-on-screenshot' + id={uniqueId} > - +
+ diff --git a/frontend/src/cards/ui/DownloadCardImageButton.tsx b/frontend/src/cards/ui/DownloadCardImageButton.tsx index d55ea43892..2d7e8d79c8 100644 --- a/frontend/src/cards/ui/DownloadCardImageButton.tsx +++ b/frontend/src/cards/ui/DownloadCardImageButton.tsx @@ -23,11 +23,7 @@ export function DownloadCardImageButton(props: DownloadCardImageButtonProps) { return ( <> - + {!props.isMulti && ( diff --git a/frontend/src/cards/ui/DownloadCardImageHelpers.ts b/frontend/src/cards/ui/DownloadCardImageHelpers.ts new file mode 100644 index 0000000000..74686480f2 --- /dev/null +++ b/frontend/src/cards/ui/DownloadCardImageHelpers.ts @@ -0,0 +1,122 @@ +import type { ScrollableHashId } from '../../utils/hooks/useStepObserver' +import domtoimage from 'dom-to-image-more' +import { CITATION_APA } from './SourcesHelpers' + +const SCALE_FACTOR = 3 +const UNSAFE_CHAR_REGEX = /[^a-zA-Z0-9_.\-\s]+/g + +interface DomToImageOptions { + scale: number + filter: (node: HTMLElement) => boolean + width?: number + height?: number +} + +export async function saveCardImage( + cardId: ScrollableHashId, + cardTitle: string, +): Promise { + const parentCardNode = document.getElementById(cardId) as HTMLElement + const articleChild = parentCardNode?.querySelector( + 'article', + ) as HTMLElement | null + const targetNode = articleChild || parentCardNode + articleChild?.classList.remove('shadow-raised') + + let heightToCrop = 0 + const removeHeightOnScreenshotElements: NodeListOf = + targetNode.querySelectorAll('.remove-height-on-screenshot') + + if (removeHeightOnScreenshotElements) { + removeHeightOnScreenshotElements.forEach((element) => { + heightToCrop += getTotalElementHeight(element) + }) + } + + const footer = targetNode?.querySelector('footer') + let addedDivider: HTMLDivElement | null = null + let addedParagraph: HTMLParagraphElement | null = null + + if (footer && targetNode) { + if (cardId !== 'multimap-modal') { + addedDivider = document.createElement('div') + addedDivider.classList.add( + 'w-full', + 'border-b', + 'border-solid', + 'border-dividerGrey', + ) + addedDivider.style.height = '0px' + footer.parentNode?.insertBefore(addedDivider, footer) + } + + addedParagraph = document.createElement('p') + addedParagraph.innerHTML = CITATION_APA + footer?.appendChild(addedParagraph) + + heightToCrop -= getTotalElementHeight(addedParagraph) + heightToCrop -= getTotalElementHeight(addedDivider) + } + + try { + const options: DomToImageOptions = { + scale: SCALE_FACTOR, + filter: hideElementsForScreenshot, + width: targetNode?.offsetWidth, + height: targetNode?.offsetHeight - heightToCrop, + } + + const dataUrl = await domtoimage.toPng(targetNode, options) + const fileName = createFileName(cardTitle) + + const link = document.createElement('a') + link.download = fileName + link.href = dataUrl + link.click() + return true + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`Screenshot failed: ${error.message}`) + } else { + console.error('Screenshot failed with unknown error') + } + return false + } finally { + cleanup([addedDivider, addedParagraph], articleChild) + } +} + +function hideElementsForScreenshot(node: HTMLElement): boolean { + return !node?.classList?.contains('hide-on-screenshot') +} + +function getTotalElementHeight(element: HTMLElement | null): number { + if (!element) { + return 0 + } + const marginTop = Number.parseInt(getComputedStyle(element).marginTop) + return element.offsetHeight + marginTop +} + +function createFileName(cardTitle: string): string { + const date = new Date().toLocaleDateString('en-US', { + month: 'short', + year: 'numeric', + }) + const fileName = `HET - ${cardTitle} ${date}.png` + return fileName.replace('+', 'plus').replace(UNSAFE_CHAR_REGEX, '') +} + +// Remove added elements, reset styles +function cleanup( + addedNodes: Array, + articleChild: HTMLElement | null, +): void { + if (addedNodes) { + addedNodes.forEach((node) => { + node?.remove() + }) + } + + articleChild?.classList.add('shadow-raised') +} diff --git a/frontend/src/cards/ui/ExtremesListBox.tsx b/frontend/src/cards/ui/ExtremesListBox.tsx index 9801b9db80..8653e6e073 100644 --- a/frontend/src/cards/ui/ExtremesListBox.tsx +++ b/frontend/src/cards/ui/ExtremesListBox.tsx @@ -61,7 +61,7 @@ export function ExtremesListBox(props: ExtremesListBoxProps) { duration={500} height={props.isOpen ? 'auto' : 47} onAnimationEnd={() => window.dispatchEvent(new Event('resize'))} - className='mt-4 rounded-md bg-listboxColor text-left' + className={`mt-4 rounded-md bg-listboxColor text-left ${props.isOpen ? '' : 'hide-on-screenshot'}`} > { @@ -138,6 +117,7 @@ export default function MultiMapDialog(props: MultiMapDialogProps) { return ( -
+
{/* card options button */} -
+ saveCardImage('multimap-modal', title) + } reportTitle={props.reportTitle} scrollToHash={props.scrollToHash} /> @@ -185,9 +166,9 @@ export default function MultiMapDialog(props: MultiMapDialogProps) { key={`${demographicGroup}-grid-item`} className='min-h-multimapMobile w-full sm:p-1 md:min-h-multimapDesktop md:p-2' > -

+

{mapLabel} -

+
{props.metricConfig && dataForValue?.length > 0 && ( ) })} + - {/* LEGEND */} -
- + +
+ + {/* Population Breadcrumbs + Legend */} +
+ {/* DESKTOP BREADCRUMBS */} +
+
- {/* Population Breadcrumbs + Legend */} -
- {/* DESKTOP BREADCRUMBS */} -
- -
- - {/* MOBILE BREADCRUMBS */} -
- -
+ {/* MOBILE BREADCRUMBS */} +
+
+
- {/* Missing Groups */} - {props.demographicGroupsNoData.length > 0 && ( -
-
- -

- Insufficient {props.metricConfig.shortLabel} data reported - at the {props.fips.getChildFipsTypeDisplayName()} level - for the following groups:{' '} - {props.demographicGroupsNoData.map((group, i) => ( - - {group} - {i < props.demographicGroupsNoData.length - 1 && '; '} - - ))} -

-
-
+ {/* Missing Groups */} + {props.demographicGroupsNoData.length > 0 && ( +
+
+ +

+ Insufficient {props.metricConfig.shortLabel} data reported + at the {props.fips.getChildFipsTypeDisplayName()} level for + the following groups:{' '} + {props.demographicGroupsNoData.map((group, i) => ( + + {group} + {i < props.demographicGroupsNoData.length - 1 && '; '} + + ))} +

+
- )} +
+ )} - - - - + + +
{/* MODAL FOOTER */} -
+
{/* Desktop only Sources and Card Options */}
{/* CLOSE button */} Close
-
+
) } diff --git a/frontend/src/cards/ui/Sources.tsx b/frontend/src/cards/ui/Sources.tsx index a6ba5e7380..3d93fab379 100644 --- a/frontend/src/cards/ui/Sources.tsx +++ b/frontend/src/cards/ui/Sources.tsx @@ -30,7 +30,6 @@ interface SourcesProps { metadata: MapOfDatasetMetadata isCensusNotAcs?: boolean hideNH?: boolean - downloadTargetScreenshot?: () => Promise isMulti?: boolean showDefinition?: boolean isCompareCard?: boolean diff --git a/frontend/src/charts/trendsChart/FilterLegend.tsx b/frontend/src/charts/trendsChart/FilterLegend.tsx index 1937b39b07..77ceb169cc 100644 --- a/frontend/src/charts/trendsChart/FilterLegend.tsx +++ b/frontend/src/charts/trendsChart/FilterLegend.tsx @@ -56,10 +56,10 @@ export function FilterLegend({ // Legend Wrapper
{/* Legend Title & Clear Button */} -
+

Select groups:

{/* Reset to Highest Lowest Averages */} -
+