lucent-ui 0.18.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +95 -38
- package/dist/index.d.ts +55 -0
- package/dist/index.js +2150 -1617
- package/dist-server/src/components/molecules/Toast/Toast.manifest.js +233 -0
- package/package.json +1 -1
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
export const COMPONENT_MANIFEST = {
|
|
2
|
+
id: 'toast',
|
|
3
|
+
name: 'Toast',
|
|
4
|
+
tier: 'molecule',
|
|
5
|
+
domain: 'neutral',
|
|
6
|
+
specVersion: '0.1',
|
|
7
|
+
description: 'Ephemeral notification system with auto-dismiss, cascading card-stack, ' +
|
|
8
|
+
'hover-to-expand, action buttons/links, and animated enter/exit transitions. ' +
|
|
9
|
+
'Comprises ToastProvider (context + portal), useToast hook, and toast card rendering.',
|
|
10
|
+
designIntent: 'Toast provides ephemeral feedback for actions that don\'t require the user to navigate away or ' +
|
|
11
|
+
'acknowledge inline. It fills the gap between Alert (persistent, inline) and Modal (blocking). ' +
|
|
12
|
+
'\n\n' +
|
|
13
|
+
'**Architecture**: ToastProvider wraps the app and manages a queue of toast entries. It renders ' +
|
|
14
|
+
'toasts into a React portal (document.body by default, configurable via portalContainer) so they ' +
|
|
15
|
+
'float above all page content regardless of stacking contexts. The useToast() hook exposes an ' +
|
|
16
|
+
'imperative `toast()` function that returns a dismissible id, and a `dismiss()` function. ' +
|
|
17
|
+
'\n\n' +
|
|
18
|
+
'**Positioning**: The viewport is fixed-positioned with a stable anchor edge. For bottom positions, ' +
|
|
19
|
+
'the toast\'s top edge is pinned 120px from the viewport bottom (ANCHOR_INSET_BOTTOM), so taller ' +
|
|
20
|
+
'toasts extend downward toward the screen edge — the top never jumps. For top positions, the top ' +
|
|
21
|
+
'edge is pinned 40px from the viewport top (ANCHOR_INSET), keeping toasts close to the top bar ' +
|
|
22
|
+
'or header area. Both distances are separate constants to allow independent tuning. Six positions ' +
|
|
23
|
+
'are supported: top-left, top-center, top-right, bottom-left, bottom-center, bottom-right. ' +
|
|
24
|
+
'\n\n' +
|
|
25
|
+
'**Cascading stack**: When multiple toasts are active, only the newest (front) toast shows its full ' +
|
|
26
|
+
'content. Older toasts render as empty card shells (border + background, no content) that peek ' +
|
|
27
|
+
'behind the front toast with progressive scaleX reduction and opacity fade. Each shell is ' +
|
|
28
|
+
'height-matched to the front toast via measurement so the peek gaps are perfectly uniform. ' +
|
|
29
|
+
'Up to 3 stacked shells are visible behind the front toast. ' +
|
|
30
|
+
'\n\n' +
|
|
31
|
+
'**Hover to expand**: Hovering the stack smoothly expands all toasts into a full vertical list ' +
|
|
32
|
+
'with content fading in, card heights animating to natural size, and shadows appearing. The ' +
|
|
33
|
+
'expanded stack uses flex column-reverse for bottom positions so older toasts fan upward into ' +
|
|
34
|
+
'the viewport. A 150ms debounced collapse timer prevents flicker when the cursor moves between ' +
|
|
35
|
+
'toasts in the expanded list. ' +
|
|
36
|
+
'\n\n' +
|
|
37
|
+
'**Enter/exit animations**: New toasts slide in from the screen edge with opacity fade and slight ' +
|
|
38
|
+
'scale-up (requestAnimationFrame double-raf triggers the CSS transition). Dismissed toasts slide ' +
|
|
39
|
+
'back toward the edge and fade out over 200ms. ' +
|
|
40
|
+
'\n\n' +
|
|
41
|
+
'**Auto-dismiss**: Each toast auto-dismisses after a configurable duration (default 5s). Pass ' +
|
|
42
|
+
'Infinity to disable. The timer is managed per-toast via useEffect, so each toast dismisses ' +
|
|
43
|
+
'independently. ' +
|
|
44
|
+
'\n\n' +
|
|
45
|
+
'**Actions**: Toasts support an inline action rendered as either a bordered button (default, ' +
|
|
46
|
+
'matching the Undo pattern from Sonner/Google) or an underlined link. Clicking the action fires ' +
|
|
47
|
+
'the callback and auto-dismisses the toast. ' +
|
|
48
|
+
'\n\n' +
|
|
49
|
+
'**Variants**: default (neutral border), success, warning, danger, info — matching Alert\'s ' +
|
|
50
|
+
'semantic token usage. Each variant sets border color and icon color from status tokens. ' +
|
|
51
|
+
'Non-default variants include a built-in 16×16 SVG icon (same as Alert). ' +
|
|
52
|
+
'\n\n' +
|
|
53
|
+
'**Multi-line text**: title is always shown (semibold). Optional description supports newlines ' +
|
|
54
|
+
'via white-space: pre-line. All text uses the Text atom.',
|
|
55
|
+
props: [
|
|
56
|
+
// ─── ToastProvider props ──────────────────────────────────────────
|
|
57
|
+
{
|
|
58
|
+
name: 'position',
|
|
59
|
+
type: 'enum',
|
|
60
|
+
required: false,
|
|
61
|
+
default: 'bottom-right',
|
|
62
|
+
description: 'Screen edge and alignment where the toast stack appears.',
|
|
63
|
+
enumValues: ['top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'duration',
|
|
67
|
+
type: 'number',
|
|
68
|
+
required: false,
|
|
69
|
+
default: '5000',
|
|
70
|
+
description: 'Default auto-dismiss duration in milliseconds. Applies to all toasts unless overridden per-toast.',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'max',
|
|
74
|
+
type: 'number',
|
|
75
|
+
required: false,
|
|
76
|
+
default: '5',
|
|
77
|
+
description: 'Maximum number of toasts in the queue. When exceeded, the oldest toast is dismissed with an exit animation.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'portalContainer',
|
|
81
|
+
type: 'object',
|
|
82
|
+
required: false,
|
|
83
|
+
description: 'DOM element to render the toast portal into. Defaults to document.body. Useful for scoping toasts to a specific container (e.g. inside an iframe or shadow DOM).',
|
|
84
|
+
},
|
|
85
|
+
// ─── ToastOptions (per-toast, passed to toast()) ─────────────────
|
|
86
|
+
{
|
|
87
|
+
name: 'title',
|
|
88
|
+
type: 'string',
|
|
89
|
+
required: true,
|
|
90
|
+
description: 'Primary message line, rendered in semibold. Always visible.',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'description',
|
|
94
|
+
type: 'string',
|
|
95
|
+
required: false,
|
|
96
|
+
description: 'Secondary description text below the title. Supports multi-line via newline characters (\\n). Rendered in secondary color.',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'variant',
|
|
100
|
+
type: 'enum',
|
|
101
|
+
required: false,
|
|
102
|
+
default: 'default',
|
|
103
|
+
description: 'Visual variant controlling border color, icon, and icon color. Matches Alert\'s semantic status tokens.',
|
|
104
|
+
enumValues: ['default', 'success', 'warning', 'danger', 'info'],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'duration (per-toast)',
|
|
108
|
+
type: 'number',
|
|
109
|
+
required: false,
|
|
110
|
+
default: '5000',
|
|
111
|
+
description: 'Override the provider-level auto-dismiss duration for this specific toast. Pass Infinity to disable auto-dismiss.',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'action',
|
|
115
|
+
type: 'object',
|
|
116
|
+
required: false,
|
|
117
|
+
description: 'Inline action rendered beside the dismiss button. Object with { label: string, onClick: () => void, style?: "button" | "link" }. Button style renders a bordered pill button (default). Link style renders an underlined text link. Clicking fires onClick and auto-dismisses the toast.',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'icon',
|
|
121
|
+
type: 'ReactNode',
|
|
122
|
+
required: false,
|
|
123
|
+
description: 'Custom icon to replace the built-in variant icon. Only used when variant is not "default". Pass null to suppress the icon entirely.',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
usageExamples: [
|
|
127
|
+
{
|
|
128
|
+
title: 'Provider setup',
|
|
129
|
+
description: 'Wrap your app with ToastProvider inside LucentProvider. Position and duration are configurable at the provider level.',
|
|
130
|
+
code: `<LucentProvider>
|
|
131
|
+
<ToastProvider position="bottom-right" duration={5000}>
|
|
132
|
+
<App />
|
|
133
|
+
</ToastProvider>
|
|
134
|
+
</LucentProvider>`,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
title: 'Basic toast',
|
|
138
|
+
description: 'Minimal toast with just a title. Uses the default variant (neutral border, no icon).',
|
|
139
|
+
code: `const { toast } = useToast();
|
|
140
|
+
toast({ title: 'Changes saved' });`,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
title: 'Success with description',
|
|
144
|
+
description: 'Two-line toast with a success variant. The description supports newlines via \\n.',
|
|
145
|
+
code: `toast({
|
|
146
|
+
title: 'Order placed!',
|
|
147
|
+
description: 'You\\'ll receive a confirmation email shortly.',
|
|
148
|
+
variant: 'success',
|
|
149
|
+
});`,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
title: 'Danger with undo button',
|
|
153
|
+
description: 'Destructive action feedback with an inline undo button. Clicking the action auto-dismisses.',
|
|
154
|
+
code: `toast({
|
|
155
|
+
title: 'Message deleted',
|
|
156
|
+
variant: 'danger',
|
|
157
|
+
action: {
|
|
158
|
+
label: 'Undo',
|
|
159
|
+
onClick: () => restoreMessage(id),
|
|
160
|
+
},
|
|
161
|
+
});`,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
title: 'Action as link',
|
|
165
|
+
description: 'Action rendered as an underlined link instead of a bordered button.',
|
|
166
|
+
code: `toast({
|
|
167
|
+
title: 'Event created',
|
|
168
|
+
description: 'Sunday, December 03, 2023 at 9:00 AM',
|
|
169
|
+
action: {
|
|
170
|
+
label: 'View',
|
|
171
|
+
style: 'link',
|
|
172
|
+
onClick: () => navigate('/events/123'),
|
|
173
|
+
},
|
|
174
|
+
});`,
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
title: 'Multi-line description',
|
|
178
|
+
description: 'Description with embedded newlines rendered via white-space: pre-line.',
|
|
179
|
+
code: `toast({
|
|
180
|
+
title: 'Approaching limit',
|
|
181
|
+
description: 'You have used 80% of your monthly quota.\\nConsider upgrading your plan.',
|
|
182
|
+
variant: 'warning',
|
|
183
|
+
});`,
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
title: 'Persistent (no auto-dismiss)',
|
|
187
|
+
description: 'Pass Infinity as duration to keep the toast visible until manually dismissed.',
|
|
188
|
+
code: `toast({
|
|
189
|
+
title: 'Upload in progress',
|
|
190
|
+
description: 'Please do not close this tab.',
|
|
191
|
+
duration: Infinity,
|
|
192
|
+
});`,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
title: 'Programmatic dismiss',
|
|
196
|
+
description: 'toast() returns an id that can be passed to dismiss() to remove it programmatically.',
|
|
197
|
+
code: `const { toast, dismiss } = useToast();
|
|
198
|
+
const id = toast({ title: 'Processing...', duration: Infinity });
|
|
199
|
+
// Later:
|
|
200
|
+
dismiss(id);`,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
title: 'Custom icon',
|
|
204
|
+
description: 'Override the built-in variant icon with a custom SVG.',
|
|
205
|
+
code: `toast({
|
|
206
|
+
title: 'Synced',
|
|
207
|
+
variant: 'success',
|
|
208
|
+
icon: <CloudSyncIcon />,
|
|
209
|
+
});`,
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
compositionGraph: [
|
|
213
|
+
{ componentId: 'text', componentName: 'Text', role: 'Renders title and description text with correct typography', required: true },
|
|
214
|
+
{ componentId: 'icon', componentName: 'Icon (built-in SVGs)', role: 'Status variant icons (info, success, warning, danger)', required: false },
|
|
215
|
+
],
|
|
216
|
+
accessibility: {
|
|
217
|
+
role: 'status',
|
|
218
|
+
ariaAttributes: ['aria-live', 'aria-hidden', 'aria-label'],
|
|
219
|
+
keyboardInteractions: [
|
|
220
|
+
'Tab: focuses the dismiss button and action button within the toast',
|
|
221
|
+
'Enter/Space: activates the focused dismiss or action button',
|
|
222
|
+
'Escape: no default behavior (consider adding dismiss-on-escape if needed)',
|
|
223
|
+
],
|
|
224
|
+
notes: 'Each toast uses role="status" with aria-live="polite" so screen readers announce the content ' +
|
|
225
|
+
'without interrupting the current task. This is appropriate for non-urgent status messages like ' +
|
|
226
|
+
'"Changes saved" or "Profile updated". For truly urgent errors that must interrupt the user, ' +
|
|
227
|
+
'consider wrapping the ToastProvider output in a role="alert" region or using the Alert component ' +
|
|
228
|
+
'instead. The dismiss button carries aria-label="Dismiss" for screen reader clarity. Stacked ' +
|
|
229
|
+
'toasts behind the front toast are marked aria-hidden="true" to prevent screen readers from ' +
|
|
230
|
+
'announcing duplicate or hidden content. When the stack expands on hover, aria-hidden is removed ' +
|
|
231
|
+
'so all toasts become accessible.',
|
|
232
|
+
},
|
|
233
|
+
};
|