@cerberus-design/react 0.13.1-next-e3e9e48 → 0.13.1-next-8c8a5ee
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/build/legacy/_tsup-dts-rollup.d.cts +52 -27
- package/build/legacy/components/ModalHeader.cjs +0 -1
- package/build/legacy/components/ModalHeader.cjs.map +1 -1
- package/build/legacy/context/confirm-modal.cjs +36 -43
- package/build/legacy/context/confirm-modal.cjs.map +1 -1
- package/build/legacy/context/cta-modal.cjs +34 -35
- package/build/legacy/context/cta-modal.cjs.map +1 -1
- package/build/legacy/context/notification-center.cjs.map +1 -1
- package/build/legacy/context/prompt-modal.cjs +0 -1
- package/build/legacy/context/prompt-modal.cjs.map +1 -1
- package/build/legacy/index.cjs +72 -79
- package/build/legacy/index.cjs.map +1 -1
- package/build/modern/_tsup-dts-rollup.d.ts +52 -27
- package/build/modern/{chunk-KFGI37CO.js → chunk-4D6VIGNQ.js} +38 -44
- package/build/modern/chunk-4D6VIGNQ.js.map +1 -0
- package/build/modern/{chunk-KUT2YEEQ.js → chunk-5KHGTLFM.js} +1 -1
- package/build/modern/chunk-5KHGTLFM.js.map +1 -0
- package/build/modern/{chunk-NCUHRVW2.js → chunk-MMHCX2RG.js} +2 -2
- package/build/modern/{chunk-J4LOSTEP.js → chunk-MXMVC3ZQ.js} +36 -36
- package/build/modern/{chunk-J4LOSTEP.js.map → chunk-MXMVC3ZQ.js.map} +1 -1
- package/build/modern/{chunk-ZFAIE47A.js → chunk-XY6WL55R.js} +1 -2
- package/build/modern/{chunk-ZFAIE47A.js.map → chunk-XY6WL55R.js.map} +1 -1
- package/build/modern/components/ModalHeader.js +1 -1
- package/build/modern/context/confirm-modal.js +2 -2
- package/build/modern/context/cta-modal.js +2 -2
- package/build/modern/context/notification-center.js +1 -1
- package/build/modern/context/prompt-modal.js +2 -2
- package/build/modern/index.js +5 -5
- package/package.json +2 -2
- package/src/components/ModalHeader.tsx +0 -1
- package/src/context/confirm-modal.tsx +89 -66
- package/src/context/cta-modal.tsx +38 -38
- package/src/context/notification-center.tsx +1 -0
- package/build/modern/chunk-KFGI37CO.js.map +0 -1
- package/build/modern/chunk-KUT2YEEQ.js.map +0 -1
- /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
|
|
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
|
-
() => (
|
|
103
|
-
[
|
|
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
|
|
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
|
-
<
|
|
143
|
-
<
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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="
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
<
|
|
159
|
-
<
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
{
|
|
169
|
-
</
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/>
|
|
192
|
+
{action.text}
|
|
193
|
+
</Button>
|
|
194
|
+
))}
|
|
195
|
+
</Show>
|
|
173
196
|
</HStack>
|
|
174
|
-
|
|
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":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/context/notification-center.tsx"],"sourcesContent":["'use client'\n\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n type MouseEvent,\n type PropsWithChildren,\n type ReactNode,\n} from 'react'\nimport { Show } from '../components/Show'\nimport { NotificationHeading } from '../components/NotificationHeading'\nimport { NotificationDescription } from '../components/NotificationDescription'\nimport { Notification } from '../components/Notification'\nimport { animateIn, vstack } from '@cerberus/styled-system/patterns'\nimport { Portal, type PortalProps } from '../components/Portal'\nimport { notification } from '@cerberus/styled-system/recipes'\nimport { Button } from '../components/Button'\nimport { cx } from '@cerberus/styled-system/css'\n\n/**\n * This module provides a context and hook for notifications.\n * @module\n */\n\nexport interface NotifyOptions {\n /**\n * The palette of the notification.\n * @default 'info'\n */\n palette: 'info' | 'success' | 'warning' | 'danger'\n /**\n * The heading of the notification.\n */\n heading: string\n /**\n * The unique id of the notification.\n */\n id?: string\n /**\n * The description of the notification.\n */\n description?: ReactNode\n /**\n * The action to take when the notification is closed\n */\n onClose?: () => void\n}\n\nexport interface NotificationsValue {\n notify: (options: NotifyOptions) => void\n}\n\nconst NotificationsContext = createContext<NotificationsValue | null>(null)\n\nexport interface NotificationsProviderProps extends PortalProps {}\n\n/**\n * Provides a notification center to the app.\n * @see https://cerberus.digitalu.design/react/notification\n * @example\n * ```tsx\n * // Wrap the Provider around the root of the feature.\n * <Notifications>\n * <SomeFeatureSection />\n * </Notifications>\n *\n * // Use the hook to show a notification.\n * const notify = useNotifications()\n *\n * const handleClick = useCallback(() => {\n * notify({\n * palette: 'info',\n * heading: 'New feature!',\n * description: 'We have added a new feature to the app.',\n * })\n * }, [notify])\n * ```\n */\nexport function NotificationCenter(\n props: PropsWithChildren<NotificationsProviderProps>,\n) {\n const [activeNotifications, setActiveNotifications] = useState<\n NotifyOptions[]\n >([])\n const styles = notification()\n\n const handleNotify = useCallback((options: NotifyOptions) => {\n setActiveNotifications((prev) => {\n const id = `${options.palette}:${prev.length + 1}`\n return [...prev, { ...options, id }]\n })\n }, [])\n\n const handleClose = useCallback((e: MouseEvent<HTMLButtonElement>) => {\n const target = e.currentTarget as HTMLButtonElement\n setActiveNotifications((prev) => {\n const item = prev.find((option) => option.id === target.value)\n if (item?.onClose) item.onClose()\n return prev.filter((option) => option.id !== target.value)\n })\n }, [])\n\n const handleCloseAll = useCallback(() => {\n setActiveNotifications((prev) => {\n prev.forEach((item) => {\n if (item.onClose) item.onClose()\n })\n return []\n })\n }, [])\n\n const value = useMemo(\n () => ({\n notify: handleNotify,\n }),\n [handleNotify],\n )\n\n // For some reason, the vstack pattern alignItems is not registering here.\n // So we are forcing it with the style prop.\n\n return (\n <NotificationsContext.Provider value={value}>\n {props.children}\n\n <Show when={activeNotifications.length > 0}>\n <Portal container={props.container}>\n <div className={styles.center}>\n <Show when={activeNotifications.length >= 4}>\n <Button\n className={cx(styles.closeAll, animateIn())}\n onClick={handleCloseAll}\n palette=\"action\"\n shape=\"rounded\"\n size=\"sm\"\n usage=\"ghost\"\n >\n Close all\n </Button>\n </Show>\n <div\n className={vstack({\n alignItems: 'flex-end',\n gap: '4',\n })}\n style={{\n alignItems: 'flex-end',\n }}\n >\n {activeNotifications.map((option) => (\n <MatchNotification\n key={option.id}\n {...option}\n onClose={handleClose}\n />\n ))}\n </div>\n </div>\n </Portal>\n </Show>\n </NotificationsContext.Provider>\n )\n}\n\ninterface MatchNotificationProps extends Omit<NotifyOptions, 'onClose'> {\n onClose: (e: MouseEvent<HTMLButtonElement>) => void\n key: string | undefined\n}\n\nfunction MatchNotification(props: MatchNotificationProps) {\n const { palette, id, onClose, heading, description } = props\n\n switch (palette) {\n case 'success':\n return (\n <Notification\n id={id!}\n key={id}\n onClose={onClose}\n open\n palette=\"success\"\n >\n <NotificationHeading palette=\"success\">{heading}</NotificationHeading>\n <NotificationDescription palette=\"success\">\n {description}\n </NotificationDescription>\n </Notification>\n )\n\n case 'warning':\n return (\n <Notification\n id={id!}\n key={id}\n onClose={onClose}\n open\n palette=\"warning\"\n >\n <NotificationHeading palette=\"warning\">{heading}</NotificationHeading>\n <NotificationDescription palette=\"warning\">\n {description}\n </NotificationDescription>\n </Notification>\n )\n\n case 'danger':\n return (\n <Notification id={id!} key={id} onClose={onClose} open palette=\"danger\">\n <NotificationHeading palette=\"danger\">{heading}</NotificationHeading>\n <NotificationDescription palette=\"danger\">\n {description}\n </NotificationDescription>\n </Notification>\n )\n\n case 'info':\n default:\n return (\n <Notification id={id!} key={id} onClose={onClose} open palette=\"info\">\n <NotificationHeading palette=\"info\">{heading}</NotificationHeading>\n <NotificationDescription palette=\"info\">\n {description}\n </NotificationDescription>\n </Notification>\n )\n }\n}\n\n/**\n * The hook to use the NotificationCenter.\n * @returns The notify method to trigger a notification.\n * @example\n * ```tsx\n * const {notify} = useNotificationCenter()\n * notify({\n * palette: 'info',\n * heading: 'New feature',\n * description: 'We have added a new feature to the app.',\n * })\n * ```\n */\nexport function useNotificationCenter(): NotificationsValue {\n const context = useContext(NotificationsContext)\n if (!context) {\n throw new Error(\n 'useNotificationCenter must be used within a NotificationsProvider',\n )\n }\n return context\n}\n"],"mappings":"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","names":[]}
|
|
File without changes
|