From b4c435c56570cc13aaeb9734bd4f0b9553d10a0b Mon Sep 17 00:00:00 2001 From: Bilal Meddah Date: Sun, 10 Mar 2024 10:46:49 +0100 Subject: [PATCH 01/11] update: add react-cmdk package --- package.json | 1 + yarn.lock | 185 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 180 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 3c1f2206e..90c07a57f 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "rc-tween-one": "^2.7.3", "react": "18.3", "react-beautiful-dnd": "^13.1.0", + "react-cmdk": "^1.3.9", "react-codemirror2": "^7.2.1", "react-device-detect": "^2.2.3", "react-dom": "^18.2.0", diff --git a/yarn.lock b/yarn.lock index 5c963f485..699ddbf46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1560,6 +1560,19 @@ resolved "https://registry.yarnpkg.com/@fontsource/titillium-web/-/titillium-web-5.0.18.tgz#1d6fbcef225c9a81dc399a1f9ce9706065fe6a5e" integrity sha512-GBmp0vqS0iGvUaA1tLtxFRXzeeQ+on0mrRDr0iLUo3B1YzR41ljB5zHO5U/VBByGVzq7CATtCbXov3G2mWCPbQ== +"@headlessui/react@^1.6.4": + version "1.7.18" + resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.18.tgz#30af4634d2215b2ca1aa29d07f33d02bea82d9d7" + integrity sha512-4i5DOrzwN4qSgNsL4Si61VMkUcWbcSKueUV7sFhpHzQcSShdlHENE5+QBntMSRvHt8NyoFO2AGG8si9lq+w4zQ== + dependencies: + "@tanstack/react-virtual" "^3.0.0-beta.60" + client-only "^0.0.1" + +"@heroicons/react@^2.0.13": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.1.1.tgz#422deb80c4d6caf3371aec6f4bee8361a354dc13" + integrity sha512-JyyN9Lo66kirbCMuMMRPtJxtKJoIsXKS569ebHGGRKbl8s4CtUfLnyKJxteA+vIKySocO4s1SkTkGS4xtG/yEA== + "@icons/material@^0.2.4": version "0.2.4" resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8" @@ -1623,6 +1636,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" @@ -2394,6 +2415,18 @@ resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.5.tgz#043b731d4f56a79b4897a3de1af35e75d56bc63a" integrity sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw== +"@tanstack/react-virtual@^3.0.0-beta.60": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.1.3.tgz#4ef2a7dd819a7dd2b634d50cbd6ba498f06529ec" + integrity sha512-YCzcbF/Ws/uZ0q3Z6fagH+JVhx4JLvbSflgldMgLsuvB8aXjZLLb3HvrEVxY480F9wFlBiXlvQxOyXb5ENPrNA== + dependencies: + "@tanstack/virtual-core" "3.1.3" + +"@tanstack/virtual-core@3.1.3": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.1.3.tgz#77ced625f19ec9350f6e460f142b3be9bff03866" + integrity sha512-Y5B4EYyv1j9V8LzeAoOVeTg0LI7Fo5InYKgAjkY1Pu9GjtUwX/EKxNcU7ng3sKr99WEf+bPTcktAeybyMOYo+g== + "@testing-library/cypress@9.0.0": version "9.0.0" resolved "https://registry.yarnpkg.com/@testing-library/cypress/-/cypress-9.0.0.tgz#3facad49c4654a99bbd138f83f33b415d2d6f097" @@ -2691,6 +2724,11 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + "@types/http-errors@*": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" @@ -4010,6 +4048,14 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + camelcase-keys@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" @@ -4188,6 +4234,13 @@ classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classna resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== +clean-css@^5.2.2: + version "5.3.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" + integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg== + dependencies: + source-map "~0.6.0" + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -4242,6 +4295,11 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +client-only@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -4385,6 +4443,11 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + common-tags@^1.8.0: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" @@ -4743,7 +4806,7 @@ css-loader@^2.1.1: postcss-value-parser "^3.3.0" schema-utils "^1.0.0" -css-select@^4.2.1: +css-select@^4.1.3, css-select@^4.2.1: version "4.3.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== @@ -5356,6 +5419,13 @@ dom-align@^1.7.0: resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.4.tgz#3503992eb2a7cfcb2ed3b2a6d21e0b9c00d54511" integrity sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw== +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" @@ -5377,14 +5447,14 @@ domexception@^4.0.0: dependencies: webidl-conversions "^7.0.0" -domhandler@^4.2.0, domhandler@^4.3.1: +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== dependencies: domelementtype "^2.2.0" -domutils@^2.8.0: +domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== @@ -6769,7 +6839,7 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" -he@1.2.0: +he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -6840,6 +6910,40 @@ html-escaper@^2.0.2: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html-minifier-terser@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-webpack-plugin@^5.5.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz#50a8fa6709245608cb00e811eacecb8e0d7b7ea0" + integrity sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -8124,7 +8228,7 @@ lodash.unset@4.5.2: resolved "https://registry.yarnpkg.com/lodash.unset/-/lodash.unset-4.5.2.tgz#370d1d3e85b72a7e1b0cdf2d272121306f23e4ed" integrity sha512-bwKX88k2JhCV9D1vtE8+naDKlLiGrSmf8zi/Y9ivFHwbmRfA8RxS/aVJ+sIht2XOwqoNr4xUPUkGZpc1sHFEKg== -lodash@^4.0.1, lodash@^4.11.1, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: +lodash@^4.0.1, lodash@^4.11.1, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -9223,6 +9327,14 @@ pako@~1.0.5: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -9295,6 +9407,14 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + path-browserify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" @@ -9591,6 +9711,14 @@ pretty-bytes@^5.4.1, pretty-bytes@^5.6.0: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + pretty-format@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" @@ -10299,6 +10427,15 @@ react-beautiful-dnd@^13.1.0: redux "^4.0.4" use-memo-one "^1.1.1" +react-cmdk@^1.3.9: + version "1.3.9" + resolved "https://registry.yarnpkg.com/react-cmdk/-/react-cmdk-1.3.9.tgz#77123f5120a47e35a517a8176550e96731667654" + integrity sha512-MSVmAQZ9iqY7hO3r++XP6yWSHzGfMDGMvY3qlDT8k5RiWoRFwO1CGPlsWzhvcUbPilErzsMKK7uB4McEcX4B6g== + dependencies: + "@headlessui/react" "^1.6.4" + "@heroicons/react" "^2.0.13" + html-webpack-plugin "^5.5.0" + react-codemirror2@^7.2.1: version "7.3.0" resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-7.3.0.tgz#c26e9fe655458389c6218496076e4bc7a4c31166" @@ -10744,6 +10881,11 @@ regl@^1.3.11: resolved "https://registry.yarnpkg.com/regl/-/regl-1.7.0.tgz#0d185431044a356bf80e9b775b11b935ef2746d3" integrity sha512-bEAtp/qrtKucxXSJkD4ebopFZYP0q1+3Vb2WECWv/T8yQEgKxDxJ7ztO285tAMaYZVR6mM1GgI6CCn8FROtL1w== +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + remove-accents@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.5.0.tgz#77991f37ba212afba162e375b627631315bed687" @@ -10757,6 +10899,17 @@ rename-overwrite@^4.0.4: "@zkochan/rimraf" "^2.1.2" fs-extra "10.1.0" +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + repeat-string@^1.5.2: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" @@ -11414,7 +11567,7 @@ source-map-support@^0.3.2: dependencies: source-map "0.1.32" -source-map-support@^0.5.21: +source-map-support@^0.5.21, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -11898,6 +12051,11 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +tapable@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + tape@^4.5.1: version "4.17.0" resolved "https://registry.yarnpkg.com/tape/-/tape-4.17.0.tgz#de89f3671ddc5dad178d04c28dc6b0183f42268e" @@ -11939,6 +12097,16 @@ tdigest@^0.1.1: dependencies: bintrees "1.0.2" +terser@^5.10.0: + version "5.29.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.29.1.tgz#44e58045b70c09792ba14bfb7b4e14ca8755b9fa" + integrity sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" @@ -12561,6 +12729,11 @@ util@^0.12.3, util@^0.12.5: is-typed-array "^1.1.3" which-typed-array "^1.1.2" +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== + utility-types@^3.10.0: version "3.11.0" resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.11.0.tgz#607c40edb4f258915e901ea7995607fdf319424c" From 79f19f5814ab2e33be9cd5c4beda1ae3dba5a699 Mon Sep 17 00:00:00 2001 From: Bilal Meddah Date: Sun, 10 Mar 2024 10:47:19 +0100 Subject: [PATCH 02/11] fix: invoke resource api only when the user is ready --- src/shared/canvas/MyData/MyData.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/shared/canvas/MyData/MyData.tsx b/src/shared/canvas/MyData/MyData.tsx index e2175bdb3..f074318fd 100644 --- a/src/shared/canvas/MyData/MyData.tsx +++ b/src/shared/canvas/MyData/MyData.tsx @@ -17,6 +17,7 @@ const HomeMyData: React.FC<{}> = () => { (state: RootState) => state.auth.identities?.data?.identities ); const issuerUri = identities?.find(item => item['@type'] === 'User')?.['@id']; + const [ { dateField, @@ -80,6 +81,7 @@ const HomeMyData: React.FC<{}> = () => { const order = sort.join('-'); const resourceTypes = types?.map(item => get(item, 'value')); const { data: resources, isLoading } = useQuery({ + enabled: !!issuerUri, queryKey: [ 'my-data-resources', { From 2b3b4da0116e6f3fb0a3604dc885fd29d2c3f00b Mon Sep 17 00:00:00 2001 From: Bilal Meddah Date: Sun, 10 Mar 2024 10:47:50 +0100 Subject: [PATCH 03/11] update: add fulltext search buttin to the header --- src/shared/components/Header/Header.scss | 10 +++ src/shared/components/Header/Header.tsx | 105 +++++++++++++---------- 2 files changed, 70 insertions(+), 45 deletions(-) diff --git a/src/shared/components/Header/Header.scss b/src/shared/components/Header/Header.scss index 316747bb6..a16de3f05 100644 --- a/src/shared/components/Header/Header.scss +++ b/src/shared/components/Header/Header.scss @@ -231,3 +231,13 @@ display: none; } } + +.cmdk-shortcut { + margin: 5px; + border: 1px solid #ffffff; + background: transparent; + padding: 4px 10px; + cursor: pointer; + border-radius: 4px; + color: #efefef; +} diff --git a/src/shared/components/Header/Header.tsx b/src/shared/components/Header/Header.tsx index b62325629..5f56bd534 100644 --- a/src/shared/components/Header/Header.tsx +++ b/src/shared/components/Header/Header.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useReducer, useState } from 'react'; import { Link } from 'react-router-dom'; import { useLocation } from 'react-router'; import { Menu, Dropdown, MenuItemProps } from 'antd'; @@ -20,8 +20,9 @@ import { updateAboutModalVisibility } from '../../store/actions/modals'; import { triggerCopy as copyCmd } from '../../utils/copy'; import { AdvancedModeToggle } from '../../molecules'; import useNotification from '../../hooks/useNotification'; -import fusionLogo from '../../images/fusion_logo.svg'; +import FullTextSearch from '../FullTextSearch'; +import fusionLogo from '../../images/fusion_logo.svg'; import './Header.scss'; export interface HeaderProps { @@ -46,6 +47,8 @@ const Header: React.FunctionComponent = ({ const { openCreationPanel } = useSelector( (state: RootState) => state.uiSettings ); + const [openCmdk, onOpenCmdk] = useReducer(prev => !prev, false); + const notShowDefaultHeader = (!token && location.pathname === '/') || location.pathname === '/login'; const copyTokenCmd = () => { @@ -186,52 +189,64 @@ const Header: React.FunctionComponent = ({ dispatch({ type: UISettingsActionTypes.CHANGE_HEADER_CREATION_PANEL }); if (notShowDefaultHeader) return null; return ( -
-
- -
- Logo -
- -
- {token ? ( -
- {name && } - {name && showCreationPanel && ( -
- + <> +
+
+ +
+ Logo
- )} - {name && ( - menu} - overlayClassName="menu-overlay-custom" - getPopupContainer={() => document.getElementById('main-header')!} - > - - - {name} - - - )} -
- ) : ( -
- - - Login
- )} -
+ {token ? ( +
+ + {name && } + {name && showCreationPanel && ( +
+ +
+ )} + {name && ( + menu} + overlayClassName="menu-overlay-custom" + getPopupContainer={() => + document.getElementById('main-header')! + } + > + + + {name} + + + )} +
+ ) : ( +
+ + + Login + +
+ )} +
+ + ); }; From 5d6848fecb4d4b9abdc4394c48def50e11faa52a Mon Sep 17 00:00:00 2001 From: Bilal Meddah Date: Sun, 10 Mar 2024 10:48:26 +0100 Subject: [PATCH 04/11] update: add fulltext search component --- .../components/FullTextSearch/index.tsx | 126 ++++++++++++++++++ .../components/FullTextSearch/styles.scss | 54 ++++++++ 2 files changed, 180 insertions(+) create mode 100644 src/shared/components/FullTextSearch/index.tsx create mode 100644 src/shared/components/FullTextSearch/styles.scss diff --git a/src/shared/components/FullTextSearch/index.tsx b/src/shared/components/FullTextSearch/index.tsx new file mode 100644 index 000000000..7e32299e9 --- /dev/null +++ b/src/shared/components/FullTextSearch/index.tsx @@ -0,0 +1,126 @@ +import { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; +import { useQuery } from 'react-query'; +import CommandPalette from 'react-cmdk'; +import { useNexusContext } from '@bbp/react-nexus'; +import { Tag } from 'antd'; +import { groupBy } from 'lodash'; + +import { + getNormalizedTypes, + getOrgAndProjectFromProjectId, + getResourceLabel, +} from 'shared/utils'; +import 'react-cmdk/dist/cmdk.css'; +import './styles.scss'; + +type Props = { + openCmdk: boolean; + onOpenCmdk(): void; +}; + +const TagRenderer = ({ type }: { type?: string | string[] }) => { + if (!type) return null; + const types = getNormalizedTypes(type); + return ( +
+ {types.map(t => ( + + {t} + + ))} +
+ ); +}; + +export function useFullTextSearch() { + const [search, setSearch] = useState(''); + const nexus = useNexusContext(); + + const onSearch = async (value: string) => setSearch(value); + + const { isLoading, data } = useQuery({ + enabled: !!search, + queryKey: ['cmdk-search', { search }], + queryFn: () => + nexus.Resource.list(undefined, undefined, { + q: search, + deprecated: false, + sort: '_updatedAt', + }), + select: data => data._results, + staleTime: 2, + }); + const resources = groupBy(data, '_project'); + + const searchResults = Object.entries(resources).map(([key, value]) => { + const orgProject = getOrgAndProjectFromProjectId(key); + return { + id: key, + title: orgProject, + items: value, + }; + }); + + return { + search, + onSearch, + isLoading, + searchResults, + }; +} + +const FullTextSearch = ({ openCmdk, onOpenCmdk }: Props) => { + const { search, onSearch, searchResults } = useFullTextSearch(); + + useEffect(() => { + const keyDown = (e: KeyboardEvent) => { + if (e.key === '/') { + e.preventDefault(); + onOpenCmdk(); + } + }; + + document.addEventListener('keydown', keyDown); + return () => document.removeEventListener('keydown', keyDown); + }, []); + + return ( + + + {searchResults.map(({ id, title, items }) => ( +
+
+ {title?.orgLabel}| + {title?.projectLabel} +
+
+ {items.map(resource => { + const label = getResourceLabel(resource); + return ( + +
{label}
+ + + ); + })} +
+
+ ))} +
+ +
+ ); +}; + +export default FullTextSearch; diff --git a/src/shared/components/FullTextSearch/styles.scss b/src/shared/components/FullTextSearch/styles.scss new file mode 100644 index 000000000..dea0faac4 --- /dev/null +++ b/src/shared/components/FullTextSearch/styles.scss @@ -0,0 +1,54 @@ +@import '../../lib.scss'; + +.cmdk-list-container { + padding: 10px; + margin-top: 0 !important; +} +.cmdk-list-title { + text-transform: uppercase !important; + font-weight: bold !important; + margin-bottom: 10px !important; + color: $fusion-daybreak-7; + display: flex; + align-items: center; + gap: 5px; + + .ant-tag { + padding: 2px 4px; + } +} + +.cmdk-list-content { + display: flex; + flex-direction: column; + gap: 3px; +} + +.cmdk-list-item { + padding: 10px 8px; + margin-left: 5px; + border-radius: 4px; + font-size: 16px; + color: #333; + border: 1px solid #efefef; + + &:hover { + background: $fusion-secondary-color !important; + box-shadow: 0 2px 12px 0px rgba($color: #333, $alpha: 0.12); + } +} + +.type-tags-list { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 3px; + margin: 4px 0px; + .tag { + padding: 2px 3px; + } +} + +.item-creation_date { + font-size: 12px; +} From 04b4904c57b9401e37a9b58209237fbeb1bee59c Mon Sep 17 00:00:00 2001 From: Bilal Meddah Date: Mon, 11 Mar 2024 08:51:45 +0100 Subject: [PATCH 05/11] update: remove sort when use search query in resources listing --- src/shared/components/FullTextSearch/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shared/components/FullTextSearch/index.tsx b/src/shared/components/FullTextSearch/index.tsx index 7e32299e9..722635f8c 100644 --- a/src/shared/components/FullTextSearch/index.tsx +++ b/src/shared/components/FullTextSearch/index.tsx @@ -46,7 +46,6 @@ export function useFullTextSearch() { nexus.Resource.list(undefined, undefined, { q: search, deprecated: false, - sort: '_updatedAt', }), select: data => data._results, staleTime: 2, From 2b6f8dc2c2cb23963ec0fcd1c8e9ce158f7e2f30 Mon Sep 17 00:00:00 2001 From: Daniel Burger Date: Mon, 18 Mar 2024 11:30:01 +0100 Subject: [PATCH 06/11] Remove unused import --- src/shared/components/Header/Header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/Header/Header.tsx b/src/shared/components/Header/Header.tsx index 5f56bd534..449b317bd 100644 --- a/src/shared/components/Header/Header.tsx +++ b/src/shared/components/Header/Header.tsx @@ -1,4 +1,4 @@ -import React, { useReducer, useState } from 'react'; +import React, { useReducer } from 'react'; import { Link } from 'react-router-dom'; import { useLocation } from 'react-router'; import { Menu, Dropdown, MenuItemProps } from 'antd'; From 671dcb63948d32ddc965a3be8091305fca7a327c Mon Sep 17 00:00:00 2001 From: Dinika Saxena Date: Wed, 20 Mar 2024 10:20:17 +0100 Subject: [PATCH 07/11] Download archive as zip instead of tar in resource container Signed-off-by: Dinika Saxena --- src/shared/components/Preview/Preview.tsx | 628 +++++++++++----------- 1 file changed, 314 insertions(+), 314 deletions(-) diff --git a/src/shared/components/Preview/Preview.tsx b/src/shared/components/Preview/Preview.tsx index 0683ce47d..e9d020d1b 100644 --- a/src/shared/components/Preview/Preview.tsx +++ b/src/shared/components/Preview/Preview.tsx @@ -55,344 +55,344 @@ const Preview: React.FC<{ collapsed, handleCollapseChanged: handleCollapsedChanged, }) => { - const { apiEndpoint } = useSelector((state: RootState) => state.config); - const notification = useNotification(); - const [selectedRows, setSelectedRows] = React.useState([]); - const [previewAsset, setPreviewAsset] = React.useState(); - const [orgLabel, projectLabel] = parseProjectUrl(resource._project); - const renderFileSize = (contentSize: { - value: string; - unitCode?: string; - }) => { - if (!contentSize) { - return '-'; - } - if (contentSize.unitCode) { - if (contentSize.unitCode.toLocaleLowerCase() === 'kilo bytes') { - return `${contentSize.value} KB`; + const { apiEndpoint } = useSelector((state: RootState) => state.config); + const notification = useNotification(); + const [selectedRows, setSelectedRows] = React.useState([]); + const [previewAsset, setPreviewAsset] = React.useState(); + const [orgLabel, projectLabel] = parseProjectUrl(resource._project); + const renderFileSize = (contentSize: { + value: string; + unitCode?: string; + }) => { + if (!contentSize) { + return '-'; } - if (contentSize.unitCode.toLocaleLowerCase() === 'mega bytes') { - return `${contentSize.value} MB`; + if (contentSize.unitCode) { + if (contentSize.unitCode.toLocaleLowerCase() === 'kilo bytes') { + return `${contentSize.value} KB`; + } + if (contentSize.unitCode.toLocaleLowerCase() === 'mega bytes') { + return `${contentSize.value} MB`; + } + } + const sizeInMB = (parseInt(contentSize.value, 10) / 1000000).toFixed(2); + if (sizeInMB !== '0.00') { + return `${sizeInMB} MB`; } - } - const sizeInMB = (parseInt(contentSize.value, 10) / 1000000).toFixed(2); - if (sizeInMB !== '0.00') { - return `${sizeInMB} MB`; - } - return `${contentSize.value} Bytes`; - }; + return `${contentSize.value} Bytes`; + }; - const isNexusFile = (url: string) => { - const resourceId = parseResourceId(url); - return resourceId !== ''; - }; + const isNexusFile = (url: string) => { + const resourceId = parseResourceId(url); + return resourceId !== ''; + }; - const columns = [ - { - title: 'Name', - dataIndex: 'name', - key: 'name', - render: (text: string) => text || '-', - }, - { - title: 'Asset Type / Encoding Format', - dataIndex: 'encodingFormat', - key: 'encodingFormat', - render: (text: string) => text || '-', - }, - { - title: 'File Size', - dataIndex: 'contentSize', - key: 'contentSize', - render: renderFileSize, - }, - { - title: 'Actions', - dataIndex: 'asset', - key: 'actions', - render: (asset: { - url: string; - name: string; - encodingFormat: string; - }) => { - return ( - - - - - - - - - - - - ); + const columns = [ + { + title: 'Name', + dataIndex: 'name', + key: 'name', + render: (text: string) => text || '-', + }, + { + title: 'Asset Type / Encoding Format', + dataIndex: 'encodingFormat', + key: 'encodingFormat', + render: (text: string) => text || '-', }, - }, - ]; + { + title: 'File Size', + dataIndex: 'contentSize', + key: 'contentSize', + render: renderFileSize, + }, + { + title: 'Actions', + dataIndex: 'asset', + key: 'actions', + render: (asset: { + url: string; + name: string; + encodingFormat: string; + }) => { + return ( + + + + + + + + + + + + ); + }, + }, + ]; - const downloadMultipleFiles = async () => { - const resourcesPayload = selectedRows - .map(row => { - return row.asset.url; - }) - .map(url => { - const resourceId = parseResourceId(url); - return { - resourceId, - '@type': 'File', - project: `${orgLabel}/${projectLabel}`, - }; - }); - const archiveId = uuidv4(); - const payload: ArchivePayload = { - archiveId, - resources: resourcesPayload, - }; + const downloadMultipleFiles = async () => { + const resourcesPayload = selectedRows + .map(row => { + return row.asset.url; + }) + .map(url => { + const resourceId = parseResourceId(url); + return { + resourceId, + '@type': 'File', + project: `${orgLabel}/${projectLabel}`, + }; + }); + const archiveId = uuidv4(); + const payload: ArchivePayload = { + archiveId, + resources: resourcesPayload, + }; - try { - // TODO: fix the SDK to handle empty response - await fetch( - `${apiEndpoint}/archives/${orgLabel}/${projectLabel}/${payload.archiveId}`, - { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${localStorage.getItem('nexus__token')}`, + try { + // TODO: fix the SDK to handle empty response + await fetch( + `${apiEndpoint}/archives/${orgLabel}/${projectLabel}/${payload.archiveId}`, + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem('nexus__token')}`, + }, + body: JSON.stringify(payload), + } + ); + const archive = await nexus.httpGet({ + path: `${apiEndpoint}/archives/${orgLabel}/${projectLabel}/${payload.archiveId}?ignoreNotFound=true`, + headers: { accept: 'application/zip, application/json' }, + context: { + parseAs: 'blob', }, - body: JSON.stringify(payload), - } + }); + const blob = archive as Blob; + const archiveName = `data-${payload.archiveId}.zip`; + downloadBlobHelper(blob, archiveName); + + notification.success({ + message: `Archive ${archiveName} downloaded successfully`, + }); + } catch (error) { + Sentry.captureException({ error, message: 'Failed to download archive' }); + notification.error({ + message: 'Failed to download the file', + description: (error as TError).reason || (error as TError).message, + }); + } + }; + + const downloadButton = (disabled: boolean) => { + const isDisabled = disabled || selectedRows.length <= 0; + const disabledToolTip = + selectedRows.length <= 0 + ? 'Please Select files to download' + : 'You don’t have the required permissions to create an archive for some of the selected resources. Please contact your project administrator to request to be granted the required archives/write permission.'; + const btn = ( + ); - const archive = await nexus.httpGet({ - path: `${apiEndpoint}/archives/${orgLabel}/${projectLabel}/${payload.archiveId}?ignoreNotFound=true`, - headers: { accept: 'application/zip, application/json' }, - context: { - parseAs: 'blob', - }, - }); - const blob = archive as Blob; - const archiveName = `data-${payload.archiveId}.zip`; - downloadBlobHelper(blob, archiveName); - notification.success({ - message: `Archive ${archiveName} downloaded successfully`, - }); - } catch (error) { - Sentry.captureException({ error, message: 'Failed to download archive' }); - notification.error({ - message: 'Failed to download the file', - description: (error as TError).reason || (error as TError).message, - }); - } - }; + if (isDisabled) { + return {btn}; + } - const downloadButton = (disabled: boolean) => { - const isDisabled = disabled || selectedRows.length <= 0; - const disabledToolTip = - selectedRows.length <= 0 - ? 'Please Select files to download' - : 'You don’t have the required permissions to create an archive for some of the selected resources. Please contact your project administrator to request to be granted the required archives/write permission.'; - const btn = ( - - ); + return btn; + }; - if (isDisabled) { - return {btn}; - } + const copyURI = (id: string) => { + try { + navigator.clipboard.writeText(id); + notification.success({ message: 'URL Copied to clipboard' }); + } catch { + notification.error({ + message: 'Failed to copy the url', + }); + } + }; - return btn; - }; + const downloadSingleFile = async ( + nexus: NexusClient, + orgLabel: string, + projectLabel: string, + asset: { url: string; name: string } + ) => { + const resourceId = parseResourceId(asset.url); + let contentUrl = resourceId; + const options: GetFileOptions = { + as: 'blob', + }; - const copyURI = (id: string) => { - try { - navigator.clipboard.writeText(id); - notification.success({ message: 'URL Copied to clipboard' }); - } catch { - notification.error({ - message: 'Failed to copy the url', - }); - } - }; + if (resourceId.includes('?rev=')) { + const [url, rev] = resourceId.split('?rev='); + contentUrl = url; + options.rev = parseInt(rev, 10); + } - const downloadSingleFile = async ( - nexus: NexusClient, - orgLabel: string, - projectLabel: string, - asset: { url: string; name: string } - ) => { - const resourceId = parseResourceId(asset.url); - let contentUrl = resourceId; - const options: GetFileOptions = { - as: 'blob', + try { + const rawData = await nexus.File.get( + orgLabel, + projectLabel, + nexusUrlHardEncode(contentUrl), + options + ); + downloadBlobHelper(rawData, asset.name); + } catch (error) { + notification.error({ + message: 'Failed to download the file', + description: (error as TError).reason || (error as TError).message, + }); + } }; - if (resourceId.includes('?rev=')) { - const [url, rev] = resourceId.split('?rev='); - contentUrl = url; - options.rev = parseInt(rev, 10); - } - - try { - const rawData = await nexus.File.get( - orgLabel, - projectLabel, - nexusUrlHardEncode(contentUrl), - options - ); - downloadBlobHelper(rawData, asset.name); - } catch (error) { - notification.error({ - message: 'Failed to download the file', - description: (error as TError).reason || (error as TError).message, + const downloadBlobHelper = ( + rawData: string | NexusFile | Blob | FormData, + name: string + ) => { + const blob = new Blob([rawData as string], { + type: 'octet/stream', }); - } - }; - - const downloadBlobHelper = ( - rawData: string | NexusFile | Blob | FormData, - name: string - ) => { - const blob = new Blob([rawData as string], { - type: 'octet/stream', - }); - const src = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.style.display = 'none'; - a.download = name; - document.body.appendChild(a); - a.href = src; - a.click(); - URL.revokeObjectURL(src); - }; + const src = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.style.display = 'none'; + a.download = name; + document.body.appendChild(a); + a.href = src; + a.click(); + URL.revokeObjectURL(src); + }; - const getResourceAssets = (resource: Resource) => { - let data: any = []; + const getResourceAssets = (resource: Resource) => { + let data: any = []; - /* get assets dependening on type of resource */ - if (resource.distribution) { - const { distribution } = resource; - data = data.concat( - [distribution].flat().map((d, i) => { - return { - key: i, - name: - d.name || - (d.repository && (d.repository.name || d.repository['@id'])), - asset: { - url: d.contentUrl || d.url, - name: d.name, - encodingFormat: d.encodingFormat, - }, - encodingFormat: - d.encodingFormat || - (d.name?.includes('.') ? d.name.split('.').pop() : '-'), - contentSize: d.contentSize, - }; - }) - ); - } + /* get assets dependening on type of resource */ + if (resource.distribution) { + const { distribution } = resource; + data = data.concat( + [distribution].flat().map((d, i) => { + return { + key: i, + name: + d.name || + (d.repository && (d.repository.name || d.repository['@id'])), + asset: { + url: d.contentUrl || d.url, + name: d.name, + encodingFormat: d.encodingFormat, + }, + encodingFormat: + d.encodingFormat || + (d.name?.includes('.') ? d.name.split('.').pop() : '-'), + contentSize: d.contentSize, + }; + }) + ); + } - return data; - }; + return data; + }; - const fileFormat = - previewAsset && previewAsset.name?.includes('.') - ? previewAsset.name.split('.').pop() - : '-'; + const fileFormat = + previewAsset && previewAsset.name?.includes('.') + ? previewAsset.name.split('.').pop() + : '-'; - return ( -
- {previewAsset && previewAsset.encodingFormat === 'application/pdf' && ( - setPreviewAsset(undefined)} - /> - )} - {previewAsset && (fileFormat === 'csv' || fileFormat === 'tsv') && ( - setPreviewAsset(undefined)} + return ( +
+ {previewAsset && previewAsset.encodingFormat === 'application/pdf' && ( + setPreviewAsset(undefined)} + /> + )} + {previewAsset && (fileFormat === 'csv' || fileFormat === 'tsv') && ( + setPreviewAsset(undefined)} + /> + )} + + downloadButton(true)} + loadingComponent={downloadButton(false)} + > + {downloadButton(false)} + + { + if (selected) { + setSelectedRows([...selectedRows, record]); + } else { + const currentRows = selectedRows.filter( + s => s.key !== record.key + ); + setSelectedRows(currentRows); + } + }, + onSelectAll: (select, selectedRows) => { + if (select) { + setSelectedRows(selectedRows); + } else { + setSelectedRows([]); + } + }, + }} + columns={columns} + dataSource={getResourceAssets(resource)} + bordered={true} + >
+ + ), + }, + ]} /> - )} - - downloadButton(true)} - loadingComponent={downloadButton(false)} - > - {downloadButton(false)} - - { - if (selected) { - setSelectedRows([...selectedRows, record]); - } else { - const currentRows = selectedRows.filter( - s => s.key !== record.key - ); - setSelectedRows(currentRows); - } - }, - onSelectAll: (select, selectedRows) => { - if (select) { - setSelectedRows(selectedRows); - } else { - setSelectedRows([]); - } - }, - }} - columns={columns} - dataSource={getResourceAssets(resource)} - bordered={true} - >
- - ), - }, - ]} - /> -
- ); -}; +
+ ); + }; export default Preview; From 81b08dea7b02538f90405ab25654691f9b1ea5ed Mon Sep 17 00:00:00 2001 From: Bilal Meddah Date: Mon, 27 May 2024 16:12:04 +0200 Subject: [PATCH 08/11] update: code editor in dashboard config --- .../admin/components/ViewForm/ElasticSearchQueryInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subapps/admin/components/ViewForm/ElasticSearchQueryInput.tsx b/src/subapps/admin/components/ViewForm/ElasticSearchQueryInput.tsx index b15c9dbb2..20e37d081 100644 --- a/src/subapps/admin/components/ViewForm/ElasticSearchQueryInput.tsx +++ b/src/subapps/admin/components/ViewForm/ElasticSearchQueryInput.tsx @@ -6,7 +6,7 @@ import { } from '@ant-design/icons'; import codemirror from 'codemirror'; import { UnControlled as CodeMirror } from 'react-codemirror2'; -import 'codemirror/addon/display/placeholder'; +// import 'codemirror/addon/display/placeholder'; import 'codemirror/mode/javascript/javascript'; import 'codemirror/addon/fold/foldcode'; import 'codemirror/addon/fold/foldgutter'; From 1e099290935673a56ce7b99530cd0bf78c40005a Mon Sep 17 00:00:00 2001 From: Bilal Meddah Date: Tue, 28 May 2024 12:00:36 +0200 Subject: [PATCH 09/11] update: fix auto scroll on delete search string update: refactor content of search body area --- .../components/FullTextSearch/index.tsx | 91 ++- .../components/FullTextSearch/styles.scss | 20 +- src/shared/components/Header/Header.scss | 24 + src/shared/components/Header/Header.tsx | 21 +- src/shared/components/Preview/Preview.tsx | 628 +++++++++--------- src/shared/lib.scss | 9 +- 6 files changed, 432 insertions(+), 361 deletions(-) diff --git a/src/shared/components/FullTextSearch/index.tsx b/src/shared/components/FullTextSearch/index.tsx index 722635f8c..bdc3d24bc 100644 --- a/src/shared/components/FullTextSearch/index.tsx +++ b/src/shared/components/FullTextSearch/index.tsx @@ -3,7 +3,7 @@ import { Link } from 'react-router-dom'; import { useQuery } from 'react-query'; import CommandPalette from 'react-cmdk'; import { useNexusContext } from '@bbp/react-nexus'; -import { Tag } from 'antd'; +import { Spin, Tag, Empty } from 'antd'; import { groupBy } from 'lodash'; import { @@ -13,6 +13,7 @@ import { } from 'shared/utils'; import 'react-cmdk/dist/cmdk.css'; import './styles.scss'; +import { LoadingOutlined } from '@ant-design/icons'; type Props = { openCmdk: boolean; @@ -37,7 +38,8 @@ export function useFullTextSearch() { const [search, setSearch] = useState(''); const nexus = useNexusContext(); - const onSearch = async (value: string) => setSearch(value); + const onSearch = (value: string) => setSearch(value); + const resetSearch = () => setSearch(''); const { isLoading, data } = useQuery({ enabled: !!search, @@ -64,18 +66,69 @@ export function useFullTextSearch() { return { search, onSearch, + resetSearch, isLoading, searchResults, }; } const FullTextSearch = ({ openCmdk, onOpenCmdk }: Props) => { - const { search, onSearch, searchResults } = useFullTextSearch(); + const { + search, + onSearch, + resetSearch, + searchResults, + isLoading, + } = useFullTextSearch(); + + const onChangeOpen = () => { + onOpenCmdk(); + resetSearch(); + }; + + let content = null; + + if (isLoading) { + content = ( +
+ } /> +
+ ); + } else if (!Boolean(searchResults.length) && !Boolean(search)) { + content =
Search for ""
; + } else if (!Boolean(searchResults.length) && Boolean(search)) { + content = ; + } else { + content = searchResults.map(({ id, title, items }) => ( +
+
+ {title?.orgLabel}| + {title?.projectLabel} +
+
+ {items.map(resource => { + const label = getResourceLabel(resource); + return ( + +
{label}
+ + + ); + })} +
+
+ )); + } useEffect(() => { const keyDown = (e: KeyboardEvent) => { - if (e.key === '/') { + if (e.ctrlKey && e.key === 'e') { e.preventDefault(); + e.stopPropagation(); onOpenCmdk(); } }; @@ -87,37 +140,13 @@ const FullTextSearch = ({ openCmdk, onOpenCmdk }: Props) => { return ( - - {searchResults.map(({ id, title, items }) => ( -
-
- {title?.orgLabel}| - {title?.projectLabel} -
-
- {items.map(resource => { - const label = getResourceLabel(resource); - return ( - -
{label}
- - - ); - })} -
-
- ))} -
- + {content}
); }; diff --git a/src/shared/components/FullTextSearch/styles.scss b/src/shared/components/FullTextSearch/styles.scss index dea0faac4..63ded4ffc 100644 --- a/src/shared/components/FullTextSearch/styles.scss +++ b/src/shared/components/FullTextSearch/styles.scss @@ -33,8 +33,8 @@ border: 1px solid #efefef; &:hover { - background: $fusion-secondary-color !important; - box-shadow: 0 2px 12px 0px rgba($color: #333, $alpha: 0.12); + background: $fusion-blue-1 !important; + box-shadow: 0 2px 12px 0px rgba($color: #333, $alpha: 0.14); } } @@ -52,3 +52,19 @@ .item-creation_date { font-size: 12px; } + +.search-resources-loader { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + padding-top: 10px; + padding-bottom: 10px; +} + +.search-for-placeholder { + background-color: $fusion-neutral-2; + padding: 10px; + color: #333; + user-select: none; +} diff --git a/src/shared/components/Header/Header.scss b/src/shared/components/Header/Header.scss index a16de3f05..21b07bf33 100644 --- a/src/shared/components/Header/Header.scss +++ b/src/shared/components/Header/Header.scss @@ -240,4 +240,28 @@ cursor: pointer; border-radius: 4px; color: #efefef; + display: flex; + gap: 2px; + align-items: center; + justify-content: center; + min-width: max-content; + position: relative; + background-color: white; + color: #33333385; + + &-input { + position: absolute; + inset: 0; + } + &-btn { + position: relative; + width: max-content; + min-width: max-content; + } + kbd { + box-shadow: none; + background-color: transparent; + margin-left: 2px; + margin-right: 2px; + } } diff --git a/src/shared/components/Header/Header.tsx b/src/shared/components/Header/Header.tsx index 449b317bd..4b4363000 100644 --- a/src/shared/components/Header/Header.tsx +++ b/src/shared/components/Header/Header.tsx @@ -2,6 +2,7 @@ import React, { useReducer } from 'react'; import { Link } from 'react-router-dom'; import { useLocation } from 'react-router'; import { Menu, Dropdown, MenuItemProps } from 'antd'; + import { UserOutlined, BookOutlined, @@ -12,6 +13,7 @@ import { CopyOutlined, MenuOutlined, PlusOutlined, + SearchOutlined, } from '@ant-design/icons'; import { useDispatch, useSelector } from 'react-redux'; import { UISettingsActionTypes } from '../../store/actions/ui-settings'; @@ -200,13 +202,18 @@ const Header: React.FunctionComponent = ({ {token ? (
- +
+ +
+ Click on +
+ Ctrl+e +
+ to search +
{name && } {name && showCreationPanel && (
{ - const { apiEndpoint } = useSelector((state: RootState) => state.config); - const notification = useNotification(); - const [selectedRows, setSelectedRows] = React.useState([]); - const [previewAsset, setPreviewAsset] = React.useState(); - const [orgLabel, projectLabel] = parseProjectUrl(resource._project); - const renderFileSize = (contentSize: { - value: string; - unitCode?: string; - }) => { - if (!contentSize) { - return '-'; + const { apiEndpoint } = useSelector((state: RootState) => state.config); + const notification = useNotification(); + const [selectedRows, setSelectedRows] = React.useState([]); + const [previewAsset, setPreviewAsset] = React.useState(); + const [orgLabel, projectLabel] = parseProjectUrl(resource._project); + const renderFileSize = (contentSize: { + value: string; + unitCode?: string; + }) => { + if (!contentSize) { + return '-'; + } + if (contentSize.unitCode) { + if (contentSize.unitCode.toLocaleLowerCase() === 'kilo bytes') { + return `${contentSize.value} KB`; } - if (contentSize.unitCode) { - if (contentSize.unitCode.toLocaleLowerCase() === 'kilo bytes') { - return `${contentSize.value} KB`; - } - if (contentSize.unitCode.toLocaleLowerCase() === 'mega bytes') { - return `${contentSize.value} MB`; - } - } - const sizeInMB = (parseInt(contentSize.value, 10) / 1000000).toFixed(2); - if (sizeInMB !== '0.00') { - return `${sizeInMB} MB`; + if (contentSize.unitCode.toLocaleLowerCase() === 'mega bytes') { + return `${contentSize.value} MB`; } + } + const sizeInMB = (parseInt(contentSize.value, 10) / 1000000).toFixed(2); + if (sizeInMB !== '0.00') { + return `${sizeInMB} MB`; + } - return `${contentSize.value} Bytes`; - }; - - const isNexusFile = (url: string) => { - const resourceId = parseResourceId(url); - return resourceId !== ''; - }; - - const columns = [ - { - title: 'Name', - dataIndex: 'name', - key: 'name', - render: (text: string) => text || '-', - }, - { - title: 'Asset Type / Encoding Format', - dataIndex: 'encodingFormat', - key: 'encodingFormat', - render: (text: string) => text || '-', - }, - { - title: 'File Size', - dataIndex: 'contentSize', - key: 'contentSize', - render: renderFileSize, - }, - { - title: 'Actions', - dataIndex: 'asset', - key: 'actions', - render: (asset: { - url: string; - name: string; - encodingFormat: string; - }) => { - return ( - - - - - - - - - - - - ); - }, - }, - ]; + return `${contentSize.value} Bytes`; + }; - const downloadMultipleFiles = async () => { - const resourcesPayload = selectedRows - .map(row => { - return row.asset.url; - }) - .map(url => { - const resourceId = parseResourceId(url); - return { - resourceId, - '@type': 'File', - project: `${orgLabel}/${projectLabel}`, - }; - }); - const archiveId = uuidv4(); - const payload: ArchivePayload = { - archiveId, - resources: resourcesPayload, - }; + const isNexusFile = (url: string) => { + const resourceId = parseResourceId(url); + return resourceId !== ''; + }; - try { - // TODO: fix the SDK to handle empty response - await fetch( - `${apiEndpoint}/archives/${orgLabel}/${projectLabel}/${payload.archiveId}`, - { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${localStorage.getItem('nexus__token')}`, - }, - body: JSON.stringify(payload), - } + const columns = [ + { + title: 'Name', + dataIndex: 'name', + key: 'name', + render: (text: string) => text || '-', + }, + { + title: 'Asset Type / Encoding Format', + dataIndex: 'encodingFormat', + key: 'encodingFormat', + render: (text: string) => text || '-', + }, + { + title: 'File Size', + dataIndex: 'contentSize', + key: 'contentSize', + render: renderFileSize, + }, + { + title: 'Actions', + dataIndex: 'asset', + key: 'actions', + render: (asset: { + url: string; + name: string; + encodingFormat: string; + }) => { + return ( + + + + + + + + + + + ); - const archive = await nexus.httpGet({ - path: `${apiEndpoint}/archives/${orgLabel}/${projectLabel}/${payload.archiveId}?ignoreNotFound=true`, - headers: { accept: 'application/zip, application/json' }, - context: { - parseAs: 'blob', - }, - }); - const blob = archive as Blob; - const archiveName = `data-${payload.archiveId}.zip`; - downloadBlobHelper(blob, archiveName); + }, + }, + ]; - notification.success({ - message: `Archive ${archiveName} downloaded successfully`, - }); - } catch (error) { - Sentry.captureException({ error, message: 'Failed to download archive' }); - notification.error({ - message: 'Failed to download the file', - description: (error as TError).reason || (error as TError).message, - }); - } + const downloadMultipleFiles = async () => { + const resourcesPayload = selectedRows + .map(row => { + return row.asset.url; + }) + .map(url => { + const resourceId = parseResourceId(url); + return { + resourceId, + '@type': 'File', + project: `${orgLabel}/${projectLabel}`, + }; + }); + const archiveId = uuidv4(); + const payload: ArchivePayload = { + archiveId, + resources: resourcesPayload, }; - const downloadButton = (disabled: boolean) => { - const isDisabled = disabled || selectedRows.length <= 0; - const disabledToolTip = - selectedRows.length <= 0 - ? 'Please Select files to download' - : 'You don’t have the required permissions to create an archive for some of the selected resources. Please contact your project administrator to request to be granted the required archives/write permission.'; - const btn = ( - + try { + // TODO: fix the SDK to handle empty response + await fetch( + `${apiEndpoint}/archives/${orgLabel}/${projectLabel}/${payload.archiveId}`, + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem('nexus__token')}`, + }, + body: JSON.stringify(payload), + } ); + const archive = await nexus.httpGet({ + path: `${apiEndpoint}/archives/${orgLabel}/${projectLabel}/${payload.archiveId}?ignoreNotFound=true`, + headers: { accept: 'application/zip, application/json' }, + context: { + parseAs: 'blob', + }, + }); + const blob = archive as Blob; + const archiveName = `data-${payload.archiveId}.zip`; + downloadBlobHelper(blob, archiveName); - if (isDisabled) { - return {btn}; - } + notification.success({ + message: `Archive ${archiveName} downloaded successfully`, + }); + } catch (error) { + Sentry.captureException({ error, message: 'Failed to download archive' }); + notification.error({ + message: 'Failed to download the file', + description: (error as TError).reason || (error as TError).message, + }); + } + }; - return btn; - }; + const downloadButton = (disabled: boolean) => { + const isDisabled = disabled || selectedRows.length <= 0; + const disabledToolTip = + selectedRows.length <= 0 + ? 'Please Select files to download' + : 'You don’t have the required permissions to create an archive for some of the selected resources. Please contact your project administrator to request to be granted the required archives/write permission.'; + const btn = ( + + ); - const copyURI = (id: string) => { - try { - navigator.clipboard.writeText(id); - notification.success({ message: 'URL Copied to clipboard' }); - } catch { - notification.error({ - message: 'Failed to copy the url', - }); - } - }; + if (isDisabled) { + return {btn}; + } - const downloadSingleFile = async ( - nexus: NexusClient, - orgLabel: string, - projectLabel: string, - asset: { url: string; name: string } - ) => { - const resourceId = parseResourceId(asset.url); - let contentUrl = resourceId; - const options: GetFileOptions = { - as: 'blob', - }; + return btn; + }; - if (resourceId.includes('?rev=')) { - const [url, rev] = resourceId.split('?rev='); - contentUrl = url; - options.rev = parseInt(rev, 10); - } + const copyURI = (id: string) => { + try { + navigator.clipboard.writeText(id); + notification.success({ message: 'URL Copied to clipboard' }); + } catch { + notification.error({ + message: 'Failed to copy the url', + }); + } + }; - try { - const rawData = await nexus.File.get( - orgLabel, - projectLabel, - nexusUrlHardEncode(contentUrl), - options - ); - downloadBlobHelper(rawData, asset.name); - } catch (error) { - notification.error({ - message: 'Failed to download the file', - description: (error as TError).reason || (error as TError).message, - }); - } + const downloadSingleFile = async ( + nexus: NexusClient, + orgLabel: string, + projectLabel: string, + asset: { url: string; name: string } + ) => { + const resourceId = parseResourceId(asset.url); + let contentUrl = resourceId; + const options: GetFileOptions = { + as: 'blob', }; - const downloadBlobHelper = ( - rawData: string | NexusFile | Blob | FormData, - name: string - ) => { - const blob = new Blob([rawData as string], { - type: 'octet/stream', + if (resourceId.includes('?rev=')) { + const [url, rev] = resourceId.split('?rev='); + contentUrl = url; + options.rev = parseInt(rev, 10); + } + + try { + const rawData = await nexus.File.get( + orgLabel, + projectLabel, + nexusUrlHardEncode(contentUrl), + options + ); + downloadBlobHelper(rawData, asset.name); + } catch (error) { + notification.error({ + message: 'Failed to download the file', + description: (error as TError).reason || (error as TError).message, }); - const src = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.style.display = 'none'; - a.download = name; - document.body.appendChild(a); - a.href = src; - a.click(); - URL.revokeObjectURL(src); - }; + } + }; - const getResourceAssets = (resource: Resource) => { - let data: any = []; + const downloadBlobHelper = ( + rawData: string | NexusFile | Blob | FormData, + name: string + ) => { + const blob = new Blob([rawData as string], { + type: 'octet/stream', + }); + const src = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.style.display = 'none'; + a.download = name; + document.body.appendChild(a); + a.href = src; + a.click(); + URL.revokeObjectURL(src); + }; - /* get assets dependening on type of resource */ - if (resource.distribution) { - const { distribution } = resource; - data = data.concat( - [distribution].flat().map((d, i) => { - return { - key: i, - name: - d.name || - (d.repository && (d.repository.name || d.repository['@id'])), - asset: { - url: d.contentUrl || d.url, - name: d.name, - encodingFormat: d.encodingFormat, - }, - encodingFormat: - d.encodingFormat || - (d.name?.includes('.') ? d.name.split('.').pop() : '-'), - contentSize: d.contentSize, - }; - }) - ); - } + const getResourceAssets = (resource: Resource) => { + let data: any = []; - return data; - }; + /* get assets dependening on type of resource */ + if (resource.distribution) { + const { distribution } = resource; + data = data.concat( + [distribution].flat().map((d, i) => { + return { + key: i, + name: + d.name || + (d.repository && (d.repository.name || d.repository['@id'])), + asset: { + url: d.contentUrl || d.url, + name: d.name, + encodingFormat: d.encodingFormat, + }, + encodingFormat: + d.encodingFormat || + (d.name?.includes('.') ? d.name.split('.').pop() : '-'), + contentSize: d.contentSize, + }; + }) + ); + } - const fileFormat = - previewAsset && previewAsset.name?.includes('.') - ? previewAsset.name.split('.').pop() - : '-'; + return data; + }; - return ( -
- {previewAsset && previewAsset.encodingFormat === 'application/pdf' && ( - setPreviewAsset(undefined)} - /> - )} - {previewAsset && (fileFormat === 'csv' || fileFormat === 'tsv') && ( - setPreviewAsset(undefined)} - /> - )} - - downloadButton(true)} - loadingComponent={downloadButton(false)} - > - {downloadButton(false)} - - { - if (selected) { - setSelectedRows([...selectedRows, record]); - } else { - const currentRows = selectedRows.filter( - s => s.key !== record.key - ); - setSelectedRows(currentRows); - } - }, - onSelectAll: (select, selectedRows) => { - if (select) { - setSelectedRows(selectedRows); - } else { - setSelectedRows([]); - } - }, - }} - columns={columns} - dataSource={getResourceAssets(resource)} - bordered={true} - >
- - ), - }, - ]} + const fileFormat = + previewAsset && previewAsset.name?.includes('.') + ? previewAsset.name.split('.').pop() + : '-'; + + return ( +
+ {previewAsset && previewAsset.encodingFormat === 'application/pdf' && ( + setPreviewAsset(undefined)} /> -
- ); - }; + )} + {previewAsset && (fileFormat === 'csv' || fileFormat === 'tsv') && ( + setPreviewAsset(undefined)} + /> + )} + + downloadButton(true)} + loadingComponent={downloadButton(false)} + > + {downloadButton(false)} + + { + if (selected) { + setSelectedRows([...selectedRows, record]); + } else { + const currentRows = selectedRows.filter( + s => s.key !== record.key + ); + setSelectedRows(currentRows); + } + }, + onSelectAll: (select, selectedRows) => { + if (select) { + setSelectedRows(selectedRows); + } else { + setSelectedRows([]); + } + }, + }} + columns={columns} + dataSource={getResourceAssets(resource)} + bordered={true} + >
+ + ), + }, + ]} + /> +
+ ); +}; export default Preview; diff --git a/src/shared/lib.scss b/src/shared/lib.scss index 6eed8495f..1cecf878f 100644 --- a/src/shared/lib.scss +++ b/src/shared/lib.scss @@ -22,8 +22,6 @@ $fusion-blue-4: #40a9ff; $fusion-blue-8: #003a8c; $fusion-component-background-color: #f2f2f2; $fusion-warning-color: #faad14; -$fusion-blue-1: #e6f7ff; -$fusion-blue-1: #e6f7ff; $fusion-gray-4: #d9d9d9; $fusion-daybreak-3: #91d5ff; $fusion-daybreak-4: #69c0ff; @@ -93,7 +91,6 @@ $font-family: 'Titillium Web', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; - $primary-color: $fusion-primary-color; // primary color for all components $link-color: $fusion-active-link-color; // link color $link-hover-color: $fusion-hover-link-color; @@ -137,7 +134,6 @@ $component-background: $fusion-component-background-color; 0 0 0 1px rgba(9, 30, 66, 0.08); } - @mixin unstyleList { list-style: none; } @@ -214,8 +210,8 @@ $component-background: $fusion-component-background-color; :root { --font-family: 'Titillium Web', -apple-system, BlinkMacSystemFont, 'Segoe UI', - Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', - 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; } // Top-level styles @@ -245,7 +241,6 @@ h1 { flex-direction: row; } - .ant-tag-blue { background-color: fade($fusion-secondary-color, 10%); color: $fusion-secondary-color; From 6fb414a488941d227293edff85f8a2b53af48030 Mon Sep 17 00:00:00 2001 From: Bilal Meddah Date: Tue, 28 May 2024 12:21:59 +0200 Subject: [PATCH 10/11] update: refactor useFullTextSearch hook --- .../components/FullTextSearch/index.tsx | 53 ++----------------- .../FullTextSearch/useFullTextSearch.ts | 44 +++++++++++++++ 2 files changed, 49 insertions(+), 48 deletions(-) create mode 100644 src/shared/components/FullTextSearch/useFullTextSearch.ts diff --git a/src/shared/components/FullTextSearch/index.tsx b/src/shared/components/FullTextSearch/index.tsx index bdc3d24bc..8cb7dd143 100644 --- a/src/shared/components/FullTextSearch/index.tsx +++ b/src/shared/components/FullTextSearch/index.tsx @@ -1,19 +1,14 @@ -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { Link } from 'react-router-dom'; -import { useQuery } from 'react-query'; import CommandPalette from 'react-cmdk'; -import { useNexusContext } from '@bbp/react-nexus'; import { Spin, Tag, Empty } from 'antd'; -import { groupBy } from 'lodash'; +import { LoadingOutlined } from '@ant-design/icons'; + +import { getNormalizedTypes, getResourceLabel } from 'shared/utils'; +import { useFullTextSearch } from './useFullTextSearch'; -import { - getNormalizedTypes, - getOrgAndProjectFromProjectId, - getResourceLabel, -} from 'shared/utils'; import 'react-cmdk/dist/cmdk.css'; import './styles.scss'; -import { LoadingOutlined } from '@ant-design/icons'; type Props = { openCmdk: boolean; @@ -34,44 +29,6 @@ const TagRenderer = ({ type }: { type?: string | string[] }) => { ); }; -export function useFullTextSearch() { - const [search, setSearch] = useState(''); - const nexus = useNexusContext(); - - const onSearch = (value: string) => setSearch(value); - const resetSearch = () => setSearch(''); - - const { isLoading, data } = useQuery({ - enabled: !!search, - queryKey: ['cmdk-search', { search }], - queryFn: () => - nexus.Resource.list(undefined, undefined, { - q: search, - deprecated: false, - }), - select: data => data._results, - staleTime: 2, - }); - const resources = groupBy(data, '_project'); - - const searchResults = Object.entries(resources).map(([key, value]) => { - const orgProject = getOrgAndProjectFromProjectId(key); - return { - id: key, - title: orgProject, - items: value, - }; - }); - - return { - search, - onSearch, - resetSearch, - isLoading, - searchResults, - }; -} - const FullTextSearch = ({ openCmdk, onOpenCmdk }: Props) => { const { search, diff --git a/src/shared/components/FullTextSearch/useFullTextSearch.ts b/src/shared/components/FullTextSearch/useFullTextSearch.ts new file mode 100644 index 000000000..a9eace3ce --- /dev/null +++ b/src/shared/components/FullTextSearch/useFullTextSearch.ts @@ -0,0 +1,44 @@ +import { useState } from 'react'; +import { useNexusContext } from '@bbp/react-nexus'; +import { useQuery } from 'react-query'; +import { getOrgAndProjectFromProjectId } from 'shared/utils'; +import { groupBy } from 'lodash'; + +export function useFullTextSearch() { + const [search, setSearch] = useState(''); + const nexus = useNexusContext(); + + const onSearch = (value: string) => setSearch(value); + const resetSearch = () => setSearch(''); + + const { isLoading, data } = useQuery({ + enabled: !!search, + queryKey: ['cmdk-search', { search }], + queryFn: () => + nexus.Resource.list(undefined, undefined, { + q: search, + deprecated: false, + }), + select: data => data._results, + staleTime: 2, + }); + + const resources = groupBy(data, '_project'); + + const searchResults = Object.entries(resources).map(([key, value]) => { + const orgProject = getOrgAndProjectFromProjectId(key); + return { + id: key, + title: orgProject, + items: value, + }; + }); + + return { + search, + onSearch, + resetSearch, + isLoading, + searchResults, + }; +} From 564267ec2ac77d930039cf330e088e4f4d04d021 Mon Sep 17 00:00:00 2001 From: Bilal Meddah Date: Tue, 28 May 2024 14:14:44 +0200 Subject: [PATCH 11/11] update: fix dark mode for cmdk --- .../components/FullTextSearch/styles.scss | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/shared/components/FullTextSearch/styles.scss b/src/shared/components/FullTextSearch/styles.scss index 63ded4ffc..48b614273 100644 --- a/src/shared/components/FullTextSearch/styles.scss +++ b/src/shared/components/FullTextSearch/styles.scss @@ -67,4 +67,32 @@ padding: 10px; color: #333; user-select: none; + border-radius: 4px; +} + +@media (prefers-color-scheme: dark) { + .cmdk-list-item { + background: white; + color: black; + + &:hover { + background: #efefef !important; + box-shadow: 0 2px 12px 0px rgba($color: white, $alpha: 0.14); + color: black; + .item-title { + color: #333; + } + } + } + + .search-for-placeholder { + background-color: white; + color: #101827; + } + + .ant-empty { + .ant-empty-description { + color: white; + } + } }