aural-ui 4.0.1 → 4.2.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/README.md +8 -1
- package/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1228
- package/dist/components/avatar/Avatar.stories.tsx +219 -235
- 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/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 -636
- 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 +530 -867
- package/dist/components/dialog/Dialog.stories.tsx +501 -950
- package/dist/components/divider/Divider.stories.tsx +264 -527
- package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
- package/dist/components/drawer/Drawer.stories.tsx +659 -1023
- package/dist/components/dropdown/Dropdown.stories.tsx +643 -1028
- 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 -1254
- 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 +484 -835
- package/dist/components/marquee/Marquee.stories.tsx +356 -712
- package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -422
- package/dist/components/overlay/Overlay.stories.tsx +452 -824
- package/dist/components/pagination/Pagination.stories.tsx +721 -210
- package/dist/components/popover/Popover.stories.tsx +481 -896
- package/dist/components/radio/Radio.stories.tsx +432 -124
- package/dist/components/resizable/Resizable.stories.tsx +495 -799
- package/dist/components/scroll-area/ScrollArea.stories.tsx +383 -1059
- package/dist/components/search/Search.stories.tsx +312 -595
- package/dist/components/select/Select.stories.tsx +684 -789
- package/dist/components/sheet/Sheet.stories.tsx +671 -950
- package/dist/components/skelton/Skelton.stories.tsx +230 -764
- package/dist/components/slider/Slider.stories.tsx +383 -760
- 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 -916
- package/dist/components/tabs/Tabs.stories.tsx +458 -1455
- 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 -154
- package/dist/components/toast/Toast.stories.tsx +452 -1339
- package/dist/components/toggle/Toggle.stories.tsx +488 -931
- package/dist/components/tooltip/Tooltip.stories.tsx +344 -1388
- 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 +223 -1060
- package/dist/icons/alert-icon/AlertIcon.stories.tsx +106 -968
- package/dist/icons/all-icons.tsx +37 -16
- package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +137 -1010
- package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +145 -935
- package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +132 -1046
- package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +134 -986
- package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +135 -1028
- package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +133 -971
- package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +145 -1123
- package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +143 -1252
- package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +123 -632
- package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +141 -1223
- package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +164 -1018
- package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +121 -1236
- package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +121 -1213
- package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +116 -893
- package/dist/icons/camera-icon/CameraIcon.stories.tsx +109 -1254
- package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +114 -975
- package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +157 -994
- package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +160 -992
- package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +140 -970
- package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +126 -993
- package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +144 -987
- package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +141 -1007
- package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +147 -1187
- package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +110 -476
- package/dist/icons/coin-icon/CoinIcon.stories.tsx +120 -1364
- package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +113 -1360
- package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +111 -942
- package/dist/icons/command-icon/CommandIcon.stories.tsx +124 -1087
- package/dist/icons/copy-icon/CopyIcon.stories.tsx +119 -996
- package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +144 -1046
- package/dist/icons/cross-icon/CrossIcon.stories.tsx +136 -999
- package/dist/icons/download-icon/DownloadIcon.stories.tsx +123 -857
- package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +121 -1080
- package/dist/icons/email-icon/EmailIcon.stories.tsx +112 -979
- package/dist/icons/expand-icon/ExpandIcon.stories.tsx +109 -1146
- package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +141 -1068
- package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +140 -1081
- package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +124 -1050
- package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +123 -1091
- package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +122 -633
- package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +116 -1087
- package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +166 -1020
- package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +112 -1182
- package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +112 -1155
- package/dist/icons/globe-icon/GlobeIcon.stories.tsx +127 -325
- package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +142 -985
- package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +116 -1217
- package/dist/icons/head-icon/HeadIcon.stories.tsx +108 -953
- package/dist/icons/heart-icon/HeartIcon.stories.tsx +117 -1060
- package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +116 -716
- package/dist/icons/image-icon/ImageIcon.stories.tsx +102 -1164
- package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +108 -1233
- package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +133 -1289
- package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +155 -1012
- package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +158 -1438
- package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +121 -1011
- package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +116 -981
- package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +116 -979
- package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +105 -1252
- package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +151 -1554
- package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +107 -1227
- package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +116 -707
- package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +119 -1226
- package/dist/icons/message-icon/MessageIcon.stories.tsx +111 -557
- package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +112 -1198
- package/dist/icons/moon-icon/MoonIcon.stories.tsx +117 -557
- package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +106 -1235
- package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +112 -1185
- package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +116 -1012
- package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +108 -1137
- package/dist/icons/notes-icon/NotesIcon.stories.tsx +116 -1138
- package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +106 -1146
- package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +119 -719
- package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +110 -999
- package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +109 -912
- package/dist/icons/pause-icon/PauseIcon.stories.tsx +110 -1041
- package/dist/icons/pencil-icon/PencilIcon.stories.tsx +112 -1109
- package/dist/icons/phone-icon/PhoneIcon.stories.tsx +112 -1023
- package/dist/icons/plus-icon/PlusIcon.stories.tsx +103 -1132
- package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +104 -870
- package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +99 -476
- package/dist/icons/search-icon/SearchIcon.stories.tsx +108 -1161
- package/dist/icons/setting-icon/SettingIcon.stories.tsx +104 -1009
- package/dist/icons/share-icon/ShareIcon.stories.tsx +117 -1064
- package/dist/icons/shield-icon/ShieldIcon.stories.tsx +114 -974
- package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +134 -1160
- package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +169 -1017
- package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +161 -1016
- package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +102 -1001
- package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +155 -593
- package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +155 -608
- package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +142 -712
- package/dist/icons/star-icon/StarIcon.stories.tsx +120 -946
- package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +109 -1013
- package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +113 -891
- package/dist/icons/sun-icon/SunIcon.stories.tsx +117 -864
- package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +113 -989
- package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +120 -1027
- package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +153 -1476
- package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +143 -1187
- package/dist/icons/tick-icon/TickIcon.stories.tsx +142 -1322
- package/dist/icons/trash-icon/TrashIcon.stories.tsx +105 -970
- package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +154 -1457
- package/dist/icons/upload-icon/UploadIcon.stories.tsx +112 -930
- package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +115 -1019
- package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +122 -1092
- package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +120 -1401
- package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +107 -1212
- package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +109 -1122
- package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +112 -1124
- package/dist/icons/warning-icon/WarningIcon.stories.tsx +119 -1083
- package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +158 -983
- package/dist/index.cjs +90 -90
- package/dist/index.js +90 -90
- package/package.json +8 -3
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import React, { useState } from "react"
|
|
3
3
|
import { Button } from "@components/button"
|
|
4
|
-
import { Checkbox } from "@components/checkbox"
|
|
5
|
-
import Input from "@components/input"
|
|
6
|
-
import { Label } from "@components/label"
|
|
7
|
-
import Textarea from "@components/textarea"
|
|
8
|
-
import { AlertIcon } from "@icons/alert-icon"
|
|
9
|
-
import { EditBigIcon } from "@icons/edit-big-icon"
|
|
10
|
-
import { LightBulbSimpleIcon } from "@icons/light-bulb-simple-icon"
|
|
11
|
-
import { TickIcon } from "@icons/tick-icon"
|
|
12
|
-
import { TrashIcon } from "@icons/trash-icon"
|
|
13
4
|
import type { Meta, StoryObj } from "@storybook/react-vite"
|
|
14
5
|
|
|
6
|
+
import { AuralComponentDocsPage } from "src/ui/story-spec/components/component-story-docs-page"
|
|
7
|
+
|
|
15
8
|
import {
|
|
16
9
|
Dialog,
|
|
17
10
|
DialogClose,
|
|
@@ -29,90 +22,41 @@ const meta: Meta<typeof DialogContent> = {
|
|
|
29
22
|
component: Dialog,
|
|
30
23
|
parameters: {
|
|
31
24
|
layout: "centered",
|
|
32
|
-
backgrounds: {
|
|
33
|
-
default: "dark",
|
|
34
|
-
values: [
|
|
35
|
-
{ name: "dark", value: "#0a0a0a" },
|
|
36
|
-
{ name: "light", value: "#ffffff" },
|
|
37
|
-
],
|
|
38
|
-
},
|
|
39
25
|
docs: {
|
|
40
26
|
description: {
|
|
41
|
-
component:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
A modal dialog component built on Radix UI primitives with customizable overlays, animations, variant styling, and configurable border sides. Includes React 18 compatibility fixes for pointer-events issues.
|
|
45
|
-
|
|
46
|
-
## Features
|
|
47
|
-
|
|
48
|
-
- **Multiple Variants**: Neutral, positive, negative, warning, and info styles
|
|
49
|
-
- **Configurable Borders**: Support for all sides, specific sides, or no borders
|
|
50
|
-
- **Custom Overlays**: Configurable opacity, glass effect, and noise texture
|
|
51
|
-
- **Smooth Animations**: Zoom in/out animations with duration control
|
|
52
|
-
- **Accessible**: Full keyboard navigation and screen reader support
|
|
53
|
-
- **Flexible Content**: Header, footer, and body sections with custom styling
|
|
54
|
-
- **Close Button**: Positioned close button with icon
|
|
55
|
-
- **Portal Rendering**: Renders outside normal DOM hierarchy
|
|
56
|
-
- **Focus Management**: Automatic focus trapping and restoration
|
|
57
|
-
- **React 18 Fix**: Includes \`useDialogCleanup\` hook for pointer-events issues
|
|
58
|
-
|
|
59
|
-
## Border Configuration
|
|
60
|
-
|
|
61
|
-
The \`borderConfig\` prop allows you to control which sides of the dialog have gradient borders:
|
|
62
|
-
|
|
63
|
-
- \`"none"\` - No borders
|
|
64
|
-
- \`"all"\` - All four sides
|
|
65
|
-
- \`"top"\` - Top border only (default)
|
|
66
|
-
- \`["top", "bottom"]\` - Top and bottom borders
|
|
67
|
-
- \`["left", "right"]\` - Left and right borders
|
|
68
|
-
- \`["top", "left", "right"]\` - Top, left, and right borders
|
|
69
|
-
|
|
70
|
-
## Usage Examples
|
|
71
|
-
|
|
72
|
-
### Basic Dialog with Border Configuration
|
|
73
|
-
\`\`\`tsx
|
|
74
|
-
import { Dialog, DialogContent, DialogTrigger } from '@/components/dialog'
|
|
75
|
-
|
|
76
|
-
// All borders
|
|
77
|
-
<DialogContent borderConfig="all" variant="positive">
|
|
78
|
-
Content with borders on all sides
|
|
79
|
-
</DialogContent>
|
|
80
|
-
|
|
81
|
-
// Specific sides
|
|
82
|
-
<DialogContent borderConfig={["top", "bottom"]} variant="warning">
|
|
83
|
-
Content with top and bottom borders
|
|
84
|
-
</DialogContent>
|
|
85
|
-
|
|
86
|
-
// No borders
|
|
87
|
-
<DialogContent borderConfig="none" variant="neutral">
|
|
88
|
-
Content without any borders
|
|
89
|
-
</DialogContent>
|
|
90
|
-
\`\`\`
|
|
91
|
-
`,
|
|
27
|
+
component:
|
|
28
|
+
"A modal dialog built on Radix UI primitives with configurable variant borders, glass/noise overlays, and zoom animations. Includes useDialogCleanup for React 18 pointer-events compatibility.",
|
|
92
29
|
},
|
|
30
|
+
page: () => (
|
|
31
|
+
<AuralComponentDocsPage
|
|
32
|
+
features={[
|
|
33
|
+
{
|
|
34
|
+
title: "5 Variant Borders",
|
|
35
|
+
description: "Neutral to negative",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
title: "Glass & Noise Overlay",
|
|
39
|
+
description: "Blur and grain levels",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
title: "Focus Trap",
|
|
43
|
+
description: "Keyboard & ARIA safe",
|
|
44
|
+
},
|
|
45
|
+
]}
|
|
46
|
+
/>
|
|
47
|
+
),
|
|
93
48
|
},
|
|
94
49
|
},
|
|
95
50
|
argTypes: {
|
|
96
51
|
variant: {
|
|
97
52
|
control: { type: "select" },
|
|
98
53
|
options: ["neutral", "positive", "negative", "warning", "info"],
|
|
99
|
-
description: "Dialog variant
|
|
54
|
+
description: "Dialog border-shadow variant",
|
|
100
55
|
},
|
|
101
56
|
borderConfig: {
|
|
102
57
|
control: { type: "select" },
|
|
103
|
-
options: [
|
|
104
|
-
|
|
105
|
-
"all",
|
|
106
|
-
"top",
|
|
107
|
-
"bottom",
|
|
108
|
-
"left",
|
|
109
|
-
"right",
|
|
110
|
-
["top", "bottom"],
|
|
111
|
-
["left", "right"],
|
|
112
|
-
["top", "left", "right"],
|
|
113
|
-
["bottom", "left", "right"],
|
|
114
|
-
],
|
|
115
|
-
description: "Border configuration - which sides to show borders on",
|
|
58
|
+
options: ["none", "all", "top", "bottom", "left", "right"],
|
|
59
|
+
description: "Which sides display gradient border accents",
|
|
116
60
|
},
|
|
117
61
|
opacity: {
|
|
118
62
|
control: { type: "select" },
|
|
@@ -122,7 +66,7 @@ import { Dialog, DialogContent, DialogTrigger } from '@/components/dialog'
|
|
|
122
66
|
glass: {
|
|
123
67
|
control: { type: "select" },
|
|
124
68
|
options: ["high", "medium", "low", "none"],
|
|
125
|
-
description: "
|
|
69
|
+
description: "Backdrop blur intensity",
|
|
126
70
|
},
|
|
127
71
|
noise: {
|
|
128
72
|
control: { type: "select" },
|
|
@@ -131,7 +75,7 @@ import { Dialog, DialogContent, DialogTrigger } from '@/components/dialog'
|
|
|
131
75
|
},
|
|
132
76
|
showCloseButton: {
|
|
133
77
|
control: "boolean",
|
|
134
|
-
description: "Show
|
|
78
|
+
description: "Show the floating close button above the dialog",
|
|
135
79
|
},
|
|
136
80
|
},
|
|
137
81
|
tags: ["autodocs"],
|
|
@@ -140,964 +84,541 @@ import { Dialog, DialogContent, DialogTrigger } from '@/components/dialog'
|
|
|
140
84
|
export default meta
|
|
141
85
|
type Story = StoryObj<typeof Dialog>
|
|
142
86
|
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
<span className="text-fm-primary">
|
|
181
|
-
{Array.isArray(borderConfig)
|
|
182
|
-
? `[${borderConfig.join(", ")}]`
|
|
183
|
-
: borderConfig}
|
|
184
|
-
</span>
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
</div>
|
|
188
|
-
</div>
|
|
189
|
-
<DialogFooter>
|
|
190
|
-
<DialogClose asChild>
|
|
191
|
-
<Button onClick={handleDialogClose}>Close</Button>
|
|
192
|
-
</DialogClose>
|
|
193
|
-
</DialogFooter>
|
|
194
|
-
</DialogContent>
|
|
195
|
-
</Dialog>
|
|
196
|
-
)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return (
|
|
200
|
-
<div className="space-y-8">
|
|
201
|
-
<div className="text-center">
|
|
202
|
-
<h3 className="text-fm-primary mb-2 font-medium">
|
|
203
|
-
Border Configurations
|
|
204
|
-
</h3>
|
|
205
|
-
<p className="text-fm-secondary text-sm">
|
|
206
|
-
Explore different border configurations for dialog variants
|
|
207
|
-
</p>
|
|
208
|
-
</div>
|
|
209
|
-
|
|
210
|
-
<div className="space-y-6">
|
|
211
|
-
{/* All Borders */}
|
|
212
|
-
<div className="space-y-4">
|
|
213
|
-
<h4 className="text-fm-secondary text-sm font-medium">
|
|
214
|
-
All Borders
|
|
215
|
-
</h4>
|
|
216
|
-
<div className="flex flex-wrap gap-2">
|
|
217
|
-
<BorderConfigDialog
|
|
218
|
-
borderConfig="all"
|
|
219
|
-
variant="neutral"
|
|
220
|
-
title="All - Neutral"
|
|
221
|
-
description="Dialog with borders on all four sides"
|
|
222
|
-
/>
|
|
223
|
-
<BorderConfigDialog
|
|
224
|
-
borderConfig="all"
|
|
225
|
-
variant="positive"
|
|
226
|
-
title="All - Success"
|
|
227
|
-
description="Success dialog with complete border frame"
|
|
228
|
-
/>
|
|
229
|
-
<BorderConfigDialog
|
|
230
|
-
borderConfig="all"
|
|
231
|
-
variant="negative"
|
|
232
|
-
title="All - Error"
|
|
233
|
-
description="Error dialog with full border emphasis"
|
|
234
|
-
/>
|
|
235
|
-
</div>
|
|
236
|
-
</div>
|
|
237
|
-
|
|
238
|
-
{/* Single Sides */}
|
|
239
|
-
<div className="space-y-4">
|
|
240
|
-
<h4 className="text-fm-secondary text-sm font-medium">
|
|
241
|
-
Single Side Borders
|
|
242
|
-
</h4>
|
|
243
|
-
<div className="flex flex-wrap gap-2">
|
|
244
|
-
<BorderConfigDialog
|
|
245
|
-
borderConfig="top"
|
|
246
|
-
variant="info"
|
|
247
|
-
title="Top Only"
|
|
248
|
-
description="Dialog with top border accent"
|
|
249
|
-
/>
|
|
250
|
-
<BorderConfigDialog
|
|
251
|
-
borderConfig="bottom"
|
|
252
|
-
variant="warning"
|
|
253
|
-
title="Bottom Only"
|
|
254
|
-
description="Dialog with bottom border accent"
|
|
255
|
-
/>
|
|
256
|
-
<BorderConfigDialog
|
|
257
|
-
borderConfig="left"
|
|
258
|
-
variant="positive"
|
|
259
|
-
title="Left Only"
|
|
260
|
-
description="Dialog with left border accent"
|
|
261
|
-
/>
|
|
262
|
-
<BorderConfigDialog
|
|
263
|
-
borderConfig="right"
|
|
264
|
-
variant="negative"
|
|
265
|
-
title="Right Only"
|
|
266
|
-
description="Dialog with right border accent"
|
|
267
|
-
/>
|
|
268
|
-
</div>
|
|
269
|
-
</div>
|
|
270
|
-
|
|
271
|
-
{/* Two Sides */}
|
|
272
|
-
<div className="space-y-4">
|
|
273
|
-
<h4 className="text-fm-secondary text-sm font-medium">
|
|
274
|
-
Two Side Borders
|
|
275
|
-
</h4>
|
|
276
|
-
<div className="flex flex-wrap gap-2">
|
|
277
|
-
<BorderConfigDialog
|
|
278
|
-
borderConfig={["top", "bottom"]}
|
|
279
|
-
variant="neutral"
|
|
280
|
-
title="Top + Bottom"
|
|
281
|
-
description="Horizontal border emphasis"
|
|
282
|
-
/>
|
|
283
|
-
<BorderConfigDialog
|
|
284
|
-
borderConfig={["left", "right"]}
|
|
285
|
-
variant="info"
|
|
286
|
-
title="Left + Right"
|
|
287
|
-
description="Vertical border emphasis"
|
|
288
|
-
/>
|
|
289
|
-
<BorderConfigDialog
|
|
290
|
-
borderConfig={["top", "left"]}
|
|
291
|
-
variant="warning"
|
|
292
|
-
title="Top + Left"
|
|
293
|
-
description="Corner border accent"
|
|
294
|
-
/>
|
|
295
|
-
<BorderConfigDialog
|
|
296
|
-
borderConfig={["bottom", "right"]}
|
|
297
|
-
variant="positive"
|
|
298
|
-
title="Bottom + Right"
|
|
299
|
-
description="Opposite corner accent"
|
|
300
|
-
/>
|
|
301
|
-
</div>
|
|
302
|
-
</div>
|
|
303
|
-
|
|
304
|
-
{/* Three Sides */}
|
|
305
|
-
<div className="space-y-4">
|
|
306
|
-
<h4 className="text-fm-secondary text-sm font-medium">
|
|
307
|
-
Three Side Borders
|
|
308
|
-
</h4>
|
|
309
|
-
<div className="flex flex-wrap gap-2">
|
|
310
|
-
<BorderConfigDialog
|
|
311
|
-
borderConfig={["top", "left", "right"]}
|
|
312
|
-
variant="negative"
|
|
313
|
-
title="Top + Left + Right"
|
|
314
|
-
description="Open bottom design"
|
|
315
|
-
/>
|
|
316
|
-
<BorderConfigDialog
|
|
317
|
-
borderConfig={["bottom", "left", "right"]}
|
|
318
|
-
variant="warning"
|
|
319
|
-
title="Bottom + Left + Right"
|
|
320
|
-
description="Open top design"
|
|
321
|
-
/>
|
|
322
|
-
</div>
|
|
323
|
-
</div>
|
|
324
|
-
|
|
325
|
-
{/* No Border */}
|
|
326
|
-
<div className="space-y-4">
|
|
327
|
-
<h4 className="text-fm-secondary text-sm font-medium">
|
|
328
|
-
No Borders
|
|
329
|
-
</h4>
|
|
330
|
-
<div className="flex flex-wrap gap-2">
|
|
331
|
-
<BorderConfigDialog
|
|
332
|
-
borderConfig="none"
|
|
333
|
-
variant="neutral"
|
|
334
|
-
title="No Borders"
|
|
335
|
-
description="Clean minimal design without border accents"
|
|
336
|
-
/>
|
|
337
|
-
</div>
|
|
338
|
-
</div>
|
|
87
|
+
// ─── Configurations ────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
const ConfigDialog = ({
|
|
90
|
+
borderConfig,
|
|
91
|
+
variant,
|
|
92
|
+
opacity,
|
|
93
|
+
glass,
|
|
94
|
+
noise,
|
|
95
|
+
showCloseButton,
|
|
96
|
+
triggerLabel,
|
|
97
|
+
title,
|
|
98
|
+
description,
|
|
99
|
+
}: any) => {
|
|
100
|
+
const { handleDialogClose } = useDialogCleanup({ threshold: 100 })
|
|
101
|
+
return (
|
|
102
|
+
<Dialog>
|
|
103
|
+
<DialogTrigger asChild>
|
|
104
|
+
<Button variant="outline" size="sm">
|
|
105
|
+
{triggerLabel}
|
|
106
|
+
</Button>
|
|
107
|
+
</DialogTrigger>
|
|
108
|
+
<DialogContent
|
|
109
|
+
variant={variant}
|
|
110
|
+
borderConfig={borderConfig}
|
|
111
|
+
opacity={opacity}
|
|
112
|
+
glass={glass}
|
|
113
|
+
noise={noise}
|
|
114
|
+
showCloseButton={showCloseButton}
|
|
115
|
+
>
|
|
116
|
+
<DialogHeader>
|
|
117
|
+
<DialogTitle>{title}</DialogTitle>
|
|
118
|
+
<DialogDescription>{description}</DialogDescription>
|
|
119
|
+
</DialogHeader>
|
|
120
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
|
|
121
|
+
<code className="text-fm-secondary text-fm-sm leading-fm-sm font-(--font-fm-mono)">
|
|
122
|
+
{`variant="${variant}" borderConfig="${Array.isArray(borderConfig) ? `[${borderConfig.join(", ")}]` : borderConfig}"`}
|
|
123
|
+
</code>
|
|
339
124
|
</div>
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
},
|
|
349
|
-
},
|
|
350
|
-
},
|
|
125
|
+
<DialogFooter>
|
|
126
|
+
<DialogClose asChild>
|
|
127
|
+
<Button onClick={handleDialogClose}>Close</Button>
|
|
128
|
+
</DialogClose>
|
|
129
|
+
</DialogFooter>
|
|
130
|
+
</DialogContent>
|
|
131
|
+
</Dialog>
|
|
132
|
+
)
|
|
351
133
|
}
|
|
352
134
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
onPointerDownOutside={handleClose}
|
|
375
|
-
>
|
|
376
|
-
<DialogHeader>
|
|
377
|
-
<DialogTitle>Dialog with React 18 Fix</DialogTitle>
|
|
378
|
-
<DialogDescription>
|
|
379
|
-
This dialog uses useDialogCleanup to prevent pointer-events
|
|
380
|
-
issues in React 18.
|
|
381
|
-
</DialogDescription>
|
|
382
|
-
</DialogHeader>
|
|
383
|
-
<div className="py-4">
|
|
384
|
-
<div className="border-fm-info bg-fm-surface-info-sec rounded-lg border p-3">
|
|
385
|
-
<p className="text-fm-info text-sm">
|
|
386
|
-
The useDialogCleanup hook ensures that pointer-events: none
|
|
387
|
-
doesn't get stuck on the body element when the dialog closes,
|
|
388
|
-
which was a common issue in React 18.
|
|
389
|
-
</p>
|
|
390
|
-
</div>
|
|
391
|
-
</div>
|
|
392
|
-
<DialogFooter>
|
|
393
|
-
<DialogClose asChild>
|
|
394
|
-
<Button variant="outline" onClick={handleClose}>
|
|
395
|
-
Cancel
|
|
396
|
-
</Button>
|
|
397
|
-
</DialogClose>
|
|
398
|
-
<Button onClick={handleClose}>Confirm</Button>
|
|
399
|
-
</DialogFooter>
|
|
400
|
-
</DialogContent>
|
|
401
|
-
</Dialog>
|
|
402
|
-
)
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return (
|
|
406
|
-
<div className="space-y-8">
|
|
407
|
-
<div className="text-center">
|
|
408
|
-
<h3 className="text-fm-primary mb-2 font-medium">
|
|
409
|
-
Dialog Cleanup Hook
|
|
410
|
-
</h3>
|
|
411
|
-
<p className="text-fm-secondary text-sm">
|
|
412
|
-
Demonstrates useDialogCleanup hook for React 18 compatibility
|
|
413
|
-
</p>
|
|
414
|
-
</div>
|
|
415
|
-
<div className="flex justify-center">
|
|
416
|
-
<DialogWithCleanup />
|
|
135
|
+
export const Configurations: Story = {
|
|
136
|
+
render: () => (
|
|
137
|
+
<div className="space-y-8">
|
|
138
|
+
{/* Variant types */}
|
|
139
|
+
<div className="space-y-3">
|
|
140
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
141
|
+
Variant Types
|
|
142
|
+
</h4>
|
|
143
|
+
<div className="flex flex-wrap gap-2">
|
|
144
|
+
{(
|
|
145
|
+
["neutral", "positive", "negative", "warning", "info"] as const
|
|
146
|
+
).map((v) => (
|
|
147
|
+
<ConfigDialog
|
|
148
|
+
key={v}
|
|
149
|
+
variant={v}
|
|
150
|
+
borderConfig={["top"]}
|
|
151
|
+
triggerLabel={v.charAt(0).toUpperCase() + v.slice(1)}
|
|
152
|
+
title={`${v.charAt(0).toUpperCase() + v.slice(1)} Dialog`}
|
|
153
|
+
description={`Top border accent with ${v} variant shadow.`}
|
|
154
|
+
/>
|
|
155
|
+
))}
|
|
417
156
|
</div>
|
|
418
157
|
</div>
|
|
419
|
-
)
|
|
420
|
-
},
|
|
421
|
-
parameters: {
|
|
422
|
-
docs: {
|
|
423
|
-
description: {
|
|
424
|
-
story:
|
|
425
|
-
"Example showing how to use useDialogCleanup hook to fix React 18 pointer-events issues.",
|
|
426
|
-
},
|
|
427
|
-
},
|
|
428
|
-
},
|
|
429
|
-
}
|
|
430
158
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
description,
|
|
439
|
-
children,
|
|
440
|
-
}: any) => {
|
|
441
|
-
const { handleDialogClose } = useDialogCleanup({ threshold: 100 })
|
|
442
|
-
|
|
443
|
-
return (
|
|
444
|
-
<Dialog>
|
|
445
|
-
<DialogTrigger asChild>
|
|
446
|
-
<Button variant="outline">{title}</Button>
|
|
447
|
-
</DialogTrigger>
|
|
448
|
-
<DialogContent
|
|
449
|
-
variant={variant}
|
|
450
|
-
borderConfig={borderConfig}
|
|
451
|
-
//@ts-expect-error onEscapeKeyDown available in Radix v2.0.0
|
|
452
|
-
onEscapeKeyDown={handleDialogClose}
|
|
453
|
-
onPointerDownOutside={handleDialogClose}
|
|
454
|
-
>
|
|
455
|
-
<DialogHeader>
|
|
456
|
-
<DialogTitle>{title}</DialogTitle>
|
|
457
|
-
<DialogDescription>{description}</DialogDescription>
|
|
458
|
-
</DialogHeader>
|
|
459
|
-
<div className="py-4">{children}</div>
|
|
460
|
-
<DialogFooter>
|
|
461
|
-
<DialogClose asChild>
|
|
462
|
-
<Button variant="outline" onClick={handleDialogClose}>
|
|
463
|
-
Cancel
|
|
464
|
-
</Button>
|
|
465
|
-
</DialogClose>
|
|
466
|
-
<Button onClick={handleDialogClose}>Continue</Button>
|
|
467
|
-
</DialogFooter>
|
|
468
|
-
</DialogContent>
|
|
469
|
-
</Dialog>
|
|
470
|
-
)
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return (
|
|
474
|
-
<div className="space-y-8">
|
|
475
|
-
<div className="text-center">
|
|
476
|
-
<h3 className="text-fm-primary mb-2 font-medium">
|
|
477
|
-
Dialog Variants with Border Styles
|
|
478
|
-
</h3>
|
|
479
|
-
<p className="text-fm-secondary text-sm">
|
|
480
|
-
Different dialog variants with customized border configurations
|
|
481
|
-
</p>
|
|
482
|
-
</div>
|
|
483
|
-
|
|
484
|
-
<div className="flex flex-wrap justify-center gap-4">
|
|
485
|
-
<DialogWithCleanupVariant
|
|
159
|
+
{/* Border configurations */}
|
|
160
|
+
<div className="space-y-3">
|
|
161
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
162
|
+
Border Configurations
|
|
163
|
+
</h4>
|
|
164
|
+
<div className="flex flex-wrap gap-2">
|
|
165
|
+
<ConfigDialog
|
|
486
166
|
variant="neutral"
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
167
|
+
borderConfig="none"
|
|
168
|
+
triggerLabel="No border"
|
|
169
|
+
title="No Borders"
|
|
170
|
+
description="Clean dialog with no accent borders."
|
|
171
|
+
/>
|
|
172
|
+
<ConfigDialog
|
|
173
|
+
variant="info"
|
|
174
|
+
borderConfig={["top"]}
|
|
175
|
+
triggerLabel="Top only"
|
|
176
|
+
title="Top Border"
|
|
177
|
+
description="Single top accent — the default configuration."
|
|
178
|
+
/>
|
|
179
|
+
<ConfigDialog
|
|
497
180
|
variant="positive"
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
</div>
|
|
505
|
-
<p className="text-fm-secondary text-sm">
|
|
506
|
-
Success dialogs with full border frame provide strong positive
|
|
507
|
-
feedback.
|
|
508
|
-
</p>
|
|
509
|
-
</DialogWithCleanupVariant>
|
|
510
|
-
|
|
511
|
-
<DialogWithCleanupVariant
|
|
181
|
+
borderConfig="all"
|
|
182
|
+
triggerLabel="All sides"
|
|
183
|
+
title="All Borders"
|
|
184
|
+
description="Full border frame on all four sides."
|
|
185
|
+
/>
|
|
186
|
+
<ConfigDialog
|
|
512
187
|
variant="warning"
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
<span className="text-fm-warning font-medium">Warning</span>
|
|
520
|
-
</div>
|
|
521
|
-
<p className="text-fm-warning-sec text-sm">
|
|
522
|
-
Warning dialogs with horizontal borders create focused
|
|
523
|
-
attention.
|
|
524
|
-
</p>
|
|
525
|
-
</div>
|
|
526
|
-
</DialogWithCleanupVariant>
|
|
527
|
-
|
|
528
|
-
<DialogWithCleanupVariant
|
|
188
|
+
borderConfig={["top", "bottom"]}
|
|
189
|
+
triggerLabel="Top + Bottom"
|
|
190
|
+
title="Horizontal Borders"
|
|
191
|
+
description="Top and bottom accent borders."
|
|
192
|
+
/>
|
|
193
|
+
<ConfigDialog
|
|
529
194
|
variant="negative"
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
<span className="text-fm-negative font-medium">Error</span>
|
|
537
|
-
</div>
|
|
538
|
-
<p className="text-fm-negative-sec text-sm">
|
|
539
|
-
Error dialogs with vertical borders provide urgent visual cues.
|
|
540
|
-
</p>
|
|
541
|
-
</div>
|
|
542
|
-
</DialogWithCleanupVariant>
|
|
543
|
-
|
|
544
|
-
<DialogWithCleanupVariant
|
|
195
|
+
borderConfig={["left", "right"]}
|
|
196
|
+
triggerLabel="Left + Right"
|
|
197
|
+
title="Vertical Borders"
|
|
198
|
+
description="Left and right accent borders."
|
|
199
|
+
/>
|
|
200
|
+
<ConfigDialog
|
|
545
201
|
variant="info"
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
<LightBulbSimpleIcon className="text-fm-info h-4 w-4" />
|
|
552
|
-
<span className="text-fm-info font-medium">Information</span>
|
|
553
|
-
</div>
|
|
554
|
-
<p className="text-fm-info-sec text-sm">
|
|
555
|
-
Clean info dialogs without borders for minimal, focused content
|
|
556
|
-
presentation.
|
|
557
|
-
</p>
|
|
558
|
-
</div>
|
|
559
|
-
</DialogWithCleanupVariant>
|
|
202
|
+
borderConfig={["top", "left", "right"]}
|
|
203
|
+
triggerLabel="Three sides"
|
|
204
|
+
title="Three-Side Borders"
|
|
205
|
+
description="Open-bottom border configuration."
|
|
206
|
+
/>
|
|
560
207
|
</div>
|
|
561
208
|
</div>
|
|
562
|
-
|
|
563
|
-
|
|
209
|
+
|
|
210
|
+
{/* Glass / noise overlay */}
|
|
211
|
+
<div className="space-y-3">
|
|
212
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
213
|
+
Glass & Noise Overlay Options
|
|
214
|
+
</h4>
|
|
215
|
+
<div className="flex flex-wrap gap-2">
|
|
216
|
+
<ConfigDialog
|
|
217
|
+
variant="neutral"
|
|
218
|
+
borderConfig={["top"]}
|
|
219
|
+
opacity="low"
|
|
220
|
+
triggerLabel="Low opacity"
|
|
221
|
+
title="Low Opacity Overlay"
|
|
222
|
+
description="Subtle background dimming, more of the page remains visible."
|
|
223
|
+
/>
|
|
224
|
+
<ConfigDialog
|
|
225
|
+
variant="neutral"
|
|
226
|
+
borderConfig={["top"]}
|
|
227
|
+
opacity="high"
|
|
228
|
+
triggerLabel="High opacity"
|
|
229
|
+
title="High Opacity Overlay"
|
|
230
|
+
description="Strong background dimming that focuses attention on the dialog."
|
|
231
|
+
/>
|
|
232
|
+
<ConfigDialog
|
|
233
|
+
variant="neutral"
|
|
234
|
+
borderConfig={["top"]}
|
|
235
|
+
glass="high"
|
|
236
|
+
triggerLabel="High glass"
|
|
237
|
+
title="Glass Blur Overlay"
|
|
238
|
+
description="Frosted-glass backdrop blur effect at high intensity."
|
|
239
|
+
/>
|
|
240
|
+
<ConfigDialog
|
|
241
|
+
variant="neutral"
|
|
242
|
+
borderConfig={["top"]}
|
|
243
|
+
noise="medium"
|
|
244
|
+
triggerLabel="Noise texture"
|
|
245
|
+
title="Noise Texture Overlay"
|
|
246
|
+
description="Adds a subtle grain texture to the background overlay."
|
|
247
|
+
/>
|
|
248
|
+
<ConfigDialog
|
|
249
|
+
variant="neutral"
|
|
250
|
+
borderConfig={["top"]}
|
|
251
|
+
showCloseButton={false}
|
|
252
|
+
triggerLabel="No close btn"
|
|
253
|
+
title="No Close Button"
|
|
254
|
+
description="Dialog without the floating circular close button — close via footer."
|
|
255
|
+
/>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
),
|
|
564
260
|
parameters: {
|
|
565
261
|
docs: {
|
|
566
262
|
description: {
|
|
567
263
|
story:
|
|
568
|
-
"
|
|
264
|
+
"Comparison of all variant types, border configurations, and overlay options (glass, noise, opacity) available on the Dialog component.",
|
|
569
265
|
},
|
|
570
266
|
},
|
|
571
267
|
},
|
|
572
268
|
}
|
|
573
269
|
|
|
574
|
-
//
|
|
575
|
-
|
|
270
|
+
// ─── Interactive ───────────────────────────────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
export const Interactive: Story = {
|
|
576
273
|
render: () => {
|
|
577
|
-
const
|
|
578
|
-
const { handleDialogClose } = useDialogCleanup({ threshold:
|
|
579
|
-
const [
|
|
580
|
-
const [
|
|
274
|
+
const InteractiveDemo = () => {
|
|
275
|
+
const { handleDialogClose } = useDialogCleanup({ threshold: 100 })
|
|
276
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
277
|
+
const [variant, setVariant] = useState<
|
|
278
|
+
"neutral" | "positive" | "negative" | "warning" | "info"
|
|
279
|
+
>("neutral")
|
|
280
|
+
const [borderConfig, setBorderConfig] = useState<
|
|
281
|
+
"none" | "all" | "top" | "bottom" | "left" | "right"
|
|
282
|
+
>("top")
|
|
283
|
+
const [glass, setGlass] = useState<"high" | "medium" | "low" | "none">(
|
|
284
|
+
"none"
|
|
285
|
+
)
|
|
286
|
+
const [noise, setNoise] = useState<"high" | "medium" | "low" | "none">(
|
|
287
|
+
"none"
|
|
288
|
+
)
|
|
289
|
+
const [opacity, setOpacity] = useState<
|
|
290
|
+
"high" | "medium" | "low" | "none"
|
|
291
|
+
>("medium")
|
|
292
|
+
const [lastAction, setLastAction] = useState<string | null>(null)
|
|
581
293
|
|
|
582
|
-
const
|
|
294
|
+
const handleConfirm = () => {
|
|
295
|
+
setLastAction("confirmed")
|
|
583
296
|
handleDialogClose()
|
|
584
|
-
|
|
297
|
+
setIsOpen(false)
|
|
585
298
|
}
|
|
586
299
|
|
|
587
|
-
const
|
|
300
|
+
const handleCancel = () => {
|
|
301
|
+
setLastAction("cancelled")
|
|
588
302
|
handleDialogClose()
|
|
589
|
-
|
|
303
|
+
setIsOpen(false)
|
|
590
304
|
}
|
|
591
305
|
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
306
|
+
const variantOptions = [
|
|
307
|
+
"neutral",
|
|
308
|
+
"positive",
|
|
309
|
+
"negative",
|
|
310
|
+
"warning",
|
|
311
|
+
"info",
|
|
312
|
+
] as const
|
|
313
|
+
const borderOptions = [
|
|
314
|
+
"none",
|
|
315
|
+
"top",
|
|
316
|
+
"bottom",
|
|
317
|
+
"left",
|
|
318
|
+
"right",
|
|
319
|
+
"all",
|
|
320
|
+
] as const
|
|
321
|
+
const levelOptions = ["none", "low", "medium", "high"] as const
|
|
322
|
+
|
|
323
|
+
const btnCls = (active: boolean) =>
|
|
324
|
+
`font-fm-text text-fm-sm leading-fm-sm rounded px-2 py-1 outline-none transition-colors ${
|
|
325
|
+
active
|
|
326
|
+
? "bg-fm-surface-contrast text-fm-contrast"
|
|
327
|
+
: "bg-fm-surface-secondary text-fm-secondary hover:text-fm-primary"
|
|
328
|
+
}`
|
|
329
|
+
|
|
330
|
+
// borderConfig state is a single string key; map to actual BorderConfig when passing to component
|
|
331
|
+
const resolvedBorderConfig: any =
|
|
332
|
+
borderConfig === "all" || borderConfig === "none"
|
|
333
|
+
? borderConfig
|
|
334
|
+
: borderConfig
|
|
597
335
|
|
|
598
336
|
return (
|
|
599
|
-
|
|
600
|
-
<
|
|
601
|
-
<
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
<DialogContent
|
|
608
|
-
className="max-w-md"
|
|
609
|
-
variant="neutral"
|
|
610
|
-
//@ts-expect-error onEscapeKeyDown available in Radix v2.0.0
|
|
611
|
-
onEscapeKeyDown={handleEditClose}
|
|
612
|
-
onPointerDownOutside={handleEditClose}
|
|
613
|
-
>
|
|
614
|
-
<DialogHeader>
|
|
615
|
-
<DialogTitle>Edit User Profile</DialogTitle>
|
|
616
|
-
<DialogDescription>
|
|
617
|
-
Update user information or delete the account.
|
|
618
|
-
</DialogDescription>
|
|
619
|
-
</DialogHeader>
|
|
620
|
-
|
|
621
|
-
<div className="space-y-4">
|
|
622
|
-
<Input placeholder="User name" />
|
|
623
|
-
<Input placeholder="Email address" type="email" />
|
|
624
|
-
<Textarea placeholder="Bio" rows={3} />
|
|
625
|
-
</div>
|
|
337
|
+
<div className="w-full p-8">
|
|
338
|
+
<div className="mx-auto max-w-3xl space-y-6">
|
|
339
|
+
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
|
340
|
+
{/* Controls panel */}
|
|
341
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary space-y-5 rounded-xl border p-5">
|
|
342
|
+
<p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
|
|
343
|
+
Configuration
|
|
344
|
+
</p>
|
|
626
345
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
onClick={() => setIsDeleteOpen(true)}
|
|
643
|
-
>
|
|
644
|
-
<TrashIcon className="h-4 w-4" />
|
|
645
|
-
Delete Account
|
|
646
|
-
</Button>
|
|
346
|
+
<div className="space-y-2">
|
|
347
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
348
|
+
Variant
|
|
349
|
+
</p>
|
|
350
|
+
<div className="flex flex-wrap gap-1">
|
|
351
|
+
{variantOptions.map((v) => (
|
|
352
|
+
<button
|
|
353
|
+
key={v}
|
|
354
|
+
onClick={() => setVariant(v)}
|
|
355
|
+
className={btnCls(variant === v)}
|
|
356
|
+
>
|
|
357
|
+
{v}
|
|
358
|
+
</button>
|
|
359
|
+
))}
|
|
360
|
+
</div>
|
|
647
361
|
</div>
|
|
648
|
-
</DialogFooter>
|
|
649
|
-
</DialogContent>
|
|
650
|
-
</Dialog>
|
|
651
362
|
|
|
652
|
-
|
|
653
|
-
<DialogContent
|
|
654
|
-
variant="negative"
|
|
655
|
-
classes={{
|
|
656
|
-
root: "max-w-sm",
|
|
657
|
-
overlay: "z-60",
|
|
658
|
-
content: "z-70",
|
|
659
|
-
}}
|
|
660
|
-
//@ts-expect-error onEscapeKeyDown available in Radix v2.0.0
|
|
661
|
-
onEscapeKeyDown={handleDeleteClose}
|
|
662
|
-
onPointerDownOutside={handleDeleteClose}
|
|
663
|
-
>
|
|
664
|
-
<DialogHeader>
|
|
665
|
-
<DialogTitle className="flex items-center gap-2">
|
|
666
|
-
<AlertIcon className="text-fm-negative h-5 w-5" />
|
|
667
|
-
Delete Account
|
|
668
|
-
</DialogTitle>
|
|
669
|
-
<DialogDescription>
|
|
670
|
-
This action cannot be undone.
|
|
671
|
-
</DialogDescription>
|
|
672
|
-
</DialogHeader>
|
|
363
|
+
<div className="border-fm-divider-secondary border-t pt-4" />
|
|
673
364
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
Are you absolutely sure you want to delete this account?
|
|
678
|
-
This will permanently remove all associated data.
|
|
365
|
+
<div className="space-y-2">
|
|
366
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
367
|
+
Border Config
|
|
679
368
|
</p>
|
|
369
|
+
<div className="flex flex-wrap gap-1">
|
|
370
|
+
{borderOptions.map((b) => (
|
|
371
|
+
<button
|
|
372
|
+
key={b}
|
|
373
|
+
onClick={() => setBorderConfig(b)}
|
|
374
|
+
className={btnCls(borderConfig === b)}
|
|
375
|
+
>
|
|
376
|
+
{b}
|
|
377
|
+
</button>
|
|
378
|
+
))}
|
|
379
|
+
</div>
|
|
680
380
|
</div>
|
|
681
|
-
</div>
|
|
682
381
|
|
|
683
|
-
|
|
684
|
-
<Button variant="outline" onClick={handleDeleteClose}>
|
|
685
|
-
Cancel
|
|
686
|
-
</Button>
|
|
687
|
-
<Button onClick={handleDelete} className="gap-2">
|
|
688
|
-
<TrashIcon className="h-4 w-4" />
|
|
689
|
-
Delete Forever
|
|
690
|
-
</Button>
|
|
691
|
-
</DialogFooter>
|
|
692
|
-
</DialogContent>
|
|
693
|
-
</Dialog>
|
|
694
|
-
</>
|
|
695
|
-
)
|
|
696
|
-
}
|
|
382
|
+
<div className="border-fm-divider-secondary border-t pt-4" />
|
|
697
383
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
parameters: {
|
|
715
|
-
docs: {
|
|
716
|
-
description: {
|
|
717
|
-
story:
|
|
718
|
-
"Nested modal example showcasing different border configurations for modal hierarchy.",
|
|
719
|
-
},
|
|
720
|
-
},
|
|
721
|
-
},
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
// 5. Form Dialog with Cleanup
|
|
725
|
-
export const FormDialog: Story = {
|
|
726
|
-
render: () => {
|
|
727
|
-
const FormDialogComponent = () => {
|
|
728
|
-
const { handleDialogClose } = useDialogCleanup({ threshold: 100 })
|
|
729
|
-
const [formData, setFormData] = useState({
|
|
730
|
-
name: "",
|
|
731
|
-
email: "",
|
|
732
|
-
message: "",
|
|
733
|
-
newsletter: false,
|
|
734
|
-
})
|
|
735
|
-
|
|
736
|
-
const handleClose = () => {
|
|
737
|
-
handleDialogClose()
|
|
738
|
-
// Reset form data on close
|
|
739
|
-
setFormData({
|
|
740
|
-
name: "",
|
|
741
|
-
email: "",
|
|
742
|
-
message: "",
|
|
743
|
-
newsletter: false,
|
|
744
|
-
})
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
return (
|
|
748
|
-
<Dialog>
|
|
749
|
-
<DialogTrigger asChild>
|
|
750
|
-
<Button className="gap-2">
|
|
751
|
-
<EditBigIcon className="h-4 w-4" />
|
|
752
|
-
Contact Form
|
|
753
|
-
</Button>
|
|
754
|
-
</DialogTrigger>
|
|
755
|
-
<DialogContent
|
|
756
|
-
className="max-w-md"
|
|
757
|
-
variant="info"
|
|
758
|
-
//@ts-expect-error onEscapeKeyDown available in Radix v2.0.0
|
|
759
|
-
onEscapeKeyDown={handleClose}
|
|
760
|
-
onPointerDownOutside={handleClose}
|
|
761
|
-
>
|
|
762
|
-
<DialogHeader>
|
|
763
|
-
<DialogTitle>Contact Us</DialogTitle>
|
|
764
|
-
<DialogDescription>
|
|
765
|
-
Send us a message and we'll get back to you soon.
|
|
766
|
-
</DialogDescription>
|
|
767
|
-
</DialogHeader>
|
|
768
|
-
|
|
769
|
-
<div className="space-y-4">
|
|
770
|
-
<div className="space-y-2">
|
|
771
|
-
<Label htmlFor="name">Name</Label>
|
|
772
|
-
<Input
|
|
773
|
-
id="name"
|
|
774
|
-
placeholder="Your name"
|
|
775
|
-
value={formData.name}
|
|
776
|
-
onChange={(e) =>
|
|
777
|
-
setFormData((prev) => ({ ...prev, name: e.target.value }))
|
|
778
|
-
}
|
|
779
|
-
/>
|
|
780
|
-
</div>
|
|
781
|
-
|
|
782
|
-
<div className="space-y-2">
|
|
783
|
-
<Label htmlFor="email">Email</Label>
|
|
784
|
-
<Input
|
|
785
|
-
id="email"
|
|
786
|
-
type="email"
|
|
787
|
-
placeholder="your.email@example.com"
|
|
788
|
-
value={formData.email}
|
|
789
|
-
onChange={(e) =>
|
|
790
|
-
setFormData((prev) => ({
|
|
791
|
-
...prev,
|
|
792
|
-
email: e.target.value,
|
|
793
|
-
}))
|
|
794
|
-
}
|
|
795
|
-
/>
|
|
796
|
-
</div>
|
|
384
|
+
<div className="space-y-2">
|
|
385
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
386
|
+
Opacity
|
|
387
|
+
</p>
|
|
388
|
+
<div className="flex flex-wrap gap-1">
|
|
389
|
+
{levelOptions.map((o) => (
|
|
390
|
+
<button
|
|
391
|
+
key={o}
|
|
392
|
+
onClick={() => setOpacity(o)}
|
|
393
|
+
className={btnCls(opacity === o)}
|
|
394
|
+
>
|
|
395
|
+
{o}
|
|
396
|
+
</button>
|
|
397
|
+
))}
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
797
400
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
401
|
+
<div className="space-y-2">
|
|
402
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
403
|
+
Glass
|
|
404
|
+
</p>
|
|
405
|
+
<div className="flex flex-wrap gap-1">
|
|
406
|
+
{levelOptions.map((g) => (
|
|
407
|
+
<button
|
|
408
|
+
key={g}
|
|
409
|
+
onClick={() => setGlass(g)}
|
|
410
|
+
className={btnCls(glass === g)}
|
|
411
|
+
>
|
|
412
|
+
{g}
|
|
413
|
+
</button>
|
|
414
|
+
))}
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
813
417
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
418
|
+
<div className="space-y-2">
|
|
419
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
420
|
+
Noise
|
|
421
|
+
</p>
|
|
422
|
+
<div className="flex flex-wrap gap-1">
|
|
423
|
+
{levelOptions.map((n) => (
|
|
424
|
+
<button
|
|
425
|
+
key={n}
|
|
426
|
+
onClick={() => setNoise(n)}
|
|
427
|
+
className={btnCls(noise === n)}
|
|
428
|
+
>
|
|
429
|
+
{n}
|
|
430
|
+
</button>
|
|
431
|
+
))}
|
|
432
|
+
</div>
|
|
433
|
+
</div>
|
|
828
434
|
</div>
|
|
829
|
-
</div>
|
|
830
|
-
|
|
831
|
-
<DialogFooter>
|
|
832
|
-
<DialogClose asChild>
|
|
833
|
-
<Button variant="outline" onClick={handleClose}>
|
|
834
|
-
Cancel
|
|
835
|
-
</Button>
|
|
836
|
-
</DialogClose>
|
|
837
|
-
<Button
|
|
838
|
-
disabled={
|
|
839
|
-
!formData.name || !formData.email || !formData.message
|
|
840
|
-
}
|
|
841
|
-
onClick={() => {
|
|
842
|
-
console.log("Form submitted:", formData)
|
|
843
|
-
handleClose()
|
|
844
|
-
}}
|
|
845
|
-
>
|
|
846
|
-
Send Message
|
|
847
|
-
</Button>
|
|
848
|
-
</DialogFooter>
|
|
849
|
-
</DialogContent>
|
|
850
|
-
</Dialog>
|
|
851
|
-
)
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
return (
|
|
855
|
-
<div className="space-y-8">
|
|
856
|
-
<div className="text-center">
|
|
857
|
-
<h3 className="text-fm-primary mb-2 font-medium">Form Dialog</h3>
|
|
858
|
-
<p className="text-fm-secondary text-sm">
|
|
859
|
-
Dialog containing form elements with horizontal border accent
|
|
860
|
-
</p>
|
|
861
|
-
</div>
|
|
862
|
-
<div className="flex justify-center">
|
|
863
|
-
<FormDialogComponent />
|
|
864
|
-
</div>
|
|
865
|
-
</div>
|
|
866
|
-
)
|
|
867
|
-
},
|
|
868
|
-
parameters: {
|
|
869
|
-
docs: {
|
|
870
|
-
description: {
|
|
871
|
-
story:
|
|
872
|
-
"Form dialog with validation and horizontal border configuration for focused data entry.",
|
|
873
|
-
},
|
|
874
|
-
},
|
|
875
|
-
},
|
|
876
|
-
}
|
|
877
435
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
436
|
+
{/* Preview stage */}
|
|
437
|
+
<div className="flex flex-col gap-3 lg:col-span-2">
|
|
438
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary flex min-h-48 flex-col items-center justify-center gap-4 rounded-xl border p-8">
|
|
439
|
+
{lastAction && (
|
|
440
|
+
<div
|
|
441
|
+
className={`rounded-lg border px-4 py-2 ${
|
|
442
|
+
lastAction === "confirmed"
|
|
443
|
+
? "border-fm-divider-positive bg-fm-surface-positive-sec"
|
|
444
|
+
: "border-fm-divider-secondary bg-fm-surface-secondary"
|
|
445
|
+
}`}
|
|
446
|
+
>
|
|
447
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
448
|
+
Last action:{" "}
|
|
449
|
+
<span className="text-fm-primary font-medium">
|
|
450
|
+
{lastAction}
|
|
451
|
+
</span>
|
|
452
|
+
</p>
|
|
453
|
+
</div>
|
|
454
|
+
)}
|
|
455
|
+
|
|
456
|
+
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
457
|
+
<DialogTrigger asChild>
|
|
458
|
+
<Button onClick={() => setLastAction(null)}>
|
|
459
|
+
Open Dialog
|
|
460
|
+
</Button>
|
|
461
|
+
</DialogTrigger>
|
|
462
|
+
<DialogContent
|
|
463
|
+
variant={variant}
|
|
464
|
+
borderConfig={resolvedBorderConfig}
|
|
465
|
+
opacity={opacity}
|
|
466
|
+
glass={glass}
|
|
467
|
+
noise={noise}
|
|
468
|
+
>
|
|
469
|
+
<DialogHeader>
|
|
470
|
+
<DialogTitle>Confirm Action</DialogTitle>
|
|
471
|
+
<DialogDescription>
|
|
472
|
+
Review the current configuration then confirm or
|
|
473
|
+
cancel to see live state feedback below.
|
|
474
|
+
</DialogDescription>
|
|
475
|
+
</DialogHeader>
|
|
476
|
+
|
|
477
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
|
|
478
|
+
<code className="text-fm-secondary text-fm-sm leading-fm-sm font-(--font-fm-mono)">
|
|
479
|
+
{`variant="${variant}" border="${borderConfig}" opacity="${opacity}" glass="${glass}" noise="${noise}"`}
|
|
480
|
+
</code>
|
|
481
|
+
</div>
|
|
482
|
+
|
|
483
|
+
<DialogFooter>
|
|
484
|
+
<Button variant="outline" onClick={handleCancel}>
|
|
485
|
+
Cancel
|
|
486
|
+
</Button>
|
|
487
|
+
<Button onClick={handleConfirm}>Confirm</Button>
|
|
488
|
+
</DialogFooter>
|
|
489
|
+
</DialogContent>
|
|
490
|
+
</Dialog>
|
|
920
491
|
</div>
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
<
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
Border:{" "}
|
|
927
|
-
<span className="text-fm-primary">
|
|
928
|
-
{Array.isArray(borderConfig)
|
|
929
|
-
? `[${borderConfig.join(", ")}]`
|
|
930
|
-
: borderConfig || "top"}
|
|
931
|
-
</span>
|
|
492
|
+
|
|
493
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
|
|
494
|
+
<code className="text-fm-secondary text-fm-sm leading-fm-sm font-(--font-fm-mono)">
|
|
495
|
+
{`<DialogContent variant="${variant}" borderConfig="${borderConfig}" opacity="${opacity}" glass="${glass}" noise="${noise}" />`}
|
|
496
|
+
</code>
|
|
932
497
|
</div>
|
|
933
498
|
</div>
|
|
934
499
|
</div>
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
<Button onClick={handleDialogClose}>Close</Button>
|
|
938
|
-
</DialogClose>
|
|
939
|
-
</DialogFooter>
|
|
940
|
-
</DialogContent>
|
|
941
|
-
</Dialog>
|
|
500
|
+
</div>
|
|
501
|
+
</div>
|
|
942
502
|
)
|
|
943
503
|
}
|
|
944
504
|
|
|
945
|
-
return
|
|
946
|
-
<div className="space-y-8">
|
|
947
|
-
<div className="text-center">
|
|
948
|
-
<h3 className="text-fm-primary mb-2 font-medium">
|
|
949
|
-
Overlay Variations with Borders
|
|
950
|
-
</h3>
|
|
951
|
-
<p className="text-fm-secondary text-sm">
|
|
952
|
-
Different overlay effects combined with border configurations
|
|
953
|
-
</p>
|
|
954
|
-
</div>
|
|
955
|
-
|
|
956
|
-
<div className="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4">
|
|
957
|
-
<OverlayDialog
|
|
958
|
-
opacity="low"
|
|
959
|
-
title="Low Opacity"
|
|
960
|
-
description="Subtle background dimming (40%)"
|
|
961
|
-
/>
|
|
962
|
-
<OverlayDialog
|
|
963
|
-
opacity="medium"
|
|
964
|
-
title="Medium Opacity"
|
|
965
|
-
description="Balanced background dimming (60%)"
|
|
966
|
-
/>
|
|
967
|
-
<OverlayDialog
|
|
968
|
-
opacity="high"
|
|
969
|
-
title="High Opacity"
|
|
970
|
-
description="Strong background dimming (80%)"
|
|
971
|
-
/>
|
|
972
|
-
<OverlayDialog
|
|
973
|
-
opacity="none"
|
|
974
|
-
title="Full Opacity"
|
|
975
|
-
description="Complete background coverage (100%)"
|
|
976
|
-
/>
|
|
977
|
-
<OverlayDialog
|
|
978
|
-
glass="low"
|
|
979
|
-
title="Low Glass"
|
|
980
|
-
description="Subtle backdrop blur effect"
|
|
981
|
-
/>
|
|
982
|
-
<OverlayDialog
|
|
983
|
-
glass="medium"
|
|
984
|
-
title="Medium Glass"
|
|
985
|
-
description="Balanced backdrop blur effect"
|
|
986
|
-
/>
|
|
987
|
-
<OverlayDialog
|
|
988
|
-
glass="high"
|
|
989
|
-
title="High Glass"
|
|
990
|
-
description="Strong backdrop blur effect"
|
|
991
|
-
/>
|
|
992
|
-
<OverlayDialog
|
|
993
|
-
noise="low"
|
|
994
|
-
title="Low Noise"
|
|
995
|
-
description="Subtle texture pattern"
|
|
996
|
-
/>
|
|
997
|
-
</div>
|
|
998
|
-
</div>
|
|
999
|
-
)
|
|
505
|
+
return <InteractiveDemo />
|
|
1000
506
|
},
|
|
1001
507
|
parameters: {
|
|
1002
508
|
docs: {
|
|
1003
509
|
description: {
|
|
1004
510
|
story:
|
|
1005
|
-
"
|
|
511
|
+
"Live configurator — pick variant, borderConfig, opacity, glass, and noise, then open the dialog and confirm or cancel to see state feedback reflected below the trigger.",
|
|
1006
512
|
},
|
|
1007
513
|
},
|
|
1008
514
|
},
|
|
1009
515
|
}
|
|
1010
516
|
|
|
1011
|
-
//
|
|
1012
|
-
|
|
517
|
+
// ─── Accessibility ─────────────────────────────────────────────────────────────
|
|
518
|
+
|
|
519
|
+
export const Accessibility: Story = {
|
|
1013
520
|
render: () => {
|
|
1014
|
-
const
|
|
521
|
+
const A11yDialog = () => {
|
|
1015
522
|
const { handleDialogClose } = useDialogCleanup({ threshold: 100 })
|
|
1016
523
|
|
|
1017
524
|
return (
|
|
1018
525
|
<Dialog>
|
|
1019
526
|
<DialogTrigger asChild>
|
|
1020
|
-
<Button
|
|
527
|
+
<Button aria-label="Open accessibility demo dialog">
|
|
528
|
+
Open accessible dialog
|
|
529
|
+
</Button>
|
|
1021
530
|
</DialogTrigger>
|
|
1022
531
|
<DialogContent
|
|
1023
532
|
variant="info"
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
onPointerDownOutside={handleDialogClose}
|
|
533
|
+
borderConfig={["top"]}
|
|
534
|
+
aria-describedby="dialog-a11y-description"
|
|
1027
535
|
>
|
|
1028
536
|
<DialogHeader>
|
|
1029
|
-
<DialogTitle>
|
|
1030
|
-
<DialogDescription>
|
|
1031
|
-
This dialog
|
|
1032
|
-
|
|
537
|
+
<DialogTitle>Keyboard & Screen Reader Support</DialogTitle>
|
|
538
|
+
<DialogDescription id="dialog-a11y-description">
|
|
539
|
+
This dialog follows ARIA dialog best practices with role, label,
|
|
540
|
+
and focus management handled by Radix UI.
|
|
1033
541
|
</DialogDescription>
|
|
1034
542
|
</DialogHeader>
|
|
1035
543
|
|
|
1036
|
-
<div className="space-y-
|
|
1037
|
-
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
1038
|
-
<h4 className="text-fm-primary mb-2 font-medium">
|
|
1039
|
-
Border Configuration
|
|
1040
|
-
</h4>
|
|
1041
|
-
<ul className="text-fm-secondary space-y-1 text-sm">
|
|
1042
|
-
<li>• Complete border frame for maximum visual emphasis</li>
|
|
1043
|
-
<li>• Customizable per dialog context and importance</li>
|
|
1044
|
-
<li>• Maintains accessibility contrast ratios</li>
|
|
1045
|
-
<li>• Works with all variant color schemes</li>
|
|
1046
|
-
</ul>
|
|
1047
|
-
</div>
|
|
1048
|
-
|
|
544
|
+
<div className="space-y-3">
|
|
1049
545
|
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
1050
|
-
<h4 className="text-fm-primary
|
|
1051
|
-
Keyboard
|
|
546
|
+
<h4 className="text-fm-primary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
547
|
+
Keyboard navigation
|
|
1052
548
|
</h4>
|
|
1053
|
-
<ul className="text-fm-secondary
|
|
549
|
+
<ul className="text-fm-secondary font-fm-text text-fm-sm leading-fm-xl mt-2 space-y-1">
|
|
1054
550
|
<li>
|
|
1055
|
-
|
|
1056
|
-
<kbd className="bg-fm-surface-tertiary rounded px-1">
|
|
551
|
+
<kbd className="bg-fm-surface-tertiary text-fm-xs rounded px-1 py-0.5 font-(--font-fm-mono)">
|
|
1057
552
|
Tab
|
|
1058
553
|
</kbd>{" "}
|
|
1059
|
-
|
|
554
|
+
— move focus forward through interactive elements
|
|
1060
555
|
</li>
|
|
1061
556
|
<li>
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
Escape
|
|
557
|
+
<kbd className="bg-fm-surface-tertiary text-fm-xs rounded px-1 py-0.5 font-(--font-fm-mono)">
|
|
558
|
+
Shift+Tab
|
|
1065
559
|
</kbd>{" "}
|
|
1066
|
-
|
|
560
|
+
— move focus backward
|
|
1067
561
|
</li>
|
|
1068
562
|
<li>
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
Enter
|
|
563
|
+
<kbd className="bg-fm-surface-tertiary text-fm-xs rounded px-1 py-0.5 font-(--font-fm-mono)">
|
|
564
|
+
Escape
|
|
1072
565
|
</kbd>{" "}
|
|
1073
|
-
|
|
566
|
+
— close the dialog and restore focus to the trigger
|
|
1074
567
|
</li>
|
|
1075
568
|
<li>
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
Space
|
|
569
|
+
<kbd className="bg-fm-surface-tertiary text-fm-xs rounded px-1 py-0.5 font-(--font-fm-mono)">
|
|
570
|
+
Enter / Space
|
|
1079
571
|
</kbd>{" "}
|
|
1080
|
-
|
|
572
|
+
— activate focused button
|
|
1081
573
|
</li>
|
|
1082
574
|
</ul>
|
|
1083
575
|
</div>
|
|
1084
576
|
|
|
1085
577
|
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
1086
|
-
<h4 className="text-fm-primary
|
|
1087
|
-
|
|
578
|
+
<h4 className="text-fm-primary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
579
|
+
ARIA roles & attributes
|
|
1088
580
|
</h4>
|
|
1089
|
-
<ul className="text-fm-secondary
|
|
1090
|
-
<li
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
581
|
+
<ul className="text-fm-secondary font-fm-text text-fm-sm leading-fm-xl mt-2 space-y-1">
|
|
582
|
+
<li>
|
|
583
|
+
<code className="text-fm-secondary text-fm-sm font-(--font-fm-mono)">
|
|
584
|
+
role="dialog"
|
|
585
|
+
</code>{" "}
|
|
586
|
+
is applied automatically by Radix
|
|
587
|
+
</li>
|
|
588
|
+
<li>
|
|
589
|
+
<code className="text-fm-secondary text-fm-sm font-(--font-fm-mono)">
|
|
590
|
+
aria-labelledby
|
|
591
|
+
</code>{" "}
|
|
592
|
+
links to{" "}
|
|
593
|
+
<code className="text-fm-secondary text-fm-sm font-(--font-fm-mono)">
|
|
594
|
+
DialogTitle
|
|
595
|
+
</code>
|
|
596
|
+
</li>
|
|
597
|
+
<li>
|
|
598
|
+
<code className="text-fm-secondary text-fm-sm font-(--font-fm-mono)">
|
|
599
|
+
aria-describedby
|
|
600
|
+
</code>{" "}
|
|
601
|
+
links to{" "}
|
|
602
|
+
<code className="text-fm-secondary text-fm-sm font-(--font-fm-mono)">
|
|
603
|
+
DialogDescription
|
|
604
|
+
</code>
|
|
605
|
+
</li>
|
|
606
|
+
<li>Focus is trapped inside the dialog while it is open</li>
|
|
607
|
+
<li>
|
|
608
|
+
Focus returns to the trigger element when the dialog closes
|
|
609
|
+
</li>
|
|
1094
610
|
</ul>
|
|
1095
611
|
</div>
|
|
1096
612
|
</div>
|
|
1097
613
|
|
|
1098
614
|
<DialogFooter>
|
|
1099
615
|
<DialogClose asChild>
|
|
1100
|
-
<Button
|
|
616
|
+
<Button
|
|
617
|
+
onClick={handleDialogClose}
|
|
618
|
+
aria-label="Close accessibility demo dialog"
|
|
619
|
+
>
|
|
620
|
+
Close dialog
|
|
621
|
+
</Button>
|
|
1101
622
|
</DialogClose>
|
|
1102
623
|
</DialogFooter>
|
|
1103
624
|
</DialogContent>
|
|
@@ -1107,16 +628,46 @@ export const AccessibilityExample: Story = {
|
|
|
1107
628
|
|
|
1108
629
|
return (
|
|
1109
630
|
<div className="space-y-8">
|
|
1110
|
-
<div className="text-center">
|
|
1111
|
-
<h3 className="text-fm-primary mb-2 font-medium">
|
|
1112
|
-
Accessibility Features
|
|
1113
|
-
</h3>
|
|
1114
|
-
<p className="text-fm-secondary text-sm">
|
|
1115
|
-
Dialog with comprehensive accessibility and border configuration
|
|
1116
|
-
</p>
|
|
1117
|
-
</div>
|
|
1118
631
|
<div className="flex justify-center">
|
|
1119
|
-
<
|
|
632
|
+
<A11yDialog />
|
|
633
|
+
</div>
|
|
634
|
+
|
|
635
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
636
|
+
<p className="text-fm-secondary font-fm-text text-fm-md leading-fm-xl">
|
|
637
|
+
Accessibility best practices for Dialog:
|
|
638
|
+
</p>
|
|
639
|
+
<ul className="text-fm-secondary font-fm-text text-fm-sm leading-fm-xl mt-2 space-y-1">
|
|
640
|
+
<li>
|
|
641
|
+
Always provide a visible{" "}
|
|
642
|
+
<code className="font-(--font-fm-mono)">DialogTitle</code> — it is
|
|
643
|
+
announced by screen readers when the dialog opens.
|
|
644
|
+
</li>
|
|
645
|
+
<li>
|
|
646
|
+
Use{" "}
|
|
647
|
+
<code className="font-(--font-fm-mono)">DialogDescription</code>{" "}
|
|
648
|
+
to give users context before they interact with dialog controls.
|
|
649
|
+
</li>
|
|
650
|
+
<li>
|
|
651
|
+
Pair{" "}
|
|
652
|
+
<code className="font-(--font-fm-mono)">useDialogCleanup</code> to
|
|
653
|
+
prevent React 18 leaving{" "}
|
|
654
|
+
<code className="font-(--font-fm-mono)">
|
|
655
|
+
pointer-events: none
|
|
656
|
+
</code>{" "}
|
|
657
|
+
on the body after close.
|
|
658
|
+
</li>
|
|
659
|
+
<li>
|
|
660
|
+
Ensure trigger buttons have descriptive labels via{" "}
|
|
661
|
+
<code className="font-(--font-fm-mono)">aria-label</code> or
|
|
662
|
+
visible text.
|
|
663
|
+
</li>
|
|
664
|
+
<li>
|
|
665
|
+
Never remove{" "}
|
|
666
|
+
<code className="font-(--font-fm-mono)">DialogTitle</code> — use{" "}
|
|
667
|
+
<code className="font-(--font-fm-mono)">sr-only</code> if it must
|
|
668
|
+
be visually hidden.
|
|
669
|
+
</li>
|
|
670
|
+
</ul>
|
|
1120
671
|
</div>
|
|
1121
672
|
</div>
|
|
1122
673
|
)
|
|
@@ -1125,7 +676,7 @@ export const AccessibilityExample: Story = {
|
|
|
1125
676
|
docs: {
|
|
1126
677
|
description: {
|
|
1127
678
|
story:
|
|
1128
|
-
"
|
|
679
|
+
"Demonstrates proper ARIA roles, keyboard navigation, and focus management for Dialog. Includes a best-practices reference box.",
|
|
1129
680
|
},
|
|
1130
681
|
},
|
|
1131
682
|
},
|