@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.
Files changed (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/package.json +138 -0
  4. package/src/components/Alert/Alert.constants.ts +19 -0
  5. package/src/components/Alert/Alert.mdx +29 -0
  6. package/src/components/Alert/Alert.module.scss +74 -0
  7. package/src/components/Alert/Alert.stories.tsx +102 -0
  8. package/src/components/Alert/Alert.test.tsx +187 -0
  9. package/src/components/Alert/Alert.tsx +157 -0
  10. package/src/components/Alert/Alert.types.ts +14 -0
  11. package/src/components/Badge/Badge.mdx +28 -0
  12. package/src/components/Badge/Badge.module.scss +155 -0
  13. package/src/components/Badge/Badge.stories.tsx +52 -0
  14. package/src/components/Badge/Badge.test.tsx +74 -0
  15. package/src/components/Badge/Badge.tsx +70 -0
  16. package/src/components/Box/Box.mdx +259 -0
  17. package/src/components/Box/Box.module.scss +16 -0
  18. package/src/components/Box/Box.stories.tsx +1679 -0
  19. package/src/components/Box/Box.test.tsx +478 -0
  20. package/src/components/Box/Box.tsx +636 -0
  21. package/src/components/Button/Button.constants.ts +10 -0
  22. package/src/components/Button/Button.mdx +71 -0
  23. package/src/components/Button/Button.module.scss +312 -0
  24. package/src/components/Button/Button.stories.tsx +117 -0
  25. package/src/components/Button/Button.test.tsx +460 -0
  26. package/src/components/Button/Button.tsx +241 -0
  27. package/src/components/Card/Card.mdx +46 -0
  28. package/src/components/Card/Card.module.scss +6 -0
  29. package/src/components/Card/Card.stories.tsx +101 -0
  30. package/src/components/Card/Card.test.tsx +11 -0
  31. package/src/components/Card/Card.tsx +61 -0
  32. package/src/components/Card/components/CardFooter/CardFooter.test.tsx +11 -0
  33. package/src/components/Card/components/CardFooter/CardFooter.tsx +35 -0
  34. package/src/components/Card/components/CardHeader/CardHeader.test.tsx +23 -0
  35. package/src/components/Card/components/CardHeader/CardHeader.tsx +54 -0
  36. package/src/components/Card/components/CardSection/CardSection.test.tsx +28 -0
  37. package/src/components/Card/components/CardSection/CardSection.tsx +102 -0
  38. package/src/components/Card/components/index.ts +3 -0
  39. package/src/components/CheckboxInput/CheckboxInput.mdx +98 -0
  40. package/src/components/CheckboxInput/CheckboxInput.stories.tsx +254 -0
  41. package/src/components/CheckboxInput/CheckboxInput.test.tsx +436 -0
  42. package/src/components/CheckboxInput/CheckboxInput.tsx +171 -0
  43. package/src/components/CheckboxInput/components/Checkbox.module.scss +174 -0
  44. package/src/components/CheckboxInput/components/Checkbox.test.tsx +201 -0
  45. package/src/components/CheckboxInput/components/Checkbox.tsx +176 -0
  46. package/src/components/CheckboxInput/components/CheckboxIcon.tsx +71 -0
  47. package/src/components/DateInput/DateInput.mdx +61 -0
  48. package/src/components/DateInput/DateInput.stories.tsx +168 -0
  49. package/src/components/DateInput/DateInput.test.tsx +258 -0
  50. package/src/components/DateInput/DateInput.tsx +189 -0
  51. package/src/components/DatePicker/DatePicker.mdx +52 -0
  52. package/src/components/DatePicker/DatePicker.module.scss +603 -0
  53. package/src/components/DatePicker/DatePicker.stories.tsx +199 -0
  54. package/src/components/DatePicker/DatePicker.test.tsx +26 -0
  55. package/src/components/DatePicker/DatePicker.tsx +138 -0
  56. package/src/components/Details/Details.mdx +30 -0
  57. package/src/components/Details/Details.module.scss +32 -0
  58. package/src/components/Details/Details.stories.tsx +38 -0
  59. package/src/components/Details/Details.test.tsx +189 -0
  60. package/src/components/Details/Details.tsx +51 -0
  61. package/src/components/Details/DetailsSummary.tsx +65 -0
  62. package/src/components/Drawer/Drawer.mdx +117 -0
  63. package/src/components/Drawer/Drawer.module.scss +96 -0
  64. package/src/components/Drawer/Drawer.stories.tsx +380 -0
  65. package/src/components/Drawer/Drawer.test.tsx +90 -0
  66. package/src/components/Drawer/Drawer.tsx +249 -0
  67. package/src/components/FormControl/FormControl.tsx +78 -0
  68. package/src/components/FormLabel/FormLabel.mdx +24 -0
  69. package/src/components/FormLabel/FormLabel.module.scss +19 -0
  70. package/src/components/FormLabel/FormLabel.stories.tsx +20 -0
  71. package/src/components/FormLabel/FormLabel.test.tsx +35 -0
  72. package/src/components/FormLabel/FormLabel.tsx +96 -0
  73. package/src/components/Formik/Formik.mdx +10 -0
  74. package/src/components/Formik/Formik.stories.tsx +307 -0
  75. package/src/components/Formik/FormikCheckboxInput/FormikCheckboxInput.test.tsx +172 -0
  76. package/src/components/Formik/FormikCheckboxInput/FormikCheckboxInput.tsx +41 -0
  77. package/src/components/Formik/FormikRadioGroup/FormikRadioGroup.test.tsx +205 -0
  78. package/src/components/Formik/FormikRadioGroup/FormikRadioGroup.tsx +37 -0
  79. package/src/components/Formik/FormikSelectInput/FormikSelectInput.test.tsx +210 -0
  80. package/src/components/Formik/FormikSelectInput/FormikSelectInput.tsx +41 -0
  81. package/src/components/Formik/FormikSelectInputInset/FormikSelectInputInset.test.tsx +153 -0
  82. package/src/components/Formik/FormikSelectInputInset/FormikSelectInputInset.tsx +44 -0
  83. package/src/components/Formik/FormikSelectInputNative/FormikSelectInputNative.test.tsx +161 -0
  84. package/src/components/Formik/FormikSelectInputNative/FormikSelectInputNative.tsx +46 -0
  85. package/src/components/Formik/FormikTextInput/FormikTextInput.test.tsx +176 -0
  86. package/src/components/Formik/FormikTextInput/FormikTextInput.tsx +38 -0
  87. package/src/components/Formik/FormikTextInputInset/FormikTextInputInset.test.tsx +170 -0
  88. package/src/components/Formik/FormikTextInputInset/FormikTextInputInset.tsx +42 -0
  89. package/src/components/Formik/FormikTextareaInput/FormikTextareaInput.test.tsx +186 -0
  90. package/src/components/Formik/FormikTextareaInput/FormikTextareaInput.tsx +42 -0
  91. package/src/components/Formik/FormikTextareaInputInset/FormikTextareaInputInset.test.tsx +179 -0
  92. package/src/components/Formik/FormikTextareaInputInset/FormikTextareaInputInset.tsx +42 -0
  93. package/src/components/Formik/FormikTimePicker/FormikTimePicker.test.tsx +224 -0
  94. package/src/components/Formik/FormikTimePicker/FormikTimePicker.tsx +37 -0
  95. package/src/components/Formik/FormikTimePickerNative/FormikTimePickerNative.test.tsx +175 -0
  96. package/src/components/Formik/FormikTimePickerNative/FormikTimePickerNative.tsx +38 -0
  97. package/src/components/Formik/FormikToggle/FormikToggle.test.tsx +172 -0
  98. package/src/components/Formik/FormikToggle/FormikToggle.tsx +38 -0
  99. package/src/components/Heading/Heading.constants.ts +19 -0
  100. package/src/components/Heading/Heading.mdx +35 -0
  101. package/src/components/Heading/Heading.module.scss +5 -0
  102. package/src/components/Heading/Heading.stories.tsx +90 -0
  103. package/src/components/Heading/Heading.test.tsx +67 -0
  104. package/src/components/Heading/Heading.tsx +67 -0
  105. package/src/components/HelpText/HelpText.module.scss +14 -0
  106. package/src/components/HelpText/HelpText.tsx +33 -0
  107. package/src/components/Icon/Icon.mdx +40 -0
  108. package/src/components/Icon/Icon.stories.tsx +72 -0
  109. package/src/components/Icon/Icon.test.tsx +30 -0
  110. package/src/components/Icon/Icon.tsx +61 -0
  111. package/src/components/InputValidationMessage/InputValidationMessage.module.scss +3 -0
  112. package/src/components/InputValidationMessage/InputValidationMessage.tsx +27 -0
  113. package/src/components/Modal/Modal.mdx +60 -0
  114. package/src/components/Modal/Modal.module.scss +135 -0
  115. package/src/components/Modal/Modal.stories.tsx +194 -0
  116. package/src/components/Modal/Modal.test.tsx +81 -0
  117. package/src/components/Modal/Modal.tsx +174 -0
  118. package/src/components/Modal/components/ModalBody/ModalBody.test.tsx +20 -0
  119. package/src/components/Modal/components/ModalBody/ModalBody.tsx +24 -0
  120. package/src/components/Modal/components/ModalFooter/ModalFooter.test.tsx +32 -0
  121. package/src/components/Modal/components/ModalFooter/ModalFooter.tsx +37 -0
  122. package/src/components/Modal/components/ModalHeader/ModalHeader.test.tsx +29 -0
  123. package/src/components/Modal/components/ModalHeader/ModalHeader.tsx +58 -0
  124. package/src/components/Modal/components/index.ts +5 -0
  125. package/src/components/Pagination/Pagination.mdx +26 -0
  126. package/src/components/Pagination/Pagination.stories.tsx +55 -0
  127. package/src/components/Pagination/Pagination.test.tsx +225 -0
  128. package/src/components/Pagination/Pagination.tsx +162 -0
  129. package/src/components/Pagination/Pagination.utilities.test.ts +133 -0
  130. package/src/components/Pagination/Pagination.utilities.ts +101 -0
  131. package/src/components/Popover/Popover.mdx +104 -0
  132. package/src/components/Popover/Popover.module.scss +74 -0
  133. package/src/components/Popover/Popover.stories.tsx +471 -0
  134. package/src/components/Popover/Popover.test.tsx +128 -0
  135. package/src/components/Popover/Popover.tsx +277 -0
  136. package/src/components/RadioGroup/RadioGroup.mdx +81 -0
  137. package/src/components/RadioGroup/RadioGroup.module.scss +23 -0
  138. package/src/components/RadioGroup/RadioGroup.stories.tsx +375 -0
  139. package/src/components/RadioGroup/RadioGroup.test.tsx +282 -0
  140. package/src/components/RadioGroup/RadioGroup.tsx +145 -0
  141. package/src/components/RadioGroup/RadioInput/RadioInput.module.scss +114 -0
  142. package/src/components/RadioGroup/RadioInput/RadioInput.test.tsx +156 -0
  143. package/src/components/RadioGroup/RadioInput/RadioInput.tsx +148 -0
  144. package/src/components/RadioGroup/RadioInput/RadioInputIcon.tsx +59 -0
  145. package/src/components/ResponsiveProvider/ResponsiveProvider.mdx +36 -0
  146. package/src/components/ResponsiveProvider/ResponsiveProvider.stories.tsx +54 -0
  147. package/src/components/ResponsiveProvider/ResponsiveProvider.test.tsx +70 -0
  148. package/src/components/ResponsiveProvider/ResponsiveProvider.tsx +73 -0
  149. package/src/components/SelectInput/SelectInput.mdx +115 -0
  150. package/src/components/SelectInput/SelectInput.module.scss +357 -0
  151. package/src/components/SelectInput/SelectInput.stories.tsx +373 -0
  152. package/src/components/SelectInput/SelectInput.test.tsx +403 -0
  153. package/src/components/SelectInput/SelectInput.tsx +245 -0
  154. package/src/components/SelectInputInset/SelectInputInset.mdx +56 -0
  155. package/src/components/SelectInputInset/SelectInputInset.module.scss +397 -0
  156. package/src/components/SelectInputInset/SelectInputInset.stories.tsx +189 -0
  157. package/src/components/SelectInputInset/SelectInputInset.test.tsx +305 -0
  158. package/src/components/SelectInputInset/SelectInputInset.tsx +235 -0
  159. package/src/components/SelectInputNative/SelectInputNative.mdx +87 -0
  160. package/src/components/SelectInputNative/SelectInputNative.module.scss +356 -0
  161. package/src/components/SelectInputNative/SelectInputNative.stories.tsx +282 -0
  162. package/src/components/SelectInputNative/SelectInputNative.test.tsx +341 -0
  163. package/src/components/SelectInputNative/SelectInputNative.tsx +121 -0
  164. package/src/components/Spinner/Spinner.mdx +29 -0
  165. package/src/components/Spinner/Spinner.module.scss +16 -0
  166. package/src/components/Spinner/Spinner.stories.tsx +48 -0
  167. package/src/components/Spinner/Spinner.test.tsx +47 -0
  168. package/src/components/Spinner/Spinner.tsx +116 -0
  169. package/src/components/Table/Table.mdx +216 -0
  170. package/src/components/Table/Table.module.scss +61 -0
  171. package/src/components/Table/Table.stories.tsx +884 -0
  172. package/src/components/Table/Table.test.tsx +437 -0
  173. package/src/components/Table/Table.tsx +171 -0
  174. package/src/components/Table/TableBody/TableBody.module.scss +19 -0
  175. package/src/components/Table/TableBody/TableBody.test.tsx +38 -0
  176. package/src/components/Table/TableBody/TableBody.tsx +96 -0
  177. package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.module.scss +47 -0
  178. package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.test.tsx +81 -0
  179. package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.tsx +94 -0
  180. package/src/components/Table/TableHead/TableHead.test.tsx +20 -0
  181. package/src/components/Table/TableHead/TableHead.tsx +78 -0
  182. package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.module.scss +72 -0
  183. package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.test.tsx +187 -0
  184. package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.tsx +192 -0
  185. package/src/components/Table/common/TableRow/TableRow.module.scss +5 -0
  186. package/src/components/Table/common/TableRow/TableRow.test.tsx +52 -0
  187. package/src/components/Table/common/TableRow/TableRow.tsx +155 -0
  188. package/src/components/TextInput/TextInput.mdx +96 -0
  189. package/src/components/TextInput/TextInput.module.scss +405 -0
  190. package/src/components/TextInput/TextInput.stories.tsx +268 -0
  191. package/src/components/TextInput/TextInput.test.tsx +231 -0
  192. package/src/components/TextInput/TextInput.tsx +263 -0
  193. package/src/components/TextInputInset/TextInputInset.mdx +62 -0
  194. package/src/components/TextInputInset/TextInputInset.module.scss +418 -0
  195. package/src/components/TextInputInset/TextInputInset.stories.tsx +213 -0
  196. package/src/components/TextInputInset/TextInputInset.test.tsx +222 -0
  197. package/src/components/TextInputInset/TextInputInset.tsx +261 -0
  198. package/src/components/TextareaInput/TextareaInput.mdx +117 -0
  199. package/src/components/TextareaInput/TextareaInput.module.scss +275 -0
  200. package/src/components/TextareaInput/TextareaInput.stories.tsx +293 -0
  201. package/src/components/TextareaInput/TextareaInput.test.tsx +195 -0
  202. package/src/components/TextareaInput/TextareaInput.tsx +182 -0
  203. package/src/components/TextareaInputInset/TextareaInputInset.mdx +55 -0
  204. package/src/components/TextareaInputInset/TextareaInputInset.module.scss +337 -0
  205. package/src/components/TextareaInputInset/TextareaInputInset.stories.tsx +160 -0
  206. package/src/components/TextareaInputInset/TextareaInputInset.test.tsx +199 -0
  207. package/src/components/TextareaInputInset/TextareaInputInset.tsx +213 -0
  208. package/src/components/ThemeProvider/ThemeProvider.mdx +11 -0
  209. package/src/components/ThemeProvider/ThemeProvider.stories.tsx +56 -0
  210. package/src/components/ThemeProvider/ThemeProvider.tsx +75 -0
  211. package/src/components/TimePicker/TimePicker.mdx +75 -0
  212. package/src/components/TimePicker/TimePicker.stories.tsx +149 -0
  213. package/src/components/TimePicker/TimePicker.test.tsx +97 -0
  214. package/src/components/TimePicker/TimePicker.tsx +83 -0
  215. package/src/components/TimePickerNative/TimePickerNative.mdx +67 -0
  216. package/src/components/TimePickerNative/TimePickerNative.stories.tsx +151 -0
  217. package/src/components/TimePickerNative/TimePickerNative.test.tsx +117 -0
  218. package/src/components/TimePickerNative/TimePickerNative.tsx +93 -0
  219. package/src/components/Toast/Toast.mdx +134 -0
  220. package/src/components/Toast/Toast.store.ts +280 -0
  221. package/src/components/Toast/Toast.stories.tsx +283 -0
  222. package/src/components/Toast/Toast.test.tsx +784 -0
  223. package/src/components/Toast/Toast.types.ts +98 -0
  224. package/src/components/Toast/ToastContainer.tsx +170 -0
  225. package/src/components/Toast/ToastNotification.module.scss +63 -0
  226. package/src/components/Toast/ToastNotification.tsx +176 -0
  227. package/src/components/Toast/index.ts +4 -0
  228. package/src/components/Toast/toast.ts +102 -0
  229. package/src/components/Toast/useToasts.ts +102 -0
  230. package/src/components/Toggle/Toggle.mdx +51 -0
  231. package/src/components/Toggle/Toggle.module.scss +294 -0
  232. package/src/components/Toggle/Toggle.stories.tsx +128 -0
  233. package/src/components/Toggle/Toggle.test.tsx +308 -0
  234. package/src/components/Toggle/Toggle.tsx +184 -0
  235. package/src/constants/keyCodes.ts +2 -0
  236. package/src/docs/Brands.mdx +153 -0
  237. package/src/docs/Colors.mdx +158 -0
  238. package/src/docs/DesignTokens.mdx +415 -0
  239. package/src/docs/GetStarted.mdx +47 -0
  240. package/src/docs/intro.mdx +12 -0
  241. package/src/fonts/AvenirBold.otf +0 -0
  242. package/src/fonts/AvenirBold.woff +0 -0
  243. package/src/fonts/AvenirBold.woff2 +0 -0
  244. package/src/fonts/AvenirLight.otf +0 -0
  245. package/src/fonts/AvenirLight.woff +0 -0
  246. package/src/fonts/AvenirLight.woff2 +0 -0
  247. package/src/fonts/AvenirRegular.otf +0 -0
  248. package/src/fonts/AvenirRegular.woff +0 -0
  249. package/src/fonts/AvenirRegular.woff2 +0 -0
  250. package/src/fonts/Geist-Bold.otf +0 -0
  251. package/src/fonts/Geist-Bold.woff +0 -0
  252. package/src/fonts/Geist-Bold.woff2 +0 -0
  253. package/src/fonts/Geist-Medium.otf +0 -0
  254. package/src/fonts/Geist-Medium.woff +0 -0
  255. package/src/fonts/Geist-Medium.woff2 +0 -0
  256. package/src/fonts/Geist-Regular.otf +0 -0
  257. package/src/fonts/Geist-Regular.woff +0 -0
  258. package/src/fonts/Geist-Regular.woff2 +0 -0
  259. package/src/fonts/Geist-SemiBold.otf +0 -0
  260. package/src/fonts/Geist-SemiBold.woff +0 -0
  261. package/src/fonts/Geist-SemiBold.woff2 +0 -0
  262. package/src/fonts/GeistMono-Regular.otf +0 -0
  263. package/src/fonts/GeistMono-Regular.woff +0 -0
  264. package/src/fonts/GeistMono-Regular.woff2 +0 -0
  265. package/src/hooks/index.ts +4 -0
  266. package/src/hooks/useBreakpoint/useBreakpoint.mdx +26 -0
  267. package/src/hooks/useBreakpoint/useBreakpoint.stories.tsx +30 -0
  268. package/src/hooks/useBreakpoint/useBreakpoint.test.tsx +19 -0
  269. package/src/hooks/useBreakpoint/useBreakpoint.ts +50 -0
  270. package/src/hooks/useIsomorphicLayoutEffect/useIsomorphicLayouEffect.ts +5 -0
  271. package/src/hooks/useOpenClose/useOpenClose.mdx +15 -0
  272. package/src/hooks/useOpenClose/useOpenClose.stories.tsx +41 -0
  273. package/src/hooks/useOpenClose/useOpenClose.test.tsx +119 -0
  274. package/src/hooks/useOpenClose/useOpenClose.tsx +95 -0
  275. package/src/hooks/useWindowSize/useWindowSize.mdx +25 -0
  276. package/src/hooks/useWindowSize/useWindowSize.stories.tsx +35 -0
  277. package/src/hooks/useWindowSize/useWindowSize.ts +24 -0
  278. package/src/index.ts +48 -0
  279. package/src/lib/cssShorthandToClasses.test.ts +149 -0
  280. package/src/lib/cssShorthandToClasses.ts +133 -0
  281. package/src/lib/doesStringIncludeCssUnit.ts +6 -0
  282. package/src/lib/generateResponsiveClasses.test.ts +24 -0
  283. package/src/lib/generateResponsiveClasses.ts +30 -0
  284. package/src/lib/getAutoCompleteValue.test.ts +27 -0
  285. package/src/lib/getAutoCompleteValue.ts +12 -0
  286. package/src/lib/getColumnKeys.ts +27 -0
  287. package/src/lib/getDimensionCss.test.ts +148 -0
  288. package/src/lib/getDimensionCss.ts +73 -0
  289. package/src/lib/getElementType.test.tsx +42 -0
  290. package/src/lib/getElementType.ts +42 -0
  291. package/src/lib/getFlexCss.test.ts +122 -0
  292. package/src/lib/getFlexCss.ts +67 -0
  293. package/src/lib/index.ts +15 -0
  294. package/src/lib/isFunction.ts +6 -0
  295. package/src/lib/mergeRefs.ts +15 -0
  296. package/src/lib/prefersReducedMotion.ts +12 -0
  297. package/src/lib/react-children-utilities/filter.ts +12 -0
  298. package/src/lib/react-children-utilities/index.ts +1 -0
  299. package/src/lib/reactRouterClickHandler.ts +37 -0
  300. package/src/lib/resolveValue.ts +7 -0
  301. package/src/lib/tokens.ts +139 -0
  302. package/src/modes.ts +8 -0
  303. package/src/styles/animation.scss +152 -0
  304. package/src/styles/cursor.scss +43 -0
  305. package/src/styles/display.scss +119 -0
  306. package/src/styles/flex.scss +453 -0
  307. package/src/styles/fonts.scss +44 -0
  308. package/src/styles/globals/utilities.scss +4 -0
  309. package/src/styles/mixins.scss +14 -0
  310. package/src/styles/overflow.scss +41 -0
  311. package/src/styles/position.scss +45 -0
  312. package/src/styles/reset.scss +108 -0
  313. package/src/styles/text-align.scss +21 -0
  314. package/src/styles/utilities.scss +9 -0
  315. package/src/styles/variables/forms.scss +71 -0
  316. package/src/styles/variables/index.scss +3 -0
  317. package/src/styles/white-space.scss +21 -0
  318. package/src/types/index.ts +201 -0
  319. 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,4 @@
1
+ export * from './ToastContainer';
2
+ export * from './Toast.types';
3
+ export * from './toast';
4
+ export * from './useToasts';
@@ -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
+ };