diff options
Diffstat (limited to 'gui')
49 files changed, 623 insertions, 811 deletions
diff --git a/gui/package-lock.json b/gui/package-lock.json index 034b8c609d..bdd3476016 100644 --- a/gui/package-lock.json +++ b/gui/package-lock.json @@ -24,7 +24,7 @@ "react-simple-maps": "^3.0.0", "redux": "^4.2.0", "sprintf-js": "^1.1.2", - "styled-components": "^5.1.1" + "styled-components": "^6.1.0" }, "devDependencies": { "@electron/notarize": "^2.1.0", @@ -46,7 +46,6 @@ "@types/react-simple-maps": "^1.0.8", "@types/sinon": "^10.0.13", "@types/sprintf-js": "^1.1.2", - "@types/styled-components": "^5.1.26", "@types/topojson-specification": "^1.0.2", "@typescript-eslint/eslint-plugin": "^5.56.0", "@typescript-eslint/parser": "^5.56.0", @@ -89,18 +88,11 @@ "nseventmonitor": "^1.0.3" } }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, "node_modules/@babel/generator": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz", "integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==", + "dev": true, "dependencies": { "@babel/types": "^7.18.2", "@jridgewell/gen-mapping": "^0.3.0", @@ -110,70 +102,20 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "dependencies": { - "@babel/types": "^7.10.4" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dependencies": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dependencies": { - "@babel/types": "^7.10.4" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", - "dependencies": { - "@babel/types": "^7.12.5" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dependencies": { - "@babel/types": "^7.11.0" - } - }, "node_modules/@babel/helper-validator-identifier": { "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, "node_modules/@babel/parser": { "version": "7.18.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz", "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==", + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -192,60 +134,11 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" - } - }, - "node_modules/@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/@babel/types": { "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" @@ -486,27 +379,22 @@ } }, "node_modules/@emotion/is-prop-valid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", - "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", "dependencies": { - "@emotion/memoize": "^0.8.0" + "@emotion/memoize": "^0.8.1" } }, "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/stylis": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, "node_modules/@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" }, "node_modules/@eslint-community/eslint-utils": { "version": "4.3.0", @@ -784,6 +672,7 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -797,6 +686,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -805,6 +695,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -812,12 +703,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1568,16 +1461,10 @@ "integrity": "sha512-hkgzYF+qnIl8uTO8rmUSVSfQ8BIfMXC4yJAF4n8BE758YsKBZvFC4NumnAegj7KmylP0liEZNpb9RRGFMbFejA==", "dev": true }, - "node_modules/@types/styled-components": { - "version": "5.1.26", - "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz", - "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==", - "dev": true, - "dependencies": { - "@types/hoist-non-react-statics": "*", - "@types/react": "*", - "csstype": "^3.0.2" - } + "node_modules/@types/stylis": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.2.tgz", + "integrity": "sha512-Rm17MsTpQQP5Jq4BF7CdrxJsDufoiL/q5IbJZYZmOZAJALyijgF7BzLgobXUqraNcQdqFYLYGeglDp6QzaxPpg==" }, "node_modules/@types/topojson-specification": { "version": "1.0.2", @@ -2059,17 +1946,6 @@ "node": ">=0.10.0" } }, - "node_modules/ansi-styles": { - "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==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", @@ -2683,22 +2559,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-plugin-styled-components": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.12.0.tgz", - "integrity": "sha512-FEiD7l5ZABdJPpLssKXjBUJMYqzbcNzBowfXDCdJhOpbhWiewapUaY+LZGT8R4Jg2TwOjGjG4RKeyrO5p9sBkA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-module-imports": "^7.0.0", - "babel-plugin-syntax-jsx": "^6.18.0", - "lodash": "^4.17.11" - } - }, - "node_modules/babel-plugin-syntax-jsx": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" - }, "node_modules/bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", @@ -3404,9 +3264,12 @@ } }, "node_modules/camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/chai": { "version": "4.3.6", @@ -3444,19 +3307,6 @@ "node": ">= 4.0.0" } }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -3704,19 +3554,6 @@ "node": ">=0.10.0" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -4017,7 +3854,7 @@ "node_modules/css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", "engines": { "node": ">=4" } @@ -4032,9 +3869,9 @@ ] }, "node_modules/css-to-react-native": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", - "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", "dependencies": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", @@ -4062,9 +3899,9 @@ } }, "node_modules/csstype": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz", - "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/d": { "version": "1.0.1", @@ -5340,14 +5177,6 @@ "node": ">=6" } }, - "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": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/eslint": { "version": "8.36.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", @@ -7339,14 +7168,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", @@ -8470,6 +8291,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -8755,7 +8577,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -11079,9 +10902,9 @@ } }, "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/postcss/node_modules/source-map": { "version": "0.6.1", @@ -12501,6 +12324,14 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-resolve": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", @@ -12903,24 +12734,22 @@ } }, "node_modules/styled-components": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", - "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==", - "hasInstallScript": true, + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.0.tgz", + "integrity": "sha512-VWNfYYBuXzuLS/QYEeoPgMErP26WL+dX9//rEh80B2mmlS1yRxRxuL5eax4m6ybYEUoHWlTy2XOU32767mlMkg==", "dependencies": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.4.5", - "@emotion/is-prop-valid": "^1.1.0", - "@emotion/stylis": "^0.8.4", - "@emotion/unitless": "^0.7.4", - "babel-plugin-styled-components": ">= 1.12.0", - "css-to-react-native": "^3.0.0", - "hoist-non-react-statics": "^3.0.0", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/unitless": "^0.8.0", + "@types/stylis": "^4.0.2", + "css-to-react-native": "^3.2.0", + "csstype": "^3.1.2", + "postcss": "^8.4.31", "shallowequal": "^1.1.0", - "supports-color": "^5.5.0" + "stylis": "^4.3.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">=10" + "node": ">= 16" }, "funding": { "type": "opencollective", @@ -12928,10 +12757,68 @@ }, "peerDependencies": { "react": ">= 16.8.0", - "react-dom": ">= 16.8.0", - "react-is": ">= 16.8.0" + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/styled-components/node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/stylis": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", + "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + }, "node_modules/subarg": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", @@ -12976,17 +12863,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/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", @@ -13251,6 +13127,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, "engines": { "node": ">=4" } @@ -14381,85 +14258,28 @@ } }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "requires": { - "@babel/highlight": "^7.10.4" - } - }, "@babel/generator": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz", "integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==", + "dev": true, "requires": { "@babel/types": "^7.18.2", "@jridgewell/gen-mapping": "^0.3.0", "jsesc": "^2.5.1" } }, - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", - "requires": { - "@babel/types": "^7.12.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "requires": { - "@babel/types": "^7.11.0" - } - }, "@babel/helper-validator-identifier": { "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" - }, - "@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true }, "@babel/parser": { "version": "7.18.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz", - "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==" + "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==", + "dev": true }, "@babel/runtime": { "version": "7.19.0", @@ -14469,56 +14289,11 @@ "regenerator-runtime": "^0.13.4" } }, - "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" - } - }, - "@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, "@babel/types": { "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" @@ -14699,27 +14474,22 @@ } }, "@emotion/is-prop-valid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", - "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", "requires": { - "@emotion/memoize": "^0.8.0" + "@emotion/memoize": "^0.8.1" } }, "@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/stylis": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, "@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" }, "@eslint-community/eslint-utils": { "version": "4.3.0", @@ -14922,6 +14692,7 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, "requires": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -14931,22 +14702,26 @@ "@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true }, "@jridgewell/set-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true }, "@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true }, "@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -15610,16 +15385,10 @@ "integrity": "sha512-hkgzYF+qnIl8uTO8rmUSVSfQ8BIfMXC4yJAF4n8BE758YsKBZvFC4NumnAegj7KmylP0liEZNpb9RRGFMbFejA==", "dev": true }, - "@types/styled-components": { - "version": "5.1.26", - "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz", - "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==", - "dev": true, - "requires": { - "@types/hoist-non-react-statics": "*", - "@types/react": "*", - "csstype": "^3.0.2" - } + "@types/stylis": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.2.tgz", + "integrity": "sha512-Rm17MsTpQQP5Jq4BF7CdrxJsDufoiL/q5IbJZYZmOZAJALyijgF7BzLgobXUqraNcQdqFYLYGeglDp6QzaxPpg==" }, "@types/topojson-specification": { "version": "1.0.2", @@ -15954,14 +15723,6 @@ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "devOptional": true }, - "ansi-styles": { - "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==", - "requires": { - "color-convert": "^1.9.0" - } - }, "ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", @@ -16448,22 +16209,6 @@ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "dev": true }, - "babel-plugin-styled-components": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.12.0.tgz", - "integrity": "sha512-FEiD7l5ZABdJPpLssKXjBUJMYqzbcNzBowfXDCdJhOpbhWiewapUaY+LZGT8R4Jg2TwOjGjG4RKeyrO5p9sBkA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-module-imports": "^7.0.0", - "babel-plugin-syntax-jsx": "^6.18.0", - "lodash": "^4.17.11" - } - }, - "babel-plugin-syntax-jsx": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" - }, "bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", @@ -17078,9 +16823,9 @@ "dev": true }, "camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==" }, "chai": { "version": "4.3.6", @@ -17112,16 +16857,6 @@ "integrity": "sha512-elF2ZUczBsFoP07qCfMO/zeggs8pqCf3fZGyK5+2X4AndS8jycZYID91ztD9oQ7d/0tnS963dPkd0frQEThDsg==", "dev": true }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -17317,19 +17052,6 @@ "object-visit": "^1.0.0" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -17611,7 +17333,7 @@ "css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==" }, "css-selector-parser": { "version": "1.3.0", @@ -17620,9 +17342,9 @@ "dev": true }, "css-to-react-native": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", - "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", "requires": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", @@ -17630,9 +17352,9 @@ } }, "csstype": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz", - "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "d": { "version": "1.0.1", @@ -18677,11 +18399,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, "eslint": { "version": "8.36.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", @@ -20241,11 +19958,6 @@ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, "has-property-descriptors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", @@ -21091,7 +20803,8 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true }, "json-buffer": { "version": "3.0.1", @@ -21320,7 +21033,8 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "lodash.camelcase": { "version": "4.3.0", @@ -23093,9 +22807,9 @@ } }, "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "prebuild-install": { "version": "6.1.4", @@ -24219,6 +23933,11 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, "source-map-resolve": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", @@ -24565,22 +24284,53 @@ "dev": true }, "styled-components": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", - "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.0.tgz", + "integrity": "sha512-VWNfYYBuXzuLS/QYEeoPgMErP26WL+dX9//rEh80B2mmlS1yRxRxuL5eax4m6ybYEUoHWlTy2XOU32767mlMkg==", "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.4.5", - "@emotion/is-prop-valid": "^1.1.0", - "@emotion/stylis": "^0.8.4", - "@emotion/unitless": "^0.7.4", - "babel-plugin-styled-components": ">= 1.12.0", - "css-to-react-native": "^3.0.0", - "hoist-non-react-statics": "^3.0.0", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/unitless": "^0.8.0", + "@types/stylis": "^4.0.2", + "css-to-react-native": "^3.2.0", + "csstype": "^3.1.2", + "postcss": "^8.4.31", "shallowequal": "^1.1.0", - "supports-color": "^5.5.0" + "stylis": "^4.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, + "stylis": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", + "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + }, "subarg": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", @@ -24616,14 +24366,6 @@ } } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, "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", @@ -24846,7 +24588,8 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true }, "to-object-path": { "version": "0.3.0", diff --git a/gui/package.json b/gui/package.json index 1ca1192cbe..cae1ec64c9 100644 --- a/gui/package.json +++ b/gui/package.json @@ -26,7 +26,7 @@ "react-simple-maps": "^3.0.0", "redux": "^4.2.0", "sprintf-js": "^1.1.2", - "styled-components": "^5.1.1" + "styled-components": "^6.1.0" }, "optionalDependencies": { "grpc-tools": "^1.12.4", @@ -52,7 +52,6 @@ "@types/react-simple-maps": "^1.0.8", "@types/sinon": "^10.0.13", "@types/sprintf-js": "^1.1.2", - "@types/styled-components": "^5.1.26", "@types/topojson-specification": "^1.0.2", "@typescript-eslint/eslint-plugin": "^5.56.0", "@typescript-eslint/parser": "^5.56.0", diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx index 7c7df73796..bab17524d6 100644 --- a/gui/src/renderer/app.tsx +++ b/gui/src/renderer/app.tsx @@ -1,6 +1,7 @@ import { batch, Provider } from 'react-redux'; import { Router } from 'react-router'; import { bindActionCreators } from 'redux'; +import { StyleSheetManager } from 'styled-components'; import { hasExpired } from '../shared/account-expiry'; import { ILinuxSplitTunnelingApplication, IWindowsApplication } from '../shared/application-types'; @@ -276,19 +277,21 @@ export default class AppRenderer { return ( <AppContext.Provider value={{ app: this }}> <Provider store={this.reduxStore}> - <Lang> - <Router history={this.history.asHistory}> - <ErrorBoundary> - <ModalContainer> - <KeyboardNavigation> - <AppRouter /> - <Changelog /> - </KeyboardNavigation> - {window.env.platform === 'darwin' && <MacOsScrollbarDetection />} - </ModalContainer> - </ErrorBoundary> - </Router> - </Lang> + <StyleSheetManager enableVendorPrefixes> + <Lang> + <Router history={this.history.asHistory}> + <ErrorBoundary> + <ModalContainer> + <KeyboardNavigation> + <AppRouter /> + <Changelog /> + </KeyboardNavigation> + {window.env.platform === 'darwin' && <MacOsScrollbarDetection />} + </ModalContainer> + </ErrorBoundary> + </Router> + </Lang> + </StyleSheetManager> </Provider> </AppContext.Provider> ); diff --git a/gui/src/renderer/components/Accordion.tsx b/gui/src/renderer/components/Accordion.tsx index 682f5aa779..f70258f103 100644 --- a/gui/src/renderer/components/Accordion.tsx +++ b/gui/src/renderer/components/Accordion.tsx @@ -14,11 +14,11 @@ interface IState { containerHeight: string; } -const Container = styled.div((props: { height: string; animationDuration: number }) => ({ +const Container = styled.div<{ $height: string; $animationDuration: number }>((props) => ({ display: 'flex', - height: props.height, + height: props.$height, overflow: 'hidden', - transition: `height ${props.animationDuration}ms ease-in-out`, + transition: `height ${props.$animationDuration}ms ease-in-out`, })); const Content = styled.div({ @@ -55,8 +55,8 @@ export default class Accordion extends React.Component<IProps, IState> { return ( <Container ref={this.containerRef} - height={this.state.containerHeight} - animationDuration={this.props.animationDuration} + $height={this.state.containerHeight} + $animationDuration={this.props.animationDuration} onTransitionEnd={this.onTransitionEnd}> <Content ref={this.contentRef}>{this.state.mountChildren && this.props.children}</Content> </Container> diff --git a/gui/src/renderer/components/AppButton.tsx b/gui/src/renderer/components/AppButton.tsx index 45c93e1b2a..753f64745e 100644 --- a/gui/src/renderer/components/AppButton.tsx +++ b/gui/src/renderer/components/AppButton.tsx @@ -22,7 +22,7 @@ interface ILabelProps { } export function Label(props: ILabelProps) { - return <StyledLabel textOffset={props.textOffset ?? 0}>{props.children}</StyledLabel>; + return <StyledLabel $textOffset={props.textOffset ?? 0}>{props.children}</StyledLabel>; } interface IIconProps { @@ -108,7 +108,7 @@ const StyledSimpleButton = styled(SimpleButton)({ borderRadius: 4, border: 'none', padding: 0, - ':disabled': { + '&&:disabled': { opacity: 0.5, }, }); @@ -156,35 +156,35 @@ export function BlockingButton(props: IBlockingProps) { export const RedButton = styled(BaseButton)({ backgroundColor: colors.red, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: colors.red95, }, }); export const GreenButton = styled(BaseButton)({ backgroundColor: colors.green, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: colors.green90, }, }); export const BlueButton = styled(BaseButton)({ backgroundColor: colors.blue80, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: colors.blue60, }, }); export const TransparentButton = styled(BaseButton)(transparentButton, { backgroundColor: colors.white20, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: colors.white40, }, }); export const RedTransparentButton = styled(BaseButton)(transparentButton, { backgroundColor: colors.red60, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: colors.red80, }, }); @@ -193,7 +193,7 @@ const StyledButtonWrapper = styled.div({ display: 'flex', flexDirection: 'column', flex: 0, - ':not(:last-child)': { + '&&:not(:last-child)': { marginBottom: measurements.buttonVerticalMargin, }, }); diff --git a/gui/src/renderer/components/AppButtonStyles.tsx b/gui/src/renderer/components/AppButtonStyles.tsx index f22eb3473a..ac65f951f6 100644 --- a/gui/src/renderer/components/AppButtonStyles.tsx +++ b/gui/src/renderer/components/AppButtonStyles.tsx @@ -2,9 +2,9 @@ import styled from 'styled-components'; import { buttonText } from './common-styles'; -export const StyledLabel = styled.span(buttonText, (props: { textOffset: number }) => ({ - paddingLeft: props.textOffset > 0 ? `${props.textOffset}px` : 0, - paddingRight: props.textOffset < 0 ? `${-props.textOffset}px` : 0, +export const StyledLabel = styled.span<{ $textOffset: number }>(buttonText, (props) => ({ + paddingLeft: props.$textOffset > 0 ? `${props.$textOffset}px` : 0, + paddingRight: props.$textOffset < 0 ? `${-props.$textOffset}px` : 0, textAlign: 'center', wordBreak: 'break-word', })); diff --git a/gui/src/renderer/components/ConnectionPanelDisclosure.tsx b/gui/src/renderer/components/ConnectionPanelDisclosure.tsx index 11ec9a0136..3941c3d88f 100644 --- a/gui/src/renderer/components/ConnectionPanelDisclosure.tsx +++ b/gui/src/renderer/components/ConnectionPanelDisclosure.tsx @@ -11,18 +11,18 @@ const Container = styled.div({ width: '100%', }); -const Caption = styled.span(normalText, (props: { open: boolean }) => ({ +const Caption = styled.span<{ $open: boolean }>(normalText, (props) => ({ fontWeight: 600, lineHeight: '20px', minWidth: '0px', - color: props.open ? colors.white : colors.white40, - [Container + ':hover &']: { + color: props.$open ? colors.white : colors.white40, + [Container + ':hover &&']: { color: colors.white, }, })); const Chevron = styled(ImageView)({ - [Container + ':hover &']: { + [Container + ':hover &&']: { backgroundColor: colors.white, }, }); @@ -37,7 +37,7 @@ interface IProps { export default function ConnectionPanelDisclosure(props: IProps) { return ( <Container className={props.className} onClick={props.onToggle}> - <Caption open={props.pointsUp}>{props.children}</Caption> + <Caption $open={props.pointsUp}>{props.children}</Caption> <Chevron source={props.pointsUp ? 'icon-chevron-up' : 'icon-chevron-down'} width={22} diff --git a/gui/src/renderer/components/CustomDnsSettings.tsx b/gui/src/renderer/components/CustomDnsSettings.tsx index 824e010970..6b8024c9e2 100644 --- a/gui/src/renderer/components/CustomDnsSettings.tsx +++ b/gui/src/renderer/components/CustomDnsSettings.tsx @@ -6,7 +6,7 @@ import { messages } from '../../shared/gettext'; import { useAppContext } from '../context'; import { formatHtml } from '../lib/html-formatter'; import { IpAddress } from '../lib/ip'; -import { useBoolean, useMounted } from '../lib/utilityHooks'; +import { useBoolean, useMounted, useStyledRef } from '../lib/utilityHooks'; import { useSelector } from '../redux/store'; import Accordion from './Accordion'; import * as AppButton from './AppButton'; @@ -57,9 +57,9 @@ export default function CustomDnsSettings() { [dns], ); - const switchRef = useRef() as React.RefObject<HTMLDivElement>; - const addButtonRef = useRef() as React.RefObject<HTMLButtonElement>; - const inputContainerRef = useRef() as React.RefObject<HTMLDivElement>; + const switchRef = useStyledRef<HTMLDivElement>(); + const addButtonRef = useStyledRef<HTMLButtonElement>(); + const inputContainerRef = useStyledRef<HTMLDivElement>(); const confirm = useCallback(() => { void confirmAction?.(); @@ -323,7 +323,7 @@ function CellListItem(props: ICellListItemProps) { const [invalid, setInvalid, setValid] = useBoolean(false); const isMounted = useMounted(); - const inputContainerRef = useRef() as React.RefObject<HTMLDivElement>; + const inputContainerRef = useStyledRef<HTMLDivElement>(); const onRemove = useCallback(() => props.onRemove(props.children), [ props.onRemove, diff --git a/gui/src/renderer/components/CustomDnsSettingsStyles.tsx b/gui/src/renderer/components/CustomDnsSettingsStyles.tsx index b660504a01..b8fdc7fd38 100644 --- a/gui/src/renderer/components/CustomDnsSettingsStyles.tsx +++ b/gui/src/renderer/components/CustomDnsSettingsStyles.tsx @@ -12,19 +12,16 @@ export const StyledAddCustomDnsButton = styled(Cell.CellButton)({ backgroundColor: colors.blue40, }); -export const StyledAddCustomDnsLabel = styled(Cell.Label)( - {}, - (props: { paddingLeft?: number }) => ({ - fontFamily: 'Open Sans', - fontWeight: 400, - fontSize: '16px', - paddingLeft: (props.paddingLeft ?? 32) + 'px', - whiteSpace: 'pre-wrap', - overflowWrap: 'break-word', - width: '171px', - marginRight: '25px', - }), -); +export const StyledAddCustomDnsLabel = styled(Cell.Label)<{ $paddingLeft?: number }>((props) => ({ + fontFamily: 'Open Sans', + fontWeight: 400, + fontSize: '16px', + paddingLeft: (props.$paddingLeft ?? 32) + 'px', + whiteSpace: 'pre-wrap', + overflowWrap: 'break-word', + width: '171px', + marginRight: '25px', +})); export const StyledContainer = styled(Cell.Container)({ display: 'flex', @@ -59,7 +56,7 @@ export const StyledRemoveButton = styled.button({ }); export const StyledRemoveIcon = styled(ImageView)({ - [StyledRemoveButton + ':hover &']: { + [StyledRemoveButton + ':hover &&']: { backgroundColor: colors.white80, }, }); diff --git a/gui/src/renderer/components/CustomScrollbars.tsx b/gui/src/renderer/components/CustomScrollbars.tsx index ab95d66511..65db8c87fe 100644 --- a/gui/src/renderer/components/CustomScrollbars.tsx +++ b/gui/src/renderer/components/CustomScrollbars.tsx @@ -19,44 +19,44 @@ const StyledCustomScrollbars = styled.div({ overflow: 'hidden', }); -const StyledScrollable = styled.div((props: { fillContainer?: boolean }) => ({ - flex: props.fillContainer ? '1' : undefined, +const StyledScrollable = styled.div<{ $fillContainer?: boolean }>((props) => ({ + flex: props.$fillContainer ? '1' : undefined, width: '100%', overflow: 'auto', - '::-webkit-scrollbar': { + '&&::-webkit-scrollbar': { display: 'none', }, })); -const StyledTrack = styled.div({}, (props: { canScroll: boolean; show: boolean }) => ({ +const StyledTrack = styled.div<{ $canScroll: boolean; $show: boolean }>((props) => ({ position: 'absolute', top: 0, right: 0, bottom: 0, width: '16px', - backgroundColor: props.show ? 'rgba(0, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0)', + backgroundColor: props.$show ? 'rgba(0, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0)', borderRadius: '8px', transition: 'width 0.1s ease-in-out, background-color 0.25s ease-in-out', zIndex: 99, - pointerEvents: props.canScroll ? 'auto' : 'none', - // Thumb should be less transparent when track is hovered. - [`&:hover ${StyledThumb}`]: { - backgroundColor: 'rgba(255, 255, 255, 0.65)', - }, + pointerEvents: props.$canScroll ? 'auto' : 'none', })); -const StyledThumb = styled.div( - {}, - (props: { show: boolean; isDragging: boolean; wide: boolean }) => ({ +const StyledThumb = styled.div<{ $show: boolean; $isDragging: boolean; $wide: boolean }>( + (props) => ({ position: 'absolute', top: 0, right: 0, - borderRadius: props.wide ? '6px' : '4px', - width: props.wide ? '12px' : '8px', + borderRadius: props.$wide ? '6px' : '4px', + width: props.$wide ? '12px' : '8px', transition: 'width 0.25s ease-in-out, border-radius 0.25s ease-in-out, height 0.25s ease-in-out, opacity 0.25s ease-in-out, background-color 0.1s ease-in-out', - opacity: props.show ? 1 : 0, - backgroundColor: props.isDragging ? 'rgba(255, 255, 255, 0.65)' : 'rgba(255, 255, 255, 0.4)', + opacity: props.$show ? 1 : 0, + backgroundColor: props.$isDragging ? 'rgba(255, 255, 255, 0.65)' : 'rgba(255, 255, 255, 0.4)', + + // Thumb should be less transparent when track is hovered. + [`${StyledTrack}:hover &&`]: { + backgroundColor: 'rgba(255, 255, 255, 0.65)', + }, }), ); @@ -260,20 +260,20 @@ class CustomScrollbars extends React.Component<IProps, IState> { <StyledCustomScrollbars {...otherProps}> <StyledTrack ref={this.trackRef} - show={showScrollbars && this.state.active} - canScroll={this.state.canScroll} + $show={showScrollbars && this.state.active} + $canScroll={this.state.canScroll} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> <StyledThumb ref={this.thumbRef} - show={showScrollbars} - isDragging={this.state.isDragging} - wide={this.state.active} + $show={showScrollbars} + $isDragging={this.state.isDragging} + $wide={this.state.active} onMouseDown={this.handleMouseDown} /> </StyledTrack> <StyledScrollable - fillContainer={fillContainer} + $fillContainer={fillContainer} onScroll={this.onScroll} ref={this.scrollableRef}> <StyledScrollableContent ref={this.scrollableContentRef}> diff --git a/gui/src/renderer/components/Filter.tsx b/gui/src/renderer/components/Filter.tsx index a8e67b3f9c..2cbc9ab239 100644 --- a/gui/src/renderer/components/Filter.tsx +++ b/gui/src/renderer/components/Filter.tsx @@ -201,7 +201,7 @@ function providersSelector(state: IReduxState): Record<string, boolean> { const StyledSelector = styled(Selector)({ marginBottom: 0, -}) as typeof Selector; +}); interface IFilterByOwnershipProps { ownership: Ownership; @@ -290,7 +290,7 @@ function FilterByProvider(props: IFilterByProviderProps) { <Accordion expanded={expanded}> <CheckboxRow label={messages.pgettext('filter-view', 'All providers')} - bold + $bold checked={Object.values(props.providers).every((value) => value)} onChange={toggleAll} /> @@ -310,7 +310,7 @@ function toggleAllProviders(providers: Record<string, boolean>, value?: boolean) } interface IStyledRowTitleProps { - bold?: boolean; + $bold?: boolean; } const StyledCheckbox = styled.div({ @@ -325,13 +325,13 @@ const StyledCheckbox = styled.div({ const StyledRow = styled(Cell.Row)({ backgroundColor: colors.blue40, - ':hover': { + '&&:hover': { backgroundColor: colors.blue80, }, }); -const StyledRowTitle = styled.label(normalText, (props: IStyledRowTitleProps) => ({ - fontWeight: props.bold ? 600 : 400, +const StyledRowTitle = styled.label<IStyledRowTitleProps>(normalText, (props) => ({ + fontWeight: props.$bold ? 600 : 400, color: colors.white, marginLeft: '22px', })); @@ -350,7 +350,7 @@ function CheckboxRow(props: ICheckboxRowProps) { <StyledCheckbox role="checkbox" aria-label={props.label} aria-checked={props.checked}> {props.checked && <ImageView source="icon-tick" width={18} tintColor={colors.green} />} </StyledCheckbox> - <StyledRowTitle aria-hidden bold={props.bold}> + <StyledRowTitle aria-hidden $bold={props.$bold}> {props.label} </StyledRowTitle> </StyledRow> diff --git a/gui/src/renderer/components/FormattableTextInput.tsx b/gui/src/renderer/components/FormattableTextInput.tsx index 133a6692e0..f6c259c4c4 100644 --- a/gui/src/renderer/components/FormattableTextInput.tsx +++ b/gui/src/renderer/components/FormattableTextInput.tsx @@ -1,6 +1,6 @@ -import React, { useCallback, useEffect, useRef } from 'react'; +import React, { useCallback, useEffect } from 'react'; -import { useCombinedRefs } from '../lib/utilityHooks'; +import { useCombinedRefs, useStyledRef } from '../lib/utilityHooks'; interface IFormattableTextInputProps extends React.InputHTMLAttributes<HTMLInputElement> { allowedCharacters: string; @@ -28,7 +28,7 @@ function FormattableTextInput( ...otherProps } = props; - const ref = useRef() as React.RefObject<HTMLInputElement>; + const ref = useStyledRef<HTMLInputElement>(); const combinedRef = useCombinedRefs(ref, forwardedRef); const unformat = useCallback( diff --git a/gui/src/renderer/components/HeaderBar.tsx b/gui/src/renderer/components/HeaderBar.tsx index a5c3969f08..98502a6346 100644 --- a/gui/src/renderer/components/HeaderBar.tsx +++ b/gui/src/renderer/components/HeaderBar.tsx @@ -29,16 +29,16 @@ const headerBarStyleColorMap = { }; interface IHeaderBarContainerProps { - barStyle?: HeaderBarStyle; - accountInfoVisible: boolean; - unpinnedWindow: boolean; + $barStyle?: HeaderBarStyle; + $accountInfoVisible: boolean; + $unpinnedWindow: boolean; } -const HeaderBarContainer = styled.header({}, (props: IHeaderBarContainerProps) => ({ +const HeaderBarContainer = styled.header<IHeaderBarContainerProps>((props) => ({ padding: '15px 11px 0px 16px', - minHeight: props.accountInfoVisible ? '80px' : '68px', - height: props.accountInfoVisible ? '80px' : '68px', - backgroundColor: headerBarStyleColorMap[props.barStyle ?? HeaderBarStyle.default], + minHeight: props.$accountInfoVisible ? '80px' : '68px', + height: props.$accountInfoVisible ? '80px' : '68px', + backgroundColor: headerBarStyleColorMap[props.$barStyle ?? HeaderBarStyle.default], transitionProperty: 'height, min-height', transitionDuration: '250ms', transitionTimingFunction: 'ease-in-out', @@ -63,10 +63,10 @@ export default function HeaderBar(props: IHeaderBarProps) { return ( <HeaderBarContainer - barStyle={props.barStyle} + $barStyle={props.barStyle} className={props.className} - accountInfoVisible={props.showAccountInfo ?? false} - unpinnedWindow={unpinnedWindow}> + $accountInfoVisible={props.showAccountInfo ?? false} + $unpinnedWindow={unpinnedWindow}> <HeaderBarContent>{props.children}</HeaderBarContent> {props.showAccountInfo && <HeaderBarDeviceInfo />} </HeaderBarContainer> diff --git a/gui/src/renderer/components/ImageView.tsx b/gui/src/renderer/components/ImageView.tsx index 3be04159a2..fa3855b21a 100644 --- a/gui/src/renderer/components/ImageView.tsx +++ b/gui/src/renderer/components/ImageView.tsx @@ -1,7 +1,10 @@ import React, { useMemo } from 'react'; import styled from 'styled-components'; -export interface IImageViewProps extends IImageMaskProps { +import { NonTransientProps } from '../lib/styles'; + +export interface IImageViewProps + extends NonTransientProps<IImageMaskProps, 'tintColor' | 'tintHoverColor'> { source: string; onClick?: (event: React.MouseEvent) => void; className?: string; @@ -11,8 +14,8 @@ interface IImageMaskProps extends React.HTMLAttributes<HTMLElement> { width?: number; height?: number; disabled?: boolean; - tintColor?: string; - tintHoverColor?: string; + $tintColor?: string; + $tintHoverColor?: string; } const Wrapper = styled.div({ @@ -21,7 +24,7 @@ const Wrapper = styled.div({ justifyContent: 'center', }); -const ImageMask = styled.div((props: IImageMaskProps) => { +const ImageMask = styled.div<IImageMaskProps>((props) => { const maskWidth = props.width ? `${props.width}px` : 'auto'; const maskHeight = props.height ? `${props.height}px` : 'auto'; return { @@ -29,9 +32,9 @@ const ImageMask = styled.div((props: IImageMaskProps) => { maskSize: `${maskWidth} ${maskHeight}`, maskPosition: 'center', lineHeight: 0, - backgroundColor: props.tintColor, - ':hover': { - backgroundColor: (!props.disabled && props.tintHoverColor) || props.tintColor, + backgroundColor: props.$tintColor, + '&&:hover': { + backgroundColor: (!props.disabled && props.$tintHoverColor) || props.$tintColor, }, }; }); @@ -47,9 +50,13 @@ export default function ImageView(props: IImageViewProps) { const style = useMemo(() => ({ WebkitMaskImage: `url('${url}')` }), [url]); if (props.tintColor) { - const { source: _source, ...otherProps } = props; + const { source: _source, tintColor, tintHoverColor, ...otherProps } = props; return ( - <ImageMask style={style} {...otherProps}> + <ImageMask + style={style} + $tintColor={tintColor} + $tintHoverColor={tintHoverColor} + {...otherProps}> <HiddenImage src={url} width={props.width} height={props.height} /> </ImageMask> ); diff --git a/gui/src/renderer/components/Launch.tsx b/gui/src/renderer/components/Launch.tsx index 5f12fcce8f..3f86e4d19c 100644 --- a/gui/src/renderer/components/Launch.tsx +++ b/gui/src/renderer/components/Launch.tsx @@ -21,10 +21,10 @@ export default function Launch() { ); } -const StyledFooter = styled(Footer)({}, (props: { show: boolean }) => ({ +const StyledFooter = styled(Footer)<{ $show: boolean }>((props) => ({ backgroundColor: colors.blue, padding: `0 14px ${measurements.viewMargin}`, - opacity: props.show ? 1 : 0, + opacity: props.$show ? 1 : 0, transition: 'opacity 250ms ease-in-out', })); @@ -55,7 +55,7 @@ function SettingsFooter(props: ISettingsFooterProps) { }, []); return ( - <StyledFooter show={props.show}> + <StyledFooter $show={props.show}> <StyledSystemSettingsContainer> <StyledLaunchFooterPrompt> {messages.pgettext( diff --git a/gui/src/renderer/components/Login.tsx b/gui/src/renderer/components/Login.tsx index 07d4814f1b..32eb5bbe82 100644 --- a/gui/src/renderer/components/Login.tsx +++ b/gui/src/renderer/components/Login.tsx @@ -108,7 +108,7 @@ export default class Login extends React.Component<IProps, IState> { {this.createLoginForm()} </StyledLoginForm> - <StyledFooter show={allowInteraction}>{this.createFooter()}</StyledFooter> + <StyledFooter $show={allowInteraction}>{this.createFooter()}</StyledFooter> </Container> </Layout> ); @@ -297,9 +297,9 @@ export default class Login extends React.Component<IProps, IState> { <> <StyledSubtitle data-testid="subtitle">{this.formSubtitle()}</StyledSubtitle> <StyledAccountInputGroup - active={allowInteraction && this.state.isActive} - editable={allowInteraction} - error={hasError} + $active={allowInteraction && this.state.isActive} + $editable={allowInteraction} + $error={hasError} onSubmit={this.onSubmit}> <StyledAccountInputBackdrop> <StyledInput @@ -318,14 +318,14 @@ export default class Login extends React.Component<IProps, IState> { /> <StyledInputButton type="submit" - visible={allowLogin} + $visible={allowLogin} disabled={!allowLogin} aria-label={ // TRANSLATORS: This is used by screenreaders to communicate the login button. messages.pgettext('accessibility', 'Login') }> <StyledInputSubmitIcon - visible={ + $visible={ this.props.loginState.type !== 'logging in' && !this.props.isPerformingPostUpgrade } source="icon-arrow" diff --git a/gui/src/renderer/components/LoginStyles.tsx b/gui/src/renderer/components/LoginStyles.tsx index cff3cdfc67..4a597b07b3 100644 --- a/gui/src/renderer/components/LoginStyles.tsx +++ b/gui/src/renderer/components/LoginStyles.tsx @@ -26,13 +26,13 @@ export const StyledAccountDropdownRemoveIcon = styled(ImageView)({ marginLeft: '0px', }); -export const StyledInputSubmitIcon = styled(ImageView)((props: { visible: boolean }) => ({ +export const StyledInputSubmitIcon = styled(ImageView)<{ $visible: boolean }>((props) => ({ flex: 0, borderWidth: '0px', width: '48px', alignItems: 'center', justifyContent: 'center', - opacity: props.visible ? 1 : 0, + opacity: props.$visible ? 1 : 0, })); export const StyledAccountDropdownItem = styled.li({ @@ -40,7 +40,7 @@ export const StyledAccountDropdownItem = styled.li({ flex: 1, backgroundColor: colors.white60, cursor: 'default', - ':hover': { + '&&:hover': { backgroundColor: colors.white40, }, }); @@ -51,7 +51,7 @@ export const StyledAccountDropdownItemButton = styled(Cell.CellButton)({ flexDirection: 'row', alignItems: 'stretch', backgroundColor: 'transparent', - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: 'transparent', }, }); @@ -75,11 +75,11 @@ export const StyledTopInfo = styled.div({ flex: 1, }); -export const StyledFooter = styled(Footer)({}, (props: { show: boolean }) => ({ +export const StyledFooter = styled(Footer)<{ $show: boolean }>((props) => ({ position: 'relative', width: '100%', bottom: 0, - transform: `translateY(${props.show ? 0 : 100}%)`, + transform: `translateY(${props.$show ? 0 : 100}%)`, backgroundColor: colors.darkBlue, transition: 'transform 250ms ease-in-out', })); @@ -103,18 +103,18 @@ export const StyledLoginForm = styled.div({ }); interface IStyledAccountInputGroupProps { - editable: boolean; - active: boolean; - error: boolean; + $editable: boolean; + $active: boolean; + $error: boolean; } -export const StyledAccountInputGroup = styled.form((props: IStyledAccountInputGroupProps) => ({ +export const StyledAccountInputGroup = styled.form<IStyledAccountInputGroupProps>((props) => ({ borderWidth: '2px', borderStyle: 'solid', borderRadius: '8px', overflow: 'hidden', - borderColor: props.error ? colors.red40 : props.active ? colors.darkBlue : 'transparent', - opacity: props.editable ? 1 : 0.6, + borderColor: props.$error ? colors.red40 : props.$active ? colors.darkBlue : 'transparent', + opacity: props.$editable ? 1 : 0.6, })); export const StyledAccountInputBackdrop = styled.div({ @@ -123,14 +123,14 @@ export const StyledAccountInputBackdrop = styled.div({ borderColor: colors.darkBlue, }); -export const StyledInputButton = styled.button((props: { visible: boolean }) => ({ +export const StyledInputButton = styled.button<{ $visible: boolean }>((props) => ({ display: 'flex', flex: 0, borderWidth: 0, width: '48px', alignItems: 'center', justifyContent: 'center', - opacity: props.visible ? 1 : 0, + opacity: props.$visible ? 1 : 0, transition: 'opacity 250ms ease-in-out', backgroundColor: colors.green, })); @@ -165,7 +165,7 @@ export const StyledInput = styled(FormattableTextInput)(largeText, { color: colors.blue, backgroundColor: 'transparent', flex: 1, - '::placeholder': { + '&&::placeholder': { color: colors.blue40, }, }); diff --git a/gui/src/renderer/components/MacOsScrollbarDetection.tsx b/gui/src/renderer/components/MacOsScrollbarDetection.tsx index de636827bd..aebb144f9b 100644 --- a/gui/src/renderer/components/MacOsScrollbarDetection.tsx +++ b/gui/src/renderer/components/MacOsScrollbarDetection.tsx @@ -1,8 +1,9 @@ -import React, { useEffect, useRef } from 'react'; +import { useEffect } from 'react'; import styled from 'styled-components'; import { MacOsScrollbarVisibility } from '../../shared/ipc-schema'; import useActions from '../lib/actionsHook'; +import { useStyledRef } from '../lib/utilityHooks'; import { useSelector } from '../redux/store'; import userInterface from '../redux/userinterface/actions'; @@ -21,7 +22,7 @@ const StyledContainer = styled.div({ export default function MacOsScrollbarDetection() { const visibility = useSelector((state) => state.userInterface.macOsScrollbarVisibility); const { setMacOsScrollbarVisibility } = useActions(userInterface); - const ref = useRef() as React.RefObject<HTMLDivElement>; + const ref = useStyledRef<HTMLDivElement>(); useEffect(() => { if (visibility === MacOsScrollbarVisibility.automatic) { diff --git a/gui/src/renderer/components/Marquee.tsx b/gui/src/renderer/components/Marquee.tsx index 5175fdc4a0..c654e473d2 100644 --- a/gui/src/renderer/components/Marquee.tsx +++ b/gui/src/renderer/components/Marquee.tsx @@ -7,14 +7,16 @@ const Container = styled.div({ overflow: 'hidden', }); -const Text = styled.span({}, (props: { overflow: number; alignRight: boolean }) => ({ +const Text = styled.span<{ $overflow: number; $alignRight: boolean }>((props) => ({ display: 'inline-block', // Prevents Container from adding 2px below the text. verticalAlign: 'middle', whiteSpace: 'nowrap', - willChange: props.overflow > 0 ? 'transform' : 'auto', - transform: props.alignRight ? `translate3d(${-props.overflow}px, 0, 0)` : 'translate3d(0, 0, 0)', - transition: `transform linear ${props.overflow * 80}ms`, + willChange: props.$overflow > 0 ? 'transform' : 'auto', + transform: props.$alignRight + ? `translate3d(${-props.$overflow}px, 0, 0)` + : 'translate3d(0, 0, 0)', + transition: `transform linear ${props.$overflow * 80}ms`, })); interface IMarqueeProps { @@ -67,8 +69,8 @@ export default class Marquee extends React.Component<IMarqueeProps, IMarqueeStat <Text key={this.state.uniqueKey} ref={this.textRef} - overflow={this.calculateOverflow()} - alignRight={this.state.alignRight} + $overflow={this.calculateOverflow()} + $alignRight={this.state.alignRight} onTransitionEnd={this.scheduleToggleAlignRight} {...otherProps}> {children} diff --git a/gui/src/renderer/components/Modal.tsx b/gui/src/renderer/components/Modal.tsx index d9be9e767d..e87e2113f2 100644 --- a/gui/src/renderer/components/Modal.tsx +++ b/gui/src/renderer/components/Modal.tsx @@ -25,9 +25,9 @@ const ModalContent = styled.div({ overflow: 'hidden', }); -const ModalBackground = styled.div({}, (props: { visible: boolean }) => ({ - backgroundColor: props.visible ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0)', - backdropFilter: props.visible ? 'blur(1.5px)' : '', +const ModalBackground = styled.div<{ $visible: boolean }>((props) => ({ + backgroundColor: props.$visible ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0)', + backdropFilter: props.$visible ? 'blur(1.5px)' : '', position: 'absolute', display: 'flex', flexDirection: 'column', @@ -37,7 +37,7 @@ const ModalBackground = styled.div({}, (props: { visible: boolean }) => ({ right: 0, bottom: 0, transition: 'background-color 150ms ease-out', - pointerEvents: props.visible ? 'auto' : 'none', + pointerEvents: props.$visible ? 'auto' : 'none', zIndex: 2, })); @@ -111,11 +111,11 @@ const ModalAlertContainer = styled.div({ padding: '14px', }); -const StyledModalAlert = styled.div({}, (props: { visible: boolean; closing: boolean }) => { +const StyledModalAlert = styled.div<{ $visible: boolean; $closing: boolean }>((props) => { let transform = ''; - if (props.visible && props.closing) { + if (props.$visible && props.$closing) { transform = 'scale(80%)'; - } else if (!props.visible) { + } else if (!props.$visible) { transform = 'translateY(10px) scale(98%)'; } @@ -126,7 +126,7 @@ const StyledModalAlert = styled.div({}, (props: { visible: boolean; closing: boo borderRadius: '11px', padding: '16px 0 16px 16px', maxHeight: '80vh', - opacity: props.visible && !props.closing ? 1 : 0, + opacity: props.$visible && !props.$closing ? 1 : 0, transform, boxShadow: ' 0px 15px 35px 5px rgba(0,0,0,0.5)', transition: 'all 150ms ease-out', @@ -254,15 +254,15 @@ class ModalAlertImpl extends React.Component<IModalAlertImplProps, IModalAlertSt private renderModal() { return ( <BackAction action={this.close}> - <ModalBackground visible={this.state.visible && !this.props.closing}> + <ModalBackground $visible={this.state.visible && !this.props.closing}> <ModalAlertContainer> <StyledModalAlert ref={this.modalRef} tabIndex={-1} role="dialog" aria-modal - visible={this.state.visible} - closing={this.props.closing} + $visible={this.state.visible} + $closing={this.props.closing} onTransitionEnd={this.onTransitionEnd}> <StyledCustomScrollbars> {this.props.type && ( diff --git a/gui/src/renderer/components/NavigationBar.tsx b/gui/src/renderer/components/NavigationBar.tsx index 56c3d2b8a3..d375fc34d6 100644 --- a/gui/src/renderer/components/NavigationBar.tsx +++ b/gui/src/renderer/components/NavigationBar.tsx @@ -180,7 +180,7 @@ interface ITitleBarItemProps { export const TitleBarItem = React.memo(function TitleBarItemT(props: ITitleBarItemProps) { const { visible } = useContext(TitleBarItemContext); - return <StyledTitleBarItemLabel visible={visible}>{props.children}</StyledTitleBarItemLabel>; + return <StyledTitleBarItemLabel $visible={visible}>{props.children}</StyledTitleBarItemLabel>; }); export function BackBarItem() { diff --git a/gui/src/renderer/components/NavigationBarStyles.tsx b/gui/src/renderer/components/NavigationBarStyles.tsx index d773c868d8..eb4472c900 100644 --- a/gui/src/renderer/components/NavigationBarStyles.tsx +++ b/gui/src/renderer/components/NavigationBarStyles.tsx @@ -25,7 +25,7 @@ export const StyledNavigationBar = styled.nav({ padding: '12px', }); -export const StyledTitleBarItemLabel = styled.h1(normalText, (props: { visible?: boolean }) => ({ +export const StyledTitleBarItemLabel = styled.h1<{ $visible?: boolean }>(normalText, (props) => ({ fontWeight: 400, lineHeight: '22px', color: colors.white, @@ -33,7 +33,7 @@ export const StyledTitleBarItemLabel = styled.h1(normalText, (props: { visible?: overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', - opacity: props.visible ? 1 : 0, + opacity: props.$visible ? 1 : 0, transition: 'opacity 250ms ease-in-out', })); @@ -51,7 +51,7 @@ export const StyledBackBarItemButton = styled.button({ export const StyledBackBarItemIcon = styled(ImageView)({ marginRight: '8px', - [StyledBackBarItemButton + ':hover &']: { + [StyledBackBarItemButton + ':hover &&']: { backgroundColor: colors.white60, }, }); diff --git a/gui/src/renderer/components/NotificationArea.tsx b/gui/src/renderer/components/NotificationArea.tsx index 79fb3ef17c..9bce47eea6 100644 --- a/gui/src/renderer/components/NotificationArea.tsx +++ b/gui/src/renderer/components/NotificationArea.tsx @@ -97,7 +97,7 @@ export default function NotificationArea(props: IProps) { if (notification) { return ( <NotificationBanner className={props.className}> - <NotificationIndicator type={notification.indicator} /> + <NotificationIndicator $type={notification.indicator} /> <NotificationContent role="status" aria-live="polite"> <NotificationTitle>{notification.title}</NotificationTitle> <NotificationSubtitle>{formatHtml(notification.subtitle ?? '')}</NotificationSubtitle> diff --git a/gui/src/renderer/components/NotificationBanner.tsx b/gui/src/renderer/components/NotificationBanner.tsx index 18e18ce4f5..3b66abb204 100644 --- a/gui/src/renderer/components/NotificationBanner.tsx +++ b/gui/src/renderer/components/NotificationBanner.tsx @@ -4,6 +4,7 @@ import styled from 'styled-components'; import { colors } from '../../config.json'; import { messages } from '../../shared/gettext'; import { InAppNotificationIndicatorType } from '../../shared/notifications/notification'; +import { useStyledRef } from '../lib/utilityHooks'; import * as AppButton from './AppButton'; import { tinyText } from './common-styles'; import ImageView from './ImageView'; @@ -104,7 +105,7 @@ export const NotificationActions = styled.div({ }); interface INotificationIndicatorProps { - type?: InAppNotificationIndicatorType; + $type?: InAppNotificationIndicatorType; } const notificationIndicatorTypeColorMap = { @@ -113,29 +114,29 @@ const notificationIndicatorTypeColorMap = { error: colors.red, }; -export const NotificationIndicator = styled.div((props: INotificationIndicatorProps) => ({ +export const NotificationIndicator = styled.div<INotificationIndicatorProps>((props) => ({ width: '10px', height: '10px', borderRadius: '5px', marginTop: '4px', marginRight: '8px', - backgroundColor: props.type ? notificationIndicatorTypeColorMap[props.type] : 'transparent', + backgroundColor: props.$type ? notificationIndicatorTypeColorMap[props.$type] : 'transparent', })); interface ICollapsibleProps { - alignBottom: boolean; - height?: number; + $alignBottom: boolean; + $height?: number; } -const Collapsible = styled.div({}, (props: ICollapsibleProps) => { +const Collapsible = styled.div<ICollapsibleProps>((props) => { return { display: 'flex', flexDirection: 'column', - justifyContent: props.alignBottom ? 'flex-end' : 'flex-start', + justifyContent: props.$alignBottom ? 'flex-end' : 'flex-start', backgroundColor: 'rgba(25, 38, 56, 0.95)', overflow: 'hidden', // Using auto as the initial value prevents transition if a notification is visible on mount. - height: props.height === undefined ? 'auto' : `${props.height}px`, + height: props.$height === undefined ? 'auto' : `${props.$height}px`, transition: 'height 250ms ease-in-out', }; }); @@ -156,7 +157,7 @@ export function NotificationBanner(props: INotificationBannerProps) { const [contentHeight, setContentHeight] = useState<number>(); const [alignBottom, setAlignBottom] = useState(false); - const contentRef = useRef() as React.RefObject<HTMLDivElement>; + const contentRef = useStyledRef<HTMLDivElement>(); // Save last non-undefined children to be able to show them during the hide-transition. const prevChildren = useRef<React.ReactNode>(); @@ -174,7 +175,7 @@ export function NotificationBanner(props: INotificationBannerProps) { }); return ( - <Collapsible height={contentHeight} className={props.className} alignBottom={alignBottom}> + <Collapsible $height={contentHeight} className={props.className} $alignBottom={alignBottom}> <Content ref={contentRef}>{props.children ?? prevChildren.current}</Content> </Collapsible> ); diff --git a/gui/src/renderer/components/RedeemVoucherStyles.tsx b/gui/src/renderer/components/RedeemVoucherStyles.tsx index 2ef0cc2634..37f51ee5e4 100644 --- a/gui/src/renderer/components/RedeemVoucherStyles.tsx +++ b/gui/src/renderer/components/RedeemVoucherStyles.tsx @@ -20,7 +20,7 @@ export const StyledInput = styled(FormattableTextInput)(normalText, { backgroundColor: colors.white, border: 'none', borderRadius: '4px', - '::placeholder': { + '&&::placeholder': { color: colors.blue40, }, }); diff --git a/gui/src/renderer/components/RelayStatusIndicator.tsx b/gui/src/renderer/components/RelayStatusIndicator.tsx index 7d90e1491d..549fec4e70 100644 --- a/gui/src/renderer/components/RelayStatusIndicator.tsx +++ b/gui/src/renderer/components/RelayStatusIndicator.tsx @@ -3,12 +3,12 @@ import styled from 'styled-components'; import { colors } from '../../config.json'; import * as Cell from './cell'; -const StyledRelayStatus = styled.div((props: { active: boolean }) => ({ +const StyledRelayStatus = styled.div<{ $active: boolean }>((props) => ({ width: '16px', height: '16px', borderRadius: '8px', margin: '0 12px 0 4px', - backgroundColor: props.active ? colors.green90 : colors.red95, + backgroundColor: props.$active ? colors.green90 : colors.red95, })); const TickIcon = styled(Cell.Icon)({ @@ -25,6 +25,6 @@ export default function RelayStatusIndicator(props: IProps) { return props.selected ? ( <TickIcon tintColor={colors.white} source="icon-tick" width={18} /> ) : ( - <StyledRelayStatus active={props.active} /> + <StyledRelayStatus $active={props.active} /> ); } diff --git a/gui/src/renderer/components/SearchBar.tsx b/gui/src/renderer/components/SearchBar.tsx index 7983441ed8..e21439fbbf 100644 --- a/gui/src/renderer/components/SearchBar.tsx +++ b/gui/src/renderer/components/SearchBar.tsx @@ -1,8 +1,9 @@ -import { useCallback, useEffect, useRef } from 'react'; +import { useCallback, useEffect } from 'react'; import styled from 'styled-components'; import { colors } from '../../config.json'; import { messages } from '../../shared/gettext'; +import { useStyledRef } from '../lib/utilityHooks'; import { normalText } from './common-styles'; import ImageView from './ImageView'; @@ -21,15 +22,15 @@ export const StyledSearchInput = styled.input.attrs({ type: 'text' })({ lineHeight: '24px', color: colors.white60, backgroundColor: colors.white10, - '::placeholder': { + '&&::placeholder': { color: colors.white60, }, - ':focus': { + '&&:focus': { color: colors.blue, backgroundColor: colors.white, - '::placeholder': { - color: colors.blue40, - }, + }, + '&&:focus::placeholder': { + color: colors.blue40, }, }); @@ -48,20 +49,20 @@ export const StyledSearchIcon = styled(ImageView)({ top: '50%', transform: 'translateY(-50%)', left: '9px', - [`${StyledSearchInput}:focus ~ &`]: { + [`${StyledSearchInput}:focus ~ &&`]: { backgroundColor: colors.blue, }, }); export const StyledClearIcon = styled(ImageView)({ - ':hover': { + '&&:hover': { backgroundColor: colors.white60, }, - [`${StyledSearchInput}:focus ~ ${StyledClearButton} &`]: { + [`${StyledSearchInput}:focus ~ ${StyledClearButton} &&`]: { backgroundColor: colors.blue40, - ':hover': { - backgroundColor: colors.blue, - }, + }, + [`${StyledSearchInput}:focus ~ ${StyledClearButton} &&:hover`]: { + backgroundColor: colors.blue, }, }); @@ -73,7 +74,7 @@ interface ISearchBarProps { } export default function SearchBar(props: ISearchBarProps) { - const inputRef = useRef() as React.RefObject<HTMLInputElement>; + const inputRef = useStyledRef<HTMLInputElement>(); const onInput = useCallback( (event: React.FormEvent) => { diff --git a/gui/src/renderer/components/SecuredLabel.tsx b/gui/src/renderer/components/SecuredLabel.tsx index 736297b480..534a01a58c 100644 --- a/gui/src/renderer/components/SecuredLabel.tsx +++ b/gui/src/renderer/components/SecuredLabel.tsx @@ -25,10 +25,10 @@ const securedDisplayStyleColorMap = { [SecuredDisplayStyle.failedToSecure]: colors.red, }; -const StyledSecuredLabel = styled.span((props: { displayStyle: SecuredDisplayStyle }) => ({ +const StyledSecuredLabel = styled.span<{ $displayStyle: SecuredDisplayStyle }>((props) => ({ display: 'inline-block', minHeight: '22px', - color: securedDisplayStyleColorMap[props.displayStyle], + color: securedDisplayStyleColorMap[props.$displayStyle], })); interface ISecuredLabelProps { @@ -37,8 +37,13 @@ interface ISecuredLabelProps { } export default function SecuredLabel(props: ISecuredLabelProps) { + const { displayStyle, ...otherProps } = props; return ( - <StyledSecuredLabel {...props} role="status" aria-live="polite"> + <StyledSecuredLabel + $displayStyle={displayStyle} + {...otherProps} + role="status" + aria-live="polite"> {getLabelText(props.displayStyle)} </StyledSecuredLabel> ); diff --git a/gui/src/renderer/components/SelectLanguage.tsx b/gui/src/renderer/components/SelectLanguage.tsx index b97243a456..eb0ad038da 100644 --- a/gui/src/renderer/components/SelectLanguage.tsx +++ b/gui/src/renderer/components/SelectLanguage.tsx @@ -21,7 +21,7 @@ import SettingsHeader, { HeaderTitle } from './SettingsHeader'; const StyledSelector = styled(Selector)({ marginBottom: 0, -}) as typeof Selector; +}); export default function SelectLanguage() { const history = useHistory(); diff --git a/gui/src/renderer/components/SettingsHeader.tsx b/gui/src/renderer/components/SettingsHeader.tsx index 8724ea93d9..47e1f47f7b 100644 --- a/gui/src/renderer/components/SettingsHeader.tsx +++ b/gui/src/renderer/components/SettingsHeader.tsx @@ -13,7 +13,7 @@ export const Container = styled.div({ }); export const ContentWrapper = styled.div({ - ':not(:first-child)': { + '&&:not(:first-child)': { paddingTop: '8px', }, }); diff --git a/gui/src/renderer/components/SplitTunnelingSettings.tsx b/gui/src/renderer/components/SplitTunnelingSettings.tsx index c8242cdd88..cb4de70deb 100644 --- a/gui/src/renderer/components/SplitTunnelingSettings.tsx +++ b/gui/src/renderer/components/SplitTunnelingSettings.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; import { sprintf } from 'sprintf-js'; @@ -12,7 +12,7 @@ import { messages } from '../../shared/gettext'; import { useAppContext } from '../context'; import { useHistory } from '../lib/history'; import { formatHtml } from '../lib/html-formatter'; -import { useAsyncEffect } from '../lib/utilityHooks'; +import { useAsyncEffect, useStyledRef } from '../lib/utilityHooks'; import { IReduxState } from '../redux/store'; import Accordion from './Accordion'; import * as AppButton from './AppButton'; @@ -49,13 +49,13 @@ import Switch from './Switch'; export default function SplitTunneling() { const { pop } = useHistory(); const [browsing, setBrowsing] = useState(false); - const scrollbarsRef = useRef() as React.RefObject<CustomScrollbarsRef>; + const scrollbarsRef = useStyledRef<CustomScrollbarsRef>(); const scrollToTop = useCallback(() => scrollbarsRef.current?.scrollToTop(true), [scrollbarsRef]); return ( <> - <StyledPageCover show={browsing} /> + <StyledPageCover $show={browsing} /> <BackAction action={pop}> <Layout> <SettingsContainer> @@ -173,7 +173,21 @@ function LinuxSplitTunnelingSettings(props: IPlatformSplitTunnelingSettingsProps </SettingsHeader> <StyledSearchBar searchTerm={searchTerm} onSearch={setSearchTerm} /> - <ApplicationList applications={filteredApplications} rowRenderer={rowRenderer} /> + {filteredApplications !== undefined && filteredApplications.length > 0 && ( + <ApplicationList applications={filteredApplications} rowRenderer={rowRenderer} /> + )} + + {searchTerm !== '' && + (filteredApplications === undefined || filteredApplications.length === 0) && ( + <StyledNoResult> + <StyledNoResultText> + {formatHtml( + sprintf(messages.gettext('No result for <b>%(searchTerm)s</b>.'), { searchTerm }), + )} + </StyledNoResultText> + <StyledNoResultText>{messages.gettext('Try a different search.')}</StyledNoResultText> + </StyledNoResult> + )} <StyledBrowseButton onClick={launchWithFilePicker}> {messages.pgettext('split-tunneling-view', 'Find another app')} @@ -258,18 +272,18 @@ function LinuxApplicationRow(props: ILinuxApplicationRowProps) { <> <StyledCellButton onClick={props.application.warning ? showWarningDialog : launch} - lookDisabled={disabled}> + $lookDisabled={disabled}> {props.application.icon ? ( <StyledIcon source={props.application.icon} width={35} height={35} - lookDisabled={disabled} + $lookDisabled={disabled} /> ) : ( <StyledIconPlaceholder /> )} - <StyledCellLabel lookDisabled={disabled}>{props.application.name}</StyledCellLabel> + <StyledCellLabel $lookDisabled={disabled}>{props.application.name}</StyledCellLabel> {props.application.warning && ( <StyledCellWarningIcon source="icon-alert" tintColor={warningColor} width={18} /> )} diff --git a/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx b/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx index 028fac30a3..1aea5108a1 100644 --- a/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx +++ b/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx @@ -9,7 +9,7 @@ import { NavigationScrollbars } from './NavigationBar'; import SearchBar from './SearchBar'; import { HeaderTitle } from './SettingsHeader'; -export const StyledPageCover = styled.div({}, (props: { show: boolean }) => ({ +export const StyledPageCover = styled.div<{ $show: boolean }>((props) => ({ position: 'absolute', zIndex: 2, top: 0, @@ -18,7 +18,7 @@ export const StyledPageCover = styled.div({}, (props: { show: boolean }) => ({ bottom: 0, backgroundColor: colors.black, opacity: 0.5, - display: props.show ? 'block' : 'none', + display: props.$show ? 'block' : 'none', })); export const StyledNavigationScrollbars = styled(NavigationScrollbars)({ @@ -31,17 +31,21 @@ export const StyledContent = styled.div({ flex: 1, }); -export const StyledCellButton = styled(Cell.CellButton)((props: { lookDisabled?: boolean }) => ({ - ':not(:disabled):hover': { - backgroundColor: props.lookDisabled ? colors.blue : undefined, +export const StyledCellButton = styled(Cell.CellButton)<{ $lookDisabled?: boolean }>((props) => ({ + '&&:not(:disabled):hover': { + backgroundColor: props.$lookDisabled ? colors.blue : undefined, }, })); -const disabledApplication = (props: { lookDisabled?: boolean }) => ({ - opacity: props.lookDisabled ? 0.6 : undefined, +interface DisabledApplicationProps { + $lookDisabled?: boolean; +} + +const disabledApplication = (props: DisabledApplicationProps) => ({ + opacity: props.$lookDisabled ? 0.6 : undefined, }); -export const StyledIcon = styled(Cell.UntintedIcon)(disabledApplication, { +export const StyledIcon = styled(Cell.UntintedIcon)<DisabledApplicationProps>(disabledApplication, { marginRight: '12px', }); @@ -54,11 +58,15 @@ export const StyledCellWarningIcon = styled(Cell.Icon)({ marginRight: '3px', }); -export const StyledCellLabel = styled(Cell.Label)(disabledApplication, normalText, { - fontWeight: 400, - wordWrap: 'break-word', - overflow: 'hidden', -}); +export const StyledCellLabel = styled(Cell.Label)<DisabledApplicationProps>( + disabledApplication, + normalText, + { + fontWeight: 400, + wordWrap: 'break-word', + overflow: 'hidden', + }, +); export const StyledIconPlaceholder = styled.div({ width: '35px', @@ -93,6 +101,7 @@ export const StyledNoResult = styled(Cell.CellFooter)({ flexDirection: 'column', paddingTop: 0, marginTop: 0, + marginBottom: '69px', }); export const StyledNoResultText = styled(Cell.CellFooterText)({ diff --git a/gui/src/renderer/components/Switch.tsx b/gui/src/renderer/components/Switch.tsx index cb9e86b219..e5aaec4bb0 100644 --- a/gui/src/renderer/components/Switch.tsx +++ b/gui/src/renderer/components/Switch.tsx @@ -14,7 +14,7 @@ interface IProps { innerRef?: React.Ref<HTMLDivElement>; } -const SwitchContainer = styled.div({}, (props: { disabled: boolean }) => ({ +const SwitchContainer = styled.div<{ disabled: boolean }>((props) => ({ position: 'relative', width: '48px', height: '30px', @@ -25,10 +25,10 @@ const SwitchContainer = styled.div({}, (props: { disabled: boolean }) => ({ padding: '2px', })); -const Knob = styled.div({}, (props: { isOn: boolean; disabled: boolean }) => { - let backgroundColor = props.isOn ? colors.green : colors.red; +const Knob = styled.div<{ $isOn: boolean; disabled: boolean }>((props) => { + let backgroundColor = props.$isOn ? colors.green : colors.red; if (props.disabled) { - backgroundColor = props.isOn ? colors.green40 : colors.red40; + backgroundColor = props.$isOn ? colors.green40 : colors.red40; } return { @@ -40,7 +40,7 @@ const Knob = styled.div({}, (props: { isOn: boolean; disabled: boolean }) => { backgroundColor, // When enabled the button should be placed all the way to the right (100%) minus padding (2px) // minus it's own width (22px). - left: props.isOn ? 'calc(100% - 2px - 22px)' : '2px', + left: props.$isOn ? 'calc(100% - 2px - 22px)' : '2px', }; }); @@ -59,7 +59,7 @@ export default class Switch extends React.PureComponent<IProps> { aria-disabled={this.props.disabled ?? false} tabIndex={-1} className={this.props.className}> - <Knob disabled={this.props.disabled ?? false} isOn={this.props.isOn} /> + <Knob disabled={this.props.disabled ?? false} $isOn={this.props.isOn} /> </SwitchContainer> ); } diff --git a/gui/src/renderer/components/TransitionContainer.tsx b/gui/src/renderer/components/TransitionContainer.tsx index 56315d5dcb..b9a60185ed 100644 --- a/gui/src/renderer/components/TransitionContainer.tsx +++ b/gui/src/renderer/components/TransitionContainer.tsx @@ -41,29 +41,36 @@ interface IState { export const StyledTransitionContainer = styled.div({ flex: 1 }); -export const StyledTransitionContent = styled.div.attrs({ 'data-testid': 'transition-content' })( - {}, - (props: { transition?: IItemStyle; disableUserInteraction?: boolean }) => { - const x = `${props.transition?.x ?? 0}%`; - const y = `${props.transition?.y ?? 0}%`; - const duration = props.transition?.duration ?? 450; +interface StyledTransitionContentProps { + $transition?: IItemStyle; + $disableUserInteraction?: boolean; +} - return { - display: 'flex', - flexDirection: 'column', - position: 'absolute', - left: 0, - right: 0, - top: 0, - bottom: 0, - zIndex: props.transition?.inFront ? 1 : 0, - willChange: 'transform', - transform: `translate3d(${x}, ${y}, 0)`, - transition: `transform ${duration}ms ease-in-out`, - pointerEvents: props.disableUserInteraction ? 'none' : undefined, - }; - }, -); +export const StyledTransitionContent = styled.div.attrs< + StyledTransitionContentProps, + { 'data-testid': string } +>({ + 'data-testid': 'transition-content', +})((props) => { + const x = `${props.$transition?.x ?? 0}%`; + const y = `${props.$transition?.y ?? 0}%`; + const duration = props.$transition?.duration ?? 450; + + return { + display: 'flex', + flexDirection: 'column', + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + zIndex: props.$transition?.inFront ? 1 : 0, + willChange: 'transform', + transform: `translate3d(${x}, ${y}, 0)`, + transition: `transform ${duration}ms ease-in-out`, + pointerEvents: props.$disableUserInteraction ? 'none' : undefined, + }; +}); export const StyledTransitionView = styled.div({ display: 'flex', @@ -132,9 +139,9 @@ export default class TransitionContainer extends React.Component<IProps, IState> <WillExit key={this.state.currentItem.view.props.routePath} value={willExit}> <StyledTransitionContent ref={this.setCurrentContentRef} - transition={this.state.currentItemStyle} + $transition={this.state.currentItemStyle} onTransitionEnd={this.onTransitionEnd} - disableUserInteraction={willExit}> + $disableUserInteraction={willExit}> {this.state.currentItem.view} </StyledTransitionContent> </WillExit> @@ -144,7 +151,7 @@ export default class TransitionContainer extends React.Component<IProps, IState> <WillExit key={this.state.nextItem.view.props.routePath} value={false}> <StyledTransitionContent ref={this.setNextContentRef} - transition={this.state.nextItemStyle} + $transition={this.state.nextItemStyle} onTransitionEnd={this.onTransitionEnd}> {this.state.nextItem.view} </StyledTransitionContent> diff --git a/gui/src/renderer/components/cell/CellButton.tsx b/gui/src/renderer/components/cell/CellButton.tsx index 0111574f70..b0079226f3 100644 --- a/gui/src/renderer/components/cell/CellButton.tsx +++ b/gui/src/renderer/components/cell/CellButton.tsx @@ -8,17 +8,17 @@ import { Row } from './Row'; import { CellSectionContext } from './Section'; interface IStyledCellButtonProps extends React.HTMLAttributes<HTMLButtonElement> { - selected?: boolean; - containedInSection: boolean; + $selected?: boolean; + $containedInSection: boolean; } -const StyledCellButton = styled(Row)({}, (props: IStyledCellButtonProps) => { - const backgroundColor = props.selected +const StyledCellButton = styled(Row)<IStyledCellButtonProps>((props) => { + const backgroundColor = props.$selected ? colors.green - : props.containedInSection + : props.$containedInSection ? colors.blue40 : colors.blue; - const backgroundColorHover = props.selected ? colors.green : colors.blue80; + const backgroundColorHover = props.$selected ? colors.green : colors.blue80; return { paddingRight: '16px', @@ -27,7 +27,7 @@ const StyledCellButton = styled(Row)({}, (props: IStyledCellButtonProps) => { cursor: 'default', border: 'none', backgroundColor, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: props.onClick ? backgroundColorHover : backgroundColor, }, }; @@ -39,14 +39,16 @@ interface ICellButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> export const CellButton = styled( React.forwardRef(function Button(props: ICellButtonProps, ref: React.Ref<HTMLButtonElement>) { + const { selected, ...otherProps } = props; const containedInSection = useContext(CellSectionContext); return ( <CellDisabledContext.Provider value={props.disabled ?? false}> <StyledCellButton as="button" ref={ref} - containedInSection={containedInSection} - {...props} + $selected={selected} + $containedInSection={containedInSection} + {...otherProps} /> </CellDisabledContext.Provider> ); diff --git a/gui/src/renderer/components/cell/Group.tsx b/gui/src/renderer/components/cell/Group.tsx index a3198cabb0..86d95f0a33 100644 --- a/gui/src/renderer/components/cell/Group.tsx +++ b/gui/src/renderer/components/cell/Group.tsx @@ -3,12 +3,12 @@ import styled from 'styled-components'; import { measurements } from '../common-styles'; interface IStyledGroupProps { - noMarginBottom?: boolean; + $noMarginBottom?: boolean; } -export const Group = styled.div({}, (props: IStyledGroupProps) => ({ +export const Group = styled.div<IStyledGroupProps>((props) => ({ display: 'flex', flexDirection: 'column', flex: 1, - marginBottom: props.noMarginBottom ? '0px' : measurements.rowVerticalMargin, + marginBottom: props.$noMarginBottom ? '0px' : measurements.rowVerticalMargin, })); diff --git a/gui/src/renderer/components/cell/Input.tsx b/gui/src/renderer/components/cell/Input.tsx index bff8364d21..97fe01a185 100644 --- a/gui/src/renderer/components/cell/Input.tsx +++ b/gui/src/renderer/components/cell/Input.tsx @@ -1,8 +1,8 @@ -import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useState } from 'react'; import styled from 'styled-components'; import { colors } from '../../../config.json'; -import { useBoolean, useCombinedRefs } from '../../lib/utilityHooks'; +import { useBoolean, useCombinedRefs, useStyledRef } from '../../lib/utilityHooks'; import { normalText } from '../common-styles'; import ImageView from '../ImageView'; import { BackAction } from '../KeyboardNavigation'; @@ -24,15 +24,15 @@ const inputTextStyles: React.CSSProperties = { padding: '0px', }; -const StyledInput = styled.input({}, (props: { focused: boolean; valid?: boolean }) => ({ +const StyledInput = styled.input<{ $focused: boolean; $valid?: boolean }>((props) => ({ ...inputTextStyles, backgroundColor: 'transparent', border: 'none', width: '100%', height: '100%', - color: props.valid === false ? colors.red : props.focused ? colors.blue : colors.white, - '::placeholder': { - color: props.focused ? colors.blue60 : colors.white60, + color: props.$valid === false ? colors.red : props.$focused ? colors.blue : colors.white, + '&&::placeholder': { + color: props.$focused ? colors.blue60 : colors.white60, }, })); @@ -68,7 +68,7 @@ function InputWithRef(props: IInputProps, forwardedRef: React.Ref<HTMLInputEleme const [internalValue, setInternalValue] = useState(props.value ?? props.initialValue ?? ''); const value = props.value ?? internalValue; - const inputRef = useRef() as React.RefObject<HTMLInputElement>; + const inputRef = useStyledRef<HTMLInputElement>(); const combinedRef = useCombinedRefs(inputRef, forwardedRef); const onSubmit = useCallback( @@ -150,8 +150,8 @@ function InputWithRef(props: IInputProps, forwardedRef: React.Ref<HTMLInputEleme {...otherProps} ref={combinedRef} type="text" - valid={valid} - focused={isFocused} + $valid={valid} + $focused={isFocused} aria-invalid={!valid} onChange={onChange} onFocus={onFocus} @@ -167,10 +167,10 @@ function InputWithRef(props: IInputProps, forwardedRef: React.Ref<HTMLInputEleme export const Input = React.memo(React.forwardRef(InputWithRef)); -const InputFrame = styled.div((props: { focused: boolean }) => ({ +const InputFrame = styled.div<{ $focused: boolean }>((props) => ({ display: 'flex', flexGrow: 0, - backgroundColor: props.focused ? colors.white : 'rgba(255,255,255,0.1)', + backgroundColor: props.$focused ? colors.white : 'rgba(255,255,255,0.1)', borderRadius: '4px', padding: '6px 8px', })); @@ -197,7 +197,7 @@ function AutoSizingTextInputWithRef(props: IInputProps, forwardedRef: React.Ref< const { onFocus, onBlur, ...otherProps } = props; const [focused, setFocused, setBlurred] = useBoolean(false); - const inputRef = useRef() as React.RefObject<HTMLInputElement>; + const inputRef = useStyledRef<HTMLInputElement>(); const combinedRef = useCombinedRefs(inputRef, forwardedRef); const onBlurWrapper = useCallback( @@ -222,7 +222,7 @@ function AutoSizingTextInputWithRef(props: IInputProps, forwardedRef: React.Ref< return ( <BackAction disabled={!focused} action={blur}> - <InputFrame focused={focused}> + <InputFrame $focused={focused}> <StyledAutoSizingTextInputContainer> <StyledAutoSizingTextInputWrapper> <Input @@ -254,11 +254,11 @@ const StyledSubmitButton = styled.button({ padding: '10px 0', }); -const StyledInputWrapper = styled.div(normalText, (props: { marginLeft: number }) => ({ +const StyledInputWrapper = styled.div<{ $marginLeft: number }>(normalText, (props) => ({ position: 'relative', flex: 1, width: '171px', - marginLeft: props.marginLeft + 'px', + marginLeft: props.$marginLeft + 'px', lineHeight: '24px', minHeight: '24px', fontWeight: 400, @@ -266,7 +266,7 @@ const StyledInputWrapper = styled.div(normalText, (props: { marginLeft: number } maxWidth: '100%', })); -const StyledTextArea = styled.textarea(normalText, (props: { invalid?: boolean }) => ({ +const StyledTextArea = styled.textarea<{ $invalid?: boolean }>(normalText, (props) => ({ position: 'absolute', top: 0, left: 0, @@ -279,7 +279,7 @@ const StyledTextArea = styled.textarea(normalText, (props: { invalid?: boolean } fontWeight: 400, resize: 'none', padding: '10px 25px 10px 0', - color: props.invalid ? colors.red : 'auto', + color: props.$invalid ? colors.red : 'auto', })); const StyledInputFiller = styled.div({ @@ -304,7 +304,7 @@ interface IRowInputProps { export function RowInput(props: IRowInputProps) { const [value, setValue] = useState(props.initialValue ?? ''); - const textAreaRef = useRef() as React.RefObject<HTMLTextAreaElement>; + const textAreaRef = useStyledRef<HTMLTextAreaElement>(); const [focused, setFocused, setBlurred] = useBoolean(false); const submit = useCallback(() => props.onSubmit(value), [props.onSubmit, value]); @@ -366,7 +366,7 @@ export function RowInput(props: IRowInputProps) { return ( <BackAction disabled={!focused} action={blur}> <StyledCellInputRowContainer> - <StyledInputWrapper marginLeft={props.paddingLeft ?? 0}> + <StyledInputWrapper $marginLeft={props.paddingLeft ?? 0}> <StyledInputFiller>{value}</StyledInputFiller> <StyledTextArea ref={textAreaRef} @@ -374,7 +374,7 @@ export function RowInput(props: IRowInputProps) { onKeyDown={onKeyDown} rows={1} value={value} - invalid={props.invalid} + $invalid={props.invalid} onFocus={onFocus} onBlur={onBlur} placeholder={props.placeholder} diff --git a/gui/src/renderer/components/cell/Label.tsx b/gui/src/renderer/components/cell/Label.tsx index f3d3624d45..5e9963ded9 100644 --- a/gui/src/renderer/components/cell/Label.tsx +++ b/gui/src/renderer/components/cell/Label.tsx @@ -7,14 +7,14 @@ import ImageView, { IImageViewProps } from '../ImageView'; import { CellButton } from './CellButton'; import { CellDisabledContext } from './Container'; -const StyledLabel = styled.div(buttonText, (props: { disabled: boolean }) => ({ +const StyledLabel = styled.div<{ disabled: boolean }>(buttonText, (props) => ({ margin: '10px 0', flex: 1, color: props.disabled ? colors.white40 : colors.white, textAlign: 'left', })); -const StyledSubText = styled.span(tinyText, (props: { disabled: boolean }) => ({ +const StyledSubText = styled.span<{ disabled: boolean }>(tinyText, (props) => ({ color: props.disabled ? colors.white20 : colors.white60, flex: -1, textAlign: 'right', @@ -22,7 +22,7 @@ const StyledSubText = styled.span(tinyText, (props: { disabled: boolean }) => ({ marginRight: '8px', })); -const StyledIconContainer = styled.div((props: { disabled: boolean }) => ({ +const StyledIconContainer = styled.div<{ disabled: boolean }>((props) => ({ opacity: props.disabled ? 0.4 : 1, })); @@ -30,7 +30,7 @@ const StyledTintedIcon = styled(ImageView).attrs((props: IImageViewProps) => ({ tintColor: props.tintColor ?? colors.white60, tintHoverColor: props.tintHoverColor ?? props.tintColor ?? colors.white60, }))((props: IImageViewProps) => ({ - ':hover': { + '&&:hover': { backgroundColor: props.tintColor, }, [`${CellButton}:not(:disabled):hover &&`]: { diff --git a/gui/src/renderer/components/cell/Row.tsx b/gui/src/renderer/components/cell/Row.tsx index 08309d6843..9aca25d3a0 100644 --- a/gui/src/renderer/components/cell/Row.tsx +++ b/gui/src/renderer/components/cell/Row.tsx @@ -1,10 +1,17 @@ +import React from 'react'; import styled from 'styled-components'; import { colors } from '../../../config.json'; import { measurements } from '../common-styles'; import { Group } from './Group'; -export const Row = styled.div((props: { includeMarginBottomOnLast?: boolean }) => ({ +interface RowProps extends React.HTMLAttributes<HTMLDivElement> { + includeMarginBottomOnLast?: boolean; +} + +export const Row = styled.div.withConfig({ + shouldForwardProp: (prop) => prop !== 'includeMarginBottomOnLast', +})<RowProps>((props) => ({ display: 'flex', alignItems: 'center', backgroundColor: colors.blue, @@ -12,7 +19,7 @@ export const Row = styled.div((props: { includeMarginBottomOnLast?: boolean }) = paddingLeft: measurements.viewMargin, paddingRight: measurements.viewMargin, marginBottom: '1px', - [`${Group} > &:last-child`]: { + [`${Group} > &&:last-child`]: { marginBottom: props.includeMarginBottomOnLast ? '1px' : '0px', }, })); diff --git a/gui/src/renderer/components/cell/Section.tsx b/gui/src/renderer/components/cell/Section.tsx index 8ba5975476..056aa8ceac 100644 --- a/gui/src/renderer/components/cell/Section.tsx +++ b/gui/src/renderer/components/cell/Section.tsx @@ -18,15 +18,15 @@ const StyledSection = styled.div({ interface SectionTitleProps { disabled?: boolean; - thin?: boolean; + $thin?: boolean; } -export const SectionTitle = styled(Row)(buttonText, (props: SectionTitleProps) => ({ +export const SectionTitle = styled(Row)<SectionTitleProps>(buttonText, (props) => ({ paddingRight: '16px', color: props.disabled ? colors.white20 : colors.white, - fontWeight: props.thin ? 400 : 600, - fontSize: props.thin ? '15px' : '18px', - ...(props.thin ? openSans : sourceSansPro), + fontWeight: props.$thin ? 400 : 600, + fontSize: props.$thin ? '15px' : '18px', + ...(props.$thin ? openSans : sourceSansPro), })); export const CellSectionContext = React.createContext<boolean>(false); diff --git a/gui/src/renderer/components/cell/Selector.tsx b/gui/src/renderer/components/cell/Selector.tsx index 5eb9762eb3..f1d9e14360 100644 --- a/gui/src/renderer/components/cell/Selector.tsx +++ b/gui/src/renderer/components/cell/Selector.tsx @@ -3,6 +3,7 @@ import styled from 'styled-components'; import { colors } from '../../../config.json'; import { messages } from '../../../shared/gettext'; +import { useStyledRef } from '../../lib/utilityHooks'; import { AriaDetails, AriaInput, AriaLabel } from '../AriaGroup'; import { normalText } from '../common-styles'; import InfoButton from '../InfoButton'; @@ -76,7 +77,7 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) { const title = props.title ? ( <> <AriaLabel> - <StyledTitleLabel as="label" disabled={props.disabled} thin={props.thinTitle}> + <StyledTitleLabel as="label" disabled={props.disabled} $thin={props.thinTitle}> {props.title} </StyledTitleLabel> </AriaLabel> @@ -90,7 +91,7 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) { // Add potential additional items to the list. Used for custom entry. const children = ( - <Cell.Group noMarginBottom> + <Cell.Group $noMarginBottom> {items} {props.children} </Cell.Group> @@ -120,8 +121,8 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) { } } -const StyledCellIcon = styled(Cell.Icon)((props: { visible: boolean }) => ({ - opacity: props.visible ? 1 : 0, +const StyledCellIcon = styled(Cell.Icon)<{ $visible: boolean }>((props) => ({ + opacity: props.$visible ? 1 : 0, marginRight: '8px', })); @@ -155,7 +156,7 @@ function SelectorCell<T>(props: SelectorCellProps<T>) { aria-selected={props.isSelected} aria-disabled={props.disabled}> <StyledCellIcon - visible={props.isSelected} + $visible={props.isSelected} source="icon-tick" width={18} tintColor={colors.white} @@ -169,9 +170,9 @@ interface StyledCustomContainerProps { selected: boolean; } -const StyledCustomContainer = styled(Cell.Container)((props: StyledCustomContainerProps) => ({ +const StyledCustomContainer = styled(Cell.Container)<StyledCustomContainerProps>((props) => ({ backgroundColor: props.selected ? colors.green : colors.blue40, - ':hover': { + '&&:hover': { backgroundColor: props.selected ? colors.green : colors.blue, }, })); @@ -212,7 +213,7 @@ export function SelectorWithCustomItem<T, U>(props: SelectorWithCustomItemProps< const [customValue, setCustomValue] = useState(itemIsSelected ? undefined : `${value}`); const customIsSelected = customValue !== undefined; - const inputRef = useRef() as React.RefObject<HTMLInputElement>; + const inputRef = useStyledRef<HTMLInputElement>(); const handleClickCustom = useCallback(() => { inputRef.current?.focus(); @@ -285,7 +286,7 @@ export function SelectorWithCustomItem<T, U>(props: SelectorWithCustomItemProps< aria-selected={customIsSelected} aria-disabled={props.disabled}> <StyledCellIcon - visible={customIsSelected} + $visible={customIsSelected} source="icon-tick" width={18} tintColor={colors.white} diff --git a/gui/src/renderer/components/select-location/CustomLists.tsx b/gui/src/renderer/components/select-location/CustomLists.tsx index b87de1fe46..7fb66f630e 100644 --- a/gui/src/renderer/components/select-location/CustomLists.tsx +++ b/gui/src/renderer/components/select-location/CustomLists.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import styled from 'styled-components'; import { colors } from '../../../config.json'; @@ -6,7 +6,7 @@ import { CustomListError, CustomLists, RelayLocation } from '../../../shared/dae import { messages } from '../../../shared/gettext'; import log from '../../../shared/logging'; import { useAppContext } from '../../context'; -import { useBoolean } from '../../lib/utilityHooks'; +import { useBoolean, useStyledRef } from '../../lib/utilityHooks'; import Accordion from '../Accordion'; import * as Cell from '../cell'; import { measurements } from '../common-styles'; @@ -57,8 +57,8 @@ const StyledSideButtonIcon = styled(Cell.Icon)({ }, }); -const StyledInput = styled(SimpleInput)((props: { error: boolean }) => ({ - color: props.error ? colors.red : 'auto', +const StyledInput = styled(SimpleInput)<{ $error: boolean }>((props) => ({ + color: props.$error ? colors.red : 'auto', })); interface CustomListsProps { @@ -93,8 +93,8 @@ export default function CustomLists(props: CustomListsProps) { {messages.pgettext('select-location-view', 'Custom lists')} </StyledHeaderLabel> <StyledCellButton - backgroundColor={colors.blue} - backgroundColorHover={colors.blue80} + $backgroundColor={colors.blue} + $backgroundColorHover={colors.blue80} onClick={showAddList}> <StyledSideButtonIcon source="icon-add" tintColor={colors.white60} width={18} /> </StyledCellButton> @@ -118,8 +118,8 @@ interface AddListFormProps { function AddListForm(props: AddListFormProps) { const [name, setName] = useState(''); const [error, setError, unsetError] = useBoolean(); - const containerRef = useRef<HTMLDivElement>() as React.RefObject<HTMLDivElement>; - const inputRef = useRef<HTMLInputElement>() as React.RefObject<HTMLInputElement>; + const containerRef = useStyledRef<HTMLDivElement>(); + const inputRef = useStyledRef<HTMLInputElement>(); // Errors should be reset when editing the value const onChange = useCallback((value: string) => { @@ -172,14 +172,14 @@ function AddListForm(props: AddListFormProps) { onSubmitValue={createList} onBlur={onBlur} maxLength={30} - error={error} + $error={error} autoFocus /> </StyledInputContainer> <StyledAddListCellButton - backgroundColor={colors.blue} - backgroundColorHover={colors.blue80} + $backgroundColor={colors.blue} + $backgroundColorHover={colors.blue80} onClick={createList}> <StyledSideButtonIcon source="icon-check" tintColor={colors.white60} width={18} /> </StyledAddListCellButton> diff --git a/gui/src/renderer/components/select-location/LocationRow.tsx b/gui/src/renderer/components/select-location/LocationRow.tsx index 514fd19207..97c8528fa9 100644 --- a/gui/src/renderer/components/select-location/LocationRow.tsx +++ b/gui/src/renderer/components/select-location/LocationRow.tsx @@ -11,7 +11,7 @@ import { import { messages } from '../../../shared/gettext'; import log from '../../../shared/logging'; import { useAppContext } from '../../context'; -import { useBoolean } from '../../lib/utilityHooks'; +import { useBoolean, useStyledRef } from '../../lib/utilityHooks'; import { useSelector } from '../../redux/store'; import Accordion from '../Accordion'; import * as Cell from '../cell'; @@ -29,15 +29,15 @@ import { } from './select-location-types'; interface IButtonColorProps { - backgroundColor: string; - backgroundColorHover: string; + $backgroundColor: string; + $backgroundColorHover: string; } const buttonColor = (props: IButtonColorProps) => { return { - backgroundColor: props.backgroundColor, - ':not(:disabled):hover': { - backgroundColor: props.backgroundColorHover, + backgroundColor: props.$backgroundColor, + '&&:not(:disabled):hover': { + backgroundColor: props.$backgroundColorHover, }, }; }; @@ -48,10 +48,10 @@ export const StyledLocationRowContainer = styled(Cell.Container)({ background: 'none', }); -export const StyledLocationRowButton = styled(Cell.Row)( +export const StyledLocationRowButton = styled(Cell.Row)<IButtonColorProps & { $level: number }>( buttonColor, - (props: IButtonColorProps & { level: number }) => { - const paddingLeft = (props.level + 1) * 16 + 2; + (props) => { + const paddingLeft = (props.$level + 1) * 16 + 2; return { display: 'flex', @@ -64,13 +64,13 @@ export const StyledLocationRowButton = styled(Cell.Row)( }, ); -export const StyledLocationRowIcon = styled.button(buttonColor, { +export const StyledLocationRowIcon = styled.button<IButtonColorProps>(buttonColor, { position: 'relative', alignSelf: 'stretch', paddingLeft: measurements.viewMargin, paddingRight: measurements.viewMargin, - '&::before': { + '&&::before': { content: '""', position: 'absolute', margin: 'auto', @@ -93,26 +93,26 @@ export const StyledLocationRowLabel = styled(Cell.Label)(normalText, { whiteSpace: 'nowrap', }); -const StyledHoverIconButton = styled.button( +const StyledHoverIconButton = styled.button<IButtonColorProps & { $isLast?: boolean }>( buttonColor, - (props: { isLast?: boolean } & IButtonColorProps) => ({ + (props) => ({ flex: 0, display: 'none', padding: '0 10px', - paddingRight: props.isLast ? '17px' : '10px', + paddingRight: props.$isLast ? '17px' : '10px', margin: 0, border: 0, height: measurements.rowMinHeight, appearance: 'none', - ':not(:disabled):hover': { - backgroundColor: props.backgroundColor, + '&&:not(:disabled):hover': { + backgroundColor: props.$backgroundColor, }, [`${StyledLocationRowContainer}:hover &&`]: { display: 'block', }, [`${StyledLocationRowButton}:hover ~ &&`]: { - backgroundColor: props.backgroundColorHover, + backgroundColor: props.$backgroundColorHover, }, }), ); @@ -152,7 +152,7 @@ interface IProps<C extends LocationSpecification> { // Renders the rows and its children for countries, cities and relays function LocationRow<C extends LocationSpecification>(props: IProps<C>) { const hasChildren = getLocationChildren(props.source).some((child) => child.visible); - const buttonRef = useRef<HTMLButtonElement>() as React.RefObject<HTMLButtonElement>; + const buttonRef = useStyledRef<HTMLButtonElement>(); const userInvokedExpand = useRef(false); const { updateCustomList, deleteCustomList } = useAppContext(); @@ -240,7 +240,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { as="button" ref={buttonRef} onClick={handleClick} - level={props.level} + $level={props.level} disabled={props.source.disabled} includeMarginBottomOnLast {...background}> @@ -249,7 +249,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { </StyledLocationRowButton> {props.allowAddToCustomList ? ( - <StyledHoverIconButton onClick={showAddToListDialog} isLast {...background}> + <StyledHoverIconButton onClick={showAddToListDialog} $isLast {...background}> <StyledHoverIcon source="icon-add" /> </StyledHoverIconButton> ) : null} @@ -258,7 +258,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { {'customList' in props.source.location && 'country' in props.source.location && props.level === 1 ? ( - <StyledHoverIconButton onClick={onRemoveFromList} isLast {...background}> + <StyledHoverIconButton onClick={onRemoveFromList} $isLast {...background}> <StyledHoverIcon source="icon-remove" /> </StyledHoverIconButton> ) : null} @@ -269,7 +269,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { <StyledHoverIconButton onClick={showEditDialog} {...background}> <StyledHoverIcon source="icon-edit" /> </StyledHoverIconButton> - <StyledHoverIconButton onClick={onRemoveCustomList} isLast {...background}> + <StyledHoverIconButton onClick={onRemoveCustomList} $isLast {...background}> <StyledHoverIcon source="icon-close" /> </StyledHoverIconButton> </> @@ -297,7 +297,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { onWillExpand={onWillExpand} onTransitionEnd={props.onTransitionEnd} animationDuration={150}> - <Cell.Group noMarginBottom>{props.children}</Cell.Group> + <Cell.Group $noMarginBottom>{props.children}</Cell.Group> </Accordion> )} @@ -333,8 +333,8 @@ export function getButtonColor(selected: boolean, level: number, disabled?: bool } return { - backgroundColor, - backgroundColorHover: selected || disabled ? backgroundColor : colors.blue80, + $backgroundColor: backgroundColor, + $backgroundColorHover: selected || disabled ? backgroundColor : colors.blue80, }; } diff --git a/gui/src/renderer/components/select-location/RelayLocationList.tsx b/gui/src/renderer/components/select-location/RelayLocationList.tsx index 27fe75ea50..49e1f2a38c 100644 --- a/gui/src/renderer/components/select-location/RelayLocationList.tsx +++ b/gui/src/renderer/components/select-location/RelayLocationList.tsx @@ -25,7 +25,7 @@ interface RelayLocationsProps extends CommonProps { export default function RelayLocationList({ source, ...props }: RelayLocationsProps) { return ( - <Cell.Group noMarginBottom> + <Cell.Group $noMarginBottom> {source.map((country) => ( <RelayLocation key={getLocationKey(country.location)} diff --git a/gui/src/renderer/components/select-location/ScopeBar.tsx b/gui/src/renderer/components/select-location/ScopeBar.tsx index 94c80dea7c..bbe4935993 100644 --- a/gui/src/renderer/components/select-location/ScopeBar.tsx +++ b/gui/src/renderer/components/select-location/ScopeBar.tsx @@ -35,7 +35,7 @@ export function ScopeBar(props: IScopeBarProps) { return <StyledScopeBar className={props.className}>{children}</StyledScopeBar>; } -const StyledScopeBarItem = styled.button(smallText, (props: { selected?: boolean }) => ({ +const StyledScopeBarItem = styled.button<{ selected?: boolean }>(smallText, (props) => ({ cursor: 'default', flex: 1, flexBasis: 0, @@ -44,7 +44,7 @@ const StyledScopeBarItem = styled.button(smallText, (props: { selected?: boolean textAlign: 'center', border: 'none', backgroundColor: props.selected ? colors.green : 'transparent', - ':hover': { + '&&:hover': { backgroundColor: props.selected ? colors.green : colors.blue40, }, })); diff --git a/gui/src/renderer/components/select-location/ScrollPositionContext.tsx b/gui/src/renderer/components/select-location/ScrollPositionContext.tsx index 973e4484b6..04fcd0bccc 100644 --- a/gui/src/renderer/components/select-location/ScrollPositionContext.tsx +++ b/gui/src/renderer/components/select-location/ScrollPositionContext.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react'; -import { useNormalRelaySettings } from '../../lib/utilityHooks'; +import { useNormalRelaySettings, useStyledRef } from '../../lib/utilityHooks'; import { CustomScrollbarsRef } from '../CustomScrollbars'; import { LocationType } from './select-location-types'; import { useSelectLocationContext } from './SelectLocationContainer'; @@ -39,7 +39,7 @@ export function ScrollPositionContextProvider(props: ScrollPositionContextProps) const scrollPositions = useRef<Partial<Record<LocationType, ScrollPosition>>>({}); const scrollViewRef = useRef<CustomScrollbarsRef>(null); - const spacePreAllocationViewRef = useRef() as React.RefObject<SpacePreAllocationView>; + const spacePreAllocationViewRef = useStyledRef<SpacePreAllocationView>(); const selectedLocationRef = useRef<HTMLDivElement>(null); const saveScrollPosition = useCallback(() => { diff --git a/gui/src/renderer/components/select-location/SpecialLocationList.tsx b/gui/src/renderer/components/select-location/SpecialLocationList.tsx index c8c0c8e787..030520fc73 100644 --- a/gui/src/renderer/components/select-location/SpecialLocationList.tsx +++ b/gui/src/renderer/components/select-location/SpecialLocationList.tsx @@ -64,7 +64,7 @@ function SpecialLocationRow<T>(props: SpecialLocationRowProps<T>) { const background = getButtonColor(props.source.selected, 0, props.source.disabled); return ( <StyledLocationRowContainerWithMargin ref={selectedRef}> - <StyledLocationRowButton onClick={onSelect} level={0} {...background}> + <StyledLocationRowButton onClick={onSelect} $level={0} {...background}> {icon && ( <StyledSpecialLocationIcon source={icon} @@ -79,8 +79,8 @@ function SpecialLocationRow<T>(props: SpecialLocationRowProps<T>) { <StyledLocationRowIcon as={StyledSpecialLocationInfoButton} message={props.source.info} - selected={props.source.selected} aria-label={messages.pgettext('accessibility', 'info')} + {...background} /> )} </StyledLocationRowContainerWithMargin> diff --git a/gui/src/renderer/lib/styles.ts b/gui/src/renderer/lib/styles.ts new file mode 100644 index 0000000000..554e669373 --- /dev/null +++ b/gui/src/renderer/lib/styles.ts @@ -0,0 +1,9 @@ +type NonTransientPropKey<K> = K extends `$${infer L}` ? L : K; + +export type NonTransientProps<T, K extends NonTransientPropKey<keyof T>> = { + [P in keyof T as NonTransientPropKey<P> extends K ? NonTransientPropKey<P> : P]: T[P]; +}; + +export type TransientProps<T, K extends keyof T> = { + [P in keyof T as P extends K ? `$${P & string}` : P]: T[P]; +}; diff --git a/gui/src/renderer/lib/utilityHooks.ts b/gui/src/renderer/lib/utilityHooks.ts index 378a6d5ae5..49f508a883 100644 --- a/gui/src/renderer/lib/utilityHooks.ts +++ b/gui/src/renderer/lib/utilityHooks.ts @@ -16,6 +16,10 @@ export function useMounted() { return isMounted; } +export function useStyledRef<T>(): React.RefObject<T> { + return useRef() as React.RefObject<T>; +} + export function useCombinedRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> { return useCallback((element: T | null) => refs.forEach((ref) => assignToRef(element, ref)), []); } |
