aural-ui 3.0.7 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1199
- package/dist/components/avatar/Avatar.stories.tsx +235 -237
- package/dist/components/badge/Badge.stories.tsx +379 -116
- package/dist/components/banner/Banner.stories.tsx +445 -391
- package/dist/components/breadcrumb/Breadcrumb.stories.tsx +453 -199
- package/dist/components/button/Button.stories.tsx +585 -230
- package/dist/components/button/index.tsx +7 -7
- package/dist/components/card/Card.stories.tsx +619 -301
- package/dist/components/char-count/CharCount.stories.tsx +350 -248
- package/dist/components/checkbox/Checkbox.stories.tsx +309 -167
- package/dist/components/chip/Chip.stories.tsx +362 -168
- package/dist/components/circular-loader/CircularLoader.stories.tsx +221 -620
- package/dist/components/clamp-lines/ClampLines.stories.tsx +246 -117
- package/dist/components/collapsible/Collapsible.stories.tsx +391 -252
- package/dist/components/command/Command.stories.tsx +533 -856
- package/dist/components/dialog/Dialog.stories.tsx +505 -949
- package/dist/components/divider/Divider.stories.tsx +265 -502
- package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
- package/dist/components/drawer/Drawer.stories.tsx +659 -993
- package/dist/components/drawer/index.tsx +3 -3
- package/dist/components/dropdown/Dropdown.stories.tsx +643 -1018
- package/dist/components/form/Form.stories.tsx +560 -274
- package/dist/components/helper-text/HelperText.stories.tsx +199 -200
- package/dist/components/hover-card/HoverCard.stories.tsx +318 -1221
- package/dist/components/icon-button/IconButton.stories.tsx +837 -194
- package/dist/components/if-else/if-else.stories.tsx +370 -83
- package/dist/components/input/Input.stories.tsx +436 -368
- package/dist/components/label/Label.stories.tsx +156 -154
- package/dist/components/list/List.stories.tsx +485 -822
- package/dist/components/marquee/Marquee.stories.tsx +356 -694
- package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -410
- package/dist/components/overlay/Overlay.stories.tsx +452 -818
- package/dist/components/overlay/index.tsx +4 -4
- package/dist/components/pagination/Pagination.stories.tsx +721 -210
- package/dist/components/popover/Popover.stories.tsx +484 -873
- package/dist/components/radio/Radio.stories.tsx +432 -124
- package/dist/components/resizable/Resizable.stories.tsx +496 -752
- package/dist/components/scroll-area/ScrollArea.stories.tsx +384 -1006
- package/dist/components/search/Search.stories.tsx +314 -575
- package/dist/components/select/Select.stories.tsx +684 -787
- package/dist/components/sheet/Sheet.stories.tsx +671 -936
- package/dist/components/skelton/Skelton.stories.tsx +230 -764
- package/dist/components/slider/Slider.stories.tsx +384 -737
- package/dist/components/stepper/Stepper.stories.tsx +371 -514
- package/dist/components/switch/Switch.stories.tsx +461 -208
- package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
- package/dist/components/table/Table.stories.tsx +770 -914
- package/dist/components/tabs/Tabs.stories.tsx +459 -1400
- package/dist/components/tag/Tag.stories.tsx +714 -542
- package/dist/components/textarea/TextArea.stories.tsx +621 -562
- package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -148
- package/dist/components/toast/Toast.stories.tsx +452 -1333
- package/dist/components/toggle/Toggle.stories.tsx +488 -909
- package/dist/components/tooltip/Tooltip.stories.tsx +344 -1372
- package/dist/components/typography/Typography.stories.tsx +406 -89
- package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
- package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
- package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
- package/dist/icons/Icons.stories.tsx +0 -12
- package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +226 -1013
- package/dist/icons/alert-icon/AlertIcon.stories.tsx +109 -929
- package/dist/icons/all-icons.tsx +124 -87
- package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +140 -971
- package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +148 -888
- package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +135 -1019
- package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +137 -953
- package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +138 -997
- package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +136 -942
- package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +148 -1092
- package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +146 -1211
- package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +126 -615
- package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +144 -1164
- package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +167 -985
- package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +122 -1179
- package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +124 -1168
- package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +119 -850
- package/dist/icons/camera-icon/CameraIcon.stories.tsx +112 -1213
- package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +117 -934
- package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +160 -961
- package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +163 -961
- package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +144 -942
- package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +129 -966
- package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +147 -964
- package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +145 -975
- package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +150 -1142
- package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +114 -461
- package/dist/icons/coin-icon/CoinIcon.stories.tsx +124 -1322
- package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +117 -1318
- package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +114 -903
- package/dist/icons/command-icon/CommandIcon.stories.tsx +127 -1042
- package/dist/icons/copy-icon/CopyIcon.stories.tsx +123 -962
- package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +147 -999
- package/dist/icons/cross-icon/CrossIcon.stories.tsx +139 -960
- package/dist/icons/download-icon/DownloadIcon.stories.tsx +126 -820
- package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +124 -1031
- package/dist/icons/email-icon/EmailIcon.stories.tsx +115 -936
- package/dist/icons/expand-icon/ExpandIcon.stories.tsx +112 -1111
- package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +144 -1025
- package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +143 -1036
- package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +127 -1011
- package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +126 -1056
- package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +125 -614
- package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +119 -1050
- package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +169 -989
- package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +115 -1145
- package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +115 -1122
- package/dist/icons/globe-icon/GlobeIcon.stories.tsx +130 -313
- package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +145 -940
- package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +119 -1174
- package/dist/icons/head-icon/HeadIcon.stories.tsx +111 -916
- package/dist/icons/heart-icon/HeartIcon.stories.tsx +120 -1019
- package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +119 -683
- package/dist/icons/image-icon/ImageIcon.stories.tsx +105 -1121
- package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +111 -1192
- package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +136 -1256
- package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +159 -962
- package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +161 -1385
- package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +124 -972
- package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +119 -948
- package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +119 -942
- package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +108 -1215
- package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +154 -1517
- package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +110 -1188
- package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +119 -678
- package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +123 -1184
- package/dist/icons/message-icon/MessageIcon.stories.tsx +114 -538
- package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +116 -1158
- package/dist/icons/moon-icon/MoonIcon.stories.tsx +120 -536
- package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +109 -1184
- package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +115 -1134
- package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +119 -971
- package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +111 -1100
- package/dist/icons/notes-icon/NotesIcon.stories.tsx +119 -1101
- package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +109 -1111
- package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +122 -684
- package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +113 -954
- package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +112 -877
- package/dist/icons/pause-icon/PauseIcon.stories.tsx +113 -1000
- package/dist/icons/pencil-icon/PencilIcon.stories.tsx +115 -1070
- package/dist/icons/phone-icon/PhoneIcon.stories.tsx +115 -978
- package/dist/icons/plus-icon/PlusIcon.stories.tsx +106 -1093
- package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +107 -829
- package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +102 -469
- package/dist/icons/search-icon/SearchIcon.stories.tsx +111 -1124
- package/dist/icons/setting-icon/SettingIcon.stories.tsx +107 -970
- package/dist/icons/share-icon/ShareIcon.stories.tsx +120 -1025
- package/dist/icons/shield-icon/ShieldIcon.stories.tsx +117 -931
- package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +137 -1104
- package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +172 -982
- package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +164 -983
- package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +105 -958
- package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +158 -580
- package/dist/icons/spinner-gradient-icon/index.tsx +6 -1
- package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +158 -587
- package/dist/icons/spinner-solid-icon/index.tsx +6 -1
- package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +146 -682
- package/dist/icons/spinner-solid-neutral-icon/index.tsx +1 -1
- package/dist/icons/star-icon/StarIcon.stories.tsx +124 -904
- package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +112 -964
- package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +116 -852
- package/dist/icons/sun-icon/SunIcon.stories.tsx +120 -831
- package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +116 -950
- package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +123 -980
- package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +156 -1427
- package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +146 -1142
- package/dist/icons/tick-icon/TickIcon.stories.tsx +145 -1276
- package/dist/icons/trash-icon/TrashIcon.stories.tsx +108 -933
- package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +157 -1402
- package/dist/icons/upload-icon/UploadIcon.stories.tsx +115 -889
- package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +118 -984
- package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +125 -1049
- package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +123 -1356
- package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +110 -1171
- package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +112 -1093
- package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +115 -1087
- package/dist/icons/warning-icon/WarningIcon.stories.tsx +122 -1046
- package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +161 -936
- package/dist/index.cjs +84 -84
- package/dist/index.js +84 -84
- package/dist/styles/aural-all-theme.css +1222 -0
- package/dist/styles/{aural-theme.css → aural-dark-theme.css} +15 -3
- package/dist/styles/aural-light-theme.css +1047 -0
- package/package.json +1 -1
|
@@ -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,959 +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-white">
|
|
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="mb-2 font-medium text-white">Border Configurations</h3>
|
|
203
|
-
<p className="text-sm text-white/60">
|
|
204
|
-
Explore different border configurations for dialog variants
|
|
205
|
-
</p>
|
|
206
|
-
</div>
|
|
207
|
-
|
|
208
|
-
<div className="space-y-6">
|
|
209
|
-
{/* All Borders */}
|
|
210
|
-
<div className="space-y-4">
|
|
211
|
-
<h4 className="text-sm font-medium text-white/80">All Borders</h4>
|
|
212
|
-
<div className="flex flex-wrap gap-2">
|
|
213
|
-
<BorderConfigDialog
|
|
214
|
-
borderConfig="all"
|
|
215
|
-
variant="neutral"
|
|
216
|
-
title="All - Neutral"
|
|
217
|
-
description="Dialog with borders on all four sides"
|
|
218
|
-
/>
|
|
219
|
-
<BorderConfigDialog
|
|
220
|
-
borderConfig="all"
|
|
221
|
-
variant="positive"
|
|
222
|
-
title="All - Success"
|
|
223
|
-
description="Success dialog with complete border frame"
|
|
224
|
-
/>
|
|
225
|
-
<BorderConfigDialog
|
|
226
|
-
borderConfig="all"
|
|
227
|
-
variant="negative"
|
|
228
|
-
title="All - Error"
|
|
229
|
-
description="Error dialog with full border emphasis"
|
|
230
|
-
/>
|
|
231
|
-
</div>
|
|
232
|
-
</div>
|
|
233
|
-
|
|
234
|
-
{/* Single Sides */}
|
|
235
|
-
<div className="space-y-4">
|
|
236
|
-
<h4 className="text-sm font-medium text-white/80">
|
|
237
|
-
Single Side Borders
|
|
238
|
-
</h4>
|
|
239
|
-
<div className="flex flex-wrap gap-2">
|
|
240
|
-
<BorderConfigDialog
|
|
241
|
-
borderConfig="top"
|
|
242
|
-
variant="info"
|
|
243
|
-
title="Top Only"
|
|
244
|
-
description="Dialog with top border accent"
|
|
245
|
-
/>
|
|
246
|
-
<BorderConfigDialog
|
|
247
|
-
borderConfig="bottom"
|
|
248
|
-
variant="warning"
|
|
249
|
-
title="Bottom Only"
|
|
250
|
-
description="Dialog with bottom border accent"
|
|
251
|
-
/>
|
|
252
|
-
<BorderConfigDialog
|
|
253
|
-
borderConfig="left"
|
|
254
|
-
variant="positive"
|
|
255
|
-
title="Left Only"
|
|
256
|
-
description="Dialog with left border accent"
|
|
257
|
-
/>
|
|
258
|
-
<BorderConfigDialog
|
|
259
|
-
borderConfig="right"
|
|
260
|
-
variant="negative"
|
|
261
|
-
title="Right Only"
|
|
262
|
-
description="Dialog with right border accent"
|
|
263
|
-
/>
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
|
|
267
|
-
{/* Two Sides */}
|
|
268
|
-
<div className="space-y-4">
|
|
269
|
-
<h4 className="text-sm font-medium text-white/80">
|
|
270
|
-
Two Side Borders
|
|
271
|
-
</h4>
|
|
272
|
-
<div className="flex flex-wrap gap-2">
|
|
273
|
-
<BorderConfigDialog
|
|
274
|
-
borderConfig={["top", "bottom"]}
|
|
275
|
-
variant="neutral"
|
|
276
|
-
title="Top + Bottom"
|
|
277
|
-
description="Horizontal border emphasis"
|
|
278
|
-
/>
|
|
279
|
-
<BorderConfigDialog
|
|
280
|
-
borderConfig={["left", "right"]}
|
|
281
|
-
variant="info"
|
|
282
|
-
title="Left + Right"
|
|
283
|
-
description="Vertical border emphasis"
|
|
284
|
-
/>
|
|
285
|
-
<BorderConfigDialog
|
|
286
|
-
borderConfig={["top", "left"]}
|
|
287
|
-
variant="warning"
|
|
288
|
-
title="Top + Left"
|
|
289
|
-
description="Corner border accent"
|
|
290
|
-
/>
|
|
291
|
-
<BorderConfigDialog
|
|
292
|
-
borderConfig={["bottom", "right"]}
|
|
293
|
-
variant="positive"
|
|
294
|
-
title="Bottom + Right"
|
|
295
|
-
description="Opposite corner accent"
|
|
296
|
-
/>
|
|
297
|
-
</div>
|
|
298
|
-
</div>
|
|
299
|
-
|
|
300
|
-
{/* Three Sides */}
|
|
301
|
-
<div className="space-y-4">
|
|
302
|
-
<h4 className="text-sm font-medium text-white/80">
|
|
303
|
-
Three Side Borders
|
|
304
|
-
</h4>
|
|
305
|
-
<div className="flex flex-wrap gap-2">
|
|
306
|
-
<BorderConfigDialog
|
|
307
|
-
borderConfig={["top", "left", "right"]}
|
|
308
|
-
variant="negative"
|
|
309
|
-
title="Top + Left + Right"
|
|
310
|
-
description="Open bottom design"
|
|
311
|
-
/>
|
|
312
|
-
<BorderConfigDialog
|
|
313
|
-
borderConfig={["bottom", "left", "right"]}
|
|
314
|
-
variant="warning"
|
|
315
|
-
title="Bottom + Left + Right"
|
|
316
|
-
description="Open top design"
|
|
317
|
-
/>
|
|
318
|
-
</div>
|
|
319
|
-
</div>
|
|
320
|
-
|
|
321
|
-
{/* No Border */}
|
|
322
|
-
<div className="space-y-4">
|
|
323
|
-
<h4 className="text-sm font-medium text-white/80">No Borders</h4>
|
|
324
|
-
<div className="flex flex-wrap gap-2">
|
|
325
|
-
<BorderConfigDialog
|
|
326
|
-
borderConfig="none"
|
|
327
|
-
variant="neutral"
|
|
328
|
-
title="No Borders"
|
|
329
|
-
description="Clean minimal design without border accents"
|
|
330
|
-
/>
|
|
331
|
-
</div>
|
|
332
|
-
</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>
|
|
333
124
|
</div>
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
},
|
|
343
|
-
},
|
|
344
|
-
},
|
|
125
|
+
<DialogFooter>
|
|
126
|
+
<DialogClose asChild>
|
|
127
|
+
<Button onClick={handleDialogClose}>Close</Button>
|
|
128
|
+
</DialogClose>
|
|
129
|
+
</DialogFooter>
|
|
130
|
+
</DialogContent>
|
|
131
|
+
</Dialog>
|
|
132
|
+
)
|
|
345
133
|
}
|
|
346
134
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
onEscapeKeyDown={handleClose}
|
|
369
|
-
onPointerDownOutside={handleClose}
|
|
370
|
-
>
|
|
371
|
-
<DialogHeader>
|
|
372
|
-
<DialogTitle>Dialog with React 18 Fix</DialogTitle>
|
|
373
|
-
<DialogDescription>
|
|
374
|
-
This dialog uses useDialogCleanup to prevent pointer-events
|
|
375
|
-
issues in React 18.
|
|
376
|
-
</DialogDescription>
|
|
377
|
-
</DialogHeader>
|
|
378
|
-
<div className="py-4">
|
|
379
|
-
<div className="rounded-lg border border-blue-500/20 bg-blue-500/10 p-3">
|
|
380
|
-
<p className="text-sm text-blue-200">
|
|
381
|
-
The useDialogCleanup hook ensures that pointer-events: none
|
|
382
|
-
doesn't get stuck on the body element when the dialog closes,
|
|
383
|
-
which was a common issue in React 18.
|
|
384
|
-
</p>
|
|
385
|
-
</div>
|
|
386
|
-
</div>
|
|
387
|
-
<DialogFooter>
|
|
388
|
-
<DialogClose asChild>
|
|
389
|
-
<Button variant="outline" onClick={handleClose}>
|
|
390
|
-
Cancel
|
|
391
|
-
</Button>
|
|
392
|
-
</DialogClose>
|
|
393
|
-
<Button onClick={handleClose}>Confirm</Button>
|
|
394
|
-
</DialogFooter>
|
|
395
|
-
</DialogContent>
|
|
396
|
-
</Dialog>
|
|
397
|
-
)
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
return (
|
|
401
|
-
<div className="space-y-8">
|
|
402
|
-
<div className="text-center">
|
|
403
|
-
<h3 className="mb-2 font-medium text-white">Dialog Cleanup Hook</h3>
|
|
404
|
-
<p className="text-sm text-white/60">
|
|
405
|
-
Demonstrates useDialogCleanup hook for React 18 compatibility
|
|
406
|
-
</p>
|
|
407
|
-
</div>
|
|
408
|
-
<div className="flex justify-center">
|
|
409
|
-
<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
|
+
))}
|
|
410
156
|
</div>
|
|
411
157
|
</div>
|
|
412
|
-
)
|
|
413
|
-
},
|
|
414
|
-
parameters: {
|
|
415
|
-
docs: {
|
|
416
|
-
description: {
|
|
417
|
-
story:
|
|
418
|
-
"Example showing how to use useDialogCleanup hook to fix React 18 pointer-events issues.",
|
|
419
|
-
},
|
|
420
|
-
},
|
|
421
|
-
},
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// 3. Basic Dialog Variants with Border Styles
|
|
425
|
-
export const BasicVariants: Story = {
|
|
426
|
-
render: () => {
|
|
427
|
-
const DialogWithCleanupVariant = ({
|
|
428
|
-
variant,
|
|
429
|
-
borderConfig,
|
|
430
|
-
title,
|
|
431
|
-
description,
|
|
432
|
-
children,
|
|
433
|
-
}: any) => {
|
|
434
|
-
const { handleDialogClose } = useDialogCleanup({ threshold: 100 })
|
|
435
158
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
borderConfig={borderConfig}
|
|
444
|
-
//@ts-expect-error onEscapeKeyDown available in Radix v2.0.0
|
|
445
|
-
onEscapeKeyDown={handleDialogClose}
|
|
446
|
-
onPointerDownOutside={handleDialogClose}
|
|
447
|
-
>
|
|
448
|
-
<DialogHeader>
|
|
449
|
-
<DialogTitle>{title}</DialogTitle>
|
|
450
|
-
<DialogDescription>{description}</DialogDescription>
|
|
451
|
-
</DialogHeader>
|
|
452
|
-
<div className="py-4">{children}</div>
|
|
453
|
-
<DialogFooter>
|
|
454
|
-
<DialogClose asChild>
|
|
455
|
-
<Button variant="outline" onClick={handleDialogClose}>
|
|
456
|
-
Cancel
|
|
457
|
-
</Button>
|
|
458
|
-
</DialogClose>
|
|
459
|
-
<Button onClick={handleDialogClose}>Continue</Button>
|
|
460
|
-
</DialogFooter>
|
|
461
|
-
</DialogContent>
|
|
462
|
-
</Dialog>
|
|
463
|
-
)
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
return (
|
|
467
|
-
<div className="space-y-8">
|
|
468
|
-
<div className="text-center">
|
|
469
|
-
<h3 className="mb-2 font-medium text-white">
|
|
470
|
-
Dialog Variants with Border Styles
|
|
471
|
-
</h3>
|
|
472
|
-
<p className="text-sm text-white/60">
|
|
473
|
-
Different dialog variants with customized border configurations
|
|
474
|
-
</p>
|
|
475
|
-
</div>
|
|
476
|
-
|
|
477
|
-
<div className="flex flex-wrap justify-center gap-4">
|
|
478
|
-
<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
|
|
479
166
|
variant="neutral"
|
|
480
|
-
borderConfig="
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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
|
|
491
180
|
variant="positive"
|
|
492
181
|
borderConfig="all"
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
<span className="font-medium text-green-400">Success!</span>
|
|
499
|
-
</div>
|
|
500
|
-
<p className="text-sm text-white/80">
|
|
501
|
-
Success dialogs with full border frame provide strong positive
|
|
502
|
-
feedback.
|
|
503
|
-
</p>
|
|
504
|
-
</DialogWithCleanupVariant>
|
|
505
|
-
|
|
506
|
-
<DialogWithCleanupVariant
|
|
182
|
+
triggerLabel="All sides"
|
|
183
|
+
title="All Borders"
|
|
184
|
+
description="Full border frame on all four sides."
|
|
185
|
+
/>
|
|
186
|
+
<ConfigDialog
|
|
507
187
|
variant="warning"
|
|
508
188
|
borderConfig={["top", "bottom"]}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
<AlertIcon className="h-4 w-4 text-yellow-400" />
|
|
515
|
-
<span className="font-medium text-yellow-400">Warning</span>
|
|
516
|
-
</div>
|
|
517
|
-
<p className="text-sm text-yellow-200">
|
|
518
|
-
Warning dialogs with horizontal borders create focused
|
|
519
|
-
attention.
|
|
520
|
-
</p>
|
|
521
|
-
</div>
|
|
522
|
-
</DialogWithCleanupVariant>
|
|
523
|
-
|
|
524
|
-
<DialogWithCleanupVariant
|
|
189
|
+
triggerLabel="Top + Bottom"
|
|
190
|
+
title="Horizontal Borders"
|
|
191
|
+
description="Top and bottom accent borders."
|
|
192
|
+
/>
|
|
193
|
+
<ConfigDialog
|
|
525
194
|
variant="negative"
|
|
526
195
|
borderConfig={["left", "right"]}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
<AlertIcon className="h-4 w-4 text-red-400" />
|
|
533
|
-
<span className="font-medium text-red-400">Error</span>
|
|
534
|
-
</div>
|
|
535
|
-
<p className="text-sm text-red-200">
|
|
536
|
-
Error dialogs with vertical borders provide urgent visual cues.
|
|
537
|
-
</p>
|
|
538
|
-
</div>
|
|
539
|
-
</DialogWithCleanupVariant>
|
|
540
|
-
|
|
541
|
-
<DialogWithCleanupVariant
|
|
196
|
+
triggerLabel="Left + Right"
|
|
197
|
+
title="Vertical Borders"
|
|
198
|
+
description="Left and right accent borders."
|
|
199
|
+
/>
|
|
200
|
+
<ConfigDialog
|
|
542
201
|
variant="info"
|
|
543
|
-
borderConfig="
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
<div className="mb-2 flex items-center gap-2">
|
|
549
|
-
<LightBulbSimpleIcon className="h-4 w-4 text-blue-400" />
|
|
550
|
-
<span className="font-medium text-blue-400">Information</span>
|
|
551
|
-
</div>
|
|
552
|
-
<p className="text-sm text-blue-200">
|
|
553
|
-
Clean info dialogs without borders for minimal, focused content
|
|
554
|
-
presentation.
|
|
555
|
-
</p>
|
|
556
|
-
</div>
|
|
557
|
-
</DialogWithCleanupVariant>
|
|
202
|
+
borderConfig={["top", "left", "right"]}
|
|
203
|
+
triggerLabel="Three sides"
|
|
204
|
+
title="Three-Side Borders"
|
|
205
|
+
description="Open-bottom border configuration."
|
|
206
|
+
/>
|
|
558
207
|
</div>
|
|
559
208
|
</div>
|
|
560
|
-
|
|
561
|
-
|
|
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
|
+
),
|
|
562
260
|
parameters: {
|
|
563
261
|
docs: {
|
|
564
262
|
description: {
|
|
565
263
|
story:
|
|
566
|
-
"
|
|
264
|
+
"Comparison of all variant types, border configurations, and overlay options (glass, noise, opacity) available on the Dialog component.",
|
|
567
265
|
},
|
|
568
266
|
},
|
|
569
267
|
},
|
|
570
268
|
}
|
|
571
269
|
|
|
572
|
-
//
|
|
573
|
-
|
|
270
|
+
// ─── Interactive ───────────────────────────────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
export const Interactive: Story = {
|
|
574
273
|
render: () => {
|
|
575
|
-
const
|
|
576
|
-
const { handleDialogClose } = useDialogCleanup({ threshold:
|
|
577
|
-
const [
|
|
578
|
-
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)
|
|
579
293
|
|
|
580
|
-
const
|
|
294
|
+
const handleConfirm = () => {
|
|
295
|
+
setLastAction("confirmed")
|
|
581
296
|
handleDialogClose()
|
|
582
|
-
|
|
297
|
+
setIsOpen(false)
|
|
583
298
|
}
|
|
584
299
|
|
|
585
|
-
const
|
|
300
|
+
const handleCancel = () => {
|
|
301
|
+
setLastAction("cancelled")
|
|
586
302
|
handleDialogClose()
|
|
587
|
-
|
|
303
|
+
setIsOpen(false)
|
|
588
304
|
}
|
|
589
305
|
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
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
|
|
595
335
|
|
|
596
336
|
return (
|
|
597
|
-
|
|
598
|
-
<
|
|
599
|
-
<
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
<DialogContent
|
|
606
|
-
className="max-w-md"
|
|
607
|
-
borderConfig={["top", "left", "right"]}
|
|
608
|
-
variant="neutral"
|
|
609
|
-
//@ts-expect-error onEscapeKeyDown available in Radix v2.0.0
|
|
610
|
-
onEscapeKeyDown={handleEditClose}
|
|
611
|
-
onPointerDownOutside={handleEditClose}
|
|
612
|
-
>
|
|
613
|
-
<DialogHeader>
|
|
614
|
-
<DialogTitle>Edit User Profile</DialogTitle>
|
|
615
|
-
<DialogDescription>
|
|
616
|
-
Update user information or delete the account.
|
|
617
|
-
</DialogDescription>
|
|
618
|
-
</DialogHeader>
|
|
619
|
-
|
|
620
|
-
<div className="space-y-4">
|
|
621
|
-
<Input placeholder="User name" />
|
|
622
|
-
<Input placeholder="Email address" type="email" />
|
|
623
|
-
<Textarea placeholder="Bio" rows={3} />
|
|
624
|
-
</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>
|
|
625
345
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
onClick={() => setIsDeleteOpen(true)}
|
|
642
|
-
>
|
|
643
|
-
<TrashIcon className="h-4 w-4" />
|
|
644
|
-
Delete Account
|
|
645
|
-
</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>
|
|
646
361
|
</div>
|
|
647
|
-
</DialogFooter>
|
|
648
|
-
</DialogContent>
|
|
649
|
-
</Dialog>
|
|
650
362
|
|
|
651
|
-
|
|
652
|
-
<DialogContent
|
|
653
|
-
variant="negative"
|
|
654
|
-
borderConfig="all"
|
|
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="h-5 w-5 text-red-400" />
|
|
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
|
-
borderConfig={["top", "bottom"]}
|
|
758
|
-
variant="info"
|
|
759
|
-
//@ts-expect-error onEscapeKeyDown available in Radix v2.0.0
|
|
760
|
-
onEscapeKeyDown={handleClose}
|
|
761
|
-
onPointerDownOutside={handleClose}
|
|
762
|
-
>
|
|
763
|
-
<DialogHeader>
|
|
764
|
-
<DialogTitle>Contact Us</DialogTitle>
|
|
765
|
-
<DialogDescription>
|
|
766
|
-
Send us a message and we'll get back to you soon.
|
|
767
|
-
</DialogDescription>
|
|
768
|
-
</DialogHeader>
|
|
769
|
-
|
|
770
|
-
<div className="space-y-4">
|
|
771
|
-
<div className="space-y-2">
|
|
772
|
-
<Label htmlFor="name">Name</Label>
|
|
773
|
-
<Input
|
|
774
|
-
id="name"
|
|
775
|
-
placeholder="Your name"
|
|
776
|
-
value={formData.name}
|
|
777
|
-
onChange={(e) =>
|
|
778
|
-
setFormData((prev) => ({ ...prev, name: e.target.value }))
|
|
779
|
-
}
|
|
780
|
-
/>
|
|
781
|
-
</div>
|
|
782
|
-
|
|
783
|
-
<div className="space-y-2">
|
|
784
|
-
<Label htmlFor="email">Email</Label>
|
|
785
|
-
<Input
|
|
786
|
-
id="email"
|
|
787
|
-
type="email"
|
|
788
|
-
placeholder="your.email@example.com"
|
|
789
|
-
value={formData.email}
|
|
790
|
-
onChange={(e) =>
|
|
791
|
-
setFormData((prev) => ({
|
|
792
|
-
...prev,
|
|
793
|
-
email: e.target.value,
|
|
794
|
-
}))
|
|
795
|
-
}
|
|
796
|
-
/>
|
|
797
|
-
</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>
|
|
798
400
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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>
|
|
814
417
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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>
|
|
829
434
|
</div>
|
|
830
|
-
</div>
|
|
831
|
-
|
|
832
|
-
<DialogFooter>
|
|
833
|
-
<DialogClose asChild>
|
|
834
|
-
<Button variant="outline" onClick={handleClose}>
|
|
835
|
-
Cancel
|
|
836
|
-
</Button>
|
|
837
|
-
</DialogClose>
|
|
838
|
-
<Button
|
|
839
|
-
disabled={
|
|
840
|
-
!formData.name || !formData.email || !formData.message
|
|
841
|
-
}
|
|
842
|
-
onClick={() => {
|
|
843
|
-
console.log("Form submitted:", formData)
|
|
844
|
-
handleClose()
|
|
845
|
-
}}
|
|
846
|
-
>
|
|
847
|
-
Send Message
|
|
848
|
-
</Button>
|
|
849
|
-
</DialogFooter>
|
|
850
|
-
</DialogContent>
|
|
851
|
-
</Dialog>
|
|
852
|
-
)
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
return (
|
|
856
|
-
<div className="space-y-8">
|
|
857
|
-
<div className="text-center">
|
|
858
|
-
<h3 className="mb-2 font-medium text-white">Form Dialog</h3>
|
|
859
|
-
<p className="text-sm text-white/60">
|
|
860
|
-
Dialog containing form elements with horizontal border accent
|
|
861
|
-
</p>
|
|
862
|
-
</div>
|
|
863
|
-
<div className="flex justify-center">
|
|
864
|
-
<FormDialogComponent />
|
|
865
|
-
</div>
|
|
866
|
-
</div>
|
|
867
|
-
)
|
|
868
|
-
},
|
|
869
|
-
parameters: {
|
|
870
|
-
docs: {
|
|
871
|
-
description: {
|
|
872
|
-
story:
|
|
873
|
-
"Form dialog with validation and horizontal border configuration for focused data entry.",
|
|
874
|
-
},
|
|
875
|
-
},
|
|
876
|
-
},
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
// 6. Overlay Variations
|
|
880
|
-
export const OverlayVariations: Story = {
|
|
881
|
-
render: () => {
|
|
882
|
-
const OverlayDialog = ({
|
|
883
|
-
opacity,
|
|
884
|
-
glass,
|
|
885
|
-
noise,
|
|
886
|
-
borderConfig,
|
|
887
|
-
title,
|
|
888
|
-
description,
|
|
889
|
-
}: any) => {
|
|
890
|
-
const { handleDialogClose } = useDialogCleanup({ threshold: 100 })
|
|
891
435
|
|
|
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
|
-
|
|
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>
|
|
919
491
|
</div>
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
<span className="text-white">
|
|
926
|
-
{Array.isArray(borderConfig)
|
|
927
|
-
? `[${borderConfig.join(", ")}]`
|
|
928
|
-
: borderConfig || "top"}
|
|
929
|
-
</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>
|
|
930
497
|
</div>
|
|
931
498
|
</div>
|
|
932
499
|
</div>
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
<Button onClick={handleDialogClose}>Close</Button>
|
|
936
|
-
</DialogClose>
|
|
937
|
-
</DialogFooter>
|
|
938
|
-
</DialogContent>
|
|
939
|
-
</Dialog>
|
|
500
|
+
</div>
|
|
501
|
+
</div>
|
|
940
502
|
)
|
|
941
503
|
}
|
|
942
504
|
|
|
943
|
-
return
|
|
944
|
-
<div className="space-y-8">
|
|
945
|
-
<div className="text-center">
|
|
946
|
-
<h3 className="mb-2 font-medium text-white">
|
|
947
|
-
Overlay Variations with Borders
|
|
948
|
-
</h3>
|
|
949
|
-
<p className="text-sm text-white/60">
|
|
950
|
-
Different overlay effects combined with border configurations
|
|
951
|
-
</p>
|
|
952
|
-
</div>
|
|
953
|
-
|
|
954
|
-
<div className="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4">
|
|
955
|
-
<OverlayDialog
|
|
956
|
-
opacity="low"
|
|
957
|
-
borderConfig="top"
|
|
958
|
-
title="Low Opacity"
|
|
959
|
-
description="Subtle background dimming (40%)"
|
|
960
|
-
/>
|
|
961
|
-
<OverlayDialog
|
|
962
|
-
opacity="medium"
|
|
963
|
-
borderConfig={["top", "bottom"]}
|
|
964
|
-
title="Medium Opacity"
|
|
965
|
-
description="Balanced background dimming (60%)"
|
|
966
|
-
/>
|
|
967
|
-
<OverlayDialog
|
|
968
|
-
opacity="high"
|
|
969
|
-
borderConfig="all"
|
|
970
|
-
title="High Opacity"
|
|
971
|
-
description="Strong background dimming (80%)"
|
|
972
|
-
/>
|
|
973
|
-
<OverlayDialog
|
|
974
|
-
opacity="none"
|
|
975
|
-
borderConfig="none"
|
|
976
|
-
title="Full Opacity"
|
|
977
|
-
description="Complete background coverage (100%)"
|
|
978
|
-
/>
|
|
979
|
-
<OverlayDialog
|
|
980
|
-
glass="low"
|
|
981
|
-
borderConfig={["left", "right"]}
|
|
982
|
-
title="Low Glass"
|
|
983
|
-
description="Subtle backdrop blur effect"
|
|
984
|
-
/>
|
|
985
|
-
<OverlayDialog
|
|
986
|
-
glass="medium"
|
|
987
|
-
borderConfig="all"
|
|
988
|
-
title="Medium Glass"
|
|
989
|
-
description="Balanced backdrop blur effect"
|
|
990
|
-
/>
|
|
991
|
-
<OverlayDialog
|
|
992
|
-
glass="high"
|
|
993
|
-
borderConfig="top"
|
|
994
|
-
title="High Glass"
|
|
995
|
-
description="Strong backdrop blur effect"
|
|
996
|
-
/>
|
|
997
|
-
<OverlayDialog
|
|
998
|
-
noise="low"
|
|
999
|
-
borderConfig={["top", "left", "right"]}
|
|
1000
|
-
title="Low Noise"
|
|
1001
|
-
description="Subtle texture pattern"
|
|
1002
|
-
/>
|
|
1003
|
-
</div>
|
|
1004
|
-
</div>
|
|
1005
|
-
)
|
|
505
|
+
return <InteractiveDemo />
|
|
1006
506
|
},
|
|
1007
507
|
parameters: {
|
|
1008
508
|
docs: {
|
|
1009
509
|
description: {
|
|
1010
510
|
story:
|
|
1011
|
-
"
|
|
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.",
|
|
1012
512
|
},
|
|
1013
513
|
},
|
|
1014
514
|
},
|
|
1015
515
|
}
|
|
1016
516
|
|
|
1017
|
-
//
|
|
1018
|
-
|
|
517
|
+
// ─── Accessibility ─────────────────────────────────────────────────────────────
|
|
518
|
+
|
|
519
|
+
export const Accessibility: Story = {
|
|
1019
520
|
render: () => {
|
|
1020
|
-
const
|
|
521
|
+
const A11yDialog = () => {
|
|
1021
522
|
const { handleDialogClose } = useDialogCleanup({ threshold: 100 })
|
|
1022
523
|
|
|
1023
524
|
return (
|
|
1024
525
|
<Dialog>
|
|
1025
526
|
<DialogTrigger asChild>
|
|
1026
|
-
<Button
|
|
527
|
+
<Button aria-label="Open accessibility demo dialog">
|
|
528
|
+
Open accessible dialog
|
|
529
|
+
</Button>
|
|
1027
530
|
</DialogTrigger>
|
|
1028
531
|
<DialogContent
|
|
1029
|
-
borderConfig="all"
|
|
1030
532
|
variant="info"
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
onPointerDownOutside={handleDialogClose}
|
|
533
|
+
borderConfig={["top"]}
|
|
534
|
+
aria-describedby="dialog-a11y-description"
|
|
1034
535
|
>
|
|
1035
536
|
<DialogHeader>
|
|
1036
|
-
<DialogTitle>
|
|
1037
|
-
<DialogDescription>
|
|
1038
|
-
This dialog
|
|
1039
|
-
|
|
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.
|
|
1040
541
|
</DialogDescription>
|
|
1041
542
|
</DialogHeader>
|
|
1042
543
|
|
|
1043
|
-
<div className="space-y-
|
|
1044
|
-
<div className="rounded-lg border
|
|
1045
|
-
<h4 className="
|
|
1046
|
-
|
|
544
|
+
<div className="space-y-3">
|
|
545
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
546
|
+
<h4 className="text-fm-primary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
547
|
+
Keyboard navigation
|
|
1047
548
|
</h4>
|
|
1048
|
-
<ul className="
|
|
1049
|
-
<li>• Complete border frame for maximum visual emphasis</li>
|
|
1050
|
-
<li>• Customizable per dialog context and importance</li>
|
|
1051
|
-
<li>• Maintains accessibility contrast ratios</li>
|
|
1052
|
-
<li>• Works with all variant color schemes</li>
|
|
1053
|
-
</ul>
|
|
1054
|
-
</div>
|
|
1055
|
-
|
|
1056
|
-
<div className="rounded-lg border border-white/10 bg-white/5 p-4">
|
|
1057
|
-
<h4 className="mb-2 font-medium text-white">
|
|
1058
|
-
Keyboard Navigation
|
|
1059
|
-
</h4>
|
|
1060
|
-
<ul className="space-y-1 text-sm text-white/80">
|
|
549
|
+
<ul className="text-fm-secondary font-fm-text text-fm-sm leading-fm-xl mt-2 space-y-1">
|
|
1061
550
|
<li>
|
|
1062
|
-
|
|
1063
|
-
|
|
551
|
+
<kbd className="bg-fm-surface-tertiary text-fm-xs rounded px-1 py-0.5 font-(--font-fm-mono)">
|
|
552
|
+
Tab
|
|
553
|
+
</kbd>{" "}
|
|
554
|
+
— move focus forward through interactive elements
|
|
1064
555
|
</li>
|
|
1065
556
|
<li>
|
|
1066
|
-
|
|
1067
|
-
|
|
557
|
+
<kbd className="bg-fm-surface-tertiary text-fm-xs rounded px-1 py-0.5 font-(--font-fm-mono)">
|
|
558
|
+
Shift+Tab
|
|
559
|
+
</kbd>{" "}
|
|
560
|
+
— move focus backward
|
|
1068
561
|
</li>
|
|
1069
562
|
<li>
|
|
1070
|
-
|
|
1071
|
-
|
|
563
|
+
<kbd className="bg-fm-surface-tertiary text-fm-xs rounded px-1 py-0.5 font-(--font-fm-mono)">
|
|
564
|
+
Escape
|
|
565
|
+
</kbd>{" "}
|
|
566
|
+
— close the dialog and restore focus to the trigger
|
|
1072
567
|
</li>
|
|
1073
568
|
<li>
|
|
1074
|
-
|
|
1075
|
-
|
|
569
|
+
<kbd className="bg-fm-surface-tertiary text-fm-xs rounded px-1 py-0.5 font-(--font-fm-mono)">
|
|
570
|
+
Enter / Space
|
|
571
|
+
</kbd>{" "}
|
|
572
|
+
— activate focused button
|
|
1076
573
|
</li>
|
|
1077
574
|
</ul>
|
|
1078
575
|
</div>
|
|
1079
576
|
|
|
1080
|
-
<div className="rounded-lg border
|
|
1081
|
-
<h4 className="
|
|
1082
|
-
|
|
577
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
578
|
+
<h4 className="text-fm-primary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
579
|
+
ARIA roles & attributes
|
|
1083
580
|
</h4>
|
|
1084
|
-
<ul className="
|
|
1085
|
-
<li
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
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>
|
|
1089
610
|
</ul>
|
|
1090
611
|
</div>
|
|
1091
612
|
</div>
|
|
1092
613
|
|
|
1093
614
|
<DialogFooter>
|
|
1094
615
|
<DialogClose asChild>
|
|
1095
|
-
<Button
|
|
616
|
+
<Button
|
|
617
|
+
onClick={handleDialogClose}
|
|
618
|
+
aria-label="Close accessibility demo dialog"
|
|
619
|
+
>
|
|
620
|
+
Close dialog
|
|
621
|
+
</Button>
|
|
1096
622
|
</DialogClose>
|
|
1097
623
|
</DialogFooter>
|
|
1098
624
|
</DialogContent>
|
|
@@ -1102,16 +628,46 @@ export const AccessibilityExample: Story = {
|
|
|
1102
628
|
|
|
1103
629
|
return (
|
|
1104
630
|
<div className="space-y-8">
|
|
1105
|
-
<div className="text-center">
|
|
1106
|
-
<h3 className="mb-2 font-medium text-white">
|
|
1107
|
-
Accessibility Features
|
|
1108
|
-
</h3>
|
|
1109
|
-
<p className="text-sm text-white/60">
|
|
1110
|
-
Dialog with comprehensive accessibility and border configuration
|
|
1111
|
-
</p>
|
|
1112
|
-
</div>
|
|
1113
631
|
<div className="flex justify-center">
|
|
1114
|
-
<
|
|
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>
|
|
1115
671
|
</div>
|
|
1116
672
|
</div>
|
|
1117
673
|
)
|
|
@@ -1120,7 +676,7 @@ export const AccessibilityExample: Story = {
|
|
|
1120
676
|
docs: {
|
|
1121
677
|
description: {
|
|
1122
678
|
story:
|
|
1123
|
-
"
|
|
679
|
+
"Demonstrates proper ARIA roles, keyboard navigation, and focus management for Dialog. Includes a best-practices reference box.",
|
|
1124
680
|
},
|
|
1125
681
|
},
|
|
1126
682
|
},
|