diff --git a/package-lock.json b/package-lock.json index a531daab1..e04f0d14f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { + "@radix-ui/colors": "^3.0.0-rc.5", + "@radix-ui/react-toast": "^1.1.5", "@xmldom/xmldom": "^0.8.10", "classnames": "^2.3.2", "commonmark": "^0.30.0", @@ -3663,6 +3665,321 @@ "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-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-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 +4628,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" } diff --git a/package.json b/package.json index ad8497dfb..182f5637b 100644 --- a/package.json +++ b/package.json @@ -251,6 +251,8 @@ } }, "dependencies": { + "@radix-ui/colors": "^3.0.0-rc.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/renderer/assets/styles/components/toasts.css b/src/renderer/assets/styles/components/toasts.css index 179b50343..63206b6ef 100644 --- a/src/renderer/assets/styles/components/toasts.css +++ b/src/renderer/assets/styles/components/toasts.css @@ -25,6 +25,7 @@ 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;; &:focus-within { border-width: 4px; @@ -94,3 +95,21 @@ .___DEBUG___COMPONENTS_TOASTS_CSS { display: none; } + +.ToastRoot { + background-color: white; + border-radius: 6px; + box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px; + padding: 15px; + display: grid; + grid-template-areas: 'title action' 'description action'; + grid-template-columns: auto max-content; + column-gap: 15px; + align-items: center; +} +.ToastRoot[data-state='open'] { + animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1); +} +.ToastRoot[data-state='closed'] { + animation: hide 100ms ease-in; +} \ No newline at end of file diff --git a/src/renderer/common/components/toast/Toast.tsx b/src/renderer/common/components/toast/Toast.tsx index 99201b144..f4df81c9e 100644 --- a/src/renderer/common/components/toast/Toast.tsx +++ b/src/renderer/common/components/toast/Toast.tsx @@ -13,6 +13,7 @@ import { _APP_NAME } from "readium-desktop/preprocessor-directives"; import * as QuitIcon from "readium-desktop/renderer/assets/icons/baseline-close-24px.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 { TranslatorProps, withTranslator } from "../hoc/translator"; @@ -35,6 +36,7 @@ interface IBaseProps extends TranslatorProps { // ReturnType // eslint-disable-next-line @typescript-eslint/no-empty-interface interface IProps extends IBaseProps { + open: any; } interface IState { @@ -205,3 +207,50 @@ export class Toast extends React.Component { } export default withTranslator(Toast); + + +export const Toast2: React.FC = (props) => { + const timerRef = React.useRef(0); + const { type, open, id } = props; + + let typeClassName: string; + switch (type) { + case ToastType.Error: + typeClassName = stylesToasts.error; + break; + case ToastType.Success: + typeClassName = stylesToasts.success; + break; + default: + } + + + React.useEffect(() => { + return () => clearTimeout(timerRef.current); + }, []); + + return ( + + Something went wrong + +

The importation of the book has failed +

+ +
+ + + +
+ ) +}; diff --git a/src/renderer/common/components/toast/ToastManager.tsx b/src/renderer/common/components/toast/ToastManager.tsx index 14188402f..3bdbd238e 100644 --- a/src/renderer/common/components/toast/ToastManager.tsx +++ b/src/renderer/common/components/toast/ToastManager.tsx @@ -10,11 +10,12 @@ import { connect } from "react-redux"; 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 { TranslatorProps, withTranslator } from "../hoc/translator"; -import Toast from "./Toast"; -import { ToastType } from "readium-desktop/common/models/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"; // eslint-disable-next-line @typescript-eslint/no-empty-interface interface IBaseProps extends TranslatorProps { @@ -31,58 +32,106 @@ 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; + // 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 ( -
- {Object.keys(toastList).map((id: string) => { - const toast = toastList[id]; - if (toast) { - switch (toast.type) { - case ToastType.Success: - case ToastType.Default: - case ToastType.Error: - return ( - this.close(id)} - type={toast.type} - displaySystemNotification={false} - /> - ); - default: - return <>; - } - } - return undefined; - })} -
+ + this.setState({open})} + duration={10000} + > + Something went wrong + +

The importation of the book has failed +

+ +
+ + + +
+ +
); }