diff --git a/package-lock.json b/package-lock.json index a531daab1..00ff5cfb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,9 @@ "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { + "@radix-ui/colors": "^3.0.0-rc.5", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-toast": "^1.1.5", "@xmldom/xmldom": "^0.8.10", "classnames": "^2.3.2", "commonmark": "^0.30.0", @@ -3663,6 +3666,417 @@ "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, + "node_modules/@radix-ui/colors": { + "version": "3.0.0-rc.5", + "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-3.0.0-rc.5.tgz", + "integrity": "sha512-SHAmQwvoRFuX1Kqo0ZwOwp/24GQqNMUaS3tWcR5RgDaC1ZpUKQfT+IbdzGm63OVzQhpyA4PRcjimM2UMa9SYTw==" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", + "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.1.5.tgz", + "integrity": "sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@redux-devtools/extension": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/@redux-devtools/extension/-/extension-3.2.5.tgz", @@ -4311,7 +4725,7 @@ "version": "17.0.20", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.20.tgz", "integrity": "sha512-4pzIjSxDueZZ90F52mU3aPoogkHIoSIDG+oQ+wQK7Cy2B9S+MvOqY0uEA/qawKz381qrEDkvpwyt8Bm31I8sbA==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "^17" } @@ -5790,6 +6204,17 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/aria-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", + "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -12116,6 +12541,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -13859,6 +14292,14 @@ "node": ">= 0.10" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/inversify": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/inversify/-/inversify-6.0.1.tgz", @@ -20980,6 +21421,51 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz", + "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "6.14.2", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz", @@ -21010,6 +21496,28 @@ "react-dom": ">=16.8" } }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-svg-core": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/react-svg-core/-/react-svg-core-3.0.3.tgz", diff --git a/package.json b/package.json index ad8497dfb..74fa73127 100644 --- a/package.json +++ b/package.json @@ -251,6 +251,9 @@ } }, "dependencies": { + "@radix-ui/colors": "^3.0.0-rc.5", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-toast": "^1.1.5", "@xmldom/xmldom": "^0.8.10", "classnames": "^2.3.2", "commonmark": "^0.30.0", diff --git a/src/common/models/dialog.ts b/src/common/models/dialog.ts index 0f8d40416..5ee794dc0 100644 --- a/src/common/models/dialog.ts +++ b/src/common/models/dialog.ts @@ -43,6 +43,7 @@ export enum DialogTypeName { LsdReturnConfirm = "lsd-return-confirm", LsdRenewConfirm = "lsd-renew-confirm", AboutThorium = "about-thorium", + ToastInformations = "toast-informations", } export interface DialogType { @@ -79,4 +80,8 @@ export interface DialogType { publicationView: PublicationView; }; [DialogTypeName.AboutThorium]: {}; + [DialogTypeName.ToastInformations]: { + toastTitle: string; + toastMessage: string; + } } diff --git a/src/common/redux/actions/dialog/openRequest.ts b/src/common/redux/actions/dialog/openRequest.ts index 80a9db125..f5d7f8654 100644 --- a/src/common/redux/actions/dialog/openRequest.ts +++ b/src/common/redux/actions/dialog/openRequest.ts @@ -29,6 +29,10 @@ interface IDataPayload { readerReadingLocation: LocatorExtended; handleLinkUrl: ((url: string) => void) | undefined; }; + [DialogTypeName.ToastInformations]: { + toastTitle: string; + toastMessage: string; + } } type TDialogType = Omit; diff --git a/src/common/redux/reducers/toast.ts b/src/common/redux/reducers/toast.ts index e907ade94..f331b1597 100644 --- a/src/common/redux/reducers/toast.ts +++ b/src/common/redux/reducers/toast.ts @@ -15,7 +15,7 @@ import { ToastState } from "readium-desktop/common/redux/states/toast"; const initialState: ToastState = { open: false, - type: ToastType.Success, + type: ToastType.Default, data: undefined, publicationIdentifier: undefined, }; diff --git a/src/main/redux/sagas/api/publication/import/index.ts b/src/main/redux/sagas/api/publication/import/index.ts index 799b9481b..19421b470 100644 --- a/src/main/redux/sagas/api/publication/import/index.ts +++ b/src/main/redux/sagas/api/publication/import/index.ts @@ -50,7 +50,7 @@ export function* importFromLink( if (alreadyImported) { yield put( toastActions.openRequest.build( - ToastType.Success, + ToastType.Default, translate("message.import.alreadyImport", { title: publicationView.documentTitle }), ), @@ -145,7 +145,7 @@ export function* importFromFs( if (alreadyImported) { yield put( toastActions.openRequest.build( - ToastType.Success, + ToastType.Default, translate("message.import.alreadyImport", { title: publicationView.documentTitle }), ), diff --git a/src/renderer/assets/styles/components/toasts.css b/src/renderer/assets/styles/components/toasts.css index 179b50343..a45ae3c99 100644 --- a/src/renderer/assets/styles/components/toasts.css +++ b/src/renderer/assets/styles/components/toasts.css @@ -4,27 +4,31 @@ .toasts_wrapper { position: fixed; - bottom: 1rem; + bottom: 0.5rem; left: 0; z-index: 10000; } .toast { overflow: hidden; - margin-bottom: 1rem; + margin-bottom: 0.5rem; position: relative; - padding: 1rem; + /* padding: 1rem; */ border: 1px grey solid; - border-left: none; - border-top-right-radius: 0.5em; - border-bottom-right-radius: 0.5em; - width: 20rem; - height: 4.125rem; + border-radius: 0.2em; + /* border-bottom-right-radius: 0.5em; */ + width: 25rem; + height: 5.125rem; background: white; display: flex; - align-items: center; - animation-name: start; - animation-duration: 0.5s; + /* align-items: center; */ + /* animation-name: start; + animation-duration: 0.5s; */ + /* box-shadow: rgba(14, 18, 22, 0.35) 0px 10px 38px -10px, rgba(14, 18, 22, 0.2) 0px 10px 20px -15px; */ + box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px; + padding: 15px; + flex-direction: column; + justify-content: space-around; &:focus-within { border-width: 4px; @@ -83,14 +87,109 @@ @keyframes start { from { - transform: translateX(-100%); + transform: translateX(-100%), translateY(20%); } to { - transform: translateX(0); + transform: translateX(0), translateY(0); } } .___DEBUG___COMPONENTS_TOASTS_CSS { display: none; } + +.toast[data-state='open'] { + animation: slideIn 200ms cubic-bezier(0.16, 1, 0.3, 1); +} +.toast[data-state='closed'] { + animation: hide 150ms ease-in; +} + +.toastViewport { + position: fixed; + bottom: 0; + left: 0; + display: flex; + flex-direction: column; + padding-left: 25px; + width: 390px; + max-width: 100vw; + margin: 0; + list-style: none; + z-index: 2147483647; + outline: none; +} + +.ToastDescription { + margin: 10px 0; +} + +.toastTitle { + font-weight: bold; + margin-bottom: 10px; + font-size: 16px; +} + +.infoButton { + display: flex; + align-items: center; + justify-content: end; + transition: 300ms; + width: 100%; + padding-right: 1em; + & svg { + height: 1.3rem; + width: 1.3rem; + padding: 0; + margin: 3px; + } + &:hover { + text-decoration: underline; + } +} + +@keyframes hide { + from { + opacity: 1; + transform: translateX(0); + } + to { + opacity: 0; + transform: translateX(calc(-100% + 25px)); + } +} + +@keyframes slideIn { + from { + transform: translateX(calc(-100% + 25px)); + } + to { + transform: translateX(0); + } +} + +.toastProgressBar { + position: absolute; + left: -15px; + bottom : 0; + height: 5px; + width: 100%; + overflow: hidden; +} + +.animate-toast-progress-bar { + width: 100%; + height: 100%; + animation-name: loading; + animation-timing-function: linear; +} + +@keyframes loading { + from { + transform: translateX(0%); + } + to { + transform: translateX(-100%); + } +} \ No newline at end of file diff --git a/src/renderer/assets/styles/components/toasts.css.d.ts b/src/renderer/assets/styles/components/toasts.css.d.ts index 8d681724a..04201b516 100644 --- a/src/renderer/assets/styles/components/toasts.css.d.ts +++ b/src/renderer/assets/styles/components/toasts.css.d.ts @@ -4,11 +4,16 @@ declare const styles: { readonly "start": string; readonly "error": string; readonly "success": string; + readonly "default": string; readonly "icon": string; readonly "closeButton": string; readonly "leave": string; readonly "toRemove": string; readonly "___DEBUG___COMPONENTS_TOASTS_CSS": string; + readonly "toastViewport": string; + readonly "toastDescription": string; + readonly "toastTitle": string; + readonly "infoButton": string; }; export = styles; diff --git a/src/renderer/common/components/toast/Toast.tsx b/src/renderer/common/components/toast/Toast.tsx index 99201b144..7d3421b5a 100644 --- a/src/renderer/common/components/toast/Toast.tsx +++ b/src/renderer/common/components/toast/Toast.tsx @@ -5,22 +5,35 @@ // that can be found in the LICENSE file exposed on Github (readium) in the project repository. // ==LICENSE-END== -import { clipboard } from "electron"; +// import { clipboard } from "electron"; import classNames from "classnames"; import * as React from "react"; import { ToastType } from "readium-desktop/common/models/toast"; import { _APP_NAME } from "readium-desktop/preprocessor-directives"; import * as QuitIcon from "readium-desktop/renderer/assets/icons/baseline-close-24px.svg"; +// import * as Info from "readium-desktop/renderer/assets/icons/info.svg"; import * as stylesToasts from "readium-desktop/renderer/assets/styles/components/toasts.css"; import SVG from "readium-desktop/renderer/common/components/SVG"; +import * as Toasts from "@radix-ui/react-toast"; +// import * as Dialog from "@radix-ui/react-dialog"; +// import { useSelector } from "../../hooks/useSelector"; +// import { useDispatch } from "../../hooks/useDispatch"; +// import { useTranslator } from "../../hooks/useTranslator"; +// import { ILibraryRootState } from "readium-desktop/renderer/library/redux/states"; +// import { /* DialogType, */ DialogTypeName } from "readium-desktop/common/models/dialog"; +// import * as dialogActions from "readium-desktop/common/redux/actions/dialog"; +// import * as stylesButtons from "readium-desktop/renderer/assets/styles/components/buttons.css"; +// import * as stylesModals from "readium-desktop/renderer/assets/styles/components/modals.css"; +// import clsx from "clsx"; -import { TranslatorProps, withTranslator } from "../hoc/translator"; -const capitalizedAppName = _APP_NAME.charAt(0).toUpperCase() + _APP_NAME.substring(1); +import { TranslatorProps, /* withTranslator */ } from "../hoc/translator"; + +// const capitalizedAppName = _APP_NAME.charAt(0).toUpperCase() + _APP_NAME.substring(1); // eslint-disable-next-line @typescript-eslint/no-empty-interface interface IBaseProps extends TranslatorProps { - close: (id: string) => void; + // close: (id: string) => void; className?: string; id?: string; // icon?: ISVGProps; @@ -35,173 +48,318 @@ interface IBaseProps extends TranslatorProps { // ReturnType // eslint-disable-next-line @typescript-eslint/no-empty-interface interface IProps extends IBaseProps { + open: any; + onOpenChange: (open: boolean) => void; + variant?: "success" | "error" | "loading"; } -interface IState { - willLeave: boolean; - toRemove: boolean; -} +// interface IState { +// willLeave: boolean; +// toRemove: boolean; +// } -export class Toast extends React.Component { - private ref: React.RefObject; - private timer: number | undefined; - private ignoreTimer: boolean | undefined; +// export class Toast extends React.Component { +// private ref: React.RefObject; +// private timer: number | undefined; +// private ignoreTimer: boolean | undefined; - constructor(props: IProps) { - super(props); +// constructor(props: IProps) { +// super(props); - this.ref = React.createRef(); +// this.ref = React.createRef(); - this.state = { - willLeave: false, - toRemove: false, - }; +// this.state = { +// willLeave: false, +// toRemove: false, +// }; - this.handleTransitionEnd = this.handleTransitionEnd.bind(this); - this.handleClose = this.handleClose.bind(this); - } +// this.handleTransitionEnd = this.handleTransitionEnd.bind(this); +// this.handleClose = this.handleClose.bind(this); +// } - public cancelTimer(ignoreTimer: boolean) { - if (this.timer) { - window.clearTimeout(this.timer); - this.timer = undefined; - } - if (ignoreTimer) { - this.ignoreTimer = true; - } - } - public triggerTimer(fast: boolean) { - this.cancelTimer(false); - if (this.ignoreTimer) { - return; - } - - this.timer = window.setTimeout(() => { - this.timer = undefined; - this.handleClose(); - }, fast ? 500 : 5000); - } +// public cancelTimer(ignoreTimer: boolean) { +// if (this.timer) { +// window.clearTimeout(this.timer); +// this.timer = undefined; +// } +// if (ignoreTimer) { +// this.ignoreTimer = true; +// } +// } +// public triggerTimer(fast: boolean) { +// this.cancelTimer(false); +// if (this.ignoreTimer) { +// return; +// } - public componentDidMount() { - this.triggerTimer(false); +// this.timer = window.setTimeout(() => { +// this.timer = undefined; +// this.handleClose(); +// }, fast ? 500 : 5000); +// } - if (this.ref?.current) { - this.ref?.current.addEventListener("transitionend", this.handleTransitionEnd, false); - } +// public componentDidMount() { +// this.triggerTimer(false); - // https://www.electronjs.org/docs/latest/tutorial/notifications - if (this.props.displaySystemNotification) { - // tslint:disable-next-line: no-unused-expression - new Notification(capitalizedAppName, { - body: this.props.message, - }); - } - } +// if (this.ref?.current) { +// this.ref?.current.addEventListener("transitionend", this.handleTransitionEnd, false); +// } + +// // https://www.electronjs.org/docs/latest/tutorial/notifications +// if (this.props.displaySystemNotification) { +// // tslint:disable-next-line: no-unused-expression +// new Notification(capitalizedAppName, { +// body: this.props.message, +// }); +// } +// } + +// public componentWillRemove() { +// if (this.ref?.current) { +// this.ref?.current.removeEventListener("transitionend", this.handleTransitionEnd, false); +// } +// } + +// public render(): React.ReactElement<{}> { + +// if (this.props.displaySystemNotification) { +// return (<>); +// } + +// const { type } = this.props; +// const { willLeave, toRemove } = this.state; + +// let typeClassName: string; +// switch (type) { +// case ToastType.Error: +// typeClassName = stylesToasts.error; +// break; +// case ToastType.Success: +// typeClassName = stylesToasts.success; +// break; +// default: +// break; +// } + +// return ( +//
{ +// this.cancelTimer(false); +// }} +// onClick={() => { +// this.cancelTimer(true); +// }} +// onMouseOut={() => { +// this.triggerTimer(true); +// }} +// className={classNames( +// stylesToasts.toast, +// willLeave && stylesToasts.leave, +// toRemove && stylesToasts.toRemove, +// typeClassName, +// )} +// > +// { +// // icon && +// } +//

{ +// this.cancelTimer(true); +// }} +// onClick={() => { +// const clipBoardType = "clipboard"; +// try { +// clipboard.writeText(this.props.message, clipBoardType); + +// // https://www.electronjs.org/docs/latest/tutorial/notifications +// // tslint:disable-next-line: no-unused-expression +// new Notification(capitalizedAppName, { +// body: `${this.props.translator.translate("app.edit.copy")} [${this.props.message}]`, +// }); +// // this.triggerTimer(true); +// // this.handleClose(); +// } catch (_e) { +// // ignore +// } +// } +// }>{ this.props.message }

+// {/* +// onBlur={() => { +// this.triggerTimer(true); +// }} +// */} +// +//
+// ); +// } + +// private handleClose() { +// this.setState({ willLeave: true }); +// } + +// private handleTransitionEnd() { +// if (this.state.toRemove) { +// // this.props.close(this.props.id); +// } else if (this.state.willLeave) { +// this.setState({toRemove: true}); +// } +// } +// } + +// export default withTranslator(Toast); - public componentWillRemove() { - if (this.ref?.current) { - this.ref?.current.removeEventListener("transitionend", this.handleTransitionEnd, false); - } + +export const Toast2: React.FC = (props) => { + const timerRef = React.useRef(0); + const { type, open, onOpenChange, message, variant } = props; + const duration = 5000; + const [paused, setPaused] = React.useState(false); + + let toastTitle: string; + let typeClassName: string; + switch (type) { + case ToastType.Error: + typeClassName = stylesToasts.error; + toastTitle = "Something went wrong!"; + break; + case ToastType.Success: + typeClassName = stylesToasts.success; + toastTitle = "Action Successful!"; + break; + case ToastType.Default: + toastTitle = "New notification!"; + break; + default: } - public render(): React.ReactElement<{}> { - - if (this.props.displaySystemNotification) { - return (<>); - } - - const { type } = this.props; - const { willLeave, toRemove } = this.state; - - let typeClassName: string; - switch (type) { - case ToastType.Error: - typeClassName = stylesToasts.error; - break; - case ToastType.Success: - typeClassName = stylesToasts.success; - break; - default: - break; - } - - return ( -
{ - this.cancelTimer(false); - }} - onClick={() => { - this.cancelTimer(true); - }} - onMouseOut={() => { - this.triggerTimer(true); - }} - className={classNames( - stylesToasts.toast, - willLeave && stylesToasts.leave, - toRemove && stylesToasts.toRemove, - typeClassName, + // const dialogOpenState = useSelector((state: ILibraryRootState) => state.dialog.open); + + // if (open === false) { + // dialogOpenState === false; + // } + + + React.useEffect(() => { + return () => clearTimeout(timerRef.current); + }, []); + return ( + <> + - { - // icon && - } + open={open} + onOpenChange={onOpenChange} + duration={duration} + onPause={() => setPaused(true)} + onResume={() => setPaused(false)} + > + {toastTitle} +

{ - this.cancelTimer(true); - }} - onClick={() => { - const clipBoardType = "clipboard"; - try { - clipboard.writeText(this.props.message, clipBoardType); - - // https://www.electronjs.org/docs/latest/tutorial/notifications - // tslint:disable-next-line: no-unused-expression - new Notification(capitalizedAppName, { - body: `${this.props.translator.translate("app.edit.copy")} [${this.props.message}]`, - }); - // this.triggerTimer(true); - // this.handleClose(); - } catch (_e) { - // ignore - } - } - }>{ this.props.message }

- {/* - onBlur={() => { - this.triggerTimer(true); - }} - */} - + } + toastTitle={toastTitle} + message={message}> + + + } */} +
+
+
this.handleClose()} - className={stylesToasts.closeButton} - title={this.props.__("accessibility.closeDialog")} - > + >
+
+ - -
- ); - } + + + + + ) +}; - private handleClose() { - this.setState({ willLeave: true }); - } +// const ToastInfoDialog = (props: {children: []; toastTitle: string; message: string; button: React.ReactNode;}) => { +// const [__] = useTranslator(); +// const defaultOpen = false; + +// const dispatch = useDispatch(); + +// const open = useSelector((state: ILibraryRootState) => state.dialog.open); +// // const data = useSelector((state: ILibraryRootState) => +// // state.dialog.type === DialogTypeName.ToastInformations +// // ? state.dialog.data as DialogType[DialogTypeName.ToastInformations] +// // : undefined); + +// const appOverlayElement = React.useMemo(() => document.getElementById("app-overlay"), []); + +// return ( +// { +// if (open) { +// dispatch(dialogActions.openRequest.build(DialogTypeName.ToastInformations, { +// toastTitle: props.toastTitle, +// toastMessage : props.message, +// })); +// } else { +// dispatch(dialogActions.closeRequest.build()); +// } +// } +// }> - private handleTransitionEnd() { - if (this.state.toRemove) { - this.props.close(this.props.id); - } else if (this.state.willLeave) { - this.setState({toRemove: true}); - } - } -} -export default withTranslator(Toast); +// +// {props.button} +// +// +//
+// +//
+//

{props.toastTitle}

+// +// +// +//
+//
+// {props.message} +//
+//
+//
+//
+// ) +// } diff --git a/src/renderer/common/components/toast/ToastManager.tsx b/src/renderer/common/components/toast/ToastManager.tsx index 52966e99c..554af3c8f 100644 --- a/src/renderer/common/components/toast/ToastManager.tsx +++ b/src/renderer/common/components/toast/ToastManager.tsx @@ -7,14 +7,16 @@ import * as React from "react"; import { connect } from "react-redux"; -import { ToastType } from "readium-desktop/common/models/toast"; import { IRendererCommonRootState } from "readium-desktop/common/redux/states/rendererCommonRootState"; import { ToastState } from "readium-desktop/common/redux/states/toast"; -import * as stylesToasts from "readium-desktop/renderer/assets/styles/components/toasts.css"; -import { v4 as uuidv4 } from "uuid"; +// import * as stylesToasts from "readium-desktop/renderer/assets/styles/components/toasts.css"; import { TranslatorProps, withTranslator } from "../hoc/translator"; -import Toast from "./Toast"; +import * as Toasts from "@radix-ui/react-toast"; +// import classNames from "classnames"; +// import SVG from "readium-desktop/renderer/common/components/SVG"; +// import * as QuitIcon from "readium-desktop/renderer/assets/icons/baseline-close-24px.svg"; +import { Toast2 } from "./Toast"; // eslint-disable-next-line @typescript-eslint/no-empty-interface interface IBaseProps extends TranslatorProps { @@ -31,68 +33,77 @@ interface IState { toastList: { [id: string]: ToastState; }; + open: boolean; } export class ToastManager extends React.Component { + private timerRef: any; constructor(props: IProps) { super(props); + this.timerRef = 0; this.state = { toastList: {}, + open: false, }; this.close = this.close.bind(this); } - public componentDidUpdate(oldProps: IProps) { - const { toast } = this.props; - if (toast !== oldProps.toast) { - const id = uuidv4(); - const toastList = this.state.toastList; - toastList[id] = toast; - this.setState({toastList}); - } + public componentDidMount(): void { + this.timerRef = setTimeout(() => this.setState({open: false}), 5000) + } + + public componentWillUnmount() { + clearTimeout(this.timerRef) + } + + public componentDidUpdate(prevProps: Readonly): void { + if (prevProps.toast !== this.props.toast && this.props.toast) { + this.setState({open: true}) + } } + // public componentDidUpdate(oldProps: IProps) { + // const { toast } = this.props; + // if (toast !== oldProps.toast) { + // const id = uuidv4(); + // const toastList = this.state.toastList; + // toastList[id] = toast; + // this.setState({toastList}); + // } + // } + public render(): React.ReactElement<{}> { - const { toastList } = this.state; - return
- { Object.keys(toastList).map((id: string) => { - const toast = toastList[id]; - if (toast) { - switch (toast.type) { - case ToastType.Success: - return this.close(id) } - type={toast.type} - displaySystemNotification={false} - />; - case ToastType.Default: - return this.close(id) } - type={toast.type} - displaySystemNotification={false} - />; - case ToastType.Error: - return this.close(id) } - type={toast.type} - displaySystemNotification={false} - />; - default: - return (<>); - } - } - return undefined; - })} -
; + // const { toastList } = this.state; + + // let toastComponent = []; + // for (const [id, toastInfo] of Object.entries(toastList)) { + // toastComponent.push( + // this.close(id)} + // type={toastInfo.type} + // displaySystemNotification={false} + // open={false} + // />) + // } + + if (!this.props.toast) { + return <>; + } + + return ( + + this.setState({open})} + type={this.props.toast.type} + message={this.props.toast.data} /> + + ); } private close(id: string) { diff --git a/src/resources/locales/en.json b/src/resources/locales/en.json index 97fb7eded..983d48467 100644 --- a/src/resources/locales/en.json +++ b/src/resources/locales/en.json @@ -155,15 +155,18 @@ }, "message": { "download": { - "error": "The download of {{- title}} has failed: [{{- err}}]" + "error": "[{{- err}}]", + "error2": "The download of {{- title}} has failed: [{{- err}}]" }, "import": { "alreadyImport": "{{- title}} was already imported", - "fail": "The import of {{- path}} failed. {{- err}}", + "fail": "{{- err}}", + "fail2": "The import of {{- path}} failed. {{- err}}", "success": "The import of {{- title}} is finished." }, "open": { - "error": "The publication can't be opened: {{- err}}" + "error": "{{- err}}", + "error2": "The publication can't be opened: {{- err}}" } }, "opds": {