From 7f43436e9443dcbac708b11c710fe616a817e0f3 Mon Sep 17 00:00:00 2001 From: Christoph Anderson Date: Thu, 30 Mar 2023 10:44:35 +0200 Subject: [PATCH 01/10] Commited first draft to theming in kopia. - Changed variants of buttons to unify the look and feel - Added support for themes specified in Theme.css - Added a new dependency "react-select" for selecting themes. --- package-lock.json | 447 ++++++++++++++++++++------ package.json | 1 + src/App.css | 103 +++++- src/App.jsx | 12 +- src/NewSnapshot.jsx | 4 +- src/SetupRepository.jsx | 14 +- src/SnapshotsTable.jsx | 6 +- src/SourcesTable.jsx | 9 +- src/Theme.css | 64 ++++ src/ThemeSelector.tsx | 30 ++ src/ToggleDarkModeButton.tsx | 6 +- src/contexts/UIPreferencesContext.tsx | 6 +- src/uiutil.jsx | 6 +- 13 files changed, 577 insertions(+), 131 deletions(-) create mode 100644 src/Theme.css create mode 100644 src/ThemeSelector.tsx diff --git a/package-lock.json b/package-lock.json index 74374b0..d07b38a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "react-bootstrap": "^2.5.0", "react-dom": "^18.2.0", "react-router-dom": "^5.3.0", + "react-select": "^5.7.2", "react-table": "^7.8.0" }, "devDependencies": { @@ -57,7 +58,6 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, "dependencies": { "@babel/highlight": "^7.16.7" }, @@ -378,7 +378,6 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, "dependencies": { "@babel/types": "^7.16.7" }, @@ -496,7 +495,6 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -543,7 +541,6 @@ "version": "7.17.12", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", @@ -557,7 +554,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -569,7 +565,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -583,7 +578,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -591,14 +585,12 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -607,7 +599,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -616,7 +607,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -2076,7 +2066,6 @@ "version": "7.18.4", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.4.tgz", "integrity": "sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", "to-fast-properties": "^2.0.0" @@ -2328,6 +2317,117 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz", + "integrity": "sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.0", + "@emotion/memoize": "^0.8.0", + "@emotion/serialize": "^1.1.1", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.1.3" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.10.5", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.5.tgz", + "integrity": "sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==", + "dependencies": { + "@emotion/memoize": "^0.8.0", + "@emotion/sheet": "^1.2.1", + "@emotion/utils": "^1.2.0", + "@emotion/weak-memoize": "^0.3.0", + "stylis": "4.1.3" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", + "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" + }, + "node_modules/@emotion/memoize": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", + "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + }, + "node_modules/@emotion/react": { + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz", + "integrity": "sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.10.6", + "@emotion/cache": "^11.10.5", + "@emotion/serialize": "^1.1.1", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", + "@emotion/utils": "^1.2.0", + "@emotion/weak-memoize": "^0.3.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==", + "dependencies": { + "@emotion/hash": "^0.9.0", + "@emotion/memoize": "^0.8.0", + "@emotion/unitless": "^0.8.0", + "@emotion/utils": "^1.2.0", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.1.tgz", + "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", + "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", + "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", + "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" + }, "node_modules/@eslint/eslintrc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", @@ -2393,6 +2493,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@floating-ui/core": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.5.tgz", + "integrity": "sha512-qrcbyfnRVziRlB6IYwjCopYhO7Vud750JlJyuljruIXcPxr22y8zdckcJGsuOdnQ639uVD1tTXddrcH3t3QYIQ==" + }, + "node_modules/@floating-ui/dom": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.5.tgz", + "integrity": "sha512-+sAUfpQ3Frz+VCbPCqj+cZzvEESy3fjSeT/pDWkYCWOBXYNNKZfuVsHuv8/JO2zze8+Eb/Q7a6hZVgzS81fLbQ==", + "dependencies": { + "@floating-ui/core": "^1.2.4" + } + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz", @@ -3702,8 +3815,7 @@ "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "node_modules/@types/prettier": { "version": "2.6.3", @@ -4827,7 +4939,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", @@ -5225,7 +5336,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -5710,7 +5820,6 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, "dependencies": { "safe-buffer": "~5.1.1" } @@ -5785,7 +5894,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -6701,7 +6809,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -6809,7 +6916,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -7863,6 +7969,11 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -8102,8 +8213,7 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -8355,7 +8465,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -8758,7 +8867,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -8774,7 +8882,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -8872,8 +8979,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-bigint": { "version": "1.0.4", @@ -8954,7 +9060,6 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", - "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -10308,8 +10413,7 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema": { "version": "0.4.0", @@ -10451,8 +10555,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/loader-runner": { "version": "4.3.0", @@ -10638,6 +10741,11 @@ "node": ">= 4.0.0" } }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -11273,7 +11381,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -11285,7 +11392,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -11354,8 +11460,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-to-regexp": { "version": "1.8.0", @@ -11369,7 +11474,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -13295,6 +13399,26 @@ } } }, + "node_modules/react-select": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.2.tgz", + "integrity": "sha512-cTlJkQ8YjV6T/js8wW0owTzht0hHGABh29vjLscY4HfZGkv7hc3FFTmRp9NzY/Ib1uQ36GieAKEjxpHdpCFpcA==", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-table": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", @@ -13542,7 +13666,6 @@ "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, "dependencies": { "is-core-module": "^2.8.1", "path-parse": "^1.0.7", @@ -13754,8 +13877,7 @@ "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -14473,6 +14595,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz", + "integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -14502,7 +14629,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -14894,7 +15020,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, "engines": { "node": ">=4" } @@ -15212,6 +15337,19 @@ "punycode": "^2.1.0" } }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -16201,7 +16339,6 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, "engines": { "node": ">= 6" } @@ -16267,7 +16404,6 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, "requires": { "@babel/highlight": "^7.16.7" } @@ -16509,7 +16645,6 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, "requires": { "@babel/types": "^7.16.7" } @@ -16599,8 +16734,7 @@ "@babel/helper-validator-identifier": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==" }, "@babel/helper-validator-option": { "version": "7.16.7", @@ -16635,7 +16769,6 @@ "version": "7.17.12", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", @@ -16646,7 +16779,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -16655,7 +16787,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -16666,7 +16797,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -16674,26 +16804,22 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -17676,7 +17802,6 @@ "version": "7.18.4", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.4.tgz", "integrity": "sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", "to-fast-properties": "^2.0.0" @@ -17812,6 +17937,106 @@ "dev": true, "requires": {} }, + "@emotion/babel-plugin": { + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz", + "integrity": "sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==", + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.0", + "@emotion/memoize": "^0.8.0", + "@emotion/serialize": "^1.1.1", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.1.3" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + } + } + }, + "@emotion/cache": { + "version": "11.10.5", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.5.tgz", + "integrity": "sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==", + "requires": { + "@emotion/memoize": "^0.8.0", + "@emotion/sheet": "^1.2.1", + "@emotion/utils": "^1.2.0", + "@emotion/weak-memoize": "^0.3.0", + "stylis": "4.1.3" + } + }, + "@emotion/hash": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", + "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" + }, + "@emotion/memoize": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", + "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + }, + "@emotion/react": { + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz", + "integrity": "sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.10.6", + "@emotion/cache": "^11.10.5", + "@emotion/serialize": "^1.1.1", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", + "@emotion/utils": "^1.2.0", + "@emotion/weak-memoize": "^0.3.0", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==", + "requires": { + "@emotion/hash": "^0.9.0", + "@emotion/memoize": "^0.8.0", + "@emotion/unitless": "^0.8.0", + "@emotion/utils": "^1.2.0", + "csstype": "^3.0.2" + } + }, + "@emotion/sheet": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.1.tgz", + "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==" + }, + "@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, + "@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", + "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", + "requires": {} + }, + "@emotion/utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", + "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" + }, + "@emotion/weak-memoize": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", + "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" + }, "@eslint/eslintrc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", @@ -17861,6 +18086,19 @@ } } }, + "@floating-ui/core": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.5.tgz", + "integrity": "sha512-qrcbyfnRVziRlB6IYwjCopYhO7Vud750JlJyuljruIXcPxr22y8zdckcJGsuOdnQ639uVD1tTXddrcH3t3QYIQ==" + }, + "@floating-ui/dom": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.5.tgz", + "integrity": "sha512-+sAUfpQ3Frz+VCbPCqj+cZzvEESy3fjSeT/pDWkYCWOBXYNNKZfuVsHuv8/JO2zze8+Eb/Q7a6hZVgzS81fLbQ==", + "requires": { + "@floating-ui/core": "^1.2.4" + } + }, "@fortawesome/fontawesome-common-types": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz", @@ -18852,8 +19090,7 @@ "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "@types/prettier": { "version": "2.6.3", @@ -19719,7 +19956,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dev": true, "requires": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", @@ -20034,8 +20270,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, "camel-case": { "version": "4.1.2", @@ -20408,7 +20643,6 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -20465,7 +20699,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, "requires": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -21133,7 +21366,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -21225,8 +21457,7 @@ "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "escodegen": { "version": "2.0.0", @@ -22032,6 +22263,11 @@ "pkg-dir": "^4.1.0" } }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -22190,8 +22426,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "function.prototype.name": { "version": "1.1.5", @@ -22379,7 +22614,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -22686,7 +22920,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -22695,8 +22928,7 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" } } }, @@ -22772,8 +23004,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "is-bigint": { "version": "1.0.4", @@ -22819,7 +23050,6 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -23838,8 +24068,7 @@ "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema": { "version": "0.4.0", @@ -23949,8 +24178,7 @@ "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "loader-runner": { "version": "4.3.0", @@ -24102,6 +24330,11 @@ "fs-monkey": "1.0.3" } }, + "memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -24565,7 +24798,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "requires": { "callsites": "^3.0.0" } @@ -24574,7 +24806,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -24625,8 +24856,7 @@ "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-to-regexp": { "version": "1.8.0", @@ -24639,8 +24869,7 @@ "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, "performance-now": { "version": "2.1.0", @@ -25917,6 +26146,22 @@ "workbox-webpack-plugin": "^6.4.1" } }, + "react-select": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.2.tgz", + "integrity": "sha512-cTlJkQ8YjV6T/js8wW0owTzht0hHGABh29vjLscY4HfZGkv7hc3FFTmRp9NzY/Ib1uQ36GieAKEjxpHdpCFpcA==", + "requires": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + } + }, "react-table": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", @@ -26111,7 +26356,6 @@ "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, "requires": { "is-core-module": "^2.8.1", "path-parse": "^1.0.7", @@ -26251,8 +26495,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", @@ -26809,6 +27052,11 @@ "postcss-selector-parser": "^6.0.4" } }, + "stylis": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz", + "integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==" + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -26831,8 +27079,7 @@ "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "svg-parser": { "version": "2.0.4", @@ -27133,8 +27380,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" }, "to-regex-range": { "version": "5.0.1", @@ -27375,6 +27621,12 @@ "punycode": "^2.1.0" } }, + "use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -28178,8 +28430,7 @@ "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, "yargs": { "version": "16.2.0", diff --git a/package.json b/package.json index 102b845..286cafa 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "react-bootstrap": "^2.5.0", "react-dom": "^18.2.0", "react-router-dom": "^5.3.0", + "react-select": "^5.7.2", "react-table": "^7.8.0" }, "scripts": { diff --git a/src/App.css b/src/App.css index e636f69..51bea16 100644 --- a/src/App.css +++ b/src/App.css @@ -4,6 +4,105 @@ html.loading { body { line-height: 1; + color: var(--text-color-body); + background-color: var(--background-color); +} + +.options-select { + overflow: ellipsis +} + +.navbar-light .navbar-nav .nav-link { + color: var(--text-color-nav); +} + +.navbar-light .navbar-nav .nav-link.active, .navbar-light .navbar-nav .show>.nav-link { + color: var(--text-color-nav); +} + +.btn { + color: var(--text-color); + margin-right: 5px; +} + +.btn-primary { + background-color: var(--color-primary); + border-color: var(--color-primary); +} + +.btn-secondary { + background-color: var(--color-secondary); + border-color: var(--color-secondary); +} + +.btn-submit { + background-color: var(--color-submit); + border-color: var(--color-submit); +} + +.btn-success { + background-color: var(--color-success); + border-color: var(--color-success); +} + +.btn-warning { + background-color: var(--color-warning); + border-color: var(--color-primary); +} + +.btn-danger { + background-color: var(--color-danger); + border-color: var(--color-danger); +} + +.select { + color: black; +} + +.table { + color: var(--text-color-body); +} + +.btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open>.dropdown-toggle.btn-primary { + background-color: var(--color-primary); + border-color: var(--color-primary); + color: var(--text-color); + filter: brightness(80%); +} + +.btn-secondary:hover, .btn-secondary:focus, .btn-secondary:active, .btn-secondary.active, .open>.dropdown-toggle.btn-secondary { + background-color: var(--color-secondary); + border-color: var(--color-secondary); + color: var(--text-color); + filter: brightness(80%); +} + +.btn-warning:hover, .btn-warning:focus, .btn-warning:active, .btn-warning.active, .open>.dropdown-toggle.btn-warning { + background-color: var(--color-warning); + border-color: var(--color-warning); + color: var(--text-color); + filter: brightness(80%); +} + +.btn-success:hover, .btn-success:focus, .btn-success:active, .btn-success.active, .open>.dropdown-toggle.btn-success { + background-color: var(--color-success); + border-color: var(--color-success); + color: var(--text-color); + filter: brightness(80%); +} + +.btn-danger:hover, .btn-danger:focus, .btn-danger:active, .btn-danger.active, .open>.dropdown-toggle.btn-danger { + background-color: var(--color-danger); + border-color: var(--color-danger); + color: var(--text-color); + filter: brightness(80%); +} + +.btn-submit:hover, .btn-submit:focus, .btn-submit:active, .btn-submit.active, .open>.dropdown-toggle.btn-submit { + background-color: var(--color-submit); + border-color: var(--color-submit); + color: var(--text-color); + filter: brightness(80%); } .popover { @@ -78,7 +177,7 @@ body { } .error { - color: red; + color: var(--color_red); } .normal { @@ -146,7 +245,7 @@ div.tab-body { .loglevel-0 { } /* debug */ .loglevel-1 { font-weight: bold;} /* info */ .loglevel-2 { color: rgb(169, 112, 5); font-weight: bold;} /* warning */ -.loglevel-3 { color: red; font-weight: bold; } /* error */ +.loglevel-3 { color: var(--color-error); font-weight: bold; } /* error */ .counter-badge { font-size: 90%; diff --git a/src/App.jsx b/src/App.jsx index 4a9ca6e..a9ff719 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,10 +1,9 @@ import axios from 'axios'; import 'bootstrap-dark-5/dist/css/bootstrap-nightshade.min.css'; -import React, { Component } from 'react'; -import Container from 'react-bootstrap/Container'; -import Nav from 'react-bootstrap/Nav'; -import Navbar from 'react-bootstrap/Navbar'; +import { React, Component } from 'react'; +import { Navbar, Nav, Container} from 'react-bootstrap'; import { BrowserRouter as Router, NavLink, Redirect, Route, Switch } from 'react-router-dom'; +import './Theme.css'; import './App.css'; import { BeginRestore } from './BeginRestore'; import { DirectoryObject } from "./DirectoryObject"; @@ -16,8 +15,9 @@ import { TaskDetails } from './TaskDetails'; import { TasksTable } from './TasksTable'; import { NewSnapshot } from './NewSnapshot'; import { PolicyEditorPage } from './PolicyEditorPage'; -import { ToggleDarkModeButton } from './ToggleDarkModeButton'; +import { ThemeSelector } from './ThemeSelector'; import { AppContext } from './AppContext'; + import { UIPreferenceProvider } from './contexts/UIPreferencesContext'; export default class App extends Component { @@ -112,7 +112,7 @@ export default class App extends Component { Repository diff --git a/src/NewSnapshot.jsx b/src/NewSnapshot.jsx index ef53fb9..71f148d 100644 --- a/src/NewSnapshot.jsx +++ b/src/NewSnapshot.jsx @@ -166,7 +166,7 @@ export class NewSnapshot extends Component { size="sm" disabled={!this.state.resolvedSource?.path} title="Estimate" - variant="primary" + variant="secondary" onClick={this.estimate}>Estimate   diff --git a/src/SetupRepository.jsx b/src/SetupRepository.jsx index b0cff35..b8b1688 100644 --- a/src/SetupRepository.jsx +++ b/src/SetupRepository.jsx @@ -229,7 +229,7 @@ export class SetupRepository extends Component { )} @@ -313,7 +313,7 @@ export class SetupRepository extends Component { {this.connectionErrorInfo()}
- +   {this.loadingSpinner()} @@ -326,7 +326,7 @@ export class SetupRepository extends Component { const text = this.state.showAdvanced ? "Hide Advanced Options" : "Show Advanced Options"; return +   - + {this.loadingSpinner()} ; } @@ -493,9 +493,9 @@ export class SetupRepository extends Component { {this.connectionErrorInfo()}
- +   - + {this.loadingSpinner()} ; } diff --git a/src/SnapshotsTable.jsx b/src/SnapshotsTable.jsx index 54f0a55..397cc56 100644 --- a/src/SnapshotsTable.jsx +++ b/src/SnapshotsTable.jsx @@ -450,7 +450,7 @@ export class SnapshotsTable extends Component { - + @@ -473,7 +473,7 @@ export class SnapshotsTable extends Component { {this.state.savingSnapshot && } {this.state.originalSnapshotDescription && } - + @@ -496,7 +496,7 @@ export class SnapshotsTable extends Component { {this.state.savingSnapshot && } {this.state.originalPinName && } - + ; diff --git a/src/SourcesTable.jsx b/src/SourcesTable.jsx index 4369379..1d44c4e 100644 --- a/src/SourcesTable.jsx +++ b/src/SourcesTable.jsx @@ -93,10 +93,11 @@ export class SourcesTable extends Component { case "IDLE": case "PAUSED": return <> - + -   + }}>Snapshot Now + ; case "PENDING": @@ -272,7 +273,7 @@ export class SourcesTable extends Component { } - + diff --git a/src/Theme.css b/src/Theme.css new file mode 100644 index 0000000..26356da --- /dev/null +++ b/src/Theme.css @@ -0,0 +1,64 @@ +/*Supported themes */ +.light-theme { + --background-color: #ffffff; + --color-primary: #2A7FFF; + --color-secondary: #23a25a; + --color-warning: #e1704b; + --color-success: #5ea5cd; + --color-submit: #00B4D8; + + --text-color: #ffffff; + --text-color-body: #000000; + --text-color-nav: #000000; + + --color-error: #d2475a; + --color-danger: #d2475a; +} + +.dark-theme { + --background-color: #000000; + --color-primary: #1162b1; + --color-secondary: #0d9aab; + --color-warning: #b30996; + --color-success: #030303; + --color-submit: #00B4D8; + + --text-color: #ffffff; + --text-color-body: #ffffff; + --text-color-nav: #ffffff; + + --color-error: #b31f04; + --color-danger: #b31f04; +} + +.pastel-theme { + --background-color: #ffffff; + --color-primary: #9feae7; + --color-secondary: #d7a5e9; + --color-warning: #ffe070; + --color-success: #d7a5e9; + --color-submit: #00B4D8; + + --text-color: #000000; + --text-color-body: #000000; + --text-color-nav: #d7a5e9; + + --color-error: #b31f04; + --color-danger: #b31f04; +} + +.ocean-theme { + --background-color: #ffffff; + --color-primary: #03045E; + --color-secondary: #0077B6; + --color-warning: #00B4D8; + --color-success: #48CAE4; + --color-submit: #00B4D8; + + --text-color: #ffffff; + --text-color-body: #03045E; + --text-color-nav: #03045E; + + --color-error: #d2475a; + --color-danger: #d2475a; +} \ No newline at end of file diff --git a/src/ThemeSelector.tsx b/src/ThemeSelector.tsx new file mode 100644 index 0000000..013ba49 --- /dev/null +++ b/src/ThemeSelector.tsx @@ -0,0 +1,30 @@ +import Select from 'react-select' +import { useContext } from 'react'; +import { Theme, UIPreferencesContext } from './contexts/UIPreferencesContext'; + +export function ThemeSelector() { + const { theme, setTheme } = useContext(UIPreferencesContext); + + updateTheme(theme) + + const themes = [ + { value: 'light-theme', label: 'light' }, + { value: 'dark-theme', label: 'dark' }, + { value: 'pastel-theme', label: 'pastel' }, + { value: 'ocean-theme', label: 'ocean' } + ] + + const handleTheme = (event: any) => { + var selectedTheme = event.value; + // keep html class in sync with button state. + updateTheme(selectedTheme) + setTheme(selectedTheme) + }; + + return + return { return ( @@ -32,6 +38,10 @@ export function ThemeSelector() { }}}/> } +/** + * Keeps the html in sync with the current selected theme + * @param theme + */ function updateTheme(theme: Theme) { var doc = document.querySelector("html")!; doc.className = theme diff --git a/src/ToggleDarkModeButton.tsx b/src/ToggleDarkModeButton.tsx deleted file mode 100644 index 70b2937..0000000 --- a/src/ToggleDarkModeButton.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { faMoon, faSun } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { useContext } from 'react'; -import Button from 'react-bootstrap/Button'; -import { UIPreferencesContext } from './contexts/UIPreferencesContext'; - -export function ToggleDarkModeButton() { - const { theme, setTheme } = useContext(UIPreferencesContext); - - // keep html class in sync with button state. - const h = document.querySelector("html")!; - h.className = theme; - - return theme === "dark-theme" - ? - : ; -} diff --git a/src/contexts/UIPreferencesContext.tsx b/src/contexts/UIPreferencesContext.tsx index 68fc4ee..873b0a0 100644 --- a/src/contexts/UIPreferencesContext.tsx +++ b/src/contexts/UIPreferencesContext.tsx @@ -26,9 +26,12 @@ export interface UIPreferenceProviderProps { initalValue: UIPreferences | undefined } +/** + * Returns a default theme based on the user's browser settings. + * @returns Theme + */ function getDefaultTheme(): Theme { if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { - // browser supports light/dark mode and user prefers dark theme. return "dark-theme"; } From f7b80d7351085f7a7ff0ae883a62b695e2ed643f Mon Sep 17 00:00:00 2001 From: Christoph Anderson Date: Sun, 7 May 2023 15:52:07 +0200 Subject: [PATCH 04/10] Added some minor code refactorings. This PR comes with the following fixes: - Pastel theme should now be darker - Added a preference tab - Font size of the selector has been reduced - Fixed css - Moved AppContext under src/contexts - Renamed themes to provide backwards compatibility --- src/App.css | 14 ++++--- src/App.jsx | 23 +++++------ src/EstimateResults.jsx | 4 +- src/NewSnapshot.jsx | 6 +-- src/PoliciesTable.jsx | 6 +-- src/Preferences.jsx | 41 +++++++++++++++++++ src/RepoStatus.jsx | 2 +- src/SetupRepository.jsx | 2 +- src/SnapshotsTable.jsx | 8 ++-- src/SourcesTable.jsx | 4 +- src/TaskDetails.jsx | 4 +- src/TaskLogs.jsx | 5 +-- src/TasksTable.jsx | 4 +- src/Theme.css | 24 +++++------ src/ThemeSelector.tsx | 35 ++++------------ .../AppContext.tsx} | 0 src/contexts/UIPreferencesContext.tsx | 39 ++++++++++++------ src/uiutil.jsx | 21 ++++++---- 18 files changed, 139 insertions(+), 103 deletions(-) create mode 100644 src/Preferences.jsx rename src/{AppContext.js => contexts/AppContext.tsx} (100%) diff --git a/src/App.css b/src/App.css index c154ef7..88a750e 100644 --- a/src/App.css +++ b/src/App.css @@ -11,11 +11,8 @@ body { color: var(--text-color-body); } -.select { - color: var(--text-color-selector); -} - -.select select:hover { +.select_theme { + font-size: 90%; color: var(--text-color-selector); } @@ -50,7 +47,7 @@ body { .btn-warning { background-color: var(--color-warning); - border-color: var(--color-primary); + border-color: var(--color-warning); } .btn-danger { @@ -136,6 +133,11 @@ body { font-weight: bold; } +.label-description { + padding-top: 4px; + padding-bottom: 8px; +} + .label-help { padding-top: 4px; padding-bottom: 8px; diff --git a/src/App.jsx b/src/App.jsx index 03848d2..6e9ee7f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,10 +1,10 @@ -import axios from 'axios'; import 'bootstrap/dist/css/bootstrap.min.css'; -import { React, Component } from 'react'; -import { Navbar, Nav, Container} from 'react-bootstrap'; -import { BrowserRouter as Router, NavLink, Redirect, Route, Switch } from 'react-router-dom'; import './Theme.css'; import './App.css'; +import axios from 'axios'; +import { React, Component } from 'react'; +import { Navbar, Nav, Container } from 'react-bootstrap'; +import { BrowserRouter as Router, NavLink, Redirect, Route, Switch } from 'react-router-dom'; import { BeginRestore } from './BeginRestore'; import { DirectoryObject } from "./DirectoryObject"; import { PoliciesTable } from "./PoliciesTable"; @@ -15,9 +15,8 @@ import { TaskDetails } from './TaskDetails'; import { TasksTable } from './TasksTable'; import { NewSnapshot } from './NewSnapshot'; import { PolicyEditorPage } from './PolicyEditorPage'; -import { ThemeSelector } from './ThemeSelector'; -import { AppContext } from './AppContext'; - +import { Preferences } from './Preferences'; +import { AppContext } from './contexts/AppContext'; import { UIPreferenceProvider } from './contexts/UIPreferencesContext'; export default class App extends Component { @@ -50,7 +49,6 @@ export default class App extends Component { } this.fetchInitialRepositoryDescription(); - this.taskSummaryInterval = window.setInterval(this.fetchTaskSummary, 5000); } @@ -61,7 +59,7 @@ export default class App extends Component { repoDescription: result.data.description, }); } - }).catch(error => { /* ignore */}); + }).catch(error => { /* ignore */ }); } fetchTaskSummary() { @@ -110,9 +108,7 @@ export default class App extends Component { Repository - - @@ -133,8 +129,9 @@ export default class App extends Component { + - + diff --git a/src/EstimateResults.jsx b/src/EstimateResults.jsx index 9d9d5a8..24d5fee 100644 --- a/src/EstimateResults.jsx +++ b/src/EstimateResults.jsx @@ -7,7 +7,7 @@ import Button from 'react-bootstrap/Button'; import Spinner from 'react-bootstrap/esm/Spinner'; import Form from 'react-bootstrap/Form'; import { TaskLogs } from './TaskLogs'; -import { cancelTask, redirectIfNotConnected, sizeDisplayName } from './uiutil'; +import { cancelTask, redirect, sizeDisplayName } from './uiutil'; export class EstimateResults extends Component { constructor() { @@ -55,7 +55,7 @@ export class EstimateResults extends Component { this.interval = null; } }).catch(error => { - redirectIfNotConnected(error); + redirect(error); this.setState({ error, isLoading: false diff --git a/src/NewSnapshot.jsx b/src/NewSnapshot.jsx index 71f148d..1a62fd9 100644 --- a/src/NewSnapshot.jsx +++ b/src/NewSnapshot.jsx @@ -7,7 +7,7 @@ import Row from 'react-bootstrap/Row'; import { handleChange } from './forms'; import { PolicyEditor } from './PolicyEditor'; import { EstimateResults } from './EstimateResults'; -import { CLIEquivalent, DirectorySelector, errorAlert, GoBackButton, redirectIfNotConnected } from './uiutil'; +import { CLIEquivalent, DirectorySelector, errorAlert, GoBackButton, redirect } from './uiutil'; export class NewSnapshot extends Component { constructor() { @@ -35,7 +35,7 @@ export class NewSnapshot extends Component { localHost: result.data.localHost, }); }).catch(error => { - redirectIfNotConnected(error); + redirect(error); }); } @@ -54,7 +54,7 @@ export class NewSnapshot extends Component { // while we were resolving this.maybeResolveCurrentPath(currentPath); }).catch(error => { - redirectIfNotConnected(error); + redirect(error); }); } else { this.setState({ diff --git a/src/PoliciesTable.jsx b/src/PoliciesTable.jsx index 283012f..c7aac11 100644 --- a/src/PoliciesTable.jsx +++ b/src/PoliciesTable.jsx @@ -11,7 +11,7 @@ import Row from 'react-bootstrap/Row'; import { Link } from 'react-router-dom'; import { handleChange } from './forms'; import MyTable from './Table'; -import { CLIEquivalent, compare, DirectorySelector, isAbsolutePath, ownerName, policyEditorURL, redirectIfNotConnected } from './uiutil'; +import { CLIEquivalent, compare, DirectorySelector, isAbsolutePath, ownerName, policyEditorURL, redirect } from './uiutil'; const applicablePolicies = "Applicable Policies" const localPolicies = "Local Path Policies" @@ -68,7 +68,7 @@ export class PoliciesTable extends Component { isLoading: false, }); }).catch(error => { - redirectIfNotConnected(error); + redirect(error); this.setState({ error, isLoading: false @@ -87,7 +87,7 @@ export class PoliciesTable extends Component { isLoading: false, }); }).catch(error => { - redirectIfNotConnected(error); + redirect(error); this.setState({ error, isLoading: false diff --git a/src/Preferences.jsx b/src/Preferences.jsx new file mode 100644 index 0000000..348ac31 --- /dev/null +++ b/src/Preferences.jsx @@ -0,0 +1,41 @@ +import { Component } from 'react'; +import { ThemeSelector } from './ThemeSelector'; +import { UIPreferencesContext } from './contexts/UIPreferencesContext'; + +export class Preferences extends Component { + static contextType = UIPreferencesContext; + constructor() { + super(); + this.state = { + status: {}, + error: null, + }; + } + + render() { + const { pageSize, bytesStringBase2 } = this.context; + return <> +
+
+ + + The current active theme +
+
+
+ + + Represents bytes to the base of 2 +
+
+
+ + + The number of items shown in tables +
+
+ + } +} \ No newline at end of file diff --git a/src/RepoStatus.jsx b/src/RepoStatus.jsx index b95e324..b7dc6e2 100644 --- a/src/RepoStatus.jsx +++ b/src/RepoStatus.jsx @@ -13,7 +13,7 @@ import { cancelTask, CLIEquivalent } from './uiutil'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCheck, faChevronCircleDown, faChevronCircleUp, faWindowClose } from '@fortawesome/free-solid-svg-icons'; import { TaskLogs } from './TaskLogs'; -import { AppContext } from './AppContext'; +import { AppContext } from './contexts/AppContext'; export class RepoStatus extends Component { constructor() { diff --git a/src/SetupRepository.jsx b/src/SetupRepository.jsx index b8b1688..d172d72 100644 --- a/src/SetupRepository.jsx +++ b/src/SetupRepository.jsx @@ -8,7 +8,7 @@ import Collapse from 'react-bootstrap/Collapse'; import Form from 'react-bootstrap/Form'; import Row from 'react-bootstrap/Row'; import Spinner from 'react-bootstrap/Spinner'; -import { AppContext } from './AppContext'; +import { AppContext } from './contexts/AppContext'; import { handleChange, validateRequiredFields } from './forms'; import { RequiredBoolean } from './forms/RequiredBoolean'; import { RequiredField } from './forms/RequiredField'; diff --git a/src/SnapshotsTable.jsx b/src/SnapshotsTable.jsx index 397cc56..d4a4ea9 100644 --- a/src/SnapshotsTable.jsx +++ b/src/SnapshotsTable.jsx @@ -8,7 +8,7 @@ import Col from 'react-bootstrap/Col'; import Spinner from 'react-bootstrap/Spinner'; import { Link } from "react-router-dom"; import MyTable from './Table'; -import { CLIEquivalent, compare, errorAlert, GoBackButton, objectLink, parseQuery, redirectIfNotConnected, rfc3339TimestampForDisplay, sizeWithFailures, sourceQueryStringParams } from './uiutil'; +import { CLIEquivalent, compare, errorAlert, GoBackButton, objectLink, parseQuery, redirect, rfc3339TimestampForDisplay, sizeWithFailures, sourceQueryStringParams } from './uiutil'; import { faSync, faThumbtack } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import Modal from 'react-bootstrap/Modal'; @@ -142,7 +142,7 @@ export class SnapshotsTable extends Component { this.fetchSnapshots(); } }).catch(error => { - redirectIfNotConnected(error); + redirect(error); errorAlert(error); }); @@ -164,7 +164,7 @@ export class SnapshotsTable extends Component { axios.post('/api/v1/snapshots/delete', req).then(result => { this.props.history.goBack(); }).catch(error => { - redirectIfNotConnected(error); + redirect(error); errorAlert(error); }); } @@ -307,7 +307,7 @@ export class SnapshotsTable extends Component { editingDescriptionFor: undefined, savingSnapshot: false, }); - redirectIfNotConnected(e); + redirect(e); errorAlert(e); }); } diff --git a/src/SourcesTable.jsx b/src/SourcesTable.jsx index 1d44c4e..02a4479 100644 --- a/src/SourcesTable.jsx +++ b/src/SourcesTable.jsx @@ -12,7 +12,7 @@ import Spinner from 'react-bootstrap/Spinner'; import { Link } from 'react-router-dom'; import { handleChange } from './forms'; import MyTable from './Table'; -import { CLIEquivalent, compare, errorAlert, ownerName, policyEditorURL, redirectIfNotConnected, sizeDisplayName, sizeWithFailures, sourceQueryStringParams } from './uiutil'; +import { CLIEquivalent, compare, errorAlert, ownerName, policyEditorURL, redirect, sizeDisplayName, sizeWithFailures, sourceQueryStringParams } from './uiutil'; const localSnapshots = "Local Snapshots" const allSnapshots = "All Snapshots" @@ -60,7 +60,7 @@ export class SourcesTable extends Component { isRefreshing: false, }); }).catch(error => { - redirectIfNotConnected(error); + redirect(error); this.setState({ error, isRefreshing: false, diff --git a/src/TaskDetails.jsx b/src/TaskDetails.jsx index 6f5460a..7eef578 100644 --- a/src/TaskDetails.jsx +++ b/src/TaskDetails.jsx @@ -11,7 +11,7 @@ import Row from 'react-bootstrap/Row'; import Table from 'react-bootstrap/Table'; import Spinner from 'react-bootstrap/Spinner'; import { TaskLogs } from './TaskLogs'; -import { cancelTask, formatDuration, GoBackButton, redirectIfNotConnected, sizeDisplayName } from './uiutil'; +import { cancelTask, formatDuration, GoBackButton, redirect, sizeDisplayName } from './uiutil'; export class TaskDetails extends Component { constructor() { @@ -60,7 +60,7 @@ export class TaskDetails extends Component { this.interval = null; } }).catch(error => { - redirectIfNotConnected(error); + redirect(error); this.setState({ error, isLoading: false diff --git a/src/TaskLogs.jsx b/src/TaskLogs.jsx index 50f19be..38e5209 100644 --- a/src/TaskLogs.jsx +++ b/src/TaskLogs.jsx @@ -1,9 +1,8 @@ - import axios from 'axios'; import React, { Component } from 'react'; import Table from 'react-bootstrap/Table'; import { handleChange } from './forms'; -import { redirectIfNotConnected } from './uiutil'; +import { redirect } from './uiutil'; export class TaskLogs extends Component { constructor() { @@ -54,7 +53,7 @@ export class TaskLogs extends Component { this.scrollToBottom(); } }).catch(error => { - redirectIfNotConnected(error); + redirect(error); this.setState({ error, isLoading: false diff --git a/src/TasksTable.jsx b/src/TasksTable.jsx index 69f0be6..d4a9198 100644 --- a/src/TasksTable.jsx +++ b/src/TasksTable.jsx @@ -12,7 +12,7 @@ import Row from 'react-bootstrap/Row'; import { Link } from 'react-router-dom'; import { handleChange } from './forms'; import MyTable from './Table'; -import { redirectIfNotConnected, taskStatusSymbol } from './uiutil'; +import { redirect, taskStatusSymbol } from './uiutil'; export class TasksTable extends Component { constructor() { @@ -66,7 +66,7 @@ export class TasksTable extends Component { isLoading: false, }); }).catch(error => { - redirectIfNotConnected(error); + redirect(error); this.setState({ error, isLoading: false diff --git a/src/Theme.css b/src/Theme.css index 6eb4084..d644143 100644 --- a/src/Theme.css +++ b/src/Theme.css @@ -1,5 +1,5 @@ /*Supported themes */ -.light-theme { +.light { --background-color: #ffffff; --color-primary: #2A7FFF; --color-secondary: #23a25a; @@ -16,7 +16,7 @@ --color-danger: #d2475a; } -.dark-theme { +.dark { --background-color: #343c43; --color-primary: #1162b1; --color-secondary: #0d9aab; @@ -33,24 +33,24 @@ --color-danger: #b31f04; } -.pastel-theme { +.pastel { --background-color: #ffffff; - --color-primary: #9feae7; - --color-secondary: #d7a5e9; - --color-warning: #ffe070; - --color-success: #d7a5e9; - --color-submit: #00B4D8; + --color-primary: #9bf6ff; + --color-secondary: #bdb2ff; + --color-warning: #ffd6a5; + --color-success: #bdb2ff; + --color-submit: #a0c4ff; --text-color: #000000; --text-color-selector: #000000; --text-color-body: #000000; - --text-color-nav: #d7a5e9; + --text-color-nav: #c095e4; - --color-error: #b31f04; - --color-danger: #b31f04; + --color-error: #ffadad; + --color-danger: #ffadad; } -.ocean-theme { +.ocean { --background-color: #ffffff; --color-primary: #03045E; --color-secondary: #0077B6; diff --git a/src/ThemeSelector.tsx b/src/ThemeSelector.tsx index 9db8ec4..e6eb0b6 100644 --- a/src/ThemeSelector.tsx +++ b/src/ThemeSelector.tsx @@ -1,21 +1,20 @@ -import Select, { components } from 'react-select' +import Select from 'react-select' import { useContext } from 'react'; -import { Theme, UIPreferencesContext } from './contexts/UIPreferencesContext'; +import { UIPreferencesContext } from './contexts/UIPreferencesContext'; export function ThemeSelector() { const { theme, setTheme } = useContext(UIPreferencesContext); //Contains all supported themes const themes = [ - { value: 'light-theme', label: 'light' }, - { value: 'dark-theme', label: 'dark' }, - { value: 'pastel-theme', label: 'pastel' }, - { value: 'ocean-theme', label: 'ocean' } + { value: 'light', label: 'light' }, + { value: 'dark', label: 'dark' }, + { value: 'pastel', label: 'pastel' }, + { value: 'ocean', label: 'ocean' } ] - + //Finds the current selected theme within supported themes const currentTheme = themes.find(o => o.value === theme); - updateTheme(theme) /** * Handles the theme selection by the user @@ -23,26 +22,8 @@ export function ThemeSelector() { */ const handleTheme = (event: any) => { var selectedTheme = event.value; - updateTheme(selectedTheme) setTheme(selectedTheme) }; - return } \ No newline at end of file diff --git a/src/AppContext.js b/src/contexts/AppContext.tsx similarity index 100% rename from src/AppContext.js rename to src/contexts/AppContext.tsx diff --git a/src/contexts/UIPreferencesContext.tsx b/src/contexts/UIPreferencesContext.tsx index 873b0a0..6a91506 100644 --- a/src/contexts/UIPreferencesContext.tsx +++ b/src/contexts/UIPreferencesContext.tsx @@ -2,9 +2,12 @@ import React, { ReactNode, useEffect, useState } from 'react'; import axios from 'axios'; export const PAGE_SIZES = [10, 20, 30, 40, 50, 100]; +export const UIPreferencesContext = React.createContext({} as UIPreferences); -export type Theme = "light-theme" | "dark-theme" | "pastel-theme" | "ocean-theme"; +const DEFAULT_PREFERENCES = { pageSize: PAGE_SIZES[0], theme: getDefaultTheme() } as SerializedUIPreferences; +const PREFERENCES_URL = '/api/v1/ui-preferences'; +export type Theme = "light" | "dark" | "pastel" | "ocean"; export type PageSize = 10 | 20 | 30 | 40 | 50 | 100; export interface UIPreferences { @@ -19,8 +22,6 @@ interface SerializedUIPreferences { theme: Theme | undefined } -export const UIPreferencesContext = React.createContext({} as UIPreferences); - export interface UIPreferenceProviderProps { children: ReactNode, initalValue: UIPreferences | undefined @@ -32,10 +33,9 @@ export interface UIPreferenceProviderProps { */ function getDefaultTheme(): Theme { if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { - return "dark-theme"; + return "dark"; } - - return "light-theme"; + return "light"; } function normalizePageSize(pageSize: number): PageSize { @@ -51,14 +51,9 @@ function normalizePageSize(pageSize: number): PageSize { return PAGE_SIZES[index - 1] as PageSize; } } - return 100; } -const PREFERENCES_URL = '/api/v1/ui-preferences'; - -const DEFAULT_PREFERENCES = { pageSize: PAGE_SIZES[0], theme: getDefaultTheme() } as SerializedUIPreferences; - export function UIPreferenceProvider(props: UIPreferenceProviderProps) { const [preferences, setPreferences] = useState(DEFAULT_PREFERENCES); @@ -73,9 +68,10 @@ export function UIPreferenceProvider(props: UIPreferenceProviderProps) { } else { storedPreferences.pageSize = normalizePageSize(storedPreferences.pageSize); } - + syncTheme(storedPreferences.theme) setPreferences(storedPreferences); }).catch(err => console.error(err)); + }, []); useEffect(() => { @@ -83,10 +79,27 @@ export function UIPreferenceProvider(props: UIPreferenceProviderProps) { return; } - axios.put(PREFERENCES_URL, preferences).then(result => {}).catch(err => console.error(err)); + axios.put(PREFERENCES_URL, preferences).then(result => { }).catch(err => console.error(err)); }, [preferences]); + /** + * Synchronizes the selected theme with the html class + * + * @param theme + * The theme to be set + */ + const syncTheme = (theme: Theme) => { + var doc = document.querySelector("html")!; + doc.className = theme + } + + /** + * + * @param theme + * @returns + */ const setTheme = (theme: Theme) => setPreferences(oldPreferences => { + syncTheme(theme); return { ...oldPreferences, theme }; }); diff --git a/src/uiutil.jsx b/src/uiutil.jsx index b13c17f..573e76d 100644 --- a/src/uiutil.jsx +++ b/src/uiutil.jsx @@ -9,24 +9,24 @@ import InputGroup from 'react-bootstrap/InputGroup'; import Spinner from 'react-bootstrap/Spinner'; import { Link } from 'react-router-dom'; -const base10UnitPrefixes = ["", "K", "M", "G", "T"]; - // locale to use for number formatting (undefined would use default locale, but we stick to EN for now) const locale = "en-US" -function niceNumber(f) { +const base10UnitPrefixes = ["", "K", "M", "G", "T"]; + +function formatNumber(f) { return (Math.round(f * 10) / 10.0) + ''; } function toDecimalUnitString(f, thousand, prefixes, suffix) { for (var i = 0; i < prefixes.length; i++) { if (f < 0.9 * thousand) { - return niceNumber(f) + ' ' + prefixes[i] + suffix; + return formatNumber(f) + ' ' + prefixes[i] + suffix; } f /= thousand } - return niceNumber(f) + ' ' + prefixes[prefixes.length - 1] + suffix; + return formatNumber(f) + ' ' + prefixes[prefixes.length - 1] + suffix; } export function sizeWithFailures(size, summ) { @@ -105,10 +105,13 @@ export function compare(a, b) { return (a < b ? -1 : (a > b ? 1 : 0)); } -export function redirectIfNotConnected(e) { - if (e && e.response && e.response.data && e.response.data.code === "NOT_CONNECTED") { - window.location.replace("/repo"); - return; +/** + * In case of an error, redirect to the repository selection + * @param {error} The error that was returned + */ +export function redirect(e) { + if (e.response.data.code === "NOT_CONNECTED") { + window.location.replace("/repo") } } From 7f0cba6b4dd5810d32ebee02d157310415804a65 Mon Sep 17 00:00:00 2001 From: Christoph Anderson Date: Mon, 8 May 2023 22:01:49 +0200 Subject: [PATCH 05/10] - Fixed byte representation - Added a darker background for the theme "dark" - Added stripes back to the tables - Moved css files in src/css - Removed the Themeselector as it now implemented in PreferencesView --- src/App.jsx | 8 ++--- src/DirectoryItems.jsx | 5 +++- src/EstimateResults.jsx | 21 +++++++------ src/Preferences.jsx | 41 ------------------------- src/PreferencesView.jsx | 43 +++++++++++++++++++++++++++ src/SnapshotsTable.jsx | 5 +++- src/SourcesTable.jsx | 19 +++++++----- src/TaskDetails.jsx | 5 +++- src/TaskLogs.jsx | 6 ++-- src/ThemeSelector.tsx | 29 ------------------ src/contexts/UIPreferencesContext.tsx | 13 ++++++-- src/{ => css}/App.css | 19 +++++------- src/{ => css}/Theme.css | 9 +++--- src/uiutil.jsx | 19 +++++++----- 14 files changed, 119 insertions(+), 123 deletions(-) delete mode 100644 src/Preferences.jsx create mode 100644 src/PreferencesView.jsx delete mode 100644 src/ThemeSelector.tsx rename src/{ => css}/App.css (94%) rename src/{ => css}/Theme.css (90%) diff --git a/src/App.jsx b/src/App.jsx index 6e9ee7f..31e64ef 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,6 @@ import 'bootstrap/dist/css/bootstrap.min.css'; -import './Theme.css'; -import './App.css'; +import './css/Theme.css'; +import './css/App.css'; import axios from 'axios'; import { React, Component } from 'react'; import { Navbar, Nav, Container } from 'react-bootstrap'; @@ -15,7 +15,7 @@ import { TaskDetails } from './TaskDetails'; import { TasksTable } from './TasksTable'; import { NewSnapshot } from './NewSnapshot'; import { PolicyEditorPage } from './PolicyEditorPage'; -import { Preferences } from './Preferences'; +import { PreferencesView } from './PreferencesView'; import { AppContext } from './contexts/AppContext'; import { UIPreferenceProvider } from './contexts/UIPreferencesContext'; @@ -129,7 +129,7 @@ export default class App extends Component { - + diff --git a/src/DirectoryItems.jsx b/src/DirectoryItems.jsx index 6653cd4..5159e32 100644 --- a/src/DirectoryItems.jsx +++ b/src/DirectoryItems.jsx @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import { Link } from "react-router-dom"; import MyTable from './Table'; import { objectLink, rfc3339TimestampForDisplay, sizeWithFailures } from './uiutil'; +import { UIPreferencesContext } from './contexts/UIPreferencesContext'; function objectName(name, typeID) { if (typeID === "d") { @@ -33,6 +34,7 @@ function directoryLinkOrDownload(x) { export class DirectoryItems extends Component { render() { + const { bytesStringBase2 } = this.context; const columns = [{ id: "name", Header: 'Name', @@ -49,7 +51,7 @@ export class DirectoryItems extends Component { accessor: x => sizeInfo(x), Header: "Size", width: 100, - Cell: x => sizeWithFailures(x.cell.value, x.row.original.summ), + Cell: x => sizeWithFailures(x.cell.value, x.row.original.summ, bytesStringBase2), }, { id: "files", accessor: "summ.files", @@ -65,3 +67,4 @@ export class DirectoryItems extends Component { return ; } } +DirectoryItems.contextType = UIPreferencesContext \ No newline at end of file diff --git a/src/EstimateResults.jsx b/src/EstimateResults.jsx index 24d5fee..de128d3 100644 --- a/src/EstimateResults.jsx +++ b/src/EstimateResults.jsx @@ -8,6 +8,7 @@ import Spinner from 'react-bootstrap/esm/Spinner'; import Form from 'react-bootstrap/Form'; import { TaskLogs } from './TaskLogs'; import { cancelTask, redirect, sizeDisplayName } from './uiutil'; +import { UIPreferencesContext } from './contexts/UIPreferencesContext'; export class EstimateResults extends Component { constructor() { @@ -87,6 +88,7 @@ export class EstimateResults extends Component { render() { const { task, isLoading, error } = this.state; + const { bytesStringBase2 } = this.context if (error) { return

{error.message}

; } @@ -97,20 +99,21 @@ export class EstimateResults extends Component { return <> {task.counters && - {this.taskStatusDescription(task)} Bytes: {sizeDisplayName(task.counters["Bytes"]?.value)} ({sizeDisplayName(task.counters["Excluded Bytes"]?.value)} excluded) + {this.taskStatusDescription(task)} Bytes: {sizeDisplayName(task.counters["Bytes"]?.value, bytesStringBase2)} ({sizeDisplayName(task.counters["Excluded Bytes"]?.value, bytesStringBase2)} excluded) Files: {task.counters["Files"]?.value} ({task.counters["Excluded Files"]?.value} excluded) Directories: {task.counters["Directories"]?.value} ({task.counters["Excluded Directories"]?.value} excluded) Errors: {task.counters["Errors"]?.value} ({task.counters["Ignored Errors"]?.value} ignored) - + } - {task.status === "RUNNING" && <> -   - } - {this.state.showLog ? <> - - - : } + {task.status === "RUNNING" && <> +   + } + {this.state.showLog ? <> + + + : } ; } } +EstimateResults.contextType = UIPreferencesContext \ No newline at end of file diff --git a/src/Preferences.jsx b/src/Preferences.jsx deleted file mode 100644 index 348ac31..0000000 --- a/src/Preferences.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Component } from 'react'; -import { ThemeSelector } from './ThemeSelector'; -import { UIPreferencesContext } from './contexts/UIPreferencesContext'; - -export class Preferences extends Component { - static contextType = UIPreferencesContext; - constructor() { - super(); - this.state = { - status: {}, - error: null, - }; - } - - render() { - const { pageSize, bytesStringBase2 } = this.context; - return <> -
-
- - - The current active theme -
-
-
- - - Represents bytes to the base of 2 -
-
-
- - - The number of items shown in tables -
-
- - } -} \ No newline at end of file diff --git a/src/PreferencesView.jsx b/src/PreferencesView.jsx new file mode 100644 index 0000000..282585c --- /dev/null +++ b/src/PreferencesView.jsx @@ -0,0 +1,43 @@ +import { Component } from 'react'; +import { UIPreferencesContext } from './contexts/UIPreferencesContext'; + +export class PreferencesView extends Component { + constructor() { + super(); + } + + render() { + const { pageSize, theme, bytesStringBase2, setByteStringBase, setTheme } = this.context; + return <> +
+
+ + + The current active theme +
+
+
+ + + Specifies the representation of bytes +
+
+
+ + + Specifies the pagination size in tables +
+
+ + } +} +PreferencesView.contextType = UIPreferencesContext diff --git a/src/SnapshotsTable.jsx b/src/SnapshotsTable.jsx index d4a4ea9..9d89317 100644 --- a/src/SnapshotsTable.jsx +++ b/src/SnapshotsTable.jsx @@ -13,6 +13,7 @@ import { faSync, faThumbtack } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import Modal from 'react-bootstrap/Modal'; import { faFileAlt } from '@fortawesome/free-regular-svg-icons'; +import { UIPreferencesContext } from './contexts/UIPreferencesContext'; function pillVariant(tag) { if (tag.startsWith("latest-")) { @@ -314,6 +315,7 @@ export class SnapshotsTable extends Component { render() { let { snapshots, unfilteredCount, uniqueCount, isLoading, error } = this.state; + const { bytesStringBase2 } = this.context if (error) { return

{error.message}

; } @@ -362,7 +364,7 @@ export class SnapshotsTable extends Component { Header: 'Size', accessor: 'summary.size', width: 100, - Cell: x => sizeWithFailures(x.cell.value, x.row.original.summary), + Cell: x => sizeWithFailures(x.cell.value, x.row.original.summary, bytesStringBase2), }, { Header: 'Files', accessor: 'summary.files', @@ -502,3 +504,4 @@ export class SnapshotsTable extends Component { ; } } +SnapshotsTable.contextType = UIPreferencesContext diff --git a/src/SourcesTable.jsx b/src/SourcesTable.jsx index 02a4479..cacf096 100644 --- a/src/SourcesTable.jsx +++ b/src/SourcesTable.jsx @@ -13,6 +13,7 @@ import { Link } from 'react-router-dom'; import { handleChange } from './forms'; import MyTable from './Table'; import { CLIEquivalent, compare, errorAlert, ownerName, policyEditorURL, redirect, sizeDisplayName, sizeWithFailures, sourceQueryStringParams } from './uiutil'; +import { UIPreferencesContext } from './contexts/UIPreferencesContext'; const localSnapshots = "Local Snapshots" const allSnapshots = "All Snapshots" @@ -88,7 +89,7 @@ export class SourcesTable extends Component { }); } - statusCell(x, parent) { + statusCell(x, parent, bytesStringBase2) { switch (x.cell.value) { case "IDLE": case "PAUSED": @@ -96,7 +97,7 @@ export class SourcesTable extends Component { ; @@ -111,15 +112,15 @@ export class SourcesTable extends Component { let title = ""; let totals = ""; if (u) { - title = " hashed " + u.hashedFiles + " files (" + sizeDisplayName(u.hashedBytes) + ")\n" + - " cached " + u.cachedFiles + " files (" + sizeDisplayName(u.cachedBytes) + ")\n" + + title = " hashed " + u.hashedFiles + " files (" + sizeDisplayName(u.hashedBytes, bytesStringBase2) + ")\n" + + " cached " + u.cachedFiles + " files (" + sizeDisplayName(u.cachedBytes, bytesStringBase2) + ")\n" + " dir " + u.directory; const totalBytes = u.hashedBytes + u.cachedBytes; - totals = sizeDisplayName(totalBytes); + totals = sizeDisplayName(totalBytes, bytesStringBase2); if (u.estimatedBytes) { - totals += "/" + sizeDisplayName(u.estimatedBytes); + totals += "/" + sizeDisplayName(u.estimatedBytes, bytesStringBase2); const percent = Math.round(totalBytes * 1000.0 / u.estimatedBytes) / 10.0; if (percent <= 100) { @@ -178,6 +179,7 @@ export class SourcesTable extends Component { render() { let { sources, isLoading, error } = this.state; + const { bytesStringBase2 } = this.context if (error) { return

{error.message}

; } @@ -234,7 +236,7 @@ export class SourcesTable extends Component { accessor: x => x.lastSnapshot ? x.lastSnapshot.stats.totalSize : 0, Cell: x => sizeWithFailures( x.cell.value, - x.row.original.lastSnapshot && x.row.original.lastSnapshot.rootEntry ? x.row.original.lastSnapshot.rootEntry.summ : null), + x.row.original.lastSnapshot && x.row.original.lastSnapshot.rootEntry ? x.row.original.lastSnapshot.rootEntry.summ : null, bytesStringBase2), }, { id: 'lastSnapshotTime', Header: 'Last Snapshot', @@ -252,7 +254,7 @@ export class SourcesTable extends Component { Header: '', width: 300, accessor: x => x.status, - Cell: x => this.statusCell(x, this), + Cell: x => this.statusCell(x, this, bytesStringBase2), }] return <> @@ -290,3 +292,4 @@ export class SourcesTable extends Component { ; } } +SourcesTable.contextType = UIPreferencesContext \ No newline at end of file diff --git a/src/TaskDetails.jsx b/src/TaskDetails.jsx index 7eef578..3c458c3 100644 --- a/src/TaskDetails.jsx +++ b/src/TaskDetails.jsx @@ -12,6 +12,7 @@ import Table from 'react-bootstrap/Table'; import Spinner from 'react-bootstrap/Spinner'; import { TaskLogs } from './TaskLogs'; import { cancelTask, formatDuration, GoBackButton, redirect, sizeDisplayName } from './uiutil'; +import { UIPreferencesContext } from './contexts/UIPreferencesContext'; export class TaskDetails extends Component { constructor() { @@ -157,6 +158,7 @@ export class TaskDetails extends Component { render() { const { task, isLoading, error } = this.state; + const { bytesStringBase2 } = this.context if (error) { return

{error.message}

; } @@ -192,7 +194,7 @@ export class TaskDetails extends Component { - {this.sortedBadges(task.counters)} + {this.sortedBadges(task.counters, bytesStringBase2)} @@ -221,3 +223,4 @@ export class TaskDetails extends Component { ; } } +TaskDetails.contextType = UIPreferencesContext \ No newline at end of file diff --git a/src/TaskLogs.jsx b/src/TaskLogs.jsx index 38e5209..d2c43f4 100644 --- a/src/TaskLogs.jsx +++ b/src/TaskLogs.jsx @@ -82,7 +82,7 @@ export class TaskLogs extends Component { formatLogParams(entry) { // if there are any properties other than `msg, ts, level, mod` output them as JSON. - let {msg, ts, level, mod, ...parametersOnly} = entry; + let { msg, ts, level, mod, ...parametersOnly } = entry; const p = JSON.stringify(parametersOnly); if (p !== "{}") { @@ -111,11 +111,11 @@ export class TaskLogs extends Component { if (logs) { return
- {logs.map((v,ndx) => + {logs.map((v, ndx) => )}
{this.formatLogTime(v.ts)} {v.msg} {this.formatLogParams(v)}
-
+
; } diff --git a/src/ThemeSelector.tsx b/src/ThemeSelector.tsx deleted file mode 100644 index e6eb0b6..0000000 --- a/src/ThemeSelector.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import Select from 'react-select' -import { useContext } from 'react'; -import { UIPreferencesContext } from './contexts/UIPreferencesContext'; - -export function ThemeSelector() { - const { theme, setTheme } = useContext(UIPreferencesContext); - - //Contains all supported themes - const themes = [ - { value: 'light', label: 'light' }, - { value: 'dark', label: 'dark' }, - { value: 'pastel', label: 'pastel' }, - { value: 'ocean', label: 'ocean' } - ] - - //Finds the current selected theme within supported themes - const currentTheme = themes.find(o => o.value === theme); - - /** - * Handles the theme selection by the user - * @param event - */ - const handleTheme = (event: any) => { - var selectedTheme = event.value; - setTheme(selectedTheme) - }; - - return