@hyphen/hyphen-components 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +70 -0
- package/package.json +138 -0
- package/src/components/Alert/Alert.constants.ts +19 -0
- package/src/components/Alert/Alert.mdx +29 -0
- package/src/components/Alert/Alert.module.scss +74 -0
- package/src/components/Alert/Alert.stories.tsx +102 -0
- package/src/components/Alert/Alert.test.tsx +187 -0
- package/src/components/Alert/Alert.tsx +157 -0
- package/src/components/Alert/Alert.types.ts +14 -0
- package/src/components/Badge/Badge.mdx +28 -0
- package/src/components/Badge/Badge.module.scss +155 -0
- package/src/components/Badge/Badge.stories.tsx +52 -0
- package/src/components/Badge/Badge.test.tsx +74 -0
- package/src/components/Badge/Badge.tsx +70 -0
- package/src/components/Box/Box.mdx +259 -0
- package/src/components/Box/Box.module.scss +16 -0
- package/src/components/Box/Box.stories.tsx +1679 -0
- package/src/components/Box/Box.test.tsx +478 -0
- package/src/components/Box/Box.tsx +636 -0
- package/src/components/Button/Button.constants.ts +10 -0
- package/src/components/Button/Button.mdx +71 -0
- package/src/components/Button/Button.module.scss +312 -0
- package/src/components/Button/Button.stories.tsx +117 -0
- package/src/components/Button/Button.test.tsx +460 -0
- package/src/components/Button/Button.tsx +241 -0
- package/src/components/Card/Card.mdx +46 -0
- package/src/components/Card/Card.module.scss +6 -0
- package/src/components/Card/Card.stories.tsx +101 -0
- package/src/components/Card/Card.test.tsx +11 -0
- package/src/components/Card/Card.tsx +61 -0
- package/src/components/Card/components/CardFooter/CardFooter.test.tsx +11 -0
- package/src/components/Card/components/CardFooter/CardFooter.tsx +35 -0
- package/src/components/Card/components/CardHeader/CardHeader.test.tsx +23 -0
- package/src/components/Card/components/CardHeader/CardHeader.tsx +54 -0
- package/src/components/Card/components/CardSection/CardSection.test.tsx +28 -0
- package/src/components/Card/components/CardSection/CardSection.tsx +102 -0
- package/src/components/Card/components/index.ts +3 -0
- package/src/components/CheckboxInput/CheckboxInput.mdx +98 -0
- package/src/components/CheckboxInput/CheckboxInput.stories.tsx +254 -0
- package/src/components/CheckboxInput/CheckboxInput.test.tsx +436 -0
- package/src/components/CheckboxInput/CheckboxInput.tsx +171 -0
- package/src/components/CheckboxInput/components/Checkbox.module.scss +174 -0
- package/src/components/CheckboxInput/components/Checkbox.test.tsx +201 -0
- package/src/components/CheckboxInput/components/Checkbox.tsx +176 -0
- package/src/components/CheckboxInput/components/CheckboxIcon.tsx +71 -0
- package/src/components/DateInput/DateInput.mdx +61 -0
- package/src/components/DateInput/DateInput.stories.tsx +168 -0
- package/src/components/DateInput/DateInput.test.tsx +258 -0
- package/src/components/DateInput/DateInput.tsx +189 -0
- package/src/components/DatePicker/DatePicker.mdx +52 -0
- package/src/components/DatePicker/DatePicker.module.scss +603 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +199 -0
- package/src/components/DatePicker/DatePicker.test.tsx +26 -0
- package/src/components/DatePicker/DatePicker.tsx +138 -0
- package/src/components/Details/Details.mdx +30 -0
- package/src/components/Details/Details.module.scss +32 -0
- package/src/components/Details/Details.stories.tsx +38 -0
- package/src/components/Details/Details.test.tsx +189 -0
- package/src/components/Details/Details.tsx +51 -0
- package/src/components/Details/DetailsSummary.tsx +65 -0
- package/src/components/Drawer/Drawer.mdx +117 -0
- package/src/components/Drawer/Drawer.module.scss +96 -0
- package/src/components/Drawer/Drawer.stories.tsx +380 -0
- package/src/components/Drawer/Drawer.test.tsx +90 -0
- package/src/components/Drawer/Drawer.tsx +249 -0
- package/src/components/FormControl/FormControl.tsx +78 -0
- package/src/components/FormLabel/FormLabel.mdx +24 -0
- package/src/components/FormLabel/FormLabel.module.scss +19 -0
- package/src/components/FormLabel/FormLabel.stories.tsx +20 -0
- package/src/components/FormLabel/FormLabel.test.tsx +35 -0
- package/src/components/FormLabel/FormLabel.tsx +96 -0
- package/src/components/Formik/Formik.mdx +10 -0
- package/src/components/Formik/Formik.stories.tsx +307 -0
- package/src/components/Formik/FormikCheckboxInput/FormikCheckboxInput.test.tsx +172 -0
- package/src/components/Formik/FormikCheckboxInput/FormikCheckboxInput.tsx +41 -0
- package/src/components/Formik/FormikRadioGroup/FormikRadioGroup.test.tsx +205 -0
- package/src/components/Formik/FormikRadioGroup/FormikRadioGroup.tsx +37 -0
- package/src/components/Formik/FormikSelectInput/FormikSelectInput.test.tsx +210 -0
- package/src/components/Formik/FormikSelectInput/FormikSelectInput.tsx +41 -0
- package/src/components/Formik/FormikSelectInputInset/FormikSelectInputInset.test.tsx +153 -0
- package/src/components/Formik/FormikSelectInputInset/FormikSelectInputInset.tsx +44 -0
- package/src/components/Formik/FormikSelectInputNative/FormikSelectInputNative.test.tsx +161 -0
- package/src/components/Formik/FormikSelectInputNative/FormikSelectInputNative.tsx +46 -0
- package/src/components/Formik/FormikTextInput/FormikTextInput.test.tsx +176 -0
- package/src/components/Formik/FormikTextInput/FormikTextInput.tsx +38 -0
- package/src/components/Formik/FormikTextInputInset/FormikTextInputInset.test.tsx +170 -0
- package/src/components/Formik/FormikTextInputInset/FormikTextInputInset.tsx +42 -0
- package/src/components/Formik/FormikTextareaInput/FormikTextareaInput.test.tsx +186 -0
- package/src/components/Formik/FormikTextareaInput/FormikTextareaInput.tsx +42 -0
- package/src/components/Formik/FormikTextareaInputInset/FormikTextareaInputInset.test.tsx +179 -0
- package/src/components/Formik/FormikTextareaInputInset/FormikTextareaInputInset.tsx +42 -0
- package/src/components/Formik/FormikTimePicker/FormikTimePicker.test.tsx +224 -0
- package/src/components/Formik/FormikTimePicker/FormikTimePicker.tsx +37 -0
- package/src/components/Formik/FormikTimePickerNative/FormikTimePickerNative.test.tsx +175 -0
- package/src/components/Formik/FormikTimePickerNative/FormikTimePickerNative.tsx +38 -0
- package/src/components/Formik/FormikToggle/FormikToggle.test.tsx +172 -0
- package/src/components/Formik/FormikToggle/FormikToggle.tsx +38 -0
- package/src/components/Heading/Heading.constants.ts +19 -0
- package/src/components/Heading/Heading.mdx +35 -0
- package/src/components/Heading/Heading.module.scss +5 -0
- package/src/components/Heading/Heading.stories.tsx +90 -0
- package/src/components/Heading/Heading.test.tsx +67 -0
- package/src/components/Heading/Heading.tsx +67 -0
- package/src/components/HelpText/HelpText.module.scss +14 -0
- package/src/components/HelpText/HelpText.tsx +33 -0
- package/src/components/Icon/Icon.mdx +40 -0
- package/src/components/Icon/Icon.stories.tsx +72 -0
- package/src/components/Icon/Icon.test.tsx +30 -0
- package/src/components/Icon/Icon.tsx +61 -0
- package/src/components/InputValidationMessage/InputValidationMessage.module.scss +3 -0
- package/src/components/InputValidationMessage/InputValidationMessage.tsx +27 -0
- package/src/components/Modal/Modal.mdx +60 -0
- package/src/components/Modal/Modal.module.scss +135 -0
- package/src/components/Modal/Modal.stories.tsx +194 -0
- package/src/components/Modal/Modal.test.tsx +81 -0
- package/src/components/Modal/Modal.tsx +174 -0
- package/src/components/Modal/components/ModalBody/ModalBody.test.tsx +20 -0
- package/src/components/Modal/components/ModalBody/ModalBody.tsx +24 -0
- package/src/components/Modal/components/ModalFooter/ModalFooter.test.tsx +32 -0
- package/src/components/Modal/components/ModalFooter/ModalFooter.tsx +37 -0
- package/src/components/Modal/components/ModalHeader/ModalHeader.test.tsx +29 -0
- package/src/components/Modal/components/ModalHeader/ModalHeader.tsx +58 -0
- package/src/components/Modal/components/index.ts +5 -0
- package/src/components/Pagination/Pagination.mdx +26 -0
- package/src/components/Pagination/Pagination.stories.tsx +55 -0
- package/src/components/Pagination/Pagination.test.tsx +225 -0
- package/src/components/Pagination/Pagination.tsx +162 -0
- package/src/components/Pagination/Pagination.utilities.test.ts +133 -0
- package/src/components/Pagination/Pagination.utilities.ts +101 -0
- package/src/components/Popover/Popover.mdx +104 -0
- package/src/components/Popover/Popover.module.scss +74 -0
- package/src/components/Popover/Popover.stories.tsx +471 -0
- package/src/components/Popover/Popover.test.tsx +128 -0
- package/src/components/Popover/Popover.tsx +277 -0
- package/src/components/RadioGroup/RadioGroup.mdx +81 -0
- package/src/components/RadioGroup/RadioGroup.module.scss +23 -0
- package/src/components/RadioGroup/RadioGroup.stories.tsx +375 -0
- package/src/components/RadioGroup/RadioGroup.test.tsx +282 -0
- package/src/components/RadioGroup/RadioGroup.tsx +145 -0
- package/src/components/RadioGroup/RadioInput/RadioInput.module.scss +114 -0
- package/src/components/RadioGroup/RadioInput/RadioInput.test.tsx +156 -0
- package/src/components/RadioGroup/RadioInput/RadioInput.tsx +148 -0
- package/src/components/RadioGroup/RadioInput/RadioInputIcon.tsx +59 -0
- package/src/components/ResponsiveProvider/ResponsiveProvider.mdx +36 -0
- package/src/components/ResponsiveProvider/ResponsiveProvider.stories.tsx +54 -0
- package/src/components/ResponsiveProvider/ResponsiveProvider.test.tsx +70 -0
- package/src/components/ResponsiveProvider/ResponsiveProvider.tsx +73 -0
- package/src/components/SelectInput/SelectInput.mdx +115 -0
- package/src/components/SelectInput/SelectInput.module.scss +357 -0
- package/src/components/SelectInput/SelectInput.stories.tsx +373 -0
- package/src/components/SelectInput/SelectInput.test.tsx +403 -0
- package/src/components/SelectInput/SelectInput.tsx +245 -0
- package/src/components/SelectInputInset/SelectInputInset.mdx +56 -0
- package/src/components/SelectInputInset/SelectInputInset.module.scss +397 -0
- package/src/components/SelectInputInset/SelectInputInset.stories.tsx +189 -0
- package/src/components/SelectInputInset/SelectInputInset.test.tsx +305 -0
- package/src/components/SelectInputInset/SelectInputInset.tsx +235 -0
- package/src/components/SelectInputNative/SelectInputNative.mdx +87 -0
- package/src/components/SelectInputNative/SelectInputNative.module.scss +356 -0
- package/src/components/SelectInputNative/SelectInputNative.stories.tsx +282 -0
- package/src/components/SelectInputNative/SelectInputNative.test.tsx +341 -0
- package/src/components/SelectInputNative/SelectInputNative.tsx +121 -0
- package/src/components/Spinner/Spinner.mdx +29 -0
- package/src/components/Spinner/Spinner.module.scss +16 -0
- package/src/components/Spinner/Spinner.stories.tsx +48 -0
- package/src/components/Spinner/Spinner.test.tsx +47 -0
- package/src/components/Spinner/Spinner.tsx +116 -0
- package/src/components/Table/Table.mdx +216 -0
- package/src/components/Table/Table.module.scss +61 -0
- package/src/components/Table/Table.stories.tsx +884 -0
- package/src/components/Table/Table.test.tsx +437 -0
- package/src/components/Table/Table.tsx +171 -0
- package/src/components/Table/TableBody/TableBody.module.scss +19 -0
- package/src/components/Table/TableBody/TableBody.test.tsx +38 -0
- package/src/components/Table/TableBody/TableBody.tsx +96 -0
- package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.module.scss +47 -0
- package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.test.tsx +81 -0
- package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.tsx +94 -0
- package/src/components/Table/TableHead/TableHead.test.tsx +20 -0
- package/src/components/Table/TableHead/TableHead.tsx +78 -0
- package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.module.scss +72 -0
- package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.test.tsx +187 -0
- package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.tsx +192 -0
- package/src/components/Table/common/TableRow/TableRow.module.scss +5 -0
- package/src/components/Table/common/TableRow/TableRow.test.tsx +52 -0
- package/src/components/Table/common/TableRow/TableRow.tsx +155 -0
- package/src/components/TextInput/TextInput.mdx +96 -0
- package/src/components/TextInput/TextInput.module.scss +405 -0
- package/src/components/TextInput/TextInput.stories.tsx +268 -0
- package/src/components/TextInput/TextInput.test.tsx +231 -0
- package/src/components/TextInput/TextInput.tsx +263 -0
- package/src/components/TextInputInset/TextInputInset.mdx +62 -0
- package/src/components/TextInputInset/TextInputInset.module.scss +418 -0
- package/src/components/TextInputInset/TextInputInset.stories.tsx +213 -0
- package/src/components/TextInputInset/TextInputInset.test.tsx +222 -0
- package/src/components/TextInputInset/TextInputInset.tsx +261 -0
- package/src/components/TextareaInput/TextareaInput.mdx +117 -0
- package/src/components/TextareaInput/TextareaInput.module.scss +275 -0
- package/src/components/TextareaInput/TextareaInput.stories.tsx +293 -0
- package/src/components/TextareaInput/TextareaInput.test.tsx +195 -0
- package/src/components/TextareaInput/TextareaInput.tsx +182 -0
- package/src/components/TextareaInputInset/TextareaInputInset.mdx +55 -0
- package/src/components/TextareaInputInset/TextareaInputInset.module.scss +337 -0
- package/src/components/TextareaInputInset/TextareaInputInset.stories.tsx +160 -0
- package/src/components/TextareaInputInset/TextareaInputInset.test.tsx +199 -0
- package/src/components/TextareaInputInset/TextareaInputInset.tsx +213 -0
- package/src/components/ThemeProvider/ThemeProvider.mdx +11 -0
- package/src/components/ThemeProvider/ThemeProvider.stories.tsx +56 -0
- package/src/components/ThemeProvider/ThemeProvider.tsx +75 -0
- package/src/components/TimePicker/TimePicker.mdx +75 -0
- package/src/components/TimePicker/TimePicker.stories.tsx +149 -0
- package/src/components/TimePicker/TimePicker.test.tsx +97 -0
- package/src/components/TimePicker/TimePicker.tsx +83 -0
- package/src/components/TimePickerNative/TimePickerNative.mdx +67 -0
- package/src/components/TimePickerNative/TimePickerNative.stories.tsx +151 -0
- package/src/components/TimePickerNative/TimePickerNative.test.tsx +117 -0
- package/src/components/TimePickerNative/TimePickerNative.tsx +93 -0
- package/src/components/Toast/Toast.mdx +134 -0
- package/src/components/Toast/Toast.store.ts +280 -0
- package/src/components/Toast/Toast.stories.tsx +283 -0
- package/src/components/Toast/Toast.test.tsx +784 -0
- package/src/components/Toast/Toast.types.ts +98 -0
- package/src/components/Toast/ToastContainer.tsx +170 -0
- package/src/components/Toast/ToastNotification.module.scss +63 -0
- package/src/components/Toast/ToastNotification.tsx +176 -0
- package/src/components/Toast/index.ts +4 -0
- package/src/components/Toast/toast.ts +102 -0
- package/src/components/Toast/useToasts.ts +102 -0
- package/src/components/Toggle/Toggle.mdx +51 -0
- package/src/components/Toggle/Toggle.module.scss +294 -0
- package/src/components/Toggle/Toggle.stories.tsx +128 -0
- package/src/components/Toggle/Toggle.test.tsx +308 -0
- package/src/components/Toggle/Toggle.tsx +184 -0
- package/src/constants/keyCodes.ts +2 -0
- package/src/docs/Brands.mdx +153 -0
- package/src/docs/Colors.mdx +158 -0
- package/src/docs/DesignTokens.mdx +415 -0
- package/src/docs/GetStarted.mdx +47 -0
- package/src/docs/intro.mdx +12 -0
- package/src/fonts/AvenirBold.otf +0 -0
- package/src/fonts/AvenirBold.woff +0 -0
- package/src/fonts/AvenirBold.woff2 +0 -0
- package/src/fonts/AvenirLight.otf +0 -0
- package/src/fonts/AvenirLight.woff +0 -0
- package/src/fonts/AvenirLight.woff2 +0 -0
- package/src/fonts/AvenirRegular.otf +0 -0
- package/src/fonts/AvenirRegular.woff +0 -0
- package/src/fonts/AvenirRegular.woff2 +0 -0
- package/src/fonts/Geist-Bold.otf +0 -0
- package/src/fonts/Geist-Bold.woff +0 -0
- package/src/fonts/Geist-Bold.woff2 +0 -0
- package/src/fonts/Geist-Medium.otf +0 -0
- package/src/fonts/Geist-Medium.woff +0 -0
- package/src/fonts/Geist-Medium.woff2 +0 -0
- package/src/fonts/Geist-Regular.otf +0 -0
- package/src/fonts/Geist-Regular.woff +0 -0
- package/src/fonts/Geist-Regular.woff2 +0 -0
- package/src/fonts/Geist-SemiBold.otf +0 -0
- package/src/fonts/Geist-SemiBold.woff +0 -0
- package/src/fonts/Geist-SemiBold.woff2 +0 -0
- package/src/fonts/GeistMono-Regular.otf +0 -0
- package/src/fonts/GeistMono-Regular.woff +0 -0
- package/src/fonts/GeistMono-Regular.woff2 +0 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useBreakpoint/useBreakpoint.mdx +26 -0
- package/src/hooks/useBreakpoint/useBreakpoint.stories.tsx +30 -0
- package/src/hooks/useBreakpoint/useBreakpoint.test.tsx +19 -0
- package/src/hooks/useBreakpoint/useBreakpoint.ts +50 -0
- package/src/hooks/useIsomorphicLayoutEffect/useIsomorphicLayouEffect.ts +5 -0
- package/src/hooks/useOpenClose/useOpenClose.mdx +15 -0
- package/src/hooks/useOpenClose/useOpenClose.stories.tsx +41 -0
- package/src/hooks/useOpenClose/useOpenClose.test.tsx +119 -0
- package/src/hooks/useOpenClose/useOpenClose.tsx +95 -0
- package/src/hooks/useWindowSize/useWindowSize.mdx +25 -0
- package/src/hooks/useWindowSize/useWindowSize.stories.tsx +35 -0
- package/src/hooks/useWindowSize/useWindowSize.ts +24 -0
- package/src/index.ts +48 -0
- package/src/lib/cssShorthandToClasses.test.ts +149 -0
- package/src/lib/cssShorthandToClasses.ts +133 -0
- package/src/lib/doesStringIncludeCssUnit.ts +6 -0
- package/src/lib/generateResponsiveClasses.test.ts +24 -0
- package/src/lib/generateResponsiveClasses.ts +30 -0
- package/src/lib/getAutoCompleteValue.test.ts +27 -0
- package/src/lib/getAutoCompleteValue.ts +12 -0
- package/src/lib/getColumnKeys.ts +27 -0
- package/src/lib/getDimensionCss.test.ts +148 -0
- package/src/lib/getDimensionCss.ts +73 -0
- package/src/lib/getElementType.test.tsx +42 -0
- package/src/lib/getElementType.ts +42 -0
- package/src/lib/getFlexCss.test.ts +122 -0
- package/src/lib/getFlexCss.ts +67 -0
- package/src/lib/index.ts +15 -0
- package/src/lib/isFunction.ts +6 -0
- package/src/lib/mergeRefs.ts +15 -0
- package/src/lib/prefersReducedMotion.ts +12 -0
- package/src/lib/react-children-utilities/filter.ts +12 -0
- package/src/lib/react-children-utilities/index.ts +1 -0
- package/src/lib/reactRouterClickHandler.ts +37 -0
- package/src/lib/resolveValue.ts +7 -0
- package/src/lib/tokens.ts +139 -0
- package/src/modes.ts +8 -0
- package/src/styles/animation.scss +152 -0
- package/src/styles/cursor.scss +43 -0
- package/src/styles/display.scss +119 -0
- package/src/styles/flex.scss +453 -0
- package/src/styles/fonts.scss +44 -0
- package/src/styles/globals/utilities.scss +4 -0
- package/src/styles/mixins.scss +14 -0
- package/src/styles/overflow.scss +41 -0
- package/src/styles/position.scss +45 -0
- package/src/styles/reset.scss +108 -0
- package/src/styles/text-align.scss +21 -0
- package/src/styles/utilities.scss +9 -0
- package/src/styles/variables/forms.scss +71 -0
- package/src/styles/variables/index.scss +3 -0
- package/src/styles/white-space.scss +21 -0
- package/src/types/index.ts +201 -0
- package/src/types/lib.types.ts +3 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ValueOrFunction } from '../../types';
|
|
3
|
+
|
|
4
|
+
export type ToastType = 'success' | 'error' | 'loading' | 'blank' | 'custom';
|
|
5
|
+
|
|
6
|
+
export type ToastPosition =
|
|
7
|
+
| 'top-left'
|
|
8
|
+
| 'top-center'
|
|
9
|
+
| 'top-right'
|
|
10
|
+
| 'bottom-left'
|
|
11
|
+
| 'bottom-center'
|
|
12
|
+
| 'bottom-right';
|
|
13
|
+
|
|
14
|
+
export interface Toast {
|
|
15
|
+
/**
|
|
16
|
+
* Type of toast to create.
|
|
17
|
+
*/
|
|
18
|
+
type: ToastType;
|
|
19
|
+
/**
|
|
20
|
+
* Unique id for the toast.
|
|
21
|
+
*/
|
|
22
|
+
id: string;
|
|
23
|
+
/**
|
|
24
|
+
* Toast message
|
|
25
|
+
*/
|
|
26
|
+
message: ValueOrFunction<React.ReactNode, Toast>;
|
|
27
|
+
/**
|
|
28
|
+
* Determine of toast layout is compact or default.
|
|
29
|
+
*/
|
|
30
|
+
isCompact?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Custom duration for toast.
|
|
33
|
+
*/
|
|
34
|
+
duration?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Amount of time the toast timeout has been paused for.
|
|
37
|
+
* When the toast timeout is paused, its auto-dismissal will be delayed.
|
|
38
|
+
*/
|
|
39
|
+
pauseDuration: number;
|
|
40
|
+
/**
|
|
41
|
+
* Custom toast position, use as needed to override global position from ToastContainer.
|
|
42
|
+
*/
|
|
43
|
+
position?: ToastPosition;
|
|
44
|
+
/**
|
|
45
|
+
* Accessibility options
|
|
46
|
+
*/
|
|
47
|
+
ariaProps: {
|
|
48
|
+
role: 'status' | 'alert';
|
|
49
|
+
'aria-live': 'assertive' | 'off' | 'polite';
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Whether the toast can be dismissed (if true, the toast will include a close button)
|
|
53
|
+
*/
|
|
54
|
+
canDismiss?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Custom styles.
|
|
57
|
+
*/
|
|
58
|
+
style?: React.CSSProperties;
|
|
59
|
+
/**
|
|
60
|
+
* Custom className
|
|
61
|
+
*/
|
|
62
|
+
className?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Epoch timestamp
|
|
65
|
+
*/
|
|
66
|
+
createdAt: number;
|
|
67
|
+
/**
|
|
68
|
+
* Whether the toast is visible at the current time.
|
|
69
|
+
* Used in order to display a proper fade-out animation before the element is fully removed from the DOM.
|
|
70
|
+
*/
|
|
71
|
+
visible: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Height of element (calculated with getBoundingClientRect)
|
|
74
|
+
*/
|
|
75
|
+
height?: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type ToastOptions = Partial<
|
|
79
|
+
Pick<
|
|
80
|
+
Toast,
|
|
81
|
+
| 'id'
|
|
82
|
+
| 'duration'
|
|
83
|
+
| 'ariaProps'
|
|
84
|
+
| 'className'
|
|
85
|
+
| 'style'
|
|
86
|
+
| 'position'
|
|
87
|
+
| 'canDismiss'
|
|
88
|
+
| 'isCompact'
|
|
89
|
+
>
|
|
90
|
+
>;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Extends toast options so that you can include options for each type of toast in the options.
|
|
94
|
+
* e.g: { duration: 1000, success: { duration: 2000 } }
|
|
95
|
+
*/
|
|
96
|
+
export type ExtendedToastOptions = ToastOptions & {
|
|
97
|
+
[key in ToastType]?: ToastOptions;
|
|
98
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { prefersReducedMotion } from '../../lib/prefersReducedMotion';
|
|
3
|
+
import { resolveValue } from '../../lib/resolveValue';
|
|
4
|
+
import { Box } from '../Box/Box';
|
|
5
|
+
import { Toast, ToastPosition, ExtendedToastOptions } from './Toast.types';
|
|
6
|
+
import { ToastNotification } from './ToastNotification';
|
|
7
|
+
import { useToasts } from './useToasts';
|
|
8
|
+
import { toast } from './toast';
|
|
9
|
+
|
|
10
|
+
export const createRectRef =
|
|
11
|
+
(onRect: (rect: DOMRect) => void) =>
|
|
12
|
+
(el: HTMLElement | null): void => {
|
|
13
|
+
if (el) {
|
|
14
|
+
const boundingRect = el.getBoundingClientRect();
|
|
15
|
+
onRect(boundingRect);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getPositionStyle = (
|
|
20
|
+
position: ToastPosition,
|
|
21
|
+
offset: number
|
|
22
|
+
): React.CSSProperties => {
|
|
23
|
+
const top = position.includes('top');
|
|
24
|
+
const verticalStyle: React.CSSProperties = top ? { top: 0 } : { bottom: 0 };
|
|
25
|
+
const horizontalStyle = {
|
|
26
|
+
...(position.includes('center') && { justifyContent: 'center' }),
|
|
27
|
+
...(!position.includes('center') &&
|
|
28
|
+
position.includes('right') && { justifyContent: 'flex-end' }),
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
left: 0,
|
|
32
|
+
right: 0,
|
|
33
|
+
display: 'flex',
|
|
34
|
+
position: 'absolute',
|
|
35
|
+
transition: prefersReducedMotion()
|
|
36
|
+
? undefined
|
|
37
|
+
: 'all 230ms cubic-bezier(.21,1.02,.73,1)',
|
|
38
|
+
transform: `translateY(${offset * (top ? 1 : -1)}px)`,
|
|
39
|
+
...verticalStyle,
|
|
40
|
+
...horizontalStyle,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export interface ToastContainerProps {
|
|
45
|
+
/**
|
|
46
|
+
* Render function for each individual toast.
|
|
47
|
+
* This can be used to render custom toasts, although we recommend using the standard version,
|
|
48
|
+
* unless absolutely necessary.
|
|
49
|
+
*/
|
|
50
|
+
children?: (t: Toast) => JSX.Element;
|
|
51
|
+
/**
|
|
52
|
+
* Style object for the toast container if needed.
|
|
53
|
+
*/
|
|
54
|
+
containerStyle?: React.CSSProperties;
|
|
55
|
+
/**
|
|
56
|
+
* Custom classname for toast container if needed.
|
|
57
|
+
*/
|
|
58
|
+
containerClassName?: string;
|
|
59
|
+
/**
|
|
60
|
+
* The vertical gap between each toast notification when multiple toasts are on screen (in pixels).
|
|
61
|
+
*/
|
|
62
|
+
gutter?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Global position for all toasts in container.
|
|
65
|
+
* NOTE: This can be overwritten by the position property of each individual toast.
|
|
66
|
+
*/
|
|
67
|
+
position?: ToastPosition;
|
|
68
|
+
/**
|
|
69
|
+
* Display toasts in reverse order.
|
|
70
|
+
* NOTE: Reverse order in this case means that older toasts remain close to the position origin.
|
|
71
|
+
* and newer toasts appear further away from the origin.
|
|
72
|
+
*/
|
|
73
|
+
reverseOrder?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Global options for each toast. Can be overwritten for each individual toast as needed.
|
|
76
|
+
*/
|
|
77
|
+
toastOptions?: ExtendedToastOptions;
|
|
78
|
+
/**
|
|
79
|
+
* Props spread into main container.
|
|
80
|
+
*/
|
|
81
|
+
[x: string]: unknown; // eslint-disable-line
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const DEFAULT_OFFSET = 16;
|
|
85
|
+
|
|
86
|
+
const renderNotification = (
|
|
87
|
+
currentToast: Toast,
|
|
88
|
+
children: ((t: Toast) => JSX.Element) | undefined,
|
|
89
|
+
containerPosition: ToastContainerProps['position']
|
|
90
|
+
) => {
|
|
91
|
+
const toastPosition = currentToast.position || containerPosition;
|
|
92
|
+
|
|
93
|
+
if (currentToast.type === 'custom') {
|
|
94
|
+
return resolveValue(currentToast.message, currentToast);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (children) {
|
|
98
|
+
return children(currentToast);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<ToastNotification
|
|
103
|
+
toast={currentToast}
|
|
104
|
+
position={toastPosition}
|
|
105
|
+
onDismiss={() => toast.dismiss(currentToast.id)}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const ToastContainer: React.FC<ToastContainerProps> = ({
|
|
111
|
+
children = undefined,
|
|
112
|
+
containerStyle = undefined,
|
|
113
|
+
containerClassName = undefined,
|
|
114
|
+
gutter = 8,
|
|
115
|
+
position = 'top-center',
|
|
116
|
+
reverseOrder = false,
|
|
117
|
+
toastOptions,
|
|
118
|
+
...restProps
|
|
119
|
+
}) => {
|
|
120
|
+
const { toasts, handlers } = useToasts(toastOptions);
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<Box
|
|
124
|
+
style={{
|
|
125
|
+
top: DEFAULT_OFFSET,
|
|
126
|
+
left: DEFAULT_OFFSET,
|
|
127
|
+
right: DEFAULT_OFFSET,
|
|
128
|
+
bottom: DEFAULT_OFFSET,
|
|
129
|
+
pointerEvents: 'none', // ensure background elements are clickable
|
|
130
|
+
...containerStyle,
|
|
131
|
+
}}
|
|
132
|
+
position="fixed"
|
|
133
|
+
zIndex="popover"
|
|
134
|
+
className={containerClassName}
|
|
135
|
+
onMouseEnter={handlers.startPause}
|
|
136
|
+
onMouseLeave={handlers.endPause}
|
|
137
|
+
{...restProps}
|
|
138
|
+
>
|
|
139
|
+
{toasts.map((t) => {
|
|
140
|
+
const toastPosition = t.position || position;
|
|
141
|
+
const offset = handlers.calculateOffset(t, {
|
|
142
|
+
reverseOrder,
|
|
143
|
+
gutter,
|
|
144
|
+
defaultPosition: position,
|
|
145
|
+
});
|
|
146
|
+
const positionStyle = getPositionStyle(toastPosition, offset);
|
|
147
|
+
|
|
148
|
+
const ref = t.height
|
|
149
|
+
? undefined
|
|
150
|
+
: createRectRef((rect) => {
|
|
151
|
+
handlers.updateHeight(t.id, rect.height);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<Box
|
|
156
|
+
ref={ref}
|
|
157
|
+
zIndex={t.visible ? 'popover' : undefined}
|
|
158
|
+
key={t.id}
|
|
159
|
+
style={{
|
|
160
|
+
...positionStyle,
|
|
161
|
+
}}
|
|
162
|
+
display="block"
|
|
163
|
+
>
|
|
164
|
+
{renderNotification(t, children, position)}
|
|
165
|
+
</Box>
|
|
166
|
+
);
|
|
167
|
+
})}
|
|
168
|
+
</Box>
|
|
169
|
+
);
|
|
170
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
.toast-notification {
|
|
2
|
+
background-color: var(--color-background-primary);
|
|
3
|
+
color: var(--color-font-primary);
|
|
4
|
+
border: 1px solid var(--color-border-subtle);
|
|
5
|
+
border-radius: var(--size-border-radius-lg);
|
|
6
|
+
box-shadow: var(--size-box-shadow-md);
|
|
7
|
+
font-size: var(--size-font-size-sm);
|
|
8
|
+
will-change: transform;
|
|
9
|
+
pointer-events: auto;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.toast-dismiss {
|
|
13
|
+
border-color: var(--color-border-default);
|
|
14
|
+
color: var(--color-font-primary);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.toast-notification-enter-top {
|
|
18
|
+
:global {
|
|
19
|
+
animation: enterTop 0.25s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.toast-notification-exit-top {
|
|
24
|
+
:global {
|
|
25
|
+
animation: exitTop 0.3s forwards cubic-bezier(0.06, 0.71, 0.55, 1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.toast-notification-enter-bottom {
|
|
30
|
+
:global {
|
|
31
|
+
animation: enterBottom 0.25s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.toast-notification-exit-bottom {
|
|
36
|
+
:global {
|
|
37
|
+
animation: exitBottom 0.3s forwards cubic-bezier(0.06, 0.71, 0.55, 1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.toast-notification-exit-left {
|
|
42
|
+
:global {
|
|
43
|
+
animation: exitLeft 0.3s forwards cubic-bezier(0.06, 0.71, 0.55, 1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.toast-notification-exit-right {
|
|
48
|
+
:global {
|
|
49
|
+
animation: exitRight 0.3s forwards cubic-bezier(0.06, 0.71, 0.55, 1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.toast-notification-fade-in {
|
|
54
|
+
:global {
|
|
55
|
+
animation: fadeIn 0.25s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.toast-notification-fade-out {
|
|
60
|
+
:global {
|
|
61
|
+
animation: fadeOut 0.3s forwards cubic-bezier(0.06, 0.71, 0.55, 1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { Toast, ToastPosition } from './Toast.types';
|
|
4
|
+
import { resolveValue } from '../../lib/resolveValue';
|
|
5
|
+
import { prefersReducedMotion } from '../../lib/prefersReducedMotion';
|
|
6
|
+
import { Box } from '../Box/Box';
|
|
7
|
+
import { Icon } from '../Icon/Icon';
|
|
8
|
+
import { Spinner } from '../Spinner/Spinner';
|
|
9
|
+
import { IconName, FontColor } from '../../types';
|
|
10
|
+
import styles from './ToastNotification.module.scss';
|
|
11
|
+
|
|
12
|
+
interface ToastNotificationProps {
|
|
13
|
+
/**
|
|
14
|
+
* Toast object
|
|
15
|
+
*/
|
|
16
|
+
toast: Toast;
|
|
17
|
+
/**
|
|
18
|
+
* Custom position for toast (overrides global container toast position).
|
|
19
|
+
*/
|
|
20
|
+
position?: ToastPosition;
|
|
21
|
+
/**
|
|
22
|
+
* Custom styles for toast.
|
|
23
|
+
*/
|
|
24
|
+
style?: React.CSSProperties;
|
|
25
|
+
/**
|
|
26
|
+
* Render function to create custom toast notification.
|
|
27
|
+
*/
|
|
28
|
+
children?: (components: { message: React.ReactNode }) => React.ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* Handler for when the dismiss button is pressed.
|
|
31
|
+
*/
|
|
32
|
+
onDismiss?: () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const getAnimationClass = (
|
|
36
|
+
position: ToastPosition,
|
|
37
|
+
visible: boolean
|
|
38
|
+
): React.CSSProperties | string => {
|
|
39
|
+
const verticalPosition = position.includes('top') ? 'top' : 'bottom';
|
|
40
|
+
const horizontalPosition = position.includes('left') ? 'left' : 'right';
|
|
41
|
+
const isCentered = position.includes('center');
|
|
42
|
+
|
|
43
|
+
const [enter, exit] = prefersReducedMotion()
|
|
44
|
+
? [
|
|
45
|
+
styles['toast-notification-fade-in'],
|
|
46
|
+
styles['toast-notification-fade-out'],
|
|
47
|
+
]
|
|
48
|
+
: [
|
|
49
|
+
styles[`toast-notification-enter-${verticalPosition}`],
|
|
50
|
+
styles[
|
|
51
|
+
`toast-notification-exit-${
|
|
52
|
+
isCentered ? verticalPosition : horizontalPosition
|
|
53
|
+
}`
|
|
54
|
+
],
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
return visible ? enter : exit;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const renderToastIcon = (toast: Toast) => {
|
|
61
|
+
const { type } = toast;
|
|
62
|
+
|
|
63
|
+
if (type === 'blank') return;
|
|
64
|
+
|
|
65
|
+
let iconName: IconName = 'exclamation-mark';
|
|
66
|
+
let iconColor: FontColor = 'base'; // dark
|
|
67
|
+
|
|
68
|
+
if (type === 'success') {
|
|
69
|
+
iconName = 'c-check';
|
|
70
|
+
iconColor = 'success';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (type === 'error') {
|
|
74
|
+
iconName = 'c-warning';
|
|
75
|
+
iconColor = 'danger';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const icon =
|
|
79
|
+
type !== 'loading' ? (
|
|
80
|
+
<Icon name={iconName} color={iconColor} />
|
|
81
|
+
) : (
|
|
82
|
+
<Spinner />
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// eslint-disable-next-line consistent-return
|
|
86
|
+
return (
|
|
87
|
+
<Box justifyContent="center" height="100">
|
|
88
|
+
{icon}
|
|
89
|
+
</Box>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const renderDismissIcon = (
|
|
94
|
+
toast: Toast,
|
|
95
|
+
onDismiss: ToastNotificationProps['onDismiss']
|
|
96
|
+
) => {
|
|
97
|
+
if (!toast.canDismiss) return;
|
|
98
|
+
|
|
99
|
+
// eslint-disable-next-line consistent-return
|
|
100
|
+
return (
|
|
101
|
+
<Box
|
|
102
|
+
as="button"
|
|
103
|
+
borderWidth="0 0 0 sm"
|
|
104
|
+
className={styles['toast-dismiss']}
|
|
105
|
+
padding="0 0 0 sm"
|
|
106
|
+
alignItems="center"
|
|
107
|
+
justifyContent="center"
|
|
108
|
+
cursor="pointer"
|
|
109
|
+
background="transparent" // transparent
|
|
110
|
+
height="100"
|
|
111
|
+
onClick={onDismiss}
|
|
112
|
+
aria-label="dismiss notification"
|
|
113
|
+
>
|
|
114
|
+
<Icon name="remove" />
|
|
115
|
+
</Box>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
120
|
+
export const ToastNotification: React.FC<ToastNotificationProps> = React.memo(
|
|
121
|
+
({ toast, position = 'top-center', style, children, onDismiss }) => {
|
|
122
|
+
const message = (
|
|
123
|
+
<Box
|
|
124
|
+
direction="row"
|
|
125
|
+
justifyContent="center"
|
|
126
|
+
style={{
|
|
127
|
+
flex: '1 1 auto',
|
|
128
|
+
}}
|
|
129
|
+
{...toast.ariaProps}
|
|
130
|
+
>
|
|
131
|
+
{resolveValue(toast.message, toast)}
|
|
132
|
+
</Box>
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const animationClass = toast?.height
|
|
136
|
+
? getAnimationClass(toast.position || position, toast.visible)
|
|
137
|
+
: undefined;
|
|
138
|
+
|
|
139
|
+
const classes = classNames(
|
|
140
|
+
toast.className,
|
|
141
|
+
styles['toast-notification'],
|
|
142
|
+
animationClass,
|
|
143
|
+
{
|
|
144
|
+
'toast-notification--not-visible': !toast.visible,
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<Box
|
|
150
|
+
alignItems="center"
|
|
151
|
+
maxWidth="300px"
|
|
152
|
+
padding={toast.isCompact ? 'sm' : 'md'}
|
|
153
|
+
direction="row"
|
|
154
|
+
className={classes}
|
|
155
|
+
gap="sm"
|
|
156
|
+
style={{
|
|
157
|
+
...style,
|
|
158
|
+
...toast.style,
|
|
159
|
+
...(!toast.height && { opacity: 0 }),
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
{typeof children === 'function' ? (
|
|
163
|
+
children({
|
|
164
|
+
message,
|
|
165
|
+
})
|
|
166
|
+
) : (
|
|
167
|
+
<>
|
|
168
|
+
{renderToastIcon(toast)}
|
|
169
|
+
{message}
|
|
170
|
+
{renderDismissIcon(toast, onDismiss)}
|
|
171
|
+
</>
|
|
172
|
+
)}
|
|
173
|
+
</Box>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { resolveValue } from '../../lib/resolveValue';
|
|
4
|
+
import { ValueOrFunction } from '../../types';
|
|
5
|
+
import {
|
|
6
|
+
Toast,
|
|
7
|
+
ToastOptions,
|
|
8
|
+
ToastType,
|
|
9
|
+
ExtendedToastOptions,
|
|
10
|
+
} from './Toast.types';
|
|
11
|
+
import { ToastStoreActionType, dispatch } from './Toast.store';
|
|
12
|
+
|
|
13
|
+
type Message = ValueOrFunction<React.ReactNode, Toast>;
|
|
14
|
+
|
|
15
|
+
type ToastHandler = (message: Message, options?: ToastOptions) => string;
|
|
16
|
+
|
|
17
|
+
const createToast = (
|
|
18
|
+
message: Message,
|
|
19
|
+
type: ToastType,
|
|
20
|
+
opts?: ToastOptions
|
|
21
|
+
): Toast => ({
|
|
22
|
+
createdAt: Date.now(),
|
|
23
|
+
visible: true,
|
|
24
|
+
type,
|
|
25
|
+
ariaProps: {
|
|
26
|
+
role: 'status',
|
|
27
|
+
'aria-live': 'polite',
|
|
28
|
+
},
|
|
29
|
+
message,
|
|
30
|
+
pauseDuration: 0,
|
|
31
|
+
...opts,
|
|
32
|
+
id: opts?.id || uuidv4(),
|
|
33
|
+
canDismiss: type !== 'loading' && opts?.canDismiss !== false,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const createHandler =
|
|
37
|
+
(type: ToastType): ToastHandler =>
|
|
38
|
+
(message, options) => {
|
|
39
|
+
const toast = createToast(message, type, options);
|
|
40
|
+
|
|
41
|
+
dispatch({
|
|
42
|
+
type: ToastStoreActionType.UPSERT_TOAST,
|
|
43
|
+
payload: { toast },
|
|
44
|
+
});
|
|
45
|
+
return toast.id;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// eslint-disable-next-line
|
|
49
|
+
const toast = (message: Message, opts?: ToastOptions) =>
|
|
50
|
+
createHandler('blank')(message, opts);
|
|
51
|
+
|
|
52
|
+
toast.error = createHandler('error');
|
|
53
|
+
toast.success = createHandler('success');
|
|
54
|
+
toast.loading = createHandler('loading');
|
|
55
|
+
toast.custom = createHandler('custom');
|
|
56
|
+
|
|
57
|
+
toast.dismiss = (toastId?: string) => {
|
|
58
|
+
dispatch({
|
|
59
|
+
type: ToastStoreActionType.DISMISS_TOAST,
|
|
60
|
+
payload: { toastId },
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
toast.remove = (toastId?: string) =>
|
|
65
|
+
dispatch({
|
|
66
|
+
type: ToastStoreActionType.REMOVE_TOAST,
|
|
67
|
+
payload: { toastId },
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// eslint-disable-next-line func-names
|
|
71
|
+
toast.async = function <T>(
|
|
72
|
+
promise: Promise<T>,
|
|
73
|
+
messages: {
|
|
74
|
+
loading: React.ReactNode;
|
|
75
|
+
success: ValueOrFunction<React.ReactNode, T>;
|
|
76
|
+
error: ValueOrFunction<React.ReactNode, unknown>;
|
|
77
|
+
},
|
|
78
|
+
opts?: ExtendedToastOptions
|
|
79
|
+
) {
|
|
80
|
+
const id = toast.loading(messages.loading, { ...opts, ...opts?.loading });
|
|
81
|
+
|
|
82
|
+
promise
|
|
83
|
+
.then((data) => {
|
|
84
|
+
toast.success(resolveValue(messages.success, data), {
|
|
85
|
+
id,
|
|
86
|
+
...opts,
|
|
87
|
+
...opts?.success,
|
|
88
|
+
});
|
|
89
|
+
return data;
|
|
90
|
+
})
|
|
91
|
+
.catch((err) => {
|
|
92
|
+
toast.error(resolveValue(messages.error, err), {
|
|
93
|
+
id,
|
|
94
|
+
...opts,
|
|
95
|
+
...opts?.error,
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return promise;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export { toast }; // eslint-disable-line import/prefer-default-export
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useEffect, useMemo } from 'react';
|
|
2
|
+
import { ExtendedToastOptions, Toast, ToastPosition } from './Toast.types';
|
|
3
|
+
import { useToastStore, dispatch, ToastStoreActionType } from './Toast.store';
|
|
4
|
+
import { toast } from './toast';
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
7
|
+
export const useToasts = (toastOptions?: ExtendedToastOptions) => {
|
|
8
|
+
// eslint-disable-line import/prefer-default-export
|
|
9
|
+
const { toasts, pausedAt } = useToastStore(toastOptions);
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (pausedAt) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const now = Date.now();
|
|
17
|
+
const timeouts = toasts.map((t) => {
|
|
18
|
+
if (t.duration === Infinity) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const durationLeft =
|
|
23
|
+
(t.duration || 0) + t.pauseDuration - (now - t.createdAt);
|
|
24
|
+
|
|
25
|
+
if (durationLeft < 0) {
|
|
26
|
+
if (t.visible) {
|
|
27
|
+
toast.dismiss(t.id);
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return setTimeout(() => toast.dismiss(t.id), durationLeft); // eslint-disable-line consistent-return
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return () => {
|
|
35
|
+
// eslint-disable-line consistent-return
|
|
36
|
+
timeouts.forEach((timeout) => timeout && clearTimeout(timeout));
|
|
37
|
+
};
|
|
38
|
+
}, [toasts, pausedAt]);
|
|
39
|
+
|
|
40
|
+
const handlers = useMemo(
|
|
41
|
+
() => ({
|
|
42
|
+
startPause: () => {
|
|
43
|
+
dispatch({
|
|
44
|
+
type: ToastStoreActionType.START_PAUSE,
|
|
45
|
+
payload: { time: Date.now() },
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
endPause: () => {
|
|
49
|
+
if (pausedAt) {
|
|
50
|
+
dispatch({
|
|
51
|
+
type: ToastStoreActionType.END_PAUSE,
|
|
52
|
+
payload: { time: Date.now() },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
updateHeight: (toastId: string, height: number) => {
|
|
57
|
+
if (toasts.find((t) => t.id === toastId)?.height === height) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
dispatch({
|
|
61
|
+
type: ToastStoreActionType.UPDATE_TOAST,
|
|
62
|
+
payload: { toast: { id: toastId, height } },
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
calculateOffset: (
|
|
66
|
+
currentToast: Toast,
|
|
67
|
+
opts: {
|
|
68
|
+
reverseOrder: boolean;
|
|
69
|
+
gutter: number;
|
|
70
|
+
defaultPosition?: ToastPosition;
|
|
71
|
+
}
|
|
72
|
+
) => {
|
|
73
|
+
const { reverseOrder, gutter, defaultPosition } = opts || {};
|
|
74
|
+
|
|
75
|
+
const relevantToasts = toasts.filter(
|
|
76
|
+
(t) =>
|
|
77
|
+
(t.position || defaultPosition) ===
|
|
78
|
+
(currentToast.position || defaultPosition) && t.height
|
|
79
|
+
);
|
|
80
|
+
const toastIndex = relevantToasts.findIndex(
|
|
81
|
+
(t) => t.id === currentToast.id
|
|
82
|
+
);
|
|
83
|
+
const toastsBefore = relevantToasts.filter(
|
|
84
|
+
(t, i) => i < toastIndex && t.visible
|
|
85
|
+
).length;
|
|
86
|
+
|
|
87
|
+
const offset = relevantToasts
|
|
88
|
+
.filter((t) => t.visible)
|
|
89
|
+
.slice(...(reverseOrder ? [toastsBefore + 1] : [0, toastsBefore]))
|
|
90
|
+
.reduce((acc, t) => acc + (t.height || 0) + gutter, 0);
|
|
91
|
+
|
|
92
|
+
return offset;
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
[toasts, pausedAt]
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
toasts,
|
|
100
|
+
handlers,
|
|
101
|
+
};
|
|
102
|
+
};
|