@cerberus-design/react 0.13.1-next-e3e9e48 → 0.13.1-next-d1e87c5

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 (30) hide show
  1. package/build/legacy/_tsup-dts-rollup.d.cts +52 -27
  2. package/build/legacy/components/ModalHeader.cjs +0 -1
  3. package/build/legacy/components/ModalHeader.cjs.map +1 -1
  4. package/build/legacy/context/confirm-modal.cjs +36 -43
  5. package/build/legacy/context/confirm-modal.cjs.map +1 -1
  6. package/build/legacy/context/cta-modal.cjs +34 -35
  7. package/build/legacy/context/cta-modal.cjs.map +1 -1
  8. package/build/legacy/context/prompt-modal.cjs +0 -1
  9. package/build/legacy/context/prompt-modal.cjs.map +1 -1
  10. package/build/legacy/index.cjs +72 -79
  11. package/build/legacy/index.cjs.map +1 -1
  12. package/build/modern/_tsup-dts-rollup.d.ts +52 -27
  13. package/build/modern/{chunk-KFGI37CO.js → chunk-4D6VIGNQ.js} +38 -44
  14. package/build/modern/chunk-4D6VIGNQ.js.map +1 -0
  15. package/build/modern/{chunk-NCUHRVW2.js → chunk-MMHCX2RG.js} +2 -2
  16. package/build/modern/{chunk-J4LOSTEP.js → chunk-MXMVC3ZQ.js} +36 -36
  17. package/build/modern/{chunk-J4LOSTEP.js.map → chunk-MXMVC3ZQ.js.map} +1 -1
  18. package/build/modern/{chunk-ZFAIE47A.js → chunk-XY6WL55R.js} +1 -2
  19. package/build/modern/{chunk-ZFAIE47A.js.map → chunk-XY6WL55R.js.map} +1 -1
  20. package/build/modern/components/ModalHeader.js +1 -1
  21. package/build/modern/context/confirm-modal.js +2 -2
  22. package/build/modern/context/cta-modal.js +2 -2
  23. package/build/modern/context/prompt-modal.js +2 -2
  24. package/build/modern/index.js +4 -4
  25. package/package.json +2 -2
  26. package/src/components/ModalHeader.tsx +0 -1
  27. package/src/context/confirm-modal.tsx +89 -66
  28. package/src/context/cta-modal.tsx +38 -38
  29. package/build/modern/chunk-KFGI37CO.js.map +0 -1
  30. /package/build/modern/{chunk-NCUHRVW2.js.map → chunk-MMHCX2RG.js.map} +0 -0
@@ -9,6 +9,7 @@ import {
9
9
  useState,
10
10
  type MouseEvent,
11
11
  type PropsWithChildren,
12
+ type ReactNode,
12
13
  } from 'react'
13
14
  import { Portal } from '../components/Portal'
14
15
  import { Button } from '../components/Button'
@@ -23,26 +24,18 @@ import { ModalHeader } from '../components/ModalHeader'
23
24
  import { ModalHeading } from '../components/ModalHeading'
24
25
  import { ModalDescription } from '../components/ModalDescription'
25
26
  import { Avatar } from '../components/Avatar'
27
+ import { HStack, VStack } from '@cerberus/styled-system/jsx'
26
28
 
27
29
  /**
28
30
  * This module provides a context and hook for the confirm modal.
29
31
  * @module
30
32
  */
31
33
 
32
- export interface ShowConfirmModalOptions {
33
- /**
34
- * The kind of confirm modal to show.
35
- * @default 'non-destructive'
36
- */
37
- kind?: 'destructive' | 'non-destructive'
34
+ export interface BaseConfirmOptions {
38
35
  /**
39
36
  * The heading of the confirm modal.
40
37
  */
41
38
  heading: string
42
- /**
43
- * The description of the confirm modal.
44
- */
45
- description?: string
46
39
  /**
47
40
  * The text for the action button.
48
41
  */
@@ -52,6 +45,37 @@ export interface ShowConfirmModalOptions {
52
45
  */
53
46
  cancelText: string
54
47
  }
48
+
49
+ export interface DestructiveConfirmOptions extends BaseConfirmOptions {
50
+ /**
51
+ * The kind of confirm modal to show.
52
+ */
53
+ kind?: 'destructive'
54
+ /**
55
+ * The description of the confirm modal. Can only be a string for destructive confirm modals.
56
+ */
57
+ description?: string
58
+ }
59
+
60
+ export interface NonDestructiveConfirmModalOptions extends BaseConfirmOptions {
61
+ /**
62
+ * The kind of confirm modal to show.
63
+ * @default 'non-destructive'
64
+ */
65
+ kind?: 'non-destructive'
66
+ /**
67
+ * The description of the confirm modal. Can be a ReactNode for non-destructive kind if you need to display text links.
68
+ * @example
69
+ * ```tsx
70
+ * description: <>Use a Fragment because we put the content within a Paragraph tag.</>
71
+ */
72
+ description?: ReactNode
73
+ }
74
+
75
+ export type ShowConfirmModalOptions =
76
+ | NonDestructiveConfirmModalOptions
77
+ | DestructiveConfirmOptions
78
+
55
79
  export type ShowResult =
56
80
  | ((value: boolean | PromiseLike<boolean>) => void)
57
81
  | null
@@ -97,10 +121,11 @@ export function ConfirmModal(
97
121
  const [content, setContent] = useState<ShowConfirmModalOptions | null>(null)
98
122
  const focusTrap = trapFocus(modalRef)
99
123
  const ConfirmIcon = $cerberusIcons.confirmModal
124
+ const kind = content?.kind ?? 'non-destructive'
100
125
 
101
126
  const palette = useMemo(
102
- () => (content?.kind === 'destructive' ? 'danger' : 'action'),
103
- [content],
127
+ () => (kind === 'destructive' ? 'danger' : 'action'),
128
+ [kind],
104
129
  )
105
130
 
106
131
  const handleChoice = useCallback(
@@ -118,7 +143,7 @@ export function ConfirmModal(
118
143
  const handleShow = useCallback(
119
144
  (options: ShowConfirmModalOptions) => {
120
145
  return new Promise<boolean>((resolve) => {
121
- setContent({ ...options, kind: options.kind || 'non-destructive' })
146
+ setContent({ ...options })
122
147
  show()
123
148
  resolveRef.current = resolve
124
149
  })
@@ -139,65 +164,63 @@ export function ConfirmModal(
139
164
 
140
165
  <Portal>
141
166
  <Modal onKeyDown={focusTrap} ref={modalRef}>
142
- <ModalHeader>
143
- <div
144
- className={hstack({
145
- justify: 'center',
146
- w: 'full',
147
- })}
148
- >
149
- <Show
150
- when={palette === 'danger'}
151
- fallback={
167
+ <VStack gap="xl" w="full">
168
+ <ModalHeader>
169
+ <div
170
+ className={hstack({
171
+ justify: 'center',
172
+ w: 'full',
173
+ })}
174
+ >
175
+ <Show
176
+ when={palette === 'danger'}
177
+ fallback={
178
+ <Avatar
179
+ ariaLabel=""
180
+ gradient="charon-light"
181
+ icon={<ConfirmIcon size={24} />}
182
+ src=""
183
+ />
184
+ }
185
+ >
152
186
  <Avatar
153
187
  ariaLabel=""
154
- gradient="charon-light"
188
+ gradient="hades-dark"
155
189
  icon={<ConfirmIcon size={24} />}
156
190
  src=""
157
191
  />
158
- }
192
+ </Show>
193
+ </div>
194
+ <ModalHeading>{content?.heading}</ModalHeading>
195
+ <ModalDescription>{content?.description}</ModalDescription>
196
+ </ModalHeader>
197
+
198
+ <HStack gap="4" w="full">
199
+ <Button
200
+ autoFocus
201
+ className={css({
202
+ w: '1/2',
203
+ })}
204
+ name="confirm"
205
+ onClick={handleChoice}
206
+ palette={palette}
207
+ value="true"
208
+ >
209
+ {content?.actionText}
210
+ </Button>
211
+ <Button
212
+ className={css({
213
+ w: '1/2',
214
+ })}
215
+ name="cancel"
216
+ onClick={handleChoice}
217
+ usage="outlined"
218
+ value="false"
159
219
  >
160
- <Avatar
161
- ariaLabel=""
162
- gradient="hades-dark"
163
- icon={<ConfirmIcon size={24} />}
164
- src=""
165
- />
166
- </Show>
167
- </div>
168
- <ModalHeading>{content?.heading}</ModalHeading>
169
- <ModalDescription>{content?.description}</ModalDescription>
170
- </ModalHeader>
171
-
172
- <div
173
- className={hstack({
174
- gap: '4',
175
- })}
176
- >
177
- <Button
178
- autoFocus
179
- className={css({
180
- w: '1/2',
181
- })}
182
- name="confirm"
183
- onClick={handleChoice}
184
- palette={palette}
185
- value="true"
186
- >
187
- {content?.actionText}
188
- </Button>
189
- <Button
190
- className={css({
191
- w: '1/2',
192
- })}
193
- name="cancel"
194
- onClick={handleChoice}
195
- usage="outlined"
196
- value="false"
197
- >
198
- {content?.cancelText}
199
- </Button>
200
- </div>
220
+ {content?.cancelText}
221
+ </Button>
222
+ </HStack>
223
+ </VStack>
201
224
  </Modal>
202
225
  </Portal>
203
226
  </ConfirmModalContext.Provider>
@@ -155,46 +155,46 @@ export function CTAModal(props: PropsWithChildren<CTAModalProviderProps>) {
155
155
  </IconButton>
156
156
  </span>
157
157
 
158
- <ModalHeader>
159
- <HStack justify="center" w="full">
160
- <Avatar
161
- ariaLabel=""
162
- gradient="charon-light"
163
- icon={
164
- <Show
165
- when={Boolean(confirmIcon)}
166
- fallback={<FallbackIcon size={24} />}
158
+ <VStack gap="xl" w="full">
159
+ <ModalHeader>
160
+ <VStack gap="lg" w="full">
161
+ <Avatar
162
+ ariaLabel=""
163
+ gradient="charon-light"
164
+ icon={
165
+ <Show
166
+ when={Boolean(confirmIcon)}
167
+ fallback={<FallbackIcon size={24} />}
168
+ >
169
+ {confirmIcon}
170
+ </Show>
171
+ }
172
+ src=""
173
+ />
174
+ <ModalHeading>{content?.heading}</ModalHeading>
175
+ <ModalDescription>{content?.description}</ModalDescription>
176
+ </VStack>
177
+ </ModalHeader>
178
+
179
+ <HStack gap="md" w="full">
180
+ <Show when={Boolean(content?.actions?.length)}>
181
+ {content?.actions?.map((action, index) => (
182
+ <Button
183
+ data-index={index}
184
+ className={css({
185
+ w: '1/2',
186
+ })}
187
+ key={index}
188
+ onClick={handleActionClick}
189
+ shape="rounded"
190
+ usage="outlined"
167
191
  >
168
- {confirmIcon}
169
- </Show>
170
- }
171
- src=""
172
- />
192
+ {action.text}
193
+ </Button>
194
+ ))}
195
+ </Show>
173
196
  </HStack>
174
- <VStack gap="lg" w="full">
175
- <ModalHeading>{content?.heading}</ModalHeading>
176
- <ModalDescription>{content?.description}</ModalDescription>
177
- </VStack>
178
- </ModalHeader>
179
-
180
- <HStack gap="md" pt="sm" w="full">
181
- <Show when={Boolean(content?.actions?.length)}>
182
- {content?.actions?.map((action, index) => (
183
- <Button
184
- data-index={index}
185
- className={css({
186
- w: '1/2',
187
- })}
188
- key={index}
189
- onClick={handleActionClick}
190
- shape="rounded"
191
- usage="outlined"
192
- >
193
- {action.text}
194
- </Button>
195
- ))}
196
- </Show>
197
- </HStack>
197
+ </VStack>
198
198
  </Modal>
199
199
  </Portal>
200
200
  </CTAModalContext.Provider>
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/context/confirm-modal.tsx"],"sourcesContent":["'use client'\n\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type MouseEvent,\n type PropsWithChildren,\n} from 'react'\nimport { Portal } from '../components/Portal'\nimport { Button } from '../components/Button'\nimport { css } from '@cerberus/styled-system/css'\nimport { hstack } from '@cerberus/styled-system/patterns'\nimport { $cerberusIcons } from '../config/defineIcons'\nimport { trapFocus } from '../aria-helpers/trap-focus.aria'\nimport { Show } from '../components/Show'\nimport { Modal } from '../components/Modal'\nimport { useModal } from '../hooks/useModal'\nimport { ModalHeader } from '../components/ModalHeader'\nimport { ModalHeading } from '../components/ModalHeading'\nimport { ModalDescription } from '../components/ModalDescription'\nimport { Avatar } from '../components/Avatar'\n\n/**\n * This module provides a context and hook for the confirm modal.\n * @module\n */\n\nexport interface ShowConfirmModalOptions {\n /**\n * The kind of confirm modal to show.\n * @default 'non-destructive'\n */\n kind?: 'destructive' | 'non-destructive'\n /**\n * The heading of the confirm modal.\n */\n heading: string\n /**\n * The description of the confirm modal.\n */\n description?: string\n /**\n * The text for the action button.\n */\n actionText: string\n /**\n * The text for the cancel button.\n */\n cancelText: string\n}\nexport type ShowResult =\n | ((value: boolean | PromiseLike<boolean>) => void)\n | null\n\nexport interface ConfirmModalValue {\n show: (options: ShowConfirmModalOptions) => Promise<boolean>\n}\n\nconst ConfirmModalContext = createContext<ConfirmModalValue | null>(null)\n\nexport interface ConfirmModalProviderProps {}\n\n/**\n * Provides a confirm modal to the app.\n * @see https://cerberus.digitalu.design/react/confirm-modal\n * @example\n * ```tsx\n * // Wrap the Provider around the root of the feature.\n * <ConfirmModal>\n * <SomeFeatureSection />\n * </ConfirmModal>\n *\n * // Use the hook to show the confirm modal.\n * const confirm = useConfirmModal()\n *\n * const handleClick = useCallback(async () => {\n * const userConsent = await confirm.show({\n * heading: 'Add new payment method?',\n * description:\n * 'This will add a new payment method to your account to be billed for future purchases.',\n * actionText: 'Yes, add payment method',\n * cancelText: 'No, cancel',\n * })\n * setConsent(userConsent)\n * }, [confirm])\n * ```\n */\nexport function ConfirmModal(\n props: PropsWithChildren<ConfirmModalProviderProps>,\n) {\n const { modalRef, show, close } = useModal()\n const resolveRef = useRef<ShowResult>(null)\n const [content, setContent] = useState<ShowConfirmModalOptions | null>(null)\n const focusTrap = trapFocus(modalRef)\n const ConfirmIcon = $cerberusIcons.confirmModal\n\n const palette = useMemo(\n () => (content?.kind === 'destructive' ? 'danger' : 'action'),\n [content],\n )\n\n const handleChoice = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n const target = e.currentTarget as HTMLButtonElement\n if (target.value === 'true') {\n resolveRef.current?.(true)\n }\n resolveRef.current?.(false)\n close()\n },\n [close],\n )\n\n const handleShow = useCallback(\n (options: ShowConfirmModalOptions) => {\n return new Promise<boolean>((resolve) => {\n setContent({ ...options, kind: options.kind || 'non-destructive' })\n show()\n resolveRef.current = resolve\n })\n },\n [show],\n )\n\n const value = useMemo(\n () => ({\n show: handleShow,\n }),\n [handleShow],\n )\n\n return (\n <ConfirmModalContext.Provider value={value}>\n {props.children}\n\n <Portal>\n <Modal onKeyDown={focusTrap} ref={modalRef}>\n <ModalHeader>\n <div\n className={hstack({\n justify: 'center',\n w: 'full',\n })}\n >\n <Show\n when={palette === 'danger'}\n fallback={\n <Avatar\n ariaLabel=\"\"\n gradient=\"charon-light\"\n icon={<ConfirmIcon size={24} />}\n src=\"\"\n />\n }\n >\n <Avatar\n ariaLabel=\"\"\n gradient=\"hades-dark\"\n icon={<ConfirmIcon size={24} />}\n src=\"\"\n />\n </Show>\n </div>\n <ModalHeading>{content?.heading}</ModalHeading>\n <ModalDescription>{content?.description}</ModalDescription>\n </ModalHeader>\n\n <div\n className={hstack({\n gap: '4',\n })}\n >\n <Button\n autoFocus\n className={css({\n w: '1/2',\n })}\n name=\"confirm\"\n onClick={handleChoice}\n palette={palette}\n value=\"true\"\n >\n {content?.actionText}\n </Button>\n <Button\n className={css({\n w: '1/2',\n })}\n name=\"cancel\"\n onClick={handleChoice}\n usage=\"outlined\"\n value=\"false\"\n >\n {content?.cancelText}\n </Button>\n </div>\n </Modal>\n </Portal>\n </ConfirmModalContext.Provider>\n )\n}\n\nexport function useConfirmModal(): ConfirmModalValue {\n const context = useContext(ConfirmModalContext)\n if (context === null) {\n throw new Error(\n 'useConfirmModal must be used within a ConfirmModal Provider',\n )\n }\n return context\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGP,SAAS,WAAW;AACpB,SAAS,cAAc;AA8Hb,SAagB,KAbhB;AA/EV,IAAM,sBAAsB,cAAwC,IAAI;AA6BjE,SAAS,aACd,OACA;AACA,QAAM,EAAE,UAAU,MAAM,MAAM,IAAI,SAAS;AAC3C,QAAM,aAAa,OAAmB,IAAI;AAC1C,QAAM,CAAC,SAAS,UAAU,IAAI,SAAyC,IAAI;AAC3E,QAAM,YAAY,UAAU,QAAQ;AACpC,QAAM,cAAc,eAAe;AAEnC,QAAM,UAAU;AAAA,IACd,MAAO,SAAS,SAAS,gBAAgB,WAAW;AAAA,IACpD,CAAC,OAAO;AAAA,EACV;AAEA,QAAM,eAAe;AAAA,IACnB,CAAC,MAAqC;AACpC,YAAM,SAAS,EAAE;AACjB,UAAI,OAAO,UAAU,QAAQ;AAC3B,mBAAW,UAAU,IAAI;AAAA,MAC3B;AACA,iBAAW,UAAU,KAAK;AAC1B,YAAM;AAAA,IACR;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,aAAa;AAAA,IACjB,CAAC,YAAqC;AACpC,aAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,mBAAW,EAAE,GAAG,SAAS,MAAM,QAAQ,QAAQ,kBAAkB,CAAC;AAClE,aAAK;AACL,mBAAW,UAAU;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,IACA,CAAC,IAAI;AAAA,EACP;AAEA,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL,MAAM;AAAA,IACR;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AAEA,SACE,qBAAC,oBAAoB,UAApB,EAA6B,OAC3B;AAAA,UAAM;AAAA,IAEP,oBAAC,UACC,+BAAC,SAAM,WAAW,WAAW,KAAK,UAChC;AAAA,2BAAC,eACC;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,OAAO;AAAA,cAChB,SAAS;AAAA,cACT,GAAG;AAAA,YACL,CAAC;AAAA,YAED;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM,YAAY;AAAA,gBAClB,UACE;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAU;AAAA,oBACV,UAAS;AAAA,oBACT,MAAM,oBAAC,eAAY,MAAM,IAAI;AAAA,oBAC7B,KAAI;AAAA;AAAA,gBACN;AAAA,gBAGF;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAU;AAAA,oBACV,UAAS;AAAA,oBACT,MAAM,oBAAC,eAAY,MAAM,IAAI;AAAA,oBAC7B,KAAI;AAAA;AAAA,gBACN;AAAA;AAAA,YACF;AAAA;AAAA,QACF;AAAA,QACA,oBAAC,gBAAc,mBAAS,SAAQ;AAAA,QAChC,oBAAC,oBAAkB,mBAAS,aAAY;AAAA,SAC1C;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,OAAO;AAAA,YAChB,KAAK;AAAA,UACP,CAAC;AAAA,UAED;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAS;AAAA,gBACT,WAAW,IAAI;AAAA,kBACb,GAAG;AAAA,gBACL,CAAC;AAAA,gBACD,MAAK;AAAA,gBACL,SAAS;AAAA,gBACT;AAAA,gBACA,OAAM;AAAA,gBAEL,mBAAS;AAAA;AAAA,YACZ;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW,IAAI;AAAA,kBACb,GAAG;AAAA,gBACL,CAAC;AAAA,gBACD,MAAK;AAAA,gBACL,SAAS;AAAA,gBACT,OAAM;AAAA,gBACN,OAAM;AAAA,gBAEL,mBAAS;AAAA;AAAA,YACZ;AAAA;AAAA;AAAA,MACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;AAEO,SAAS,kBAAqC;AACnD,QAAM,UAAU,WAAW,mBAAmB;AAC9C,MAAI,YAAY,MAAM;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}