@cerberus-design/react 0.14.2 → 0.15.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 (241) hide show
  1. package/build/legacy/_tsup-dts-rollup.d.cts +374 -80
  2. package/build/legacy/components/Accordion.cjs +4 -187
  3. package/build/legacy/components/Accordion.cjs.map +1 -1
  4. package/build/legacy/components/Accordion.client.cjs +64 -0
  5. package/build/legacy/components/Accordion.client.cjs.map +1 -0
  6. package/build/legacy/components/AccordionItemGroup.cjs +41 -185
  7. package/build/legacy/components/AccordionItemGroup.cjs.map +1 -1
  8. package/build/legacy/components/Admonition.cjs +89 -239
  9. package/build/legacy/components/Admonition.cjs.map +1 -1
  10. package/build/legacy/components/Admonition.client.cjs +219 -0
  11. package/build/legacy/components/Admonition.client.cjs.map +1 -0
  12. package/build/legacy/components/AnimatingUploadIcon.cjs.map +1 -1
  13. package/build/legacy/components/Avatar.cjs +23 -174
  14. package/build/legacy/components/Avatar.cjs.map +1 -1
  15. package/build/legacy/components/Checkbox.cjs +22 -168
  16. package/build/legacy/components/Checkbox.cjs.map +1 -1
  17. package/build/legacy/components/DatePicker.client.cjs +129 -272
  18. package/build/legacy/components/DatePicker.client.cjs.map +1 -1
  19. package/build/legacy/components/DatePicker.server.cjs +17 -271
  20. package/build/legacy/components/DatePicker.server.cjs.map +1 -1
  21. package/build/legacy/components/Dialog.cjs +86 -0
  22. package/build/legacy/components/Dialog.cjs.map +1 -0
  23. package/build/legacy/components/Dialog.client.cjs +95 -0
  24. package/build/legacy/components/Dialog.client.cjs.map +1 -0
  25. package/build/legacy/components/FileStatus.cjs +62 -212
  26. package/build/legacy/components/FileStatus.cjs.map +1 -1
  27. package/build/legacy/components/FileUploader.cjs +29 -180
  28. package/build/legacy/components/FileUploader.cjs.map +1 -1
  29. package/build/legacy/components/Input.cjs +20 -172
  30. package/build/legacy/components/Input.cjs.map +1 -1
  31. package/build/legacy/components/Modal.cjs.map +1 -1
  32. package/build/legacy/components/ModalDescription.cjs.map +1 -1
  33. package/build/legacy/components/ModalHeader.cjs.map +1 -1
  34. package/build/legacy/components/ModalHeading.cjs.map +1 -1
  35. package/build/legacy/components/Notification.cjs +26 -176
  36. package/build/legacy/components/Notification.cjs.map +1 -1
  37. package/build/legacy/components/Select.cjs +26 -178
  38. package/build/legacy/components/Select.cjs.map +1 -1
  39. package/build/legacy/components/Tag.cjs +16 -167
  40. package/build/legacy/components/Tag.cjs.map +1 -1
  41. package/build/legacy/components/Toggle.cjs +20 -172
  42. package/build/legacy/components/Toggle.cjs.map +1 -1
  43. package/build/legacy/config/defineIcons.cjs +3 -39
  44. package/build/legacy/config/defineIcons.cjs.map +1 -1
  45. package/build/legacy/config/{cerbIcons.cjs → index.cjs} +22 -29
  46. package/build/legacy/config/index.cjs.map +1 -0
  47. package/build/legacy/config/types.cjs +19 -0
  48. package/build/legacy/config/types.cjs.map +1 -0
  49. package/build/legacy/context/cerberus.cjs +46 -0
  50. package/build/legacy/context/cerberus.cjs.map +1 -0
  51. package/build/legacy/context/confirm-modal.cjs +105 -321
  52. package/build/legacy/context/confirm-modal.cjs.map +1 -1
  53. package/build/legacy/context/cta-modal.cjs +137 -336
  54. package/build/legacy/context/cta-modal.cjs.map +1 -1
  55. package/build/legacy/context/notification-center/store.cjs +66 -0
  56. package/build/legacy/context/notification-center/store.cjs.map +1 -0
  57. package/build/legacy/context/notification-center/types.cjs +19 -0
  58. package/build/legacy/context/notification-center/types.cjs.map +1 -0
  59. package/build/legacy/context/notification-center.cjs +142 -249
  60. package/build/legacy/context/notification-center.cjs.map +1 -1
  61. package/build/legacy/context/prompt-modal.cjs +185 -388
  62. package/build/legacy/context/prompt-modal.cjs.map +1 -1
  63. package/build/legacy/index.cjs +1577 -1456
  64. package/build/legacy/index.cjs.map +1 -1
  65. package/build/modern/_tsup-dts-rollup.d.ts +374 -80
  66. package/build/modern/{chunk-BVCXVZAF.js → chunk-25HMVHLT.js} +6 -5
  67. package/build/modern/chunk-25HMVHLT.js.map +1 -0
  68. package/build/modern/chunk-36N4527B.js +1 -0
  69. package/build/modern/chunk-5EWCH7AI.js +82 -0
  70. package/build/modern/chunk-5EWCH7AI.js.map +1 -0
  71. package/build/modern/chunk-5SNLQZYP.js +25 -0
  72. package/build/modern/chunk-5SNLQZYP.js.map +1 -0
  73. package/build/modern/chunk-6BN3XKQF.js +42 -0
  74. package/build/modern/chunk-6BN3XKQF.js.map +1 -0
  75. package/build/modern/{chunk-2UXE5PDG.js → chunk-7NN3SJ7W.js} +1 -1
  76. package/build/modern/chunk-7NN3SJ7W.js.map +1 -0
  77. package/build/modern/{chunk-KWJ5FKX7.js → chunk-BAWZBF5Q.js} +5 -3
  78. package/build/modern/chunk-BAWZBF5Q.js.map +1 -0
  79. package/build/modern/{chunk-6BH5J5GF.js → chunk-BHB56M7S.js} +31 -46
  80. package/build/modern/chunk-BHB56M7S.js.map +1 -0
  81. package/build/modern/{chunk-HKJMLWVP.js → chunk-EDARV2EI.js} +5 -4
  82. package/build/modern/chunk-EDARV2EI.js.map +1 -0
  83. package/build/modern/{chunk-5OVH3INN.js → chunk-FGCO27TC.js} +25 -53
  84. package/build/modern/chunk-FGCO27TC.js.map +1 -0
  85. package/build/modern/{chunk-PVIMOXSO.js → chunk-GCQMH4QA.js} +5 -4
  86. package/build/modern/chunk-GCQMH4QA.js.map +1 -0
  87. package/build/modern/chunk-GITT5645.js +20 -0
  88. package/build/modern/chunk-GITT5645.js.map +1 -0
  89. package/build/modern/{chunk-TJCFYL5W.js → chunk-IGHMP4WA.js} +1 -20
  90. package/build/modern/chunk-IGHMP4WA.js.map +1 -0
  91. package/build/modern/chunk-ISCJ542I.js +82 -0
  92. package/build/modern/chunk-ISCJ542I.js.map +1 -0
  93. package/build/modern/{chunk-HVKM54BA.js → chunk-IW3LIRDG.js} +1 -1
  94. package/build/modern/chunk-IW3LIRDG.js.map +1 -0
  95. package/build/modern/chunk-JAROS4Q3.js +180 -0
  96. package/build/modern/chunk-JAROS4Q3.js.map +1 -0
  97. package/build/modern/{chunk-BE4EOU2P.js → chunk-JIRW4XOJ.js} +1 -1
  98. package/build/modern/chunk-JIRW4XOJ.js.map +1 -0
  99. package/build/modern/chunk-KDDPAJMR.js +9 -0
  100. package/build/modern/chunk-KDDPAJMR.js.map +1 -0
  101. package/build/modern/{chunk-XOROL3JY.js → chunk-KKHL3ZO4.js} +5 -4
  102. package/build/modern/chunk-KKHL3ZO4.js.map +1 -0
  103. package/build/modern/{chunk-U36UZJGZ.js → chunk-MZ3UCDUL.js} +5 -4
  104. package/build/modern/chunk-MZ3UCDUL.js.map +1 -0
  105. package/build/modern/{chunk-T2JOPPGL.js → chunk-N24COMHJ.js} +11 -4
  106. package/build/modern/chunk-N24COMHJ.js.map +1 -0
  107. package/build/modern/chunk-NJSETNRL.js +68 -0
  108. package/build/modern/chunk-NJSETNRL.js.map +1 -0
  109. package/build/modern/{chunk-XY6WL55R.js → chunk-NUMM4TNC.js} +1 -1
  110. package/build/modern/chunk-NUMM4TNC.js.map +1 -0
  111. package/build/modern/{chunk-FXLLRVAM.js → chunk-O6LFWUHI.js} +8 -6
  112. package/build/modern/chunk-O6LFWUHI.js.map +1 -0
  113. package/build/modern/{chunk-JJZQGR7A.js → chunk-RDRD6ACD.js} +9 -6
  114. package/build/modern/chunk-RDRD6ACD.js.map +1 -0
  115. package/build/modern/{chunk-XXWR7UGH.js → chunk-SD3OVTHT.js} +75 -103
  116. package/build/modern/chunk-SD3OVTHT.js.map +1 -0
  117. package/build/modern/chunk-TFL56AYR.js +56 -0
  118. package/build/modern/chunk-TFL56AYR.js.map +1 -0
  119. package/build/modern/chunk-V3M3ZOQI.js +38 -0
  120. package/build/modern/chunk-V3M3ZOQI.js.map +1 -0
  121. package/build/modern/{chunk-QK7R2XJM.js → chunk-XQICKZH4.js} +6 -5
  122. package/build/modern/chunk-XQICKZH4.js.map +1 -0
  123. package/build/modern/chunk-XZGXRRSQ.js +31 -0
  124. package/build/modern/chunk-XZGXRRSQ.js.map +1 -0
  125. package/build/modern/{chunk-KPUYKHLW.js → chunk-YKKNWILF.js} +71 -15
  126. package/build/modern/chunk-YKKNWILF.js.map +1 -0
  127. package/build/modern/{chunk-Q7BRMIBR.js → chunk-ZL6ZITLA.js} +1 -1
  128. package/build/modern/chunk-ZL6ZITLA.js.map +1 -0
  129. package/build/modern/components/Accordion.client.js +9 -0
  130. package/build/modern/components/Accordion.client.js.map +1 -0
  131. package/build/modern/components/Accordion.js +1 -7
  132. package/build/modern/components/AccordionItemGroup.js +4 -6
  133. package/build/modern/components/Admonition.client.js +11 -0
  134. package/build/modern/components/Admonition.client.js.map +1 -0
  135. package/build/modern/components/Admonition.js +4 -6
  136. package/build/modern/components/AnimatingUploadIcon.js +1 -1
  137. package/build/modern/components/Avatar.js +3 -5
  138. package/build/modern/components/Checkbox.js +2 -5
  139. package/build/modern/components/DatePicker.client.js +8 -7
  140. package/build/modern/components/DatePicker.server.js +3 -13
  141. package/build/modern/components/Dialog.client.js +10 -0
  142. package/build/modern/components/Dialog.client.js.map +1 -0
  143. package/build/modern/components/Dialog.js +24 -0
  144. package/build/modern/components/Dialog.js.map +1 -0
  145. package/build/modern/components/FileStatus.js +3 -6
  146. package/build/modern/components/FileUploader.js +3 -6
  147. package/build/modern/components/Input.js +2 -5
  148. package/build/modern/components/Modal.js +1 -1
  149. package/build/modern/components/ModalDescription.js +1 -1
  150. package/build/modern/components/ModalHeader.js +1 -1
  151. package/build/modern/components/ModalHeading.js +1 -1
  152. package/build/modern/components/Notification.js +2 -5
  153. package/build/modern/components/Select.js +2 -5
  154. package/build/modern/components/Tag.js +3 -5
  155. package/build/modern/components/Toggle.js +2 -5
  156. package/build/modern/config/defineIcons.js +2 -5
  157. package/build/modern/config/index.js +14 -0
  158. package/build/modern/config/index.js.map +1 -0
  159. package/build/modern/config/types.js +2 -0
  160. package/build/modern/config/types.js.map +1 -0
  161. package/build/modern/context/cerberus.js +10 -0
  162. package/build/modern/context/cerberus.js.map +1 -0
  163. package/build/modern/context/confirm-modal.js +5 -13
  164. package/build/modern/context/cta-modal.js +7 -14
  165. package/build/modern/context/notification-center/store.js +15 -0
  166. package/build/modern/context/notification-center/store.js.map +1 -0
  167. package/build/modern/context/notification-center/types.js +1 -0
  168. package/build/modern/context/notification-center/types.js.map +1 -0
  169. package/build/modern/context/notification-center.js +5 -7
  170. package/build/modern/context/prompt-modal.js +8 -15
  171. package/build/modern/index.js +111 -75
  172. package/build/modern/index.js.map +1 -1
  173. package/package.json +4 -8
  174. package/src/components/Accordion.client.tsx +46 -0
  175. package/src/components/Accordion.tsx +0 -37
  176. package/src/components/AccordionItemGroup.tsx +1 -1
  177. package/src/components/Admonition.client.tsx +73 -0
  178. package/src/components/Admonition.tsx +1 -70
  179. package/src/components/AnimatingUploadIcon.tsx +3 -3
  180. package/src/components/Avatar.tsx +5 -2
  181. package/src/components/Checkbox.tsx +10 -3
  182. package/src/components/DatePicker.client.tsx +111 -15
  183. package/src/components/DatePicker.server.tsx +2 -75
  184. package/src/components/Dialog.client.tsx +39 -0
  185. package/src/components/Dialog.tsx +165 -0
  186. package/src/components/FileStatus.tsx +5 -3
  187. package/src/components/FileUploader.tsx +3 -2
  188. package/src/components/Input.tsx +4 -2
  189. package/src/components/Modal.tsx +1 -16
  190. package/src/components/ModalDescription.tsx +1 -8
  191. package/src/components/ModalHeader.tsx +1 -10
  192. package/src/components/ModalHeading.tsx +1 -8
  193. package/src/components/Notification.tsx +9 -4
  194. package/src/components/Select.tsx +5 -2
  195. package/src/components/Tag.tsx +5 -2
  196. package/src/components/Toggle.tsx +4 -3
  197. package/src/config/defineIcons.ts +28 -16
  198. package/src/config/index.ts +28 -0
  199. package/src/config/types.ts +42 -0
  200. package/src/context/cerberus.tsx +44 -0
  201. package/src/context/confirm-modal.tsx +44 -42
  202. package/src/context/cta-modal.tsx +25 -38
  203. package/src/context/notification-center/store.ts +88 -0
  204. package/src/context/notification-center/types.ts +28 -0
  205. package/src/context/notification-center.tsx +81 -46
  206. package/src/context/prompt-modal.tsx +101 -103
  207. package/src/index.ts +15 -8
  208. package/build/legacy/config/cerbIcons.cjs.map +0 -1
  209. package/build/modern/chunk-2UXE5PDG.js.map +0 -1
  210. package/build/modern/chunk-5OVH3INN.js.map +0 -1
  211. package/build/modern/chunk-6BH5J5GF.js.map +0 -1
  212. package/build/modern/chunk-BC5SZDYY.js +0 -132
  213. package/build/modern/chunk-BC5SZDYY.js.map +0 -1
  214. package/build/modern/chunk-BE4EOU2P.js.map +0 -1
  215. package/build/modern/chunk-BVCXVZAF.js.map +0 -1
  216. package/build/modern/chunk-CRII2HNX.js +0 -55
  217. package/build/modern/chunk-CRII2HNX.js.map +0 -1
  218. package/build/modern/chunk-CVTON5DQ.js +0 -162
  219. package/build/modern/chunk-CVTON5DQ.js.map +0 -1
  220. package/build/modern/chunk-FXLLRVAM.js.map +0 -1
  221. package/build/modern/chunk-HKJMLWVP.js.map +0 -1
  222. package/build/modern/chunk-HVKM54BA.js.map +0 -1
  223. package/build/modern/chunk-JJZQGR7A.js.map +0 -1
  224. package/build/modern/chunk-KPUYKHLW.js.map +0 -1
  225. package/build/modern/chunk-KWJ5FKX7.js.map +0 -1
  226. package/build/modern/chunk-PVIMOXSO.js.map +0 -1
  227. package/build/modern/chunk-Q7BRMIBR.js.map +0 -1
  228. package/build/modern/chunk-QK7R2XJM.js.map +0 -1
  229. package/build/modern/chunk-QMF5ZNDG.js +0 -27
  230. package/build/modern/chunk-QMF5ZNDG.js.map +0 -1
  231. package/build/modern/chunk-QQOWWMZ3.js +0 -138
  232. package/build/modern/chunk-QQOWWMZ3.js.map +0 -1
  233. package/build/modern/chunk-T2JOPPGL.js.map +0 -1
  234. package/build/modern/chunk-TJCFYL5W.js.map +0 -1
  235. package/build/modern/chunk-U36UZJGZ.js.map +0 -1
  236. package/build/modern/chunk-XOROL3JY.js.map +0 -1
  237. package/build/modern/chunk-XXWR7UGH.js.map +0 -1
  238. package/build/modern/chunk-XY6WL55R.js.map +0 -1
  239. package/build/modern/config/cerbIcons.js +0 -9
  240. package/src/config/cerbIcons.ts +0 -73
  241. /package/build/modern/{config/cerbIcons.js.map → chunk-36N4527B.js.map} +0 -0
@@ -11,20 +11,19 @@ import {
11
11
  type PropsWithChildren,
12
12
  type ReactNode,
13
13
  } from 'react'
14
- import { Portal } from '../components/Portal'
15
14
  import { Button } from '../components/Button'
16
15
  import { css } from '@cerberus/styled-system/css'
17
- import { hstack } from '@cerberus/styled-system/patterns'
18
- import { $cerberusIcons } from '../config/defineIcons'
19
- import { trapFocus } from '../aria-helpers/trap-focus.aria'
20
16
  import { Show } from '../components/Show'
21
- import { Modal } from '../components/Modal'
22
- import { useModal } from '../hooks/useModal'
23
- import { ModalHeader } from '../components/ModalHeader'
24
- import { ModalHeading } from '../components/ModalHeading'
25
- import { ModalDescription } from '../components/ModalDescription'
26
17
  import { Avatar } from '../components/Avatar'
27
18
  import { HStack, VStack } from '@cerberus/styled-system/jsx'
19
+ import { useCerberusContext } from './cerberus'
20
+ import {
21
+ Dialog,
22
+ DialogCloseTrigger,
23
+ DialogDescription,
24
+ DialogHeading,
25
+ DialogProvider,
26
+ } from '../components/Dialog'
28
27
 
29
28
  /**
30
29
  * This module provides a context and hook for the confirm modal.
@@ -116,13 +115,14 @@ export type ConfirmModalProviderProps = PropsWithChildren<unknown>
116
115
  export function ConfirmModal(
117
116
  props: PropsWithChildren<ConfirmModalProviderProps>,
118
117
  ) {
119
- const { modalRef, show, close } = useModal()
120
- const resolveRef = useRef<ShowResult>(null)
118
+ const [open, setOpen] = useState<boolean>(false)
121
119
  const [content, setContent] = useState<ShowConfirmModalOptions | null>(null)
122
- const focusTrap = trapFocus(modalRef)
123
- const ConfirmIcon = $cerberusIcons.confirmModal
120
+ const resolveRef = useRef<ShowResult>(null)
124
121
  const kind = content?.kind ?? 'non-destructive'
125
122
 
123
+ const { icons } = useCerberusContext()
124
+ const { confirmModal: ConfirmIcon } = icons
125
+
126
126
  const palette = useMemo(
127
127
  () => (kind === 'destructive' ? 'danger' : 'action'),
128
128
  [kind],
@@ -135,20 +135,20 @@ export function ConfirmModal(
135
135
  resolveRef.current?.(true)
136
136
  }
137
137
  resolveRef.current?.(false)
138
- close()
138
+ setOpen(false)
139
139
  },
140
- [close],
140
+ [setOpen],
141
141
  )
142
142
 
143
143
  const handleShow = useCallback(
144
144
  (options: ShowConfirmModalOptions) => {
145
145
  return new Promise<boolean>((resolve) => {
146
146
  setContent({ ...options })
147
- show()
147
+ setOpen(true)
148
148
  resolveRef.current = resolve
149
149
  })
150
150
  },
151
- [show],
151
+ [setOpen, setContent],
152
152
  )
153
153
 
154
154
  const value = useMemo(
@@ -162,15 +162,15 @@ export function ConfirmModal(
162
162
  <ConfirmModalContext.Provider value={value}>
163
163
  {props.children}
164
164
 
165
- <Portal>
166
- <Modal onKeyDown={focusTrap} ref={modalRef}>
165
+ <DialogProvider open={open} onOpenChange={(e) => setOpen(e.open)}>
166
+ <Dialog size="sm">
167
167
  <VStack gap="xl" w="full">
168
- <ModalHeader>
169
- <div
170
- className={hstack({
171
- justify: 'center',
172
- w: 'full',
173
- })}
168
+ <VStack alignItems="flex-start" gap="md" w="full">
169
+ <HStack
170
+ alignSelf="center"
171
+ justify="center"
172
+ paddingBlockEnd="md"
173
+ w="full"
174
174
  >
175
175
  <Show
176
176
  when={palette === 'danger'}
@@ -190,10 +190,10 @@ export function ConfirmModal(
190
190
  src=""
191
191
  />
192
192
  </Show>
193
- </div>
194
- <ModalHeading>{content?.heading}</ModalHeading>
195
- <ModalDescription>{content?.description}</ModalDescription>
196
- </ModalHeader>
193
+ </HStack>
194
+ <DialogHeading>{content?.heading}</DialogHeading>
195
+ <DialogDescription>{content?.description}</DialogDescription>
196
+ </VStack>
197
197
 
198
198
  <HStack gap="4" w="full">
199
199
  <Button
@@ -208,21 +208,23 @@ export function ConfirmModal(
208
208
  >
209
209
  {content?.actionText}
210
210
  </Button>
211
- <Button
212
- className={css({
213
- w: '1/2',
214
- })}
215
- name="cancel"
216
- onClick={handleChoice}
217
- usage="outlined"
218
- value="false"
219
- >
220
- {content?.cancelText}
221
- </Button>
211
+ <DialogCloseTrigger asChild>
212
+ <Button
213
+ className={css({
214
+ w: '1/2',
215
+ })}
216
+ name="cancel"
217
+ onClick={handleChoice}
218
+ usage="outlined"
219
+ value="false"
220
+ >
221
+ {content?.cancelText}
222
+ </Button>
223
+ </DialogCloseTrigger>
222
224
  </HStack>
223
225
  </VStack>
224
- </Modal>
225
- </Portal>
226
+ </Dialog>
227
+ </DialogProvider>
226
228
  </ConfirmModalContext.Provider>
227
229
  )
228
230
  }
@@ -11,21 +11,20 @@ import {
11
11
  type PropsWithChildren,
12
12
  type ReactNode,
13
13
  } from 'react'
14
- import { Portal } from '../components/Portal'
15
14
  import { Button } from '../components/Button'
16
- import { $cerberusIcons } from '../config/defineIcons'
17
- import { trapFocus } from '../aria-helpers/trap-focus.aria'
18
15
  import { Show } from '../components/Show'
19
- import { Modal } from '../components/Modal'
20
- import { useModal } from '../hooks/useModal'
21
- import { ModalHeader } from '../components/ModalHeader'
22
- import { ModalHeading } from '../components/ModalHeading'
23
- import { ModalDescription } from '../components/ModalDescription'
24
16
  import { Avatar } from '../components/Avatar'
17
+ import { useCerberusContext } from './cerberus'
25
18
  import { HStack } from '@cerberus/styled-system/jsx'
26
- import { IconButton } from '../components/IconButton'
27
19
  import { css } from '@cerberus/styled-system/css'
28
20
  import { VStack } from '@cerberus/styled-system/jsx'
21
+ import {
22
+ Dialog,
23
+ DialogDescription,
24
+ DialogHeading,
25
+ DialogProvider,
26
+ } from '../components/Dialog'
27
+ import { DialogCloseIconTrigger } from '../components/Dialog.client'
29
28
 
30
29
  /**
31
30
  * This module provides a context and hook for the cta modal.
@@ -95,12 +94,12 @@ export type CTAModalProviderProps = PropsWithChildren<unknown>
95
94
  * ```
96
95
  */
97
96
  export function CTAModal(props: PropsWithChildren<CTAModalProviderProps>) {
98
- const { modalRef, show, close } = useModal()
97
+ const [open, setOpen] = useState<boolean>(false)
99
98
  const [content, setContent] = useState<ShowCTAModalOptions | null>(null)
100
- const focusTrap = trapFocus(modalRef)
101
- const FallbackIcon = $cerberusIcons.confirmModal
102
99
  const confirmIcon = content?.icon
103
- const { close: CloseIcon } = $cerberusIcons
100
+
101
+ const { icons } = useCerberusContext()
102
+ const { confirmModal: FallbackIcon } = icons
104
103
 
105
104
  const handleShow = useCallback(
106
105
  (options: ShowCTAModalOptions) => {
@@ -111,9 +110,9 @@ export function CTAModal(props: PropsWithChildren<CTAModalProviderProps>) {
111
110
  )
112
111
  }
113
112
  setContent({ ...options })
114
- show()
113
+ setOpen(true)
115
114
  },
116
- [show],
115
+ [setOpen],
117
116
  )
118
117
 
119
118
  const handleActionClick = useCallback(
@@ -122,9 +121,9 @@ export function CTAModal(props: PropsWithChildren<CTAModalProviderProps>) {
122
121
  const action = content?.actions[Number(index)]
123
122
  const { onClick } = action || {}
124
123
  onClick?.(event)
125
- close()
124
+ setOpen(false)
126
125
  },
127
- [content, close],
126
+ [content, setOpen],
128
127
  )
129
128
 
130
129
  const value = useMemo(
@@ -138,24 +137,12 @@ export function CTAModal(props: PropsWithChildren<CTAModalProviderProps>) {
138
137
  <CTAModalContext.Provider value={value}>
139
138
  {props.children}
140
139
 
141
- <Portal>
142
- <Modal onKeyDown={focusTrap} ref={modalRef}>
143
- <span
144
- className={css({
145
- padding: 'md',
146
- position: 'absolute',
147
- right: 0,
148
- top: 0,
149
- zIndex: 'decorator',
150
- })}
151
- >
152
- <IconButton ariaLabel="Close modal" onClick={close}>
153
- <CloseIcon />
154
- </IconButton>
155
- </span>
140
+ <DialogProvider open={open} onOpenChange={(e) => setOpen(e.open)}>
141
+ <Dialog size="sm">
142
+ <DialogCloseIconTrigger />
156
143
 
157
144
  <VStack gap="xl" w="full">
158
- <ModalHeader>
145
+ <VStack alignItems="flex-start" gap="md" w="full">
159
146
  <VStack gap="lg" w="full">
160
147
  <Avatar
161
148
  ariaLabel=""
@@ -170,10 +157,10 @@ export function CTAModal(props: PropsWithChildren<CTAModalProviderProps>) {
170
157
  }
171
158
  src=""
172
159
  />
173
- <ModalHeading>{content?.heading}</ModalHeading>
174
- <ModalDescription>{content?.description}</ModalDescription>
160
+ <DialogHeading>{content?.heading}</DialogHeading>
161
+ <DialogDescription>{content?.description}</DialogDescription>
175
162
  </VStack>
176
- </ModalHeader>
163
+ </VStack>
177
164
 
178
165
  <HStack gap="md" w="full">
179
166
  <Show when={Boolean(content?.actions?.length)}>
@@ -194,8 +181,8 @@ export function CTAModal(props: PropsWithChildren<CTAModalProviderProps>) {
194
181
  </Show>
195
182
  </HStack>
196
183
  </VStack>
197
- </Modal>
198
- </Portal>
184
+ </Dialog>
185
+ </DialogProvider>
199
186
  </CTAModalContext.Provider>
200
187
  )
201
188
  }
@@ -0,0 +1,88 @@
1
+ import type { Dispatch } from 'react'
2
+ import type {
3
+ AddNotifyAction,
4
+ ClearNotifyAction,
5
+ NotificationsStore,
6
+ RemoveNotifyAction,
7
+ UpdateNotifyAction,
8
+ } from './types'
9
+
10
+ /**
11
+ * This module contains the reducer store and actions for the notification
12
+ * center.
13
+ * @module notification-center/store
14
+ */
15
+
16
+ /**
17
+ * The reducer for the notification center.
18
+ * @param state An array of notifications.
19
+ * @param action An action to take on the notifications.
20
+ * @returns The new state of the notifications.
21
+ */
22
+ export function notificationCenterReducer(
23
+ state: NotificationsStore,
24
+ action:
25
+ | AddNotifyAction
26
+ | RemoveNotifyAction
27
+ | UpdateNotifyAction
28
+ | ClearNotifyAction,
29
+ ): NotificationsStore {
30
+ switch (action.type) {
31
+ case 'ADD_NOTIFICATION':
32
+ return [...state, action.payload]
33
+ case 'REMOVE_NOTIFICATION':
34
+ return state.filter((n) => n.id !== action.payload.id)
35
+ case 'UPDATE_NOTIFICATION':
36
+ return state.map((n) =>
37
+ n.id === action.payload.id ? { ...n, ...action.payload } : n,
38
+ )
39
+ case 'CLEAR_NOTIFICATIONS':
40
+ return []
41
+ default:
42
+ return state
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Adds a notification to the notification center.
48
+ * @param dispatch The dispatch function.
49
+ * @param options The notification options.
50
+ */
51
+ export function addNotification(
52
+ dispatch: Dispatch<AddNotifyAction>,
53
+ options: AddNotifyAction['payload'],
54
+ ) {
55
+ dispatch({ type: 'ADD_NOTIFICATION', payload: { ...options } })
56
+ }
57
+
58
+ /**
59
+ * Removes a notification from the notification center.
60
+ * @param dispatch The dispatch function.
61
+ * @param id The id of the notification to remove.
62
+ */
63
+ export function removeNotification(
64
+ dispatch: Dispatch<RemoveNotifyAction>,
65
+ id: RemoveNotifyAction['payload']['id'],
66
+ ) {
67
+ dispatch({ type: 'REMOVE_NOTIFICATION', payload: { id } })
68
+ }
69
+
70
+ /**
71
+ * Updates a notification in the notification center.
72
+ * @param dispatch The dispatch function.
73
+ * @param options The notification options.
74
+ */
75
+ export function updateNotificationState(
76
+ dispatch: Dispatch<UpdateNotifyAction>,
77
+ options: UpdateNotifyAction['payload'],
78
+ ) {
79
+ dispatch({ type: 'UPDATE_NOTIFICATION', payload: { ...options } })
80
+ }
81
+
82
+ /**
83
+ * Clears the notification state.
84
+ * @param dispatch The dispatch function.
85
+ */
86
+ export function clearNotificationState(dispatch: Dispatch<ClearNotifyAction>) {
87
+ dispatch({ type: 'CLEAR_NOTIFICATIONS' })
88
+ }
@@ -0,0 +1,28 @@
1
+ import type { NotifyOptions } from '../notification-center'
2
+
3
+ export type NotificationsStore = NotificationOption[]
4
+
5
+ export type NotificationOption = NotifyOptions & {
6
+ state: 'open' | 'closed'
7
+ }
8
+
9
+ // Actions
10
+
11
+ export type AddNotifyAction = {
12
+ type: 'ADD_NOTIFICATION'
13
+ payload: NotificationOption
14
+ }
15
+
16
+ export type RemoveNotifyAction = {
17
+ type: 'REMOVE_NOTIFICATION'
18
+ payload: { id: Required<NotificationOption['id']> }
19
+ }
20
+
21
+ export type UpdateNotifyAction = {
22
+ type: 'UPDATE_NOTIFICATION'
23
+ payload: {
24
+ id: Required<NotificationOption['id']>
25
+ } & Partial<NotificationOption>
26
+ }
27
+
28
+ export type ClearNotifyAction = { type: 'CLEAR_NOTIFICATIONS' }
@@ -5,7 +5,7 @@ import {
5
5
  useCallback,
6
6
  useContext,
7
7
  useMemo,
8
- useState,
8
+ useReducer,
9
9
  type MouseEvent,
10
10
  type PropsWithChildren,
11
11
  type ReactNode,
@@ -19,6 +19,13 @@ import { Portal, type PortalProps } from '../components/Portal'
19
19
  import { notification } from '@cerberus/styled-system/recipes'
20
20
  import { Button } from '../components/Button'
21
21
  import { cx } from '@cerberus/styled-system/css'
22
+ import {
23
+ addNotification,
24
+ clearNotificationState,
25
+ notificationCenterReducer,
26
+ removeNotification,
27
+ updateNotificationState,
28
+ } from './notification-center/store'
22
29
 
23
30
  /**
24
31
  * This module provides a context and hook for notifications.
@@ -55,7 +62,13 @@ export interface NotificationsValue {
55
62
 
56
63
  const NotificationsContext = createContext<NotificationsValue | null>(null)
57
64
 
58
- export type NotificationsProviderProps = PortalProps
65
+ export type NotificationsProviderProps = PortalProps & {
66
+ /**
67
+ * The duration in milliseconds to show the notification.
68
+ * @default 6000
69
+ */
70
+ duration?: number
71
+ }
59
72
 
60
73
  /**
61
74
  * Provides a notification center to the app.
@@ -82,35 +95,58 @@ export type NotificationsProviderProps = PortalProps
82
95
  export function NotificationCenter(
83
96
  props: PropsWithChildren<NotificationsProviderProps>,
84
97
  ) {
85
- const [activeNotifications, setActiveNotifications] = useState<
86
- NotifyOptions[]
87
- >([])
98
+ const [state, dispatch] = useReducer(notificationCenterReducer, [])
88
99
  const styles = notification()
89
100
 
90
- const handleNotify = useCallback((options: NotifyOptions) => {
91
- setActiveNotifications((prev) => {
92
- const id = `${options.palette}:${prev.length + 1}`
93
- return [...prev, { ...options, id }]
94
- })
95
- }, [])
96
-
97
- const handleClose = useCallback((e: MouseEvent<HTMLButtonElement>) => {
98
- const target = e.currentTarget as HTMLButtonElement
99
- setActiveNotifications((prev) => {
100
- const item = prev.find((option) => option.id === target.value)
101
- if (item?.onClose) item.onClose()
102
- return prev.filter((option) => option.id !== target.value)
103
- })
104
- }, [])
101
+ const timeout = useMemo<number>(
102
+ () => props.duration || 6000,
103
+ [props.duration],
104
+ )
105
105
 
106
- const handleCloseAll = useCallback(() => {
107
- setActiveNotifications((prev) => {
108
- prev.forEach((item) => {
109
- if (item.onClose) item.onClose()
106
+ const closeNotification = useCallback(
107
+ (id: string) => {
108
+ updateNotificationState(dispatch, {
109
+ id,
110
+ state: 'closed',
111
+ })
112
+ window.setTimeout(() => {
113
+ removeNotification(dispatch, id)
114
+ }, 150)
115
+ },
116
+ [dispatch],
117
+ )
118
+
119
+ const handleNotify = useCallback(
120
+ (options: NotifyOptions) => {
121
+ const id = `${options.palette}:${state.length + 1}`
122
+ addNotification(dispatch, {
123
+ ...options,
124
+ id,
125
+ state: 'open',
110
126
  })
111
- return []
127
+
128
+ window.setTimeout(() => {
129
+ closeNotification(id)
130
+ }, timeout)
131
+ },
132
+ [dispatch, state, timeout, closeNotification],
133
+ )
134
+
135
+ const handleClose = useCallback(
136
+ (e: MouseEvent<HTMLButtonElement>) => {
137
+ const target = e.currentTarget as HTMLButtonElement
138
+ closeNotification(target.value)
139
+ },
140
+ [closeNotification],
141
+ )
142
+
143
+ const handleCloseAll = useCallback(() => {
144
+ state.forEach((item) => {
145
+ if (item.onClose) item.onClose()
112
146
  })
113
- }, [])
147
+ // we don't want to animate out for this one
148
+ clearNotificationState(dispatch)
149
+ }, [state, dispatch])
114
150
 
115
151
  const value = useMemo(
116
152
  () => ({
@@ -126,10 +162,10 @@ export function NotificationCenter(
126
162
  <NotificationsContext.Provider value={value}>
127
163
  {props.children}
128
164
 
129
- <Show when={activeNotifications.length > 0}>
165
+ <Show when={state.length > 0}>
130
166
  <Portal container={props.container}>
131
167
  <div className={styles.center}>
132
- <Show when={activeNotifications.length >= 4}>
168
+ <Show when={state.length >= 4}>
133
169
  <Button
134
170
  className={cx(styles.closeAll, animateIn())}
135
171
  onClick={handleCloseAll}
@@ -151,11 +187,12 @@ export function NotificationCenter(
151
187
  alignItems: 'flex-end',
152
188
  }}
153
189
  >
154
- {activeNotifications.map((option) => (
190
+ {state.map((option) => (
155
191
  <MatchNotification
156
192
  key={option.id}
157
193
  {...option}
158
194
  onClose={handleClose}
195
+ open={option.state}
159
196
  />
160
197
  ))}
161
198
  </div>
@@ -167,23 +204,27 @@ export function NotificationCenter(
167
204
  }
168
205
 
169
206
  interface MatchNotificationProps extends Omit<NotifyOptions, 'onClose'> {
207
+ open: 'open' | 'closed'
170
208
  onClose: (e: MouseEvent<HTMLButtonElement>) => void
171
209
  key: string | undefined
172
210
  }
173
211
 
174
212
  function MatchNotification(props: MatchNotificationProps) {
175
- const { palette, id, onClose, heading, description } = props
213
+ const { palette, id, onClose, heading, description, open } = props
214
+ const sharedProps = useMemo(
215
+ () => ({
216
+ id: id!,
217
+ open: true,
218
+ onClose,
219
+ 'data-state': open,
220
+ }),
221
+ [id, open, onClose],
222
+ )
176
223
 
177
224
  switch (palette) {
178
225
  case 'success':
179
226
  return (
180
- <Notification
181
- id={id!}
182
- key={id}
183
- onClose={onClose}
184
- open
185
- palette="success"
186
- >
227
+ <Notification {...sharedProps} palette="success">
187
228
  <NotificationHeading palette="success">{heading}</NotificationHeading>
188
229
  <NotificationDescription palette="success">
189
230
  {description}
@@ -193,13 +234,7 @@ function MatchNotification(props: MatchNotificationProps) {
193
234
 
194
235
  case 'warning':
195
236
  return (
196
- <Notification
197
- id={id!}
198
- key={id}
199
- onClose={onClose}
200
- open
201
- palette="warning"
202
- >
237
+ <Notification {...sharedProps} palette="warning">
203
238
  <NotificationHeading palette="warning">{heading}</NotificationHeading>
204
239
  <NotificationDescription palette="warning">
205
240
  {description}
@@ -209,7 +244,7 @@ function MatchNotification(props: MatchNotificationProps) {
209
244
 
210
245
  case 'danger':
211
246
  return (
212
- <Notification id={id!} key={id} onClose={onClose} open palette="danger">
247
+ <Notification {...sharedProps} palette="danger">
213
248
  <NotificationHeading palette="danger">{heading}</NotificationHeading>
214
249
  <NotificationDescription palette="danger">
215
250
  {description}
@@ -220,7 +255,7 @@ function MatchNotification(props: MatchNotificationProps) {
220
255
  case 'info':
221
256
  default:
222
257
  return (
223
- <Notification id={id!} key={id} onClose={onClose} open palette="info">
258
+ <Notification {...sharedProps} palette="info">
224
259
  <NotificationHeading palette="info">{heading}</NotificationHeading>
225
260
  <NotificationDescription palette="info">
226
261
  {description}