aural-ui 3.0.7 → 4.1.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/components/aspect-ratio/AspectRatio.stories.tsx +290 -1199
- package/dist/components/avatar/Avatar.stories.tsx +235 -237
- package/dist/components/badge/Badge.stories.tsx +379 -116
- package/dist/components/banner/Banner.stories.tsx +445 -391
- package/dist/components/breadcrumb/Breadcrumb.stories.tsx +453 -199
- package/dist/components/button/Button.stories.tsx +585 -230
- package/dist/components/button/index.tsx +7 -7
- package/dist/components/card/Card.stories.tsx +619 -301
- package/dist/components/char-count/CharCount.stories.tsx +350 -248
- package/dist/components/checkbox/Checkbox.stories.tsx +309 -167
- package/dist/components/chip/Chip.stories.tsx +362 -168
- package/dist/components/circular-loader/CircularLoader.stories.tsx +221 -620
- package/dist/components/clamp-lines/ClampLines.stories.tsx +246 -117
- package/dist/components/collapsible/Collapsible.stories.tsx +391 -252
- package/dist/components/command/Command.stories.tsx +533 -856
- package/dist/components/dialog/Dialog.stories.tsx +505 -949
- package/dist/components/divider/Divider.stories.tsx +265 -502
- package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
- package/dist/components/drawer/Drawer.stories.tsx +659 -993
- package/dist/components/drawer/index.tsx +3 -3
- package/dist/components/dropdown/Dropdown.stories.tsx +643 -1018
- package/dist/components/form/Form.stories.tsx +560 -274
- package/dist/components/helper-text/HelperText.stories.tsx +199 -200
- package/dist/components/hover-card/HoverCard.stories.tsx +318 -1221
- package/dist/components/icon-button/IconButton.stories.tsx +837 -194
- package/dist/components/if-else/if-else.stories.tsx +370 -83
- package/dist/components/input/Input.stories.tsx +436 -368
- package/dist/components/label/Label.stories.tsx +156 -154
- package/dist/components/list/List.stories.tsx +485 -822
- package/dist/components/marquee/Marquee.stories.tsx +356 -694
- package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -410
- package/dist/components/overlay/Overlay.stories.tsx +452 -818
- package/dist/components/overlay/index.tsx +4 -4
- package/dist/components/pagination/Pagination.stories.tsx +721 -210
- package/dist/components/popover/Popover.stories.tsx +484 -873
- package/dist/components/radio/Radio.stories.tsx +432 -124
- package/dist/components/resizable/Resizable.stories.tsx +496 -752
- package/dist/components/scroll-area/ScrollArea.stories.tsx +384 -1006
- package/dist/components/search/Search.stories.tsx +314 -575
- package/dist/components/select/Select.stories.tsx +684 -787
- package/dist/components/sheet/Sheet.stories.tsx +671 -936
- package/dist/components/skelton/Skelton.stories.tsx +230 -764
- package/dist/components/slider/Slider.stories.tsx +384 -737
- package/dist/components/stepper/Stepper.stories.tsx +371 -514
- package/dist/components/switch/Switch.stories.tsx +461 -208
- package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
- package/dist/components/table/Table.stories.tsx +770 -914
- package/dist/components/tabs/Tabs.stories.tsx +459 -1400
- package/dist/components/tag/Tag.stories.tsx +714 -542
- package/dist/components/textarea/TextArea.stories.tsx +621 -562
- package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -148
- package/dist/components/toast/Toast.stories.tsx +452 -1333
- package/dist/components/toggle/Toggle.stories.tsx +488 -909
- package/dist/components/tooltip/Tooltip.stories.tsx +344 -1372
- package/dist/components/typography/Typography.stories.tsx +406 -89
- package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
- package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
- package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
- package/dist/icons/Icons.stories.tsx +0 -12
- package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +226 -1013
- package/dist/icons/alert-icon/AlertIcon.stories.tsx +109 -929
- package/dist/icons/all-icons.tsx +124 -87
- package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +140 -971
- package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +148 -888
- package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +135 -1019
- package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +137 -953
- package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +138 -997
- package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +136 -942
- package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +148 -1092
- package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +146 -1211
- package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +126 -615
- package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +144 -1164
- package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +167 -985
- package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +122 -1179
- package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +124 -1168
- package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +119 -850
- package/dist/icons/camera-icon/CameraIcon.stories.tsx +112 -1213
- package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +117 -934
- package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +160 -961
- package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +163 -961
- package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +144 -942
- package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +129 -966
- package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +147 -964
- package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +145 -975
- package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +150 -1142
- package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +114 -461
- package/dist/icons/coin-icon/CoinIcon.stories.tsx +124 -1322
- package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +117 -1318
- package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +114 -903
- package/dist/icons/command-icon/CommandIcon.stories.tsx +127 -1042
- package/dist/icons/copy-icon/CopyIcon.stories.tsx +123 -962
- package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +147 -999
- package/dist/icons/cross-icon/CrossIcon.stories.tsx +139 -960
- package/dist/icons/download-icon/DownloadIcon.stories.tsx +126 -820
- package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +124 -1031
- package/dist/icons/email-icon/EmailIcon.stories.tsx +115 -936
- package/dist/icons/expand-icon/ExpandIcon.stories.tsx +112 -1111
- package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +144 -1025
- package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +143 -1036
- package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +127 -1011
- package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +126 -1056
- package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +125 -614
- package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +119 -1050
- package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +169 -989
- package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +115 -1145
- package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +115 -1122
- package/dist/icons/globe-icon/GlobeIcon.stories.tsx +130 -313
- package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +145 -940
- package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +119 -1174
- package/dist/icons/head-icon/HeadIcon.stories.tsx +111 -916
- package/dist/icons/heart-icon/HeartIcon.stories.tsx +120 -1019
- package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +119 -683
- package/dist/icons/image-icon/ImageIcon.stories.tsx +105 -1121
- package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +111 -1192
- package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +136 -1256
- package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +159 -962
- package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +161 -1385
- package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +124 -972
- package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +119 -948
- package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +119 -942
- package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +108 -1215
- package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +154 -1517
- package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +110 -1188
- package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +119 -678
- package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +123 -1184
- package/dist/icons/message-icon/MessageIcon.stories.tsx +114 -538
- package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +116 -1158
- package/dist/icons/moon-icon/MoonIcon.stories.tsx +120 -536
- package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +109 -1184
- package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +115 -1134
- package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +119 -971
- package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +111 -1100
- package/dist/icons/notes-icon/NotesIcon.stories.tsx +119 -1101
- package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +109 -1111
- package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +122 -684
- package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +113 -954
- package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +112 -877
- package/dist/icons/pause-icon/PauseIcon.stories.tsx +113 -1000
- package/dist/icons/pencil-icon/PencilIcon.stories.tsx +115 -1070
- package/dist/icons/phone-icon/PhoneIcon.stories.tsx +115 -978
- package/dist/icons/plus-icon/PlusIcon.stories.tsx +106 -1093
- package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +107 -829
- package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +102 -469
- package/dist/icons/search-icon/SearchIcon.stories.tsx +111 -1124
- package/dist/icons/setting-icon/SettingIcon.stories.tsx +107 -970
- package/dist/icons/share-icon/ShareIcon.stories.tsx +120 -1025
- package/dist/icons/shield-icon/ShieldIcon.stories.tsx +117 -931
- package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +137 -1104
- package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +172 -982
- package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +164 -983
- package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +105 -958
- package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +158 -580
- package/dist/icons/spinner-gradient-icon/index.tsx +6 -1
- package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +158 -587
- package/dist/icons/spinner-solid-icon/index.tsx +6 -1
- package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +146 -682
- package/dist/icons/spinner-solid-neutral-icon/index.tsx +1 -1
- package/dist/icons/star-icon/StarIcon.stories.tsx +124 -904
- package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +112 -964
- package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +116 -852
- package/dist/icons/sun-icon/SunIcon.stories.tsx +120 -831
- package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +116 -950
- package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +123 -980
- package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +156 -1427
- package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +146 -1142
- package/dist/icons/tick-icon/TickIcon.stories.tsx +145 -1276
- package/dist/icons/trash-icon/TrashIcon.stories.tsx +108 -933
- package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +157 -1402
- package/dist/icons/upload-icon/UploadIcon.stories.tsx +115 -889
- package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +118 -984
- package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +125 -1049
- package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +123 -1356
- package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +110 -1171
- package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +112 -1093
- package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +115 -1087
- package/dist/icons/warning-icon/WarningIcon.stories.tsx +122 -1046
- package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +161 -936
- package/dist/index.cjs +84 -84
- package/dist/index.js +84 -84
- package/dist/styles/aural-all-theme.css +1222 -0
- package/dist/styles/{aural-theme.css → aural-dark-theme.css} +15 -3
- package/dist/styles/aural-light-theme.css +1047 -0
- package/package.json +1 -1
|
@@ -3,6 +3,8 @@ import { Button } from "@components/button"
|
|
|
3
3
|
import type { Meta, StoryObj } from "@storybook/react-vite"
|
|
4
4
|
import { toast } from "sonner"
|
|
5
5
|
|
|
6
|
+
import { AuralComponentDocsPage } from "src/ui/story-spec/components/component-story-docs-page"
|
|
7
|
+
|
|
6
8
|
import { Toaster } from "."
|
|
7
9
|
|
|
8
10
|
const meta: Meta<typeof Toaster> = {
|
|
@@ -10,201 +12,35 @@ const meta: Meta<typeof Toaster> = {
|
|
|
10
12
|
component: Toaster,
|
|
11
13
|
parameters: {
|
|
12
14
|
layout: "fullscreen",
|
|
13
|
-
backgrounds: {
|
|
14
|
-
default: "dark",
|
|
15
|
-
values: [
|
|
16
|
-
{ name: "dark", value: "#0a0a0a" },
|
|
17
|
-
{ name: "light", value: "#ffffff" },
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
15
|
docs: {
|
|
21
16
|
description: {
|
|
22
|
-
component:
|
|
23
|
-
A toast notification system built on
|
|
24
|
-
Provides a clean, accessible way to show success messages, errors, loading states, and other notifications.
|
|
25
|
-
|
|
26
|
-
## Features
|
|
27
|
-
- Built on Sonner for excellent performance and accessibility
|
|
28
|
-
- Multiple toast types (success, error, warning, info, loading)
|
|
29
|
-
- Customizable styling with design tokens
|
|
30
|
-
- Action buttons and dismiss functionality
|
|
31
|
-
- Promise-based toasts for async operations
|
|
32
|
-
- Rich content support (icons, descriptions, custom content)
|
|
33
|
-
- Keyboard navigation and screen reader support
|
|
34
|
-
- Automatic dismiss with customizable duration
|
|
35
|
-
- Positioning options (top, bottom, left, right)
|
|
36
|
-
- Swipe to dismiss on mobile
|
|
37
|
-
- Queue management for multiple toasts
|
|
38
|
-
|
|
39
|
-
## Installation
|
|
40
|
-
\`\`\`bash
|
|
41
|
-
npm install sonner
|
|
42
|
-
\`\`\`
|
|
43
|
-
|
|
44
|
-
## Usage
|
|
45
|
-
|
|
46
|
-
### Basic Setup
|
|
47
|
-
First, add the Toaster component to your app root:
|
|
48
|
-
|
|
49
|
-
\`\`\`tsx
|
|
50
|
-
import { Toaster } from '@/ui/components/toast'
|
|
51
|
-
|
|
52
|
-
export default function App() {
|
|
53
|
-
return (
|
|
54
|
-
<>
|
|
55
|
-
{/* Your app content */}
|
|
56
|
-
<Toaster />
|
|
57
|
-
</>
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
\`\`\`
|
|
61
|
-
|
|
62
|
-
### Triggering Toasts
|
|
63
|
-
\`\`\`tsx
|
|
64
|
-
import { toast } from 'sonner'
|
|
65
|
-
|
|
66
|
-
// Basic toast
|
|
67
|
-
toast('Hello World!')
|
|
68
|
-
|
|
69
|
-
// Success toast
|
|
70
|
-
toast.success('Profile updated successfully!')
|
|
71
|
-
|
|
72
|
-
// Error toast
|
|
73
|
-
toast.error('Something went wrong')
|
|
74
|
-
|
|
75
|
-
// Warning toast
|
|
76
|
-
toast.warning('Please save your changes')
|
|
77
|
-
|
|
78
|
-
// Info toast
|
|
79
|
-
toast.info('New version available')
|
|
80
|
-
|
|
81
|
-
// Loading toast
|
|
82
|
-
toast.loading('Uploading file...')
|
|
83
|
-
|
|
84
|
-
// Toast with description
|
|
85
|
-
toast('Event Created', {
|
|
86
|
-
description: 'Your event has been created successfully'
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
// Toast with action
|
|
90
|
-
toast('Event Created', {
|
|
91
|
-
action: {
|
|
92
|
-
label: 'View',
|
|
93
|
-
onClick: () => console.log('View clicked')
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
// Promise-based toast
|
|
98
|
-
const promise = fetch('/api/data')
|
|
99
|
-
toast.promise(promise, {
|
|
100
|
-
loading: 'Loading data...',
|
|
101
|
-
success: 'Data loaded successfully!',
|
|
102
|
-
error: 'Failed to load data'
|
|
103
|
-
})
|
|
104
|
-
\`\`\`
|
|
105
|
-
|
|
106
|
-
### Custom Styling
|
|
107
|
-
The component uses your design system tokens and can be customized through the \`toastOptions\` prop:
|
|
108
|
-
|
|
109
|
-
\`\`\`tsx
|
|
110
|
-
<Toaster
|
|
111
|
-
position="top-right"
|
|
112
|
-
toastOptions={{
|
|
113
|
-
duration: 4000,
|
|
114
|
-
style: {
|
|
115
|
-
background: 'var(--color-fm-surface-primary)',
|
|
116
|
-
color: 'var(--color-fm-text-primary)',
|
|
117
|
-
}
|
|
118
|
-
}}
|
|
119
|
-
/>
|
|
120
|
-
\`\`\`
|
|
121
|
-
`,
|
|
17
|
+
component:
|
|
18
|
+
"A toast notification system built on Sonner. The `Toaster` component must be mounted once at the app root; individual toasts are triggered imperatively with the `toast` helper from sonner. Supports six toast types (default, success, error, warning, info, loading), promise-based automatic state transitions, action/cancel buttons, configurable duration (including persistent), and six screen positions. Styling is applied via design-system tokens in the component's `toastOptions.classNames` — no custom CSS is needed in stories.",
|
|
122
19
|
},
|
|
20
|
+
page: () => (
|
|
21
|
+
<AuralComponentDocsPage
|
|
22
|
+
features={[
|
|
23
|
+
{
|
|
24
|
+
title: "6 Toast Types",
|
|
25
|
+
description: "Default to loading",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: "Promise Support",
|
|
29
|
+
description: "Auto state transitions",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
title: "6 Positions",
|
|
33
|
+
description: "Configurable screen side",
|
|
34
|
+
},
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
),
|
|
123
38
|
},
|
|
124
39
|
},
|
|
125
40
|
tags: ["autodocs"],
|
|
126
|
-
argTypes: {
|
|
127
|
-
position: {
|
|
128
|
-
control: { type: "select" },
|
|
129
|
-
options: [
|
|
130
|
-
"top-left",
|
|
131
|
-
"top-center",
|
|
132
|
-
"top-right",
|
|
133
|
-
"bottom-left",
|
|
134
|
-
"bottom-center",
|
|
135
|
-
"bottom-right",
|
|
136
|
-
],
|
|
137
|
-
description: "Position where toasts appear on screen",
|
|
138
|
-
table: {
|
|
139
|
-
type: {
|
|
140
|
-
summary:
|
|
141
|
-
'"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right"',
|
|
142
|
-
},
|
|
143
|
-
defaultValue: { summary: '"bottom-right"' },
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
expand: {
|
|
147
|
-
control: { type: "boolean" },
|
|
148
|
-
description: "Whether toasts should expand when hovered",
|
|
149
|
-
table: {
|
|
150
|
-
type: { summary: "boolean" },
|
|
151
|
-
defaultValue: { summary: "false" },
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
richColors: {
|
|
155
|
-
control: { type: "boolean" },
|
|
156
|
-
description: "Whether to use rich colors for different toast types",
|
|
157
|
-
table: {
|
|
158
|
-
type: { summary: "boolean" },
|
|
159
|
-
defaultValue: { summary: "false" },
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
closeButton: {
|
|
163
|
-
control: { type: "boolean" },
|
|
164
|
-
description: "Whether to show close button on toasts",
|
|
165
|
-
table: {
|
|
166
|
-
type: { summary: "boolean" },
|
|
167
|
-
defaultValue: { summary: "false" },
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
theme: {
|
|
171
|
-
control: { type: "select" },
|
|
172
|
-
options: ["light", "dark", "system"],
|
|
173
|
-
description: "Color theme for toasts",
|
|
174
|
-
table: {
|
|
175
|
-
type: { summary: '"light" | "dark" | "system"' },
|
|
176
|
-
defaultValue: { summary: '"system"' },
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
toastOptions: {
|
|
180
|
-
control: { type: "object" },
|
|
181
|
-
description: "Default options for all toasts",
|
|
182
|
-
table: {
|
|
183
|
-
type: { summary: "ToastOptions" },
|
|
184
|
-
defaultValue: { summary: "{}" },
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
offset: {
|
|
188
|
-
control: { type: "text" },
|
|
189
|
-
description: "Offset from screen edge (CSS value)",
|
|
190
|
-
table: {
|
|
191
|
-
type: { summary: "string | number" },
|
|
192
|
-
defaultValue: { summary: '"32px"' },
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
dir: {
|
|
196
|
-
control: { type: "select" },
|
|
197
|
-
options: ["ltr", "rtl"],
|
|
198
|
-
description: "Text direction for RTL support",
|
|
199
|
-
table: {
|
|
200
|
-
type: { summary: '"ltr" | "rtl"' },
|
|
201
|
-
defaultValue: { summary: '"ltr"' },
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
41
|
decorators: [
|
|
206
42
|
(Story) => (
|
|
207
|
-
<div
|
|
43
|
+
<div className="min-h-screen p-8">
|
|
208
44
|
<Story />
|
|
209
45
|
<Toaster />
|
|
210
46
|
</div>
|
|
@@ -215,174 +51,173 @@ The component uses your design system tokens and can be customized through the \
|
|
|
215
51
|
export default meta
|
|
216
52
|
type Story = StoryObj<typeof meta>
|
|
217
53
|
|
|
218
|
-
|
|
219
|
-
args: {
|
|
220
|
-
position: "bottom-right",
|
|
221
|
-
expand: false,
|
|
222
|
-
richColors: false,
|
|
223
|
-
closeButton: false,
|
|
224
|
-
},
|
|
225
|
-
render: (args) => (
|
|
226
|
-
<div className="text-fm-primary space-y-4">
|
|
227
|
-
<h2 className="text-xl font-semibold">Toast Examples</h2>
|
|
228
|
-
<p>
|
|
229
|
-
Click the buttons below to see different types of toast notifications.
|
|
230
|
-
</p>
|
|
231
|
-
|
|
232
|
-
<div className="flex flex-wrap gap-2">
|
|
233
|
-
<Button onClick={() => toast("Hello World!")}>Basic Toast</Button>
|
|
234
|
-
|
|
235
|
-
<Button
|
|
236
|
-
onClick={() => toast.success("Operation completed successfully!")}
|
|
237
|
-
>
|
|
238
|
-
Success Toast
|
|
239
|
-
</Button>
|
|
240
|
-
|
|
241
|
-
<Button onClick={() => toast.error("Something went wrong")}>
|
|
242
|
-
Error Toast
|
|
243
|
-
</Button>
|
|
244
|
-
</div>
|
|
245
|
-
|
|
246
|
-
<Toaster {...args} />
|
|
247
|
-
</div>
|
|
248
|
-
),
|
|
249
|
-
}
|
|
54
|
+
// ─── Configurations ───────────────────────────────────────────────────────────
|
|
250
55
|
|
|
251
|
-
export const
|
|
56
|
+
export const Configurations: Story = {
|
|
252
57
|
render: () => (
|
|
253
|
-
<div className="
|
|
254
|
-
|
|
255
|
-
<
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
58
|
+
<div className="space-y-8">
|
|
59
|
+
{/* Toast types */}
|
|
60
|
+
<div className="space-y-3">
|
|
61
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
62
|
+
Toast Types
|
|
63
|
+
</h4>
|
|
64
|
+
<div className="flex flex-wrap gap-3">
|
|
65
|
+
<div className="space-y-2 text-center">
|
|
66
|
+
<Button
|
|
67
|
+
variant="secondary"
|
|
68
|
+
onClick={() => toast("Default notification message")}
|
|
69
|
+
>
|
|
70
|
+
Default
|
|
71
|
+
</Button>
|
|
72
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
73
|
+
default
|
|
74
|
+
</p>
|
|
75
|
+
</div>
|
|
76
|
+
<div className="space-y-2 text-center">
|
|
77
|
+
<Button
|
|
78
|
+
variant="secondary"
|
|
79
|
+
onClick={() => toast.success("Track added to library")}
|
|
80
|
+
>
|
|
81
|
+
Success
|
|
82
|
+
</Button>
|
|
83
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
84
|
+
success
|
|
85
|
+
</p>
|
|
86
|
+
</div>
|
|
87
|
+
<div className="space-y-2 text-center">
|
|
88
|
+
<Button
|
|
89
|
+
variant="secondary"
|
|
90
|
+
onClick={() => toast.error("Upload failed — try again")}
|
|
91
|
+
>
|
|
92
|
+
Error
|
|
93
|
+
</Button>
|
|
94
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
95
|
+
error
|
|
96
|
+
</p>
|
|
97
|
+
</div>
|
|
98
|
+
<div className="space-y-2 text-center">
|
|
99
|
+
<Button
|
|
100
|
+
variant="secondary"
|
|
101
|
+
onClick={() => toast.warning("Offline — changes saved locally")}
|
|
102
|
+
>
|
|
103
|
+
Warning
|
|
104
|
+
</Button>
|
|
105
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
106
|
+
warning
|
|
107
|
+
</p>
|
|
108
|
+
</div>
|
|
109
|
+
<div className="space-y-2 text-center">
|
|
110
|
+
<Button
|
|
111
|
+
variant="secondary"
|
|
112
|
+
onClick={() => toast.info("New version available")}
|
|
113
|
+
>
|
|
114
|
+
Info
|
|
115
|
+
</Button>
|
|
116
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
117
|
+
info
|
|
118
|
+
</p>
|
|
119
|
+
</div>
|
|
120
|
+
<div className="space-y-2 text-center">
|
|
121
|
+
<Button
|
|
122
|
+
variant="secondary"
|
|
123
|
+
onClick={() => toast.loading("Syncing library…")}
|
|
124
|
+
>
|
|
125
|
+
Loading
|
|
126
|
+
</Button>
|
|
127
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
128
|
+
loading
|
|
129
|
+
</p>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
275
132
|
</div>
|
|
276
|
-
</div>
|
|
277
|
-
),
|
|
278
|
-
parameters: {
|
|
279
|
-
docs: {
|
|
280
|
-
description: {
|
|
281
|
-
story:
|
|
282
|
-
"Different toast types available: default, success, error, warning, info, and loading.",
|
|
283
|
-
},
|
|
284
|
-
},
|
|
285
|
-
},
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export const WithDescription: Story = {
|
|
289
|
-
render: () => (
|
|
290
|
-
<div className="text-fm-primary space-y-4">
|
|
291
|
-
<h2 className="text-xl font-semibold">Toasts with Descriptions</h2>
|
|
292
|
-
<p>Add additional context with description text.</p>
|
|
293
|
-
|
|
294
|
-
<div className="flex flex-wrap gap-2">
|
|
295
|
-
<Button
|
|
296
|
-
onClick={() =>
|
|
297
|
-
toast("Profile Updated", {
|
|
298
|
-
description:
|
|
299
|
-
"Your profile information has been saved successfully.",
|
|
300
|
-
})
|
|
301
|
-
}
|
|
302
|
-
>
|
|
303
|
-
With Description
|
|
304
|
-
</Button>
|
|
305
|
-
|
|
306
|
-
<Button
|
|
307
|
-
onClick={() =>
|
|
308
|
-
toast.success("File Uploaded", {
|
|
309
|
-
description: "document.pdf has been uploaded to the cloud.",
|
|
310
|
-
})
|
|
311
|
-
}
|
|
312
|
-
>
|
|
313
|
-
Success with Description
|
|
314
|
-
</Button>
|
|
315
133
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
134
|
+
{/* Position options */}
|
|
135
|
+
<div className="space-y-3">
|
|
136
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
137
|
+
Position Options
|
|
138
|
+
</h4>
|
|
139
|
+
<div className="flex flex-wrap gap-3">
|
|
140
|
+
{(
|
|
141
|
+
[
|
|
142
|
+
"top-left",
|
|
143
|
+
"top-center",
|
|
144
|
+
"top-right",
|
|
145
|
+
"bottom-left",
|
|
146
|
+
"bottom-center",
|
|
147
|
+
"bottom-right",
|
|
148
|
+
] as const
|
|
149
|
+
).map((pos) => (
|
|
150
|
+
<Button
|
|
151
|
+
key={pos}
|
|
152
|
+
variant="outline"
|
|
153
|
+
size="sm"
|
|
154
|
+
onClick={() => {
|
|
155
|
+
toast.dismiss()
|
|
156
|
+
toast(`Position: ${pos}`, { position: pos, duration: 3000 })
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
{pos}
|
|
160
|
+
</Button>
|
|
161
|
+
))}
|
|
162
|
+
</div>
|
|
325
163
|
</div>
|
|
326
|
-
</div>
|
|
327
|
-
),
|
|
328
|
-
parameters: {
|
|
329
|
-
docs: {
|
|
330
|
-
description: {
|
|
331
|
-
story:
|
|
332
|
-
"Toasts can include additional description text for more context.",
|
|
333
|
-
},
|
|
334
|
-
},
|
|
335
|
-
},
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export const WithActions: Story = {
|
|
339
|
-
render: () => (
|
|
340
|
-
<div className="text-fm-primary space-y-4">
|
|
341
|
-
<h2 className="text-xl font-semibold">Toasts with Actions</h2>
|
|
342
|
-
<p>Include action buttons for user interaction.</p>
|
|
343
|
-
|
|
344
|
-
<div className="flex flex-wrap gap-2">
|
|
345
|
-
<Button
|
|
346
|
-
onClick={() =>
|
|
347
|
-
toast("Event Created", {
|
|
348
|
-
description: "Your event has been scheduled for tomorrow.",
|
|
349
|
-
action: {
|
|
350
|
-
label: "View",
|
|
351
|
-
onClick: () => toast("Viewing event..."),
|
|
352
|
-
},
|
|
353
|
-
})
|
|
354
|
-
}
|
|
355
|
-
>
|
|
356
|
-
With Action
|
|
357
|
-
</Button>
|
|
358
164
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
165
|
+
{/* Duration */}
|
|
166
|
+
<div className="space-y-3">
|
|
167
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
168
|
+
Duration (Timed vs Persistent)
|
|
169
|
+
</h4>
|
|
170
|
+
<div className="flex flex-wrap gap-3">
|
|
171
|
+
<div className="space-y-2 text-center">
|
|
172
|
+
<Button
|
|
173
|
+
variant="secondary"
|
|
174
|
+
onClick={() =>
|
|
175
|
+
toast("Quick notification", {
|
|
176
|
+
duration: 1500,
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
>
|
|
180
|
+
1.5 s
|
|
181
|
+
</Button>
|
|
182
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
183
|
+
1 500 ms
|
|
184
|
+
</p>
|
|
185
|
+
</div>
|
|
186
|
+
<div className="space-y-2 text-center">
|
|
187
|
+
<Button
|
|
188
|
+
variant="secondary"
|
|
189
|
+
onClick={() =>
|
|
190
|
+
toast("Standard notification", {
|
|
191
|
+
duration: 4000,
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
>
|
|
195
|
+
4 s (default)
|
|
196
|
+
</Button>
|
|
197
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
198
|
+
4 000 ms
|
|
199
|
+
</p>
|
|
200
|
+
</div>
|
|
201
|
+
<div className="space-y-2 text-center">
|
|
202
|
+
<Button
|
|
203
|
+
variant="secondary"
|
|
204
|
+
onClick={() =>
|
|
205
|
+
toast("Persistent — dismiss manually", {
|
|
206
|
+
duration: Infinity,
|
|
207
|
+
action: {
|
|
208
|
+
label: "Dismiss",
|
|
209
|
+
onClick: () => toast.dismiss(),
|
|
210
|
+
},
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
>
|
|
214
|
+
Persistent
|
|
215
|
+
</Button>
|
|
216
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
217
|
+
Infinity
|
|
218
|
+
</p>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
386
221
|
</div>
|
|
387
222
|
</div>
|
|
388
223
|
),
|
|
@@ -390,71 +225,189 @@ export const WithActions: Story = {
|
|
|
390
225
|
docs: {
|
|
391
226
|
description: {
|
|
392
227
|
story:
|
|
393
|
-
"
|
|
228
|
+
"All configuration axes in one view: the six toast types (default, success, error, warning, info, loading), the six supported screen positions triggered live, and three duration presets including an Infinity persistent toast that must be manually dismissed.",
|
|
394
229
|
},
|
|
395
230
|
},
|
|
396
231
|
},
|
|
397
232
|
}
|
|
398
233
|
|
|
399
|
-
|
|
234
|
+
// ─── Interactive ──────────────────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
export const Interactive: Story = {
|
|
400
237
|
render: () => {
|
|
401
|
-
const
|
|
402
|
-
|
|
238
|
+
const simulateUpload = (shouldFail = false, ms = 2000) =>
|
|
239
|
+
new Promise<string>((resolve, reject) => {
|
|
403
240
|
setTimeout(() => {
|
|
404
|
-
if (shouldFail)
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
resolve("Data loaded successfully")
|
|
408
|
-
}
|
|
409
|
-
}, delay)
|
|
241
|
+
if (shouldFail) reject(new Error("Network error"))
|
|
242
|
+
else resolve("track.mp3")
|
|
243
|
+
}, ms)
|
|
410
244
|
})
|
|
411
|
-
}
|
|
412
245
|
|
|
413
246
|
return (
|
|
414
|
-
<div className="
|
|
415
|
-
<
|
|
416
|
-
|
|
247
|
+
<div className="w-full p-8">
|
|
248
|
+
<div className="mx-auto max-w-3xl space-y-6">
|
|
249
|
+
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
|
250
|
+
{/* Controls panel */}
|
|
251
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary space-y-5 rounded-xl border p-5">
|
|
252
|
+
<p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
|
|
253
|
+
Toast Triggers
|
|
254
|
+
</p>
|
|
255
|
+
|
|
256
|
+
<div className="space-y-2">
|
|
257
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
258
|
+
Basic Types
|
|
259
|
+
</p>
|
|
260
|
+
<div className="space-y-2">
|
|
261
|
+
<Button
|
|
262
|
+
variant="secondary"
|
|
263
|
+
size="sm"
|
|
264
|
+
className="w-full"
|
|
265
|
+
onClick={() => toast("Track added to queue")}
|
|
266
|
+
>
|
|
267
|
+
Default
|
|
268
|
+
</Button>
|
|
269
|
+
<Button
|
|
270
|
+
variant="secondary"
|
|
271
|
+
size="sm"
|
|
272
|
+
className="w-full"
|
|
273
|
+
onClick={() => toast.success("Liked — saved to favourites")}
|
|
274
|
+
>
|
|
275
|
+
Success
|
|
276
|
+
</Button>
|
|
277
|
+
<Button
|
|
278
|
+
variant="secondary"
|
|
279
|
+
size="sm"
|
|
280
|
+
className="w-full"
|
|
281
|
+
onClick={() =>
|
|
282
|
+
toast.error("Playback error — track unavailable")
|
|
283
|
+
}
|
|
284
|
+
>
|
|
285
|
+
Error
|
|
286
|
+
</Button>
|
|
287
|
+
<Button
|
|
288
|
+
variant="secondary"
|
|
289
|
+
size="sm"
|
|
290
|
+
className="w-full"
|
|
291
|
+
onClick={() =>
|
|
292
|
+
toast.warning("Storage almost full (95% used)")
|
|
293
|
+
}
|
|
294
|
+
>
|
|
295
|
+
Warning
|
|
296
|
+
</Button>
|
|
297
|
+
<Button
|
|
298
|
+
variant="secondary"
|
|
299
|
+
size="sm"
|
|
300
|
+
className="w-full"
|
|
301
|
+
onClick={() => toast.info("3 new releases from Luna Vex")}
|
|
302
|
+
>
|
|
303
|
+
Info
|
|
304
|
+
</Button>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
417
307
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
308
|
+
<div className="border-fm-divider-secondary border-t pt-4" />
|
|
309
|
+
|
|
310
|
+
<div className="space-y-2">
|
|
311
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
312
|
+
With Actions
|
|
313
|
+
</p>
|
|
314
|
+
<div className="space-y-2">
|
|
315
|
+
<Button
|
|
316
|
+
variant="secondary"
|
|
317
|
+
size="sm"
|
|
318
|
+
className="w-full"
|
|
319
|
+
onClick={() =>
|
|
320
|
+
toast("Track removed from queue", {
|
|
321
|
+
action: {
|
|
322
|
+
label: "Undo",
|
|
323
|
+
onClick: () => toast.success("Track restored"),
|
|
324
|
+
},
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
>
|
|
328
|
+
With Undo
|
|
329
|
+
</Button>
|
|
330
|
+
<Button
|
|
331
|
+
variant="secondary"
|
|
332
|
+
size="sm"
|
|
333
|
+
className="w-full"
|
|
334
|
+
onClick={() =>
|
|
335
|
+
toast.success("Playlist shared", {
|
|
336
|
+
description: "Link copied to clipboard",
|
|
337
|
+
action: {
|
|
338
|
+
label: "View",
|
|
339
|
+
onClick: () => toast("Opening shared link…"),
|
|
340
|
+
},
|
|
341
|
+
})
|
|
342
|
+
}
|
|
343
|
+
>
|
|
344
|
+
Success + Action
|
|
345
|
+
</Button>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
431
349
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
350
|
+
{/* Preview stage */}
|
|
351
|
+
<div className="flex flex-col gap-4 lg:col-span-2">
|
|
352
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary space-y-4 rounded-xl border p-6">
|
|
353
|
+
<p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
|
|
354
|
+
Promise Toasts
|
|
355
|
+
</p>
|
|
356
|
+
<p className="text-fm-secondary font-fm-text text-fm-md leading-fm-xl">
|
|
357
|
+
Promise toasts automatically transition from loading → success
|
|
358
|
+
or loading → error based on the promise result.
|
|
359
|
+
</p>
|
|
360
|
+
<div className="flex flex-wrap gap-3">
|
|
361
|
+
<Button
|
|
362
|
+
variant="secondary"
|
|
363
|
+
onClick={() => {
|
|
364
|
+
const p = simulateUpload(false, 2500)
|
|
365
|
+
toast.promise(p, {
|
|
366
|
+
loading: "Uploading track…",
|
|
367
|
+
success: (file) => `${file} uploaded successfully`,
|
|
368
|
+
error: "Upload failed",
|
|
369
|
+
})
|
|
370
|
+
}}
|
|
371
|
+
>
|
|
372
|
+
Successful upload
|
|
373
|
+
</Button>
|
|
374
|
+
<Button
|
|
375
|
+
variant="outline"
|
|
376
|
+
onClick={() => {
|
|
377
|
+
const p = simulateUpload(true, 1800)
|
|
378
|
+
toast.promise(p, {
|
|
379
|
+
loading: "Uploading track…",
|
|
380
|
+
success: "Uploaded",
|
|
381
|
+
error: (err: Error) => `Upload failed: ${err.message}`,
|
|
382
|
+
})
|
|
383
|
+
}}
|
|
384
|
+
>
|
|
385
|
+
Failed upload
|
|
386
|
+
</Button>
|
|
387
|
+
<Button
|
|
388
|
+
variant="secondary"
|
|
389
|
+
onClick={() => {
|
|
390
|
+
const p = simulateUpload(false, 3000)
|
|
391
|
+
toast.promise(p, {
|
|
392
|
+
loading: "Syncing library…",
|
|
393
|
+
success: "Library synced — 142 tracks updated",
|
|
394
|
+
error: "Sync failed — check your connection",
|
|
395
|
+
})
|
|
396
|
+
}}
|
|
397
|
+
>
|
|
398
|
+
Library sync
|
|
399
|
+
</Button>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
445
402
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}}
|
|
455
|
-
>
|
|
456
|
-
Custom Messages
|
|
457
|
-
</Button>
|
|
403
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
|
|
404
|
+
<code className="text-fm-secondary text-fm-md leading-fm-md font-(--font-fm-mono)">
|
|
405
|
+
Toasts stack and auto-dismiss — trigger multiple to see the
|
|
406
|
+
queue.
|
|
407
|
+
</code>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
458
411
|
</div>
|
|
459
412
|
</div>
|
|
460
413
|
)
|
|
@@ -463,971 +416,137 @@ export const PromiseToasts: Story = {
|
|
|
463
416
|
docs: {
|
|
464
417
|
description: {
|
|
465
418
|
story:
|
|
466
|
-
"
|
|
467
|
-
},
|
|
468
|
-
},
|
|
469
|
-
},
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
export const CustomDuration: Story = {
|
|
473
|
-
render: () => (
|
|
474
|
-
<div className="text-fm-primary space-y-4">
|
|
475
|
-
<h2 className="text-xl font-semibold">Custom Duration</h2>
|
|
476
|
-
<p>Control how long toasts stay visible.</p>
|
|
477
|
-
|
|
478
|
-
<div className="flex flex-wrap gap-2">
|
|
479
|
-
<Button onClick={() => toast("Quick message", { duration: 1000 })}>
|
|
480
|
-
1 Second
|
|
481
|
-
</Button>
|
|
482
|
-
|
|
483
|
-
<Button onClick={() => toast("Normal message", { duration: 4000 })}>
|
|
484
|
-
4 Seconds (Default)
|
|
485
|
-
</Button>
|
|
486
|
-
|
|
487
|
-
<Button onClick={() => toast("Long message", { duration: 10000 })}>
|
|
488
|
-
10 Seconds
|
|
489
|
-
</Button>
|
|
490
|
-
|
|
491
|
-
<Button
|
|
492
|
-
onClick={() =>
|
|
493
|
-
toast("Persistent message", {
|
|
494
|
-
description: "Your event has been scheduled for tomorrow.",
|
|
495
|
-
action: {
|
|
496
|
-
label: "View",
|
|
497
|
-
onClick: () => toast("Viewing event..."),
|
|
498
|
-
},
|
|
499
|
-
cancel: {
|
|
500
|
-
label: "Cancel",
|
|
501
|
-
onClick: () => console.log("Cancel!"),
|
|
502
|
-
},
|
|
503
|
-
duration: Infinity,
|
|
504
|
-
})
|
|
505
|
-
}
|
|
506
|
-
>
|
|
507
|
-
Persistent (Manual dismiss)
|
|
508
|
-
</Button>
|
|
509
|
-
</div>
|
|
510
|
-
</div>
|
|
511
|
-
),
|
|
512
|
-
parameters: {
|
|
513
|
-
docs: {
|
|
514
|
-
description: {
|
|
515
|
-
story:
|
|
516
|
-
"Customize how long toasts remain visible. Use Infinity for persistent toasts.",
|
|
517
|
-
},
|
|
518
|
-
},
|
|
519
|
-
},
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
export const Positioning: Story = {
|
|
523
|
-
render: () => (
|
|
524
|
-
<div className="text-fm-primary space-y-4">
|
|
525
|
-
<h2 className="text-xl font-semibold">Toast Positioning</h2>
|
|
526
|
-
<p>Try different positions for the toast container.</p>
|
|
527
|
-
|
|
528
|
-
<div className="grid max-w-md grid-cols-3 gap-2">
|
|
529
|
-
<Button
|
|
530
|
-
size="sm"
|
|
531
|
-
onClick={() => {
|
|
532
|
-
toast.dismiss()
|
|
533
|
-
toast("Top Left", { duration: 3000 })
|
|
534
|
-
}}
|
|
535
|
-
>
|
|
536
|
-
Top Left
|
|
537
|
-
</Button>
|
|
538
|
-
|
|
539
|
-
<Button
|
|
540
|
-
size="sm"
|
|
541
|
-
onClick={() => {
|
|
542
|
-
toast.dismiss()
|
|
543
|
-
toast("Top Center", { duration: 3000 })
|
|
544
|
-
}}
|
|
545
|
-
>
|
|
546
|
-
Top Center
|
|
547
|
-
</Button>
|
|
548
|
-
|
|
549
|
-
<Button
|
|
550
|
-
size="sm"
|
|
551
|
-
onClick={() => {
|
|
552
|
-
toast.dismiss()
|
|
553
|
-
toast("Top Right", { duration: 3000 })
|
|
554
|
-
}}
|
|
555
|
-
>
|
|
556
|
-
Top Right
|
|
557
|
-
</Button>
|
|
558
|
-
|
|
559
|
-
<Button
|
|
560
|
-
size="sm"
|
|
561
|
-
onClick={() => {
|
|
562
|
-
toast.dismiss()
|
|
563
|
-
toast("Bottom Left", { duration: 3000 })
|
|
564
|
-
}}
|
|
565
|
-
>
|
|
566
|
-
Bottom Left
|
|
567
|
-
</Button>
|
|
568
|
-
|
|
569
|
-
<Button
|
|
570
|
-
size="sm"
|
|
571
|
-
onClick={() => {
|
|
572
|
-
toast.dismiss()
|
|
573
|
-
toast("Bottom Center", { duration: 3000 })
|
|
574
|
-
}}
|
|
575
|
-
>
|
|
576
|
-
Bottom Center
|
|
577
|
-
</Button>
|
|
578
|
-
|
|
579
|
-
<Button
|
|
580
|
-
size="sm"
|
|
581
|
-
onClick={() => {
|
|
582
|
-
toast.dismiss()
|
|
583
|
-
toast("Bottom Right", { duration: 3000 })
|
|
584
|
-
}}
|
|
585
|
-
>
|
|
586
|
-
Bottom Right
|
|
587
|
-
</Button>
|
|
588
|
-
</div>
|
|
589
|
-
|
|
590
|
-
<p className="text-sm">
|
|
591
|
-
Note: Change the position prop on the Toaster component to see different
|
|
592
|
-
positions.
|
|
593
|
-
</p>
|
|
594
|
-
</div>
|
|
595
|
-
),
|
|
596
|
-
parameters: {
|
|
597
|
-
docs: {
|
|
598
|
-
description: {
|
|
599
|
-
story:
|
|
600
|
-
"Toasts can be positioned in different areas of the screen. Configure via the position prop on Toaster.",
|
|
601
|
-
},
|
|
602
|
-
},
|
|
603
|
-
},
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
export const RichColors: Story = {
|
|
607
|
-
args: {
|
|
608
|
-
richColors: true,
|
|
609
|
-
},
|
|
610
|
-
render: (args) => (
|
|
611
|
-
<div className="text-fm-primary space-y-4">
|
|
612
|
-
<h2 className="text-xl font-semibold">Rich Colors</h2>
|
|
613
|
-
<p>Enable rich colors for more vibrant toast notifications.</p>
|
|
614
|
-
|
|
615
|
-
<div className="flex flex-wrap gap-2">
|
|
616
|
-
<Button onClick={() => toast.success("Success with rich colors!")}>
|
|
617
|
-
Success
|
|
618
|
-
</Button>
|
|
619
|
-
|
|
620
|
-
<Button onClick={() => toast.error("Error with rich colors!")}>
|
|
621
|
-
Error
|
|
622
|
-
</Button>
|
|
623
|
-
|
|
624
|
-
<Button onClick={() => toast.warning("Warning with rich colors!")}>
|
|
625
|
-
Warning
|
|
626
|
-
</Button>
|
|
627
|
-
|
|
628
|
-
<Button onClick={() => toast.info("Info with rich colors!")}>
|
|
629
|
-
Info
|
|
630
|
-
</Button>
|
|
631
|
-
</div>
|
|
632
|
-
|
|
633
|
-
<Toaster {...args} />
|
|
634
|
-
</div>
|
|
635
|
-
),
|
|
636
|
-
parameters: {
|
|
637
|
-
docs: {
|
|
638
|
-
description: {
|
|
639
|
-
story:
|
|
640
|
-
"Enable richColors prop for more vibrant, colorful toast notifications.",
|
|
419
|
+
"Live interactive story with two panels. The left panel provides one-click triggers for every toast type (default, success, error, warning, info) plus two variants with action buttons. The right panel demonstrates promise-based toasts that automatically transition through loading → success/error states — simulating real async operations like track uploads and library syncs.",
|
|
641
420
|
},
|
|
642
421
|
},
|
|
643
422
|
},
|
|
644
423
|
}
|
|
645
424
|
|
|
646
|
-
|
|
647
|
-
args: {
|
|
648
|
-
closeButton: true,
|
|
649
|
-
},
|
|
650
|
-
render: (args) => (
|
|
651
|
-
<div className="text-fm-primary space-y-4">
|
|
652
|
-
<h2 className="text-xl font-semibold">With Close Button</h2>
|
|
653
|
-
<p>Show close buttons on all toasts for manual dismissal.</p>
|
|
654
|
-
|
|
655
|
-
<div className="flex flex-wrap gap-2">
|
|
656
|
-
<Button onClick={() => toast("Message with close button")}>
|
|
657
|
-
Show Toast
|
|
658
|
-
</Button>
|
|
425
|
+
// ─── Accessibility ─────────────────────────────────────────────────────────────
|
|
659
426
|
|
|
660
|
-
|
|
661
|
-
Show Success
|
|
662
|
-
</Button>
|
|
663
|
-
|
|
664
|
-
<Button
|
|
665
|
-
onClick={() => toast("Persistent with close", { duration: Infinity })}
|
|
666
|
-
>
|
|
667
|
-
Persistent Toast
|
|
668
|
-
</Button>
|
|
669
|
-
</div>
|
|
670
|
-
|
|
671
|
-
<Toaster {...args} />
|
|
672
|
-
</div>
|
|
673
|
-
),
|
|
674
|
-
parameters: {
|
|
675
|
-
docs: {
|
|
676
|
-
description: {
|
|
677
|
-
story: "Enable closeButton prop to show close buttons on all toasts.",
|
|
678
|
-
},
|
|
679
|
-
},
|
|
680
|
-
},
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
export const CustomStyling: Story = {
|
|
427
|
+
export const Accessibility: Story = {
|
|
684
428
|
render: () => (
|
|
685
|
-
<div className="
|
|
686
|
-
|
|
687
|
-
<
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
toast("Custom styled toast", {
|
|
693
|
-
style: {
|
|
694
|
-
background: "#1f2937",
|
|
695
|
-
color: "#f9fafb",
|
|
696
|
-
border: "1px solid #374151",
|
|
697
|
-
},
|
|
698
|
-
})
|
|
699
|
-
}
|
|
700
|
-
>
|
|
701
|
-
Dark Style
|
|
702
|
-
</Button>
|
|
703
|
-
|
|
704
|
-
<Button
|
|
705
|
-
onClick={() =>
|
|
706
|
-
toast.success("Gradient success", {
|
|
707
|
-
style: {
|
|
708
|
-
background: "linear-gradient(to right, #10b981, #059669)",
|
|
709
|
-
color: "white",
|
|
710
|
-
},
|
|
711
|
-
})
|
|
712
|
-
}
|
|
713
|
-
>
|
|
714
|
-
Gradient Style
|
|
715
|
-
</Button>
|
|
716
|
-
|
|
717
|
-
<Button
|
|
718
|
-
onClick={() =>
|
|
719
|
-
toast("Large toast", {
|
|
720
|
-
className: "text-lg p-6",
|
|
721
|
-
style: {
|
|
722
|
-
minHeight: "80px",
|
|
723
|
-
},
|
|
724
|
-
})
|
|
725
|
-
}
|
|
726
|
-
>
|
|
727
|
-
Large Size
|
|
728
|
-
</Button>
|
|
729
|
-
</div>
|
|
730
|
-
</div>
|
|
731
|
-
),
|
|
732
|
-
parameters: {
|
|
733
|
-
docs: {
|
|
734
|
-
description: {
|
|
735
|
-
story:
|
|
736
|
-
"Customize individual toasts with inline styles and CSS classes.",
|
|
737
|
-
},
|
|
738
|
-
},
|
|
739
|
-
},
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
export const ToastManagement: Story = {
|
|
743
|
-
render: () => {
|
|
744
|
-
const [toastCount, setToastCount] = React.useState(0)
|
|
745
|
-
|
|
746
|
-
return (
|
|
747
|
-
<div className="text-fm-primary space-y-4">
|
|
748
|
-
<h2 className="text-xl font-semibold">Toast Management</h2>
|
|
749
|
-
<p>Programmatically control toast behavior.</p>
|
|
750
|
-
|
|
751
|
-
<div className="flex flex-wrap gap-2">
|
|
429
|
+
<div className="space-y-8">
|
|
430
|
+
{/* Live region demo */}
|
|
431
|
+
<div className="space-y-3">
|
|
432
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
433
|
+
ARIA Live Region
|
|
434
|
+
</h4>
|
|
435
|
+
<div className="flex flex-wrap gap-3">
|
|
752
436
|
<Button
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
toast(
|
|
756
|
-
|
|
437
|
+
variant="secondary"
|
|
438
|
+
onClick={() =>
|
|
439
|
+
toast("Track saved", {
|
|
440
|
+
description: "Midnight Echoes has been added to your library.",
|
|
441
|
+
})
|
|
442
|
+
}
|
|
757
443
|
>
|
|
758
|
-
|
|
759
|
-
</Button>
|
|
760
|
-
|
|
761
|
-
<Button variant="outline" onClick={() => toast.dismiss()}>
|
|
762
|
-
Dismiss All
|
|
444
|
+
Announce success
|
|
763
445
|
</Button>
|
|
764
|
-
|
|
765
446
|
<Button
|
|
766
|
-
variant="
|
|
767
|
-
onClick={() =>
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
duration: Infinity,
|
|
771
|
-
action: {
|
|
772
|
-
label: "Dismiss",
|
|
773
|
-
onClick: () => toast.dismiss(toastId),
|
|
774
|
-
},
|
|
447
|
+
variant="secondary"
|
|
448
|
+
onClick={() =>
|
|
449
|
+
toast.error("Playback blocked", {
|
|
450
|
+
description: "This track is not available in your region.",
|
|
775
451
|
})
|
|
776
|
-
}
|
|
452
|
+
}
|
|
777
453
|
>
|
|
778
|
-
|
|
454
|
+
Announce error
|
|
779
455
|
</Button>
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
780
458
|
|
|
459
|
+
{/* Keyboard dismissal */}
|
|
460
|
+
<div className="space-y-3">
|
|
461
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
462
|
+
Keyboard Dismissal
|
|
463
|
+
</h4>
|
|
464
|
+
<div className="flex flex-wrap gap-3">
|
|
781
465
|
<Button
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
toast(
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
466
|
+
variant="secondary"
|
|
467
|
+
onClick={() =>
|
|
468
|
+
toast(
|
|
469
|
+
"Press Escape or Tab to a toast then Enter/Space to dismiss",
|
|
470
|
+
{
|
|
471
|
+
duration: 8000,
|
|
472
|
+
action: {
|
|
473
|
+
label: "Dismiss",
|
|
474
|
+
onClick: () => toast.dismiss(),
|
|
475
|
+
},
|
|
476
|
+
}
|
|
477
|
+
)
|
|
478
|
+
}
|
|
788
479
|
>
|
|
789
|
-
|
|
480
|
+
Focusable toast
|
|
481
|
+
</Button>
|
|
482
|
+
<Button variant="outline" onClick={() => toast.dismiss()}>
|
|
483
|
+
Dismiss all (toast.dismiss)
|
|
790
484
|
</Button>
|
|
791
|
-
</div>
|
|
792
|
-
|
|
793
|
-
<div className="text-fm-primary text-sm">
|
|
794
|
-
<p>Toast count: {toastCount}</p>
|
|
795
485
|
</div>
|
|
796
486
|
</div>
|
|
797
|
-
)
|
|
798
|
-
},
|
|
799
|
-
parameters: {
|
|
800
|
-
docs: {
|
|
801
|
-
description: {
|
|
802
|
-
story:
|
|
803
|
-
"Manage toasts programmatically with dismiss, queuing, and ID-based control.",
|
|
804
|
-
},
|
|
805
|
-
},
|
|
806
|
-
},
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
export const AccessibilityFeatures: Story = {
|
|
810
|
-
render: () => (
|
|
811
|
-
<div className="text-fm-primary space-y-4">
|
|
812
|
-
<h2 className="text-xl font-semibold">Accessibility Features</h2>
|
|
813
|
-
<p>Toast notifications are built with accessibility in mind.</p>
|
|
814
487
|
|
|
488
|
+
{/* Role demo */}
|
|
815
489
|
<div className="space-y-3">
|
|
816
|
-
<
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
<p className="text-sm text-green-700">
|
|
827
|
-
Toasts are announced to screen readers with proper ARIA labels.
|
|
828
|
-
</p>
|
|
829
|
-
</div>
|
|
830
|
-
|
|
831
|
-
<div className="rounded-lg bg-purple-50 p-4">
|
|
832
|
-
<h3 className="font-medium text-purple-900">Reduced Motion</h3>
|
|
833
|
-
<p className="text-sm text-purple-700">
|
|
834
|
-
Respects user's motion preferences for animations.
|
|
835
|
-
</p>
|
|
836
|
-
</div>
|
|
837
|
-
</div>
|
|
838
|
-
|
|
839
|
-
<div className="flex gap-2">
|
|
840
|
-
<Button
|
|
841
|
-
onClick={() =>
|
|
842
|
-
toast.success("Accessible success message", {
|
|
843
|
-
description: "This toast is fully accessible to screen readers",
|
|
844
|
-
})
|
|
845
|
-
}
|
|
846
|
-
>
|
|
847
|
-
Try Accessible Toast
|
|
848
|
-
</Button>
|
|
849
|
-
</div>
|
|
850
|
-
</div>
|
|
851
|
-
),
|
|
852
|
-
parameters: {
|
|
853
|
-
docs: {
|
|
854
|
-
description: {
|
|
855
|
-
story:
|
|
856
|
-
"Toast component includes comprehensive accessibility features including keyboard navigation, screen reader support, and reduced motion respect.",
|
|
857
|
-
},
|
|
858
|
-
},
|
|
859
|
-
},
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
export const RealWorldExamples: Story = {
|
|
863
|
-
render: () => {
|
|
864
|
-
const handleSave = () => {
|
|
865
|
-
toast.loading("Saving changes...")
|
|
866
|
-
|
|
867
|
-
// Simulate API call
|
|
868
|
-
setTimeout(() => {
|
|
869
|
-
toast.dismiss()
|
|
870
|
-
toast.success("Changes saved successfully!", {
|
|
871
|
-
description: "Your document has been updated.",
|
|
872
|
-
action: {
|
|
873
|
-
label: "View",
|
|
874
|
-
onClick: () => toast("Opening document..."),
|
|
875
|
-
},
|
|
876
|
-
})
|
|
877
|
-
}, 2000)
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
const handleDelete = () => {
|
|
881
|
-
toast("Are you sure?", {
|
|
882
|
-
description: "This action cannot be undone.",
|
|
883
|
-
action: {
|
|
884
|
-
label: "Delete",
|
|
885
|
-
onClick: () => {
|
|
886
|
-
toast.dismiss()
|
|
887
|
-
toast.error("Item deleted", {
|
|
888
|
-
description: "The item has been permanently removed.",
|
|
889
|
-
})
|
|
890
|
-
},
|
|
891
|
-
},
|
|
892
|
-
})
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
const handleUpload = () => {
|
|
896
|
-
const files = ["document.pdf", "image.jpg", "data.csv"]
|
|
897
|
-
let completed = 0
|
|
898
|
-
|
|
899
|
-
files.forEach((file, index) => {
|
|
900
|
-
setTimeout(
|
|
901
|
-
() => {
|
|
902
|
-
completed++
|
|
903
|
-
if (completed === files.length) {
|
|
904
|
-
toast.success("All files uploaded!", {
|
|
905
|
-
description: `Successfully uploaded ${files.length} files.`,
|
|
490
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
491
|
+
role="status" vs role="alert"
|
|
492
|
+
</h4>
|
|
493
|
+
<div className="flex flex-wrap gap-3">
|
|
494
|
+
<Button
|
|
495
|
+
variant="secondary"
|
|
496
|
+
onClick={() =>
|
|
497
|
+
toast.info("Shuffle mode enabled", {
|
|
498
|
+
description:
|
|
499
|
+
'Announced politely via aria-live="polite" (role=status).',
|
|
906
500
|
})
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
|
|
501
|
+
}
|
|
502
|
+
>
|
|
503
|
+
Polite (info)
|
|
504
|
+
</Button>
|
|
505
|
+
<Button
|
|
506
|
+
variant="secondary"
|
|
507
|
+
onClick={() =>
|
|
508
|
+
toast.error("Session expired — please log in again", {
|
|
509
|
+
description:
|
|
510
|
+
'Announced immediately via aria-live="assertive" (role=alert).',
|
|
511
|
+
duration: 6000,
|
|
910
512
|
})
|
|
911
513
|
}
|
|
912
|
-
},
|
|
913
|
-
(index + 1) * 1000
|
|
914
|
-
)
|
|
915
|
-
})
|
|
916
|
-
|
|
917
|
-
toast.loading("Uploading files...", {
|
|
918
|
-
description: "Please wait while we upload your files.",
|
|
919
|
-
})
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
return (
|
|
923
|
-
<div className="text-fm-primary space-y-4">
|
|
924
|
-
<h2 className="text-xl font-semibold">Real World Examples</h2>
|
|
925
|
-
<p>Common patterns and use cases for toast notifications.</p>
|
|
926
|
-
|
|
927
|
-
<div className="space-y-3">
|
|
928
|
-
<div className="rounded-lg border p-4">
|
|
929
|
-
<h3 className="mb-2 font-medium">Document Editor</h3>
|
|
930
|
-
<div className="flex gap-2">
|
|
931
|
-
<Button onClick={handleSave}>Save Document</Button>
|
|
932
|
-
<Button onClick={handleDelete}>Delete Document</Button>
|
|
933
|
-
</div>
|
|
934
|
-
</div>
|
|
935
|
-
|
|
936
|
-
<div className="rounded-lg border p-4">
|
|
937
|
-
<h3 className="mb-2 font-medium">File Upload</h3>
|
|
938
|
-
<Button onClick={handleUpload}>Upload Multiple Files</Button>
|
|
939
|
-
</div>
|
|
940
|
-
|
|
941
|
-
<div className="rounded-lg border p-4">
|
|
942
|
-
<h3 className="mb-2 font-medium">Network Status</h3>
|
|
943
|
-
<div className="flex gap-2">
|
|
944
|
-
<Button
|
|
945
|
-
onClick={() =>
|
|
946
|
-
toast.info("Connection restored", {
|
|
947
|
-
description: "You're back online!",
|
|
948
|
-
})
|
|
949
|
-
}
|
|
950
|
-
>
|
|
951
|
-
Simulate Online
|
|
952
|
-
</Button>
|
|
953
|
-
<Button
|
|
954
|
-
variant="outline"
|
|
955
|
-
onClick={() =>
|
|
956
|
-
toast.warning("Connection lost", {
|
|
957
|
-
description: "Check your internet connection.",
|
|
958
|
-
duration: Infinity,
|
|
959
|
-
})
|
|
960
|
-
}
|
|
961
|
-
>
|
|
962
|
-
Simulate Offline
|
|
963
|
-
</Button>
|
|
964
|
-
</div>
|
|
965
|
-
</div>
|
|
966
|
-
</div>
|
|
967
|
-
</div>
|
|
968
|
-
)
|
|
969
|
-
},
|
|
970
|
-
parameters: {
|
|
971
|
-
docs: {
|
|
972
|
-
description: {
|
|
973
|
-
story:
|
|
974
|
-
"Real-world examples showing how to use toasts for common application scenarios like saving, uploading, and network status.",
|
|
975
|
-
},
|
|
976
|
-
},
|
|
977
|
-
},
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
export const HeadlessCustomDesign: Story = {
|
|
981
|
-
render: () => {
|
|
982
|
-
const customToast = (
|
|
983
|
-
message: string,
|
|
984
|
-
type: "success" | "error" | "warning" | "info" = "info"
|
|
985
|
-
) => {
|
|
986
|
-
toast.custom((t) => (
|
|
987
|
-
<div
|
|
988
|
-
className={`flex items-center gap-3 rounded-xl border p-4 shadow-lg backdrop-blur-sm ${type === "success" ? "border-green-200 bg-green-50/90 text-green-800" : ""} ${type === "error" ? "border-red-200 bg-red-50/90 text-red-800" : ""} ${type === "warning" ? "border-orange-200 bg-orange-50/90 text-orange-800" : ""} ${type === "info" ? "border-blue-200 bg-blue-50/90 text-blue-800" : ""} transform transition-all duration-300 ease-in-out ${t ? "translate-x-0 opacity-100" : "translate-x-full opacity-0"} `}
|
|
989
|
-
>
|
|
990
|
-
{/* Custom Icons */}
|
|
991
|
-
<div className="flex-shrink-0">
|
|
992
|
-
{type === "success" && (
|
|
993
|
-
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-green-100">
|
|
994
|
-
<svg
|
|
995
|
-
className="h-5 w-5 text-green-600"
|
|
996
|
-
fill="none"
|
|
997
|
-
stroke="currentColor"
|
|
998
|
-
viewBox="0 0 24 24"
|
|
999
|
-
>
|
|
1000
|
-
<path
|
|
1001
|
-
strokeLinecap="round"
|
|
1002
|
-
strokeLinejoin="round"
|
|
1003
|
-
strokeWidth={2}
|
|
1004
|
-
d="M5 13l4 4L19 7"
|
|
1005
|
-
/>
|
|
1006
|
-
</svg>
|
|
1007
|
-
</div>
|
|
1008
|
-
)}
|
|
1009
|
-
{type === "error" && (
|
|
1010
|
-
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-red-100">
|
|
1011
|
-
<svg
|
|
1012
|
-
className="h-5 w-5 text-red-600"
|
|
1013
|
-
fill="none"
|
|
1014
|
-
stroke="currentColor"
|
|
1015
|
-
viewBox="0 0 24 24"
|
|
1016
|
-
>
|
|
1017
|
-
<path
|
|
1018
|
-
strokeLinecap="round"
|
|
1019
|
-
strokeLinejoin="round"
|
|
1020
|
-
strokeWidth={2}
|
|
1021
|
-
d="M6 18L18 6M6 6l12 12"
|
|
1022
|
-
/>
|
|
1023
|
-
</svg>
|
|
1024
|
-
</div>
|
|
1025
|
-
)}
|
|
1026
|
-
{type === "warning" && (
|
|
1027
|
-
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-orange-100">
|
|
1028
|
-
<svg
|
|
1029
|
-
className="h-5 w-5 text-orange-600"
|
|
1030
|
-
fill="none"
|
|
1031
|
-
stroke="currentColor"
|
|
1032
|
-
viewBox="0 0 24 24"
|
|
1033
|
-
>
|
|
1034
|
-
<path
|
|
1035
|
-
strokeLinecap="round"
|
|
1036
|
-
strokeLinejoin="round"
|
|
1037
|
-
strokeWidth={2}
|
|
1038
|
-
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
|
|
1039
|
-
/>
|
|
1040
|
-
</svg>
|
|
1041
|
-
</div>
|
|
1042
|
-
)}
|
|
1043
|
-
{type === "info" && (
|
|
1044
|
-
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-100">
|
|
1045
|
-
<svg
|
|
1046
|
-
className="h-5 w-5 text-blue-600"
|
|
1047
|
-
fill="none"
|
|
1048
|
-
stroke="currentColor"
|
|
1049
|
-
viewBox="0 0 24 24"
|
|
1050
|
-
>
|
|
1051
|
-
<path
|
|
1052
|
-
strokeLinecap="round"
|
|
1053
|
-
strokeLinejoin="round"
|
|
1054
|
-
strokeWidth={2}
|
|
1055
|
-
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
1056
|
-
/>
|
|
1057
|
-
</svg>
|
|
1058
|
-
</div>
|
|
1059
|
-
)}
|
|
1060
|
-
</div>
|
|
1061
|
-
|
|
1062
|
-
{/* Message */}
|
|
1063
|
-
<div className="flex-1 font-medium">{message}</div>
|
|
1064
|
-
|
|
1065
|
-
{/* Close Button */}
|
|
1066
|
-
<button
|
|
1067
|
-
onClick={() => toast.dismiss(t)}
|
|
1068
|
-
className="flex-shrink-0 rounded-full p-1 transition-colors hover:bg-black/10"
|
|
1069
514
|
>
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
fill="none"
|
|
1073
|
-
stroke="currentColor"
|
|
1074
|
-
viewBox="0 0 24 24"
|
|
1075
|
-
>
|
|
1076
|
-
<path
|
|
1077
|
-
strokeLinecap="round"
|
|
1078
|
-
strokeLinejoin="round"
|
|
1079
|
-
strokeWidth={2}
|
|
1080
|
-
d="M6 18L18 6M6 6l12 12"
|
|
1081
|
-
/>
|
|
1082
|
-
</svg>
|
|
1083
|
-
</button>
|
|
1084
|
-
</div>
|
|
1085
|
-
))
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
const cardToast = (title: string, description: string, avatar?: string) => {
|
|
1089
|
-
toast.custom((t) => (
|
|
1090
|
-
<div
|
|
1091
|
-
className={`max-w-sm transform rounded-2xl border border-gray-200 bg-white p-4 shadow-xl transition-all duration-300 ease-in-out ${t ? "translate-y-0 scale-100 opacity-100" : "translate-y-2 scale-95 opacity-0"} `}
|
|
1092
|
-
>
|
|
1093
|
-
<div className="flex items-start gap-3">
|
|
1094
|
-
{avatar && (
|
|
1095
|
-
<img
|
|
1096
|
-
src={avatar}
|
|
1097
|
-
alt=""
|
|
1098
|
-
className="h-10 w-10 flex-shrink-0 rounded-full object-cover"
|
|
1099
|
-
/>
|
|
1100
|
-
)}
|
|
1101
|
-
<div className="min-w-0 flex-1">
|
|
1102
|
-
<div className="flex items-start justify-between">
|
|
1103
|
-
<div>
|
|
1104
|
-
<p className="text-sm font-semibold text-gray-900">{title}</p>
|
|
1105
|
-
<p className="mt-1 text-sm text-gray-600">{description}</p>
|
|
1106
|
-
</div>
|
|
1107
|
-
<button
|
|
1108
|
-
onClick={() => toast.dismiss(t)}
|
|
1109
|
-
className="ml-2 flex-shrink-0 rounded-full p-1 transition-colors hover:bg-gray-100"
|
|
1110
|
-
>
|
|
1111
|
-
<svg
|
|
1112
|
-
className="h-4 w-4 text-gray-400"
|
|
1113
|
-
fill="none"
|
|
1114
|
-
stroke="currentColor"
|
|
1115
|
-
viewBox="0 0 24 24"
|
|
1116
|
-
>
|
|
1117
|
-
<path
|
|
1118
|
-
strokeLinecap="round"
|
|
1119
|
-
strokeLinejoin="round"
|
|
1120
|
-
strokeWidth={2}
|
|
1121
|
-
d="M6 18L18 6M6 6l12 12"
|
|
1122
|
-
/>
|
|
1123
|
-
</svg>
|
|
1124
|
-
</button>
|
|
1125
|
-
</div>
|
|
1126
|
-
<div className="mt-3 flex gap-2">
|
|
1127
|
-
<button className="rounded-full bg-blue-600 px-3 py-1 text-xs text-white transition-colors hover:bg-blue-700">
|
|
1128
|
-
View
|
|
1129
|
-
</button>
|
|
1130
|
-
<button
|
|
1131
|
-
onClick={() => toast.dismiss(t)}
|
|
1132
|
-
className="rounded-full bg-gray-100 px-3 py-1 text-xs text-gray-700 transition-colors hover:bg-gray-200"
|
|
1133
|
-
>
|
|
1134
|
-
Dismiss
|
|
1135
|
-
</button>
|
|
1136
|
-
</div>
|
|
1137
|
-
</div>
|
|
1138
|
-
</div>
|
|
1139
|
-
</div>
|
|
1140
|
-
))
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
const progressToast = (message: string) => {
|
|
1144
|
-
const toastId = toast.custom((t) => (
|
|
1145
|
-
<div
|
|
1146
|
-
className={`min-w-[300px] transform rounded-xl border border-gray-200 bg-white p-4 shadow-lg transition-all duration-300 ease-in-out ${t ? "translate-x-0 opacity-100" : "translate-x-full opacity-0"} `}
|
|
1147
|
-
>
|
|
1148
|
-
<div className="flex items-center gap-3">
|
|
1149
|
-
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-100">
|
|
1150
|
-
<div className="h-4 w-4 animate-spin rounded-full border-2 border-blue-600 border-t-transparent"></div>
|
|
1151
|
-
</div>
|
|
1152
|
-
<div className="flex-1">
|
|
1153
|
-
<p className="font-medium text-gray-900">{message}</p>
|
|
1154
|
-
<div className="mt-2 h-2 w-full rounded-full bg-gray-200">
|
|
1155
|
-
<div
|
|
1156
|
-
className="progress-bar h-2 rounded-full bg-blue-600"
|
|
1157
|
-
style={{ width: "0%" }}
|
|
1158
|
-
></div>
|
|
1159
|
-
</div>
|
|
1160
|
-
</div>
|
|
1161
|
-
<button
|
|
1162
|
-
onClick={() => toast.dismiss(t)}
|
|
1163
|
-
className="flex-shrink-0 rounded-full p-1 transition-colors hover:bg-gray-100"
|
|
1164
|
-
>
|
|
1165
|
-
<svg
|
|
1166
|
-
className="h-4 w-4 text-gray-400"
|
|
1167
|
-
fill="none"
|
|
1168
|
-
stroke="currentColor"
|
|
1169
|
-
viewBox="0 0 24 24"
|
|
1170
|
-
>
|
|
1171
|
-
<path
|
|
1172
|
-
strokeLinecap="round"
|
|
1173
|
-
strokeLinejoin="round"
|
|
1174
|
-
strokeWidth={2}
|
|
1175
|
-
d="M6 18L18 6M6 6l12 12"
|
|
1176
|
-
/>
|
|
1177
|
-
</svg>
|
|
1178
|
-
</button>
|
|
1179
|
-
</div>
|
|
1180
|
-
</div>
|
|
1181
|
-
))
|
|
1182
|
-
|
|
1183
|
-
// Simulate progress
|
|
1184
|
-
let progress = 0
|
|
1185
|
-
const interval = setInterval(() => {
|
|
1186
|
-
progress += 10
|
|
1187
|
-
const progressBar = document.querySelector(
|
|
1188
|
-
".progress-bar"
|
|
1189
|
-
) as HTMLElement
|
|
1190
|
-
if (progressBar) {
|
|
1191
|
-
progressBar.style.width = `${progress}%`
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
if (progress >= 100) {
|
|
1195
|
-
clearInterval(interval)
|
|
1196
|
-
setTimeout(() => {
|
|
1197
|
-
toast.dismiss(toastId)
|
|
1198
|
-
toast.custom(() => (
|
|
1199
|
-
<div className="flex items-center gap-3 rounded-xl border border-green-200 bg-green-50 p-4">
|
|
1200
|
-
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-green-100">
|
|
1201
|
-
<svg
|
|
1202
|
-
className="h-5 w-5 text-green-600"
|
|
1203
|
-
fill="none"
|
|
1204
|
-
stroke="currentColor"
|
|
1205
|
-
viewBox="0 0 24 24"
|
|
1206
|
-
>
|
|
1207
|
-
<path
|
|
1208
|
-
strokeLinecap="round"
|
|
1209
|
-
strokeLinejoin="round"
|
|
1210
|
-
strokeWidth={2}
|
|
1211
|
-
d="M5 13l4 4L19 7"
|
|
1212
|
-
/>
|
|
1213
|
-
</svg>
|
|
1214
|
-
</div>
|
|
1215
|
-
<span className="font-medium text-green-800">
|
|
1216
|
-
Upload completed!
|
|
1217
|
-
</span>
|
|
1218
|
-
</div>
|
|
1219
|
-
))
|
|
1220
|
-
}, 500)
|
|
1221
|
-
}
|
|
1222
|
-
}, 300)
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
const glassmorphismToast = (message: string, emoji: string) => {
|
|
1226
|
-
toast.custom((t) => (
|
|
1227
|
-
<div
|
|
1228
|
-
className={`transform rounded-2xl border border-white/30 bg-white/20 p-4 shadow-xl backdrop-blur-md transition-all duration-500 ease-out ${t ? "translate-y-0 scale-100 opacity-100" : "translate-y-4 scale-95 opacity-0"} `}
|
|
1229
|
-
>
|
|
1230
|
-
<div className="flex items-center gap-3">
|
|
1231
|
-
<span className="text-2xl">{emoji}</span>
|
|
1232
|
-
<span className="font-medium text-gray-800">{message}</span>
|
|
1233
|
-
<button
|
|
1234
|
-
onClick={() => toast.dismiss(t)}
|
|
1235
|
-
className="ml-auto rounded-full p-1 transition-colors hover:bg-white/20"
|
|
1236
|
-
>
|
|
1237
|
-
<svg
|
|
1238
|
-
className="h-4 w-4 text-gray-600"
|
|
1239
|
-
fill="none"
|
|
1240
|
-
stroke="currentColor"
|
|
1241
|
-
viewBox="0 0 24 24"
|
|
1242
|
-
>
|
|
1243
|
-
<path
|
|
1244
|
-
strokeLinecap="round"
|
|
1245
|
-
strokeLinejoin="round"
|
|
1246
|
-
strokeWidth={2}
|
|
1247
|
-
d="M6 18L18 6M6 6l12 12"
|
|
1248
|
-
/>
|
|
1249
|
-
</svg>
|
|
1250
|
-
</button>
|
|
1251
|
-
</div>
|
|
1252
|
-
</div>
|
|
1253
|
-
))
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
return (
|
|
1257
|
-
<div className="text-fm-primary space-y-6">
|
|
1258
|
-
<div className="space-y-4">
|
|
1259
|
-
<h2 className="text-xl font-semibold">Headless Custom Design</h2>
|
|
1260
|
-
<p>
|
|
1261
|
-
Create completely custom toast designs using toast.custom() with
|
|
1262
|
-
your own components and styling.
|
|
1263
|
-
</p>
|
|
515
|
+
Assertive (error)
|
|
516
|
+
</Button>
|
|
1264
517
|
</div>
|
|
518
|
+
</div>
|
|
1265
519
|
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
<
|
|
1283
|
-
|
|
1284
|
-
>
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
onClick={() =>
|
|
1289
|
-
customToast("Please review your settings", "warning")
|
|
1290
|
-
}
|
|
1291
|
-
>
|
|
1292
|
-
Custom Warning
|
|
1293
|
-
</Button>
|
|
1294
|
-
<Button
|
|
1295
|
-
onClick={() => customToast("New feature available", "info")}
|
|
1296
|
-
>
|
|
1297
|
-
Custom Info
|
|
1298
|
-
</Button>
|
|
1299
|
-
</div>
|
|
1300
|
-
</div>
|
|
1301
|
-
|
|
1302
|
-
{/* Card-style Toasts */}
|
|
1303
|
-
<div className="space-y-3">
|
|
1304
|
-
<h3 className="text-lg font-medium">Card-style Notifications</h3>
|
|
1305
|
-
<p className="text-sm">
|
|
1306
|
-
Rich card-style toasts with avatars and action buttons.
|
|
1307
|
-
</p>
|
|
1308
|
-
<div className="flex flex-wrap gap-2">
|
|
1309
|
-
<Button
|
|
1310
|
-
onClick={() =>
|
|
1311
|
-
cardToast(
|
|
1312
|
-
"New Message",
|
|
1313
|
-
"John sent you a new message about the project timeline.",
|
|
1314
|
-
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=40&h=40&fit=crop&crop=face"
|
|
1315
|
-
)
|
|
1316
|
-
}
|
|
1317
|
-
>
|
|
1318
|
-
Message Notification
|
|
1319
|
-
</Button>
|
|
1320
|
-
<Button
|
|
1321
|
-
onClick={() =>
|
|
1322
|
-
cardToast(
|
|
1323
|
-
"System Update",
|
|
1324
|
-
"A new system update is available. Update now to get the latest features."
|
|
1325
|
-
)
|
|
1326
|
-
}
|
|
1327
|
-
>
|
|
1328
|
-
System Notification
|
|
1329
|
-
</Button>
|
|
1330
|
-
</div>
|
|
1331
|
-
</div>
|
|
1332
|
-
|
|
1333
|
-
{/* Progress Toasts */}
|
|
1334
|
-
<div className="space-y-3">
|
|
1335
|
-
<h3 className="text-lg font-medium">Progress Notifications</h3>
|
|
1336
|
-
<p className="text-sm">
|
|
1337
|
-
Custom toasts with progress bars for long-running operations.
|
|
1338
|
-
</p>
|
|
1339
|
-
<div className="flex flex-wrap gap-2">
|
|
1340
|
-
<Button onClick={() => progressToast("Uploading files...")}>
|
|
1341
|
-
Upload Progress
|
|
1342
|
-
</Button>
|
|
1343
|
-
</div>
|
|
1344
|
-
</div>
|
|
1345
|
-
|
|
1346
|
-
{/* Glassmorphism Toasts */}
|
|
1347
|
-
<div className="space-y-3">
|
|
1348
|
-
<h3 className="text-lg font-medium">Glassmorphism Style</h3>
|
|
1349
|
-
<p className="text-sm">
|
|
1350
|
-
Modern glassmorphism design with backdrop blur effects.
|
|
1351
|
-
</p>
|
|
1352
|
-
<div className="flex flex-wrap gap-2">
|
|
1353
|
-
<Button
|
|
1354
|
-
onClick={() =>
|
|
1355
|
-
glassmorphismToast("Welcome to the future!", "🚀")
|
|
1356
|
-
}
|
|
1357
|
-
>
|
|
1358
|
-
Glassmorphism Toast
|
|
1359
|
-
</Button>
|
|
1360
|
-
<Button
|
|
1361
|
-
onClick={() =>
|
|
1362
|
-
glassmorphismToast("Achievement unlocked!", "🏆")
|
|
1363
|
-
}
|
|
1364
|
-
>
|
|
1365
|
-
Achievement Toast
|
|
1366
|
-
</Button>
|
|
1367
|
-
<Button
|
|
1368
|
-
onClick={() =>
|
|
1369
|
-
glassmorphismToast("You have a new follower", "👋")
|
|
1370
|
-
}
|
|
1371
|
-
>
|
|
1372
|
-
Social Toast
|
|
1373
|
-
</Button>
|
|
1374
|
-
</div>
|
|
1375
|
-
</div>
|
|
1376
|
-
|
|
1377
|
-
{/* Code Example */}
|
|
1378
|
-
<div className="space-y-3">
|
|
1379
|
-
<h3 className="text-lg font-medium">Implementation Example</h3>
|
|
1380
|
-
<div className="rounded-lg bg-gray-50 p-4 text-sm">
|
|
1381
|
-
<pre className="overflow-x-auto text-gray-800">
|
|
1382
|
-
{`// Custom toast with full control
|
|
1383
|
-
toast.custom((t) => (
|
|
1384
|
-
<div className={\`
|
|
1385
|
-
bg-white border rounded-xl p-4 shadow-lg
|
|
1386
|
-
transform transition-all duration-300
|
|
1387
|
-
\${t ? 'translate-x-0 opacity-100' : 'translate-x-full opacity-0'}
|
|
1388
|
-
\`}>
|
|
1389
|
-
<div className="flex items-center gap-3">
|
|
1390
|
-
<div className="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
|
|
1391
|
-
<CheckIcon className="w-5 h-5 text-blue-600" />
|
|
520
|
+
{/* Best practices */}
|
|
521
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
522
|
+
<p className="text-fm-secondary font-fm-text text-fm-md leading-fm-xl">
|
|
523
|
+
Accessibility best practices for Toast:
|
|
524
|
+
</p>
|
|
525
|
+
<ul className="mt-3 list-none space-y-2">
|
|
526
|
+
{[
|
|
527
|
+
'Sonner mounts toasts in a <ol> with aria-live="polite" by default — assertive is used automatically for error toasts.',
|
|
528
|
+
"Keep messages brief and descriptive so screen readers can convey them in one phrase without cutting off.",
|
|
529
|
+
"For destructive or time-sensitive actions, use a duration of at least 5 000 ms or set duration: Infinity with a manual dismiss action.",
|
|
530
|
+
"Action and cancel buttons inside toasts are focusable via Tab — ensure their labels (label prop) are self-explanatory without surrounding context.",
|
|
531
|
+
"Never convey information through color alone — always pair a type (success/error/warning/info) with a clear message string.",
|
|
532
|
+
"Use the description prop to add context without cluttering the title — screen readers announce both in sequence.",
|
|
533
|
+
"toast.dismiss() is the programmatic way to remove all active toasts — useful for route changes or cleanup on unmount.",
|
|
534
|
+
].map((tip) => (
|
|
535
|
+
<li key={tip}>
|
|
536
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
537
|
+
• {tip}
|
|
538
|
+
</p>
|
|
539
|
+
</li>
|
|
540
|
+
))}
|
|
541
|
+
</ul>
|
|
1392
542
|
</div>
|
|
1393
|
-
<span className="font-medium">Custom message</span>
|
|
1394
|
-
<button onClick={() => toast.dismiss(t)}>
|
|
1395
|
-
<XIcon className="w-4 h-4" />
|
|
1396
|
-
</button>
|
|
1397
543
|
</div>
|
|
1398
|
-
|
|
1399
|
-
))`}
|
|
1400
|
-
</pre>
|
|
1401
|
-
</div>
|
|
1402
|
-
</div>
|
|
1403
|
-
</div>
|
|
1404
|
-
</div>
|
|
1405
|
-
)
|
|
1406
|
-
},
|
|
544
|
+
),
|
|
1407
545
|
parameters: {
|
|
1408
546
|
docs: {
|
|
1409
547
|
description: {
|
|
1410
|
-
story:
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
**Key Features:**
|
|
1414
|
-
- Full design control with custom React components
|
|
1415
|
-
- Access to toast state (\`t\` parameter) for animations
|
|
1416
|
-
- Custom dismiss logic and interactions
|
|
1417
|
-
- Integration with your design system
|
|
1418
|
-
- Advanced layouts like cards, progress bars, and glassmorphism
|
|
1419
|
-
- Custom icons, avatars, and action buttons
|
|
1420
|
-
|
|
1421
|
-
**Use Cases:**
|
|
1422
|
-
- Branded notifications matching your design system
|
|
1423
|
-
- Rich notifications with images and multiple actions
|
|
1424
|
-
- Progress indicators for uploads/downloads
|
|
1425
|
-
- Social media style notifications
|
|
1426
|
-
- Achievement and gamification toasts
|
|
1427
|
-
- Complex form validation feedback
|
|
1428
|
-
|
|
1429
|
-
The \`t\` parameter in the render function represents the toast's visibility state, allowing you to create smooth enter/exit animations.
|
|
1430
|
-
`,
|
|
548
|
+
story:
|
|
549
|
+
"Accessibility-focused story demonstrating ARIA live region announcements, keyboard dismissal via Tab focus and Escape, and the distinction between polite (aria-live=polite / role=status for info toasts) and assertive (aria-live=assertive / role=alert for error toasts) announcement modes. The infoBox at the bottom lists actionable best practices for building accessible toast notifications.",
|
|
1431
550
|
},
|
|
1432
551
|
},
|
|
1433
552
|
},
|