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