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.
Files changed (175) hide show
  1. package/README.md +8 -1
  2. package/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1228
  3. package/dist/components/avatar/Avatar.stories.tsx +219 -235
  4. package/dist/components/badge/Badge.stories.tsx +379 -116
  5. package/dist/components/banner/Banner.stories.tsx +445 -391
  6. package/dist/components/breadcrumb/Breadcrumb.stories.tsx +453 -199
  7. package/dist/components/button/Button.stories.tsx +585 -230
  8. package/dist/components/card/Card.stories.tsx +619 -301
  9. package/dist/components/char-count/CharCount.stories.tsx +350 -248
  10. package/dist/components/checkbox/Checkbox.stories.tsx +309 -167
  11. package/dist/components/chip/Chip.stories.tsx +362 -168
  12. package/dist/components/circular-loader/CircularLoader.stories.tsx +221 -636
  13. package/dist/components/clamp-lines/ClampLines.stories.tsx +246 -117
  14. package/dist/components/collapsible/Collapsible.stories.tsx +391 -252
  15. package/dist/components/command/Command.stories.tsx +530 -867
  16. package/dist/components/dialog/Dialog.stories.tsx +501 -950
  17. package/dist/components/divider/Divider.stories.tsx +264 -527
  18. package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
  19. package/dist/components/drawer/Drawer.stories.tsx +659 -1023
  20. package/dist/components/dropdown/Dropdown.stories.tsx +643 -1028
  21. package/dist/components/form/Form.stories.tsx +560 -274
  22. package/dist/components/helper-text/HelperText.stories.tsx +199 -200
  23. package/dist/components/hover-card/HoverCard.stories.tsx +318 -1254
  24. package/dist/components/icon-button/IconButton.stories.tsx +837 -194
  25. package/dist/components/if-else/if-else.stories.tsx +370 -83
  26. package/dist/components/input/Input.stories.tsx +436 -368
  27. package/dist/components/label/Label.stories.tsx +156 -154
  28. package/dist/components/list/List.stories.tsx +484 -835
  29. package/dist/components/marquee/Marquee.stories.tsx +356 -712
  30. package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -422
  31. package/dist/components/overlay/Overlay.stories.tsx +452 -824
  32. package/dist/components/pagination/Pagination.stories.tsx +721 -210
  33. package/dist/components/popover/Popover.stories.tsx +481 -896
  34. package/dist/components/radio/Radio.stories.tsx +432 -124
  35. package/dist/components/resizable/Resizable.stories.tsx +495 -799
  36. package/dist/components/scroll-area/ScrollArea.stories.tsx +383 -1059
  37. package/dist/components/search/Search.stories.tsx +312 -595
  38. package/dist/components/select/Select.stories.tsx +684 -789
  39. package/dist/components/sheet/Sheet.stories.tsx +671 -950
  40. package/dist/components/skelton/Skelton.stories.tsx +230 -764
  41. package/dist/components/slider/Slider.stories.tsx +383 -760
  42. package/dist/components/stepper/Stepper.stories.tsx +371 -514
  43. package/dist/components/switch/Switch.stories.tsx +461 -208
  44. package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
  45. package/dist/components/table/Table.stories.tsx +770 -916
  46. package/dist/components/tabs/Tabs.stories.tsx +458 -1455
  47. package/dist/components/tag/Tag.stories.tsx +714 -542
  48. package/dist/components/textarea/TextArea.stories.tsx +621 -562
  49. package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -154
  50. package/dist/components/toast/Toast.stories.tsx +452 -1339
  51. package/dist/components/toggle/Toggle.stories.tsx +488 -931
  52. package/dist/components/tooltip/Tooltip.stories.tsx +344 -1388
  53. package/dist/components/typography/Typography.stories.tsx +406 -89
  54. package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
  55. package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
  56. package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
  57. package/dist/icons/Icons.stories.tsx +0 -12
  58. package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +223 -1060
  59. package/dist/icons/alert-icon/AlertIcon.stories.tsx +106 -968
  60. package/dist/icons/all-icons.tsx +37 -16
  61. package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +137 -1010
  62. package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +145 -935
  63. package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +132 -1046
  64. package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +134 -986
  65. package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +135 -1028
  66. package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +133 -971
  67. package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +145 -1123
  68. package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +143 -1252
  69. package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +123 -632
  70. package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +141 -1223
  71. package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +164 -1018
  72. package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +121 -1236
  73. package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +121 -1213
  74. package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +116 -893
  75. package/dist/icons/camera-icon/CameraIcon.stories.tsx +109 -1254
  76. package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +114 -975
  77. package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +157 -994
  78. package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +160 -992
  79. package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +140 -970
  80. package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +126 -993
  81. package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +144 -987
  82. package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +141 -1007
  83. package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +147 -1187
  84. package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +110 -476
  85. package/dist/icons/coin-icon/CoinIcon.stories.tsx +120 -1364
  86. package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +113 -1360
  87. package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +111 -942
  88. package/dist/icons/command-icon/CommandIcon.stories.tsx +124 -1087
  89. package/dist/icons/copy-icon/CopyIcon.stories.tsx +119 -996
  90. package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +144 -1046
  91. package/dist/icons/cross-icon/CrossIcon.stories.tsx +136 -999
  92. package/dist/icons/download-icon/DownloadIcon.stories.tsx +123 -857
  93. package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +121 -1080
  94. package/dist/icons/email-icon/EmailIcon.stories.tsx +112 -979
  95. package/dist/icons/expand-icon/ExpandIcon.stories.tsx +109 -1146
  96. package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +141 -1068
  97. package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +140 -1081
  98. package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +124 -1050
  99. package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +123 -1091
  100. package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +122 -633
  101. package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +116 -1087
  102. package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +166 -1020
  103. package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +112 -1182
  104. package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +112 -1155
  105. package/dist/icons/globe-icon/GlobeIcon.stories.tsx +127 -325
  106. package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +142 -985
  107. package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +116 -1217
  108. package/dist/icons/head-icon/HeadIcon.stories.tsx +108 -953
  109. package/dist/icons/heart-icon/HeartIcon.stories.tsx +117 -1060
  110. package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +116 -716
  111. package/dist/icons/image-icon/ImageIcon.stories.tsx +102 -1164
  112. package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +108 -1233
  113. package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +133 -1289
  114. package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +155 -1012
  115. package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +158 -1438
  116. package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +121 -1011
  117. package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +116 -981
  118. package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +116 -979
  119. package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +105 -1252
  120. package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +151 -1554
  121. package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +107 -1227
  122. package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +116 -707
  123. package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +119 -1226
  124. package/dist/icons/message-icon/MessageIcon.stories.tsx +111 -557
  125. package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +112 -1198
  126. package/dist/icons/moon-icon/MoonIcon.stories.tsx +117 -557
  127. package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +106 -1235
  128. package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +112 -1185
  129. package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +116 -1012
  130. package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +108 -1137
  131. package/dist/icons/notes-icon/NotesIcon.stories.tsx +116 -1138
  132. package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +106 -1146
  133. package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +119 -719
  134. package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +110 -999
  135. package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +109 -912
  136. package/dist/icons/pause-icon/PauseIcon.stories.tsx +110 -1041
  137. package/dist/icons/pencil-icon/PencilIcon.stories.tsx +112 -1109
  138. package/dist/icons/phone-icon/PhoneIcon.stories.tsx +112 -1023
  139. package/dist/icons/plus-icon/PlusIcon.stories.tsx +103 -1132
  140. package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +104 -870
  141. package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +99 -476
  142. package/dist/icons/search-icon/SearchIcon.stories.tsx +108 -1161
  143. package/dist/icons/setting-icon/SettingIcon.stories.tsx +104 -1009
  144. package/dist/icons/share-icon/ShareIcon.stories.tsx +117 -1064
  145. package/dist/icons/shield-icon/ShieldIcon.stories.tsx +114 -974
  146. package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +134 -1160
  147. package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +169 -1017
  148. package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +161 -1016
  149. package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +102 -1001
  150. package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +155 -593
  151. package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +155 -608
  152. package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +142 -712
  153. package/dist/icons/star-icon/StarIcon.stories.tsx +120 -946
  154. package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +109 -1013
  155. package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +113 -891
  156. package/dist/icons/sun-icon/SunIcon.stories.tsx +117 -864
  157. package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +113 -989
  158. package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +120 -1027
  159. package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +153 -1476
  160. package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +143 -1187
  161. package/dist/icons/tick-icon/TickIcon.stories.tsx +142 -1322
  162. package/dist/icons/trash-icon/TrashIcon.stories.tsx +105 -970
  163. package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +154 -1457
  164. package/dist/icons/upload-icon/UploadIcon.stories.tsx +112 -930
  165. package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +115 -1019
  166. package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +122 -1092
  167. package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +120 -1401
  168. package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +107 -1212
  169. package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +109 -1122
  170. package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +112 -1124
  171. package/dist/icons/warning-icon/WarningIcon.stories.tsx +119 -1083
  172. package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +158 -983
  173. package/dist/index.cjs +90 -90
  174. package/dist/index.js +90 -90
  175. package/package.json +8 -3
@@ -1,8 +1,9 @@
1
- import React from "react"
1
+ import React, { useState } from "react"
2
2
  import { AngleDownIcon } from "@icons/angle-down-icon"
3
3
  import type { Meta, StoryObj } from "@storybook/react-vite"
4
4
 
5
- import { IconButton } from "../icon-button"
5
+ import { AuralComponentDocsPage } from "src/ui/story-spec/components/component-story-docs-page"
6
+
6
7
  import {
7
8
  Collapsible,
8
9
  CollapsibleContent,
@@ -16,18 +17,29 @@ const meta: Meta<typeof Collapsible> = {
16
17
  component: Collapsible,
17
18
  parameters: {
18
19
  layout: "centered",
19
- backgrounds: {
20
- default: "dark",
21
- values: [
22
- { name: "dark", value: "#0a0a0a" },
23
- { name: "light", value: "#ffffff" },
24
- ],
25
- },
26
20
  docs: {
27
21
  description: {
28
22
  component:
29
- "A collapsible component built on Radix UI that can expand and collapse content.",
23
+ "A compound collapsible component built on Radix UI. Composed of CollapsibleHeader, CollapsibleTrigger, CollapsibleTitle, and CollapsibleContent for building expandable sections with defaultOpen and disabled states.",
30
24
  },
25
+ page: () => (
26
+ <AuralComponentDocsPage
27
+ features={[
28
+ {
29
+ title: "Compound Structure",
30
+ description: "Header, trigger, content",
31
+ },
32
+ {
33
+ title: "Stateful by Design",
34
+ description: "Open, closed, disabled",
35
+ },
36
+ {
37
+ title: "Expandable Content",
38
+ description: "Smooth reveal pattern",
39
+ },
40
+ ]}
41
+ />
42
+ ),
31
43
  },
32
44
  },
33
45
  tags: ["autodocs"],
@@ -46,281 +58,408 @@ const meta: Meta<typeof Collapsible> = {
46
58
  export default meta
47
59
  type Story = StoryObj<typeof Collapsible>
48
60
 
49
- export const Default: Story = {
50
- render: (args) => (
51
- <div className="w-[800px]">
52
- <Collapsible {...args}>
53
- <CollapsibleHeader title="Frequently Asked Questions" />
54
- <CollapsibleContent>
55
- <div className="pt-4">
56
- <p>
57
- This is the collapsible content area. You can put any content here
58
- including text, images, forms, or other components. The content
59
- will smoothly animate when the collapsible is opened or closed.
60
- </p>
61
- </div>
62
- </CollapsibleContent>
63
- </Collapsible>
61
+ function InfoBox({ children }: { children: React.ReactNode }) {
62
+ return (
63
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
64
+ <p className="text-fm-secondary font-fm-text text-fm-md leading-fm-xl">
65
+ {children}
66
+ </p>
64
67
  </div>
65
- ),
66
- args: {
67
- defaultOpen: false,
68
- },
68
+ )
69
69
  }
70
70
 
71
- export const DefaultOpen: Story = {
72
- render: () => (
73
- <div className="w-[800px]">
74
- <Collapsible defaultOpen>
75
- <CollapsibleHeader title="Settings Panel" />
76
- <CollapsibleContent>
77
- <div className="space-y-3 pt-4">
78
- <div className="flex items-center justify-between">
79
- <span>Enable notifications</span>
80
- <input type="checkbox" defaultChecked />
81
- </div>
82
- <div className="flex items-center justify-between">
83
- <span>Dark mode</span>
84
- <input type="checkbox" />
85
- </div>
86
- <div className="flex items-center justify-between">
87
- <span>Auto-save</span>
88
- <input type="checkbox" defaultChecked />
89
- </div>
90
- </div>
91
- </CollapsibleContent>
92
- </Collapsible>
71
+ function PartCard({
72
+ title,
73
+ example,
74
+ description,
75
+ }: {
76
+ title: string
77
+ example: React.ReactNode
78
+ description: React.ReactNode
79
+ }) {
80
+ return (
81
+ <div className="space-y-3">
82
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
83
+ {title}
84
+ </h4>
85
+ <div className="border-fm-divider-secondary bg-fm-surface-primary rounded-xl border p-4">
86
+ {example}
87
+ </div>
88
+ <InfoBox>{description}</InfoBox>
93
89
  </div>
94
- ),
90
+ )
95
91
  }
96
92
 
97
- export const Disabled: Story = {
98
- render: () => (
99
- <div className="w-[800px]">
100
- <Collapsible disabled>
101
- <CollapsibleHeader title="Disabled Section" />
102
- <CollapsibleContent>
103
- <div className="pt-4">
104
- <p>
105
- This content cannot be accessed because the collapsible is
106
- disabled.
107
- </p>
108
- </div>
109
- </CollapsibleContent>
110
- </Collapsible>
93
+ function TrackMeta() {
94
+ return (
95
+ <div className="space-y-1 pt-4">
96
+ <p className="text-fm-primary font-fm-text text-fm-md leading-fm-md">
97
+ Midnight Rain
98
+ </p>
99
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
100
+ Taylor Swift · Midnights · 2:53
101
+ </p>
111
102
  </div>
112
- ),
103
+ )
113
104
  }
114
105
 
115
- export const CustomTrigger: Story = {
116
- render: () => (
117
- <div className="w-[800px]">
118
- <Collapsible>
119
- <div className="flex items-center justify-between gap-2">
120
- <CollapsibleTitle>Advanced Settings</CollapsibleTitle>
121
- <CollapsibleTrigger asChild>
122
- <IconButton
123
- variant="ghost"
124
- size="small"
125
- icon={
126
- <AngleDownIcon className="toggle-icon" height={32} width={32} />
127
- }
128
- label="Toggle Settings"
129
- className="text-fm-icon-active disabled:text-fm-icon-inactive flex [&>.toggle-icon]:transition-transform [&>.toggle-icon]:duration-50 data-[state=open]:[&>.toggle-icon]:-rotate-180"
130
- />
131
- </CollapsibleTrigger>
132
- </div>
133
- <CollapsibleContent>
134
- <div className="pt-4">
135
- <p>
136
- Custom trigger button with settings icon that rotates 45 degrees
137
- when opened.
138
- </p>
139
- </div>
140
- </CollapsibleContent>
141
- </Collapsible>
142
- </div>
143
- ),
144
- }
106
+ // ─── Parts ───────────────────────────────────────────────────────────────────
145
107
 
146
- export const MultipleCollapsibles: Story = {
108
+ export const Parts: Story = {
109
+ parameters: {
110
+ docs: {
111
+ description: {
112
+ story:
113
+ "The compound parts demonstrated in valid compositions so each piece is visible and interactive in context.",
114
+ },
115
+ },
116
+ },
147
117
  render: () => (
148
- <div className="w-[800px] space-y-4">
149
- <Collapsible>
150
- <CollapsibleHeader title="Section 1: Getting Started" />
151
- <CollapsibleContent>
152
- <div className="pt-4">
153
- <p>
154
- Learn the basics of using our platform. This section covers
155
- initial setup, account creation, and basic navigation.
118
+ <div className="space-y-10">
119
+ <PartCard
120
+ title="CollapsibleTitle"
121
+ example={
122
+ <div className="space-y-3">
123
+ <CollapsibleTitle>Track Information</CollapsibleTitle>
124
+ <p className="text-fm-tertiary font-fm-text text-fm-sm leading-fm-sm">
125
+ Standalone title styling for section headings.
156
126
  </p>
157
127
  </div>
158
- </CollapsibleContent>
159
- </Collapsible>
128
+ }
129
+ description={
130
+ <>
131
+ Renders the heading text for a collapsible section. By default it
132
+ maps to <code>&lt;h2&gt;</code> and provides the large title
133
+ treatment used in the header row.
134
+ </>
135
+ }
136
+ />
160
137
 
161
- <Collapsible>
162
- <CollapsibleHeader title="Section 2: Advanced Features" />
163
- <CollapsibleContent>
164
- <div className="pt-4">
165
- <p>
166
- Explore advanced functionality including custom integrations, API
167
- usage, and automation tools.
168
- </p>
169
- </div>
170
- </CollapsibleContent>
171
- </Collapsible>
138
+ <PartCard
139
+ title="CollapsibleTrigger"
140
+ example={
141
+ <Collapsible className="border-0 pb-0" defaultOpen>
142
+ <div className="space-y-3">
143
+ <div className="flex items-center gap-3">
144
+ <CollapsibleTrigger asChild>
145
+ <button className="text-fm-primary border-fm-divider-secondary bg-fm-surface-secondary flex items-center gap-2 rounded-lg border px-3 py-2 [&>.toggle-icon]:transition-transform [&>.toggle-icon]:duration-150 data-[state=open]:[&>.toggle-icon]:-rotate-180">
146
+ <span className="font-fm-text text-fm-sm leading-fm-sm">
147
+ Toggle Details
148
+ </span>
149
+ <AngleDownIcon
150
+ className="toggle-icon text-fm-icon-active"
151
+ height={20}
152
+ width={20}
153
+ />
154
+ </button>
155
+ </CollapsibleTrigger>
156
+ <p className="text-fm-tertiary font-fm-text text-fm-sm leading-fm-sm">
157
+ Custom trigger using <code>asChild</code>
158
+ </p>
159
+ </div>
160
+ <CollapsibleContent>
161
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-3">
162
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
163
+ Clicking the trigger collapses and reveals this panel.
164
+ </p>
165
+ </div>
166
+ </CollapsibleContent>
167
+ </div>
168
+ </Collapsible>
169
+ }
170
+ description={
171
+ <>
172
+ The trigger controls the open state. Here it is rendered with{" "}
173
+ <code>asChild</code> so the toggle state is visible on a custom
174
+ button while still using the same collapsible trigger primitive.
175
+ </>
176
+ }
177
+ />
172
178
 
173
- <Collapsible>
174
- <CollapsibleHeader title="Section 3: Troubleshooting" />
175
- <CollapsibleContent>
176
- <div className="pt-4">
177
- <p>
178
- Common issues and their solutions. Find answers to frequently
179
- encountered problems.
180
- </p>
181
- </div>
182
- </CollapsibleContent>
183
- </Collapsible>
184
- </div>
185
- ),
186
- }
179
+ <PartCard
180
+ title="CollapsibleHeader"
181
+ example={
182
+ <Collapsible className="border-0 pb-0">
183
+ <CollapsibleHeader title="EQ Settings" />
184
+ <CollapsibleContent>
185
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-3">
186
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
187
+ The full header row acts as the trigger.
188
+ </p>
189
+ </div>
190
+ </CollapsibleContent>
191
+ </Collapsible>
192
+ }
193
+ description={
194
+ <>
195
+ A convenience wrapper that combines <code>CollapsibleTitle</code>{" "}
196
+ and <code>CollapsibleTrigger</code> into one clickable header row.
197
+ </>
198
+ }
199
+ />
187
200
 
188
- export const RichContent: Story = {
189
- render: () => (
190
- <div className="w-[800px]">
191
- <Collapsible>
192
- <CollapsibleHeader title="Product Features" />
193
- <CollapsibleContent>
194
- <div className="space-y-4 pt-4">
195
- <div>
196
- <h4 className="mb-2 font-semibold">Key Benefits:</h4>
197
- <ul className="list-inside list-disc space-y-1">
198
- <li>Easy to use interface</li>
199
- <li>Powerful automation tools</li>
200
- <li>24/7 customer support</li>
201
- <li>Enterprise-grade security</li>
202
- </ul>
203
- </div>
204
- <div>
205
- <h4 className="mb-2 font-semibold">Pricing:</h4>
206
- <div className="bg-fm-surface-secondary rounded p-3">
207
- <p className="text-sm">Starting at $29/month for basic plan</p>
201
+ <PartCard
202
+ title="CollapsibleContent"
203
+ example={
204
+ <Collapsible defaultOpen className="border-0 pb-0">
205
+ <CollapsibleHeader title="Current EQ Preset" />
206
+ <CollapsibleContent>
207
+ <div className="space-y-2 pt-4">
208
+ <p className="text-fm-primary font-fm-text text-fm-md leading-fm-md">
209
+ Warm Vocal Boost
210
+ </p>
211
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
212
+ Bass 80 Hz · +3 dB
213
+ </p>
214
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
215
+ Mid 1 kHz · 0 dB
216
+ </p>
217
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
218
+ Treble 10 kHz · −2 dB
219
+ </p>
208
220
  </div>
209
- </div>
210
- </div>
211
- </CollapsibleContent>
212
- </Collapsible>
221
+ </CollapsibleContent>
222
+ </Collapsible>
223
+ }
224
+ description={
225
+ <>
226
+ The content region is the expandable body. It stays mounted within
227
+ the collapsible and becomes visible when the section is opened.
228
+ </>
229
+ }
230
+ />
213
231
  </div>
214
232
  ),
215
233
  }
216
234
 
217
- export const WithCustomStyling: Story = {
218
- render: () => (
219
- <div className="w-[800px]">
220
- <Collapsible className="rounded-lg border-2 border-blue-200 bg-blue-50 p-4">
221
- <div className="flex items-center justify-between gap-2">
222
- <CollapsibleTitle className="font-bold text-blue-800">
223
- Important Notice
224
- </CollapsibleTitle>
225
- <CollapsibleTrigger asChild>
226
- <IconButton
227
- variant="ghost"
228
- size="small"
229
- className="text-blue-600 hover:bg-blue-100"
230
- icon={
231
- <AngleDownIcon className="transition-transform duration-200 data-[state=open]:rotate-180" />
232
- }
233
- label="Toggle Notice"
234
- />
235
- </CollapsibleTrigger>
236
- </div>
237
- <CollapsibleContent className="text-blue-700">
238
- <div className="pt-4">
239
- <p>
240
- This is an important system notification with custom styling. The
241
- entire collapsible has a blue theme to draw attention to critical
242
- information.
243
- </p>
244
- </div>
245
- </CollapsibleContent>
246
- </Collapsible>
247
- </div>
248
- ),
249
- }
235
+ // ─── States ──────────────────────────────────────────────────────────────────
250
236
 
251
- export const LongContent: Story = {
237
+ export const States: Story = {
238
+ parameters: {
239
+ docs: {
240
+ description: {
241
+ story:
242
+ "Open, closed, and disabled states side by side, using realistic content so each state reads clearly.",
243
+ },
244
+ },
245
+ },
252
246
  render: () => (
253
- <div className="w-[800px]">
254
- <Collapsible>
255
- <CollapsibleHeader title="Terms and Conditions" />
256
- <CollapsibleContent>
257
- <div className="max-h-60 overflow-y-auto pt-4">
258
- <div className="space-y-4 text-sm">
259
- <p>
260
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
261
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
262
- enim ad minim veniam, quis nostrud exercitation ullamco laboris.
263
- </p>
264
- <p>
265
- Duis aute irure dolor in reprehenderit in voluptate velit esse
266
- cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
267
- cupidatat non proident, sunt in culpa qui officia deserunt
268
- mollit anim id est laborum.
269
- </p>
270
- <p>
271
- Sed ut perspiciatis unde omnis iste natus error sit voluptatem
272
- accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
273
- quae ab illo inventore veritatis et quasi architecto beatae
274
- vitae dicta sunt explicabo.
275
- </p>
276
- <p>
277
- Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit
278
- aut fugit, sed quia consequuntur magni dolores eos qui ratione
279
- voluptatem sequi nesciunt.
247
+ <div className="space-y-8">
248
+ <div className="space-y-3">
249
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
250
+ Open
251
+ </h4>
252
+ <Collapsible defaultOpen>
253
+ <CollapsibleHeader title="Now Playing" />
254
+ <CollapsibleContent>
255
+ <TrackMeta />
256
+ </CollapsibleContent>
257
+ </Collapsible>
258
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm text-center">
259
+ Default Open
260
+ </p>
261
+ </div>
262
+
263
+ <div className="space-y-3">
264
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
265
+ Closed
266
+ </h4>
267
+ <Collapsible>
268
+ <CollapsibleHeader title="Playlist Details" />
269
+ <CollapsibleContent>
270
+ <div className="pt-4">
271
+ <p className="text-fm-primary font-fm-text text-fm-md leading-fm-md">
272
+ Hidden until expanded
280
273
  </p>
281
- <p>
282
- At vero eos et accusamus et iusto odio dignissimos ducimus qui
283
- blanditiis praesentium voluptatum deleniti atque corrupti quos
284
- dolores et quas molestias excepturi sint occaecati cupiditate
285
- non provident.
274
+ </div>
275
+ </CollapsibleContent>
276
+ </Collapsible>
277
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm text-center">
278
+ Default closed
279
+ </p>
280
+ </div>
281
+
282
+ <div className="space-y-3">
283
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
284
+ Disabled
285
+ </h4>
286
+ <Collapsible disabled>
287
+ <CollapsibleHeader title="Locked Section" />
288
+ <CollapsibleContent>
289
+ <div className="pt-4">
290
+ <p className="text-fm-primary font-fm-text text-fm-md leading-fm-md">
291
+ This content cannot be expanded while disabled.
286
292
  </p>
287
293
  </div>
288
- </div>
289
- </CollapsibleContent>
290
- </Collapsible>
294
+ </CollapsibleContent>
295
+ </Collapsible>
296
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm text-center">
297
+ Disabled trigger and title
298
+ </p>
299
+ </div>
291
300
  </div>
292
301
  ),
293
302
  }
294
303
 
295
- export const NestedCollapsibles: Story = {
296
- render: () => (
297
- <div className="w-[800px]">
298
- <Collapsible>
299
- <CollapsibleHeader title="Main Category" />
300
- <CollapsibleContent>
304
+ // ─── Interactive ─────────────────────────────────────────────────────────────
305
+
306
+ export const Interactive: Story = {
307
+ parameters: {
308
+ docs: {
309
+ description: {
310
+ story:
311
+ "A multi-panel audio settings surface with local state controls for opening individual panels or expanding everything at once.",
312
+ },
313
+ },
314
+ },
315
+ render: () => {
316
+ const sections = [
317
+ {
318
+ id: "track",
319
+ title: "Track Info",
320
+ content: (
321
+ <div className="space-y-3 pt-4">
322
+ {[
323
+ ["Title", "Midnight Rain"],
324
+ ["Artist", "Taylor Swift"],
325
+ ["Album", "Midnights"],
326
+ ["Duration", "2:53"],
327
+ ].map(([label, value]) => (
328
+ <div key={label} className="flex items-center justify-between">
329
+ <span className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
330
+ {label}
331
+ </span>
332
+ <span className="text-fm-primary font-fm-text text-fm-md leading-fm-md">
333
+ {value}
334
+ </span>
335
+ </div>
336
+ ))}
337
+ </div>
338
+ ),
339
+ },
340
+ {
341
+ id: "eq",
342
+ title: "EQ Settings",
343
+ content: (
344
+ <div className="space-y-3 pt-4">
345
+ {[
346
+ ["Sub-Bass", "60 Hz · +2 dB"],
347
+ ["Bass", "250 Hz · +4 dB"],
348
+ ["Mid", "1 kHz · 0 dB"],
349
+ ["Presence", "4 kHz · −1 dB"],
350
+ ["Treble", "10 kHz · −3 dB"],
351
+ ].map(([label, value]) => (
352
+ <div key={label} className="flex items-center justify-between">
353
+ <span className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
354
+ {label}
355
+ </span>
356
+ <span className="text-fm-primary font-fm-text text-fm-md leading-fm-md">
357
+ {value}
358
+ </span>
359
+ </div>
360
+ ))}
361
+ </div>
362
+ ),
363
+ },
364
+ {
365
+ id: "playlist",
366
+ title: "Playlist Details",
367
+ content: (
301
368
  <div className="space-y-2 pt-4">
302
- <p className="mb-4">This section contains nested collapsibles:</p>
369
+ {[
370
+ "Anti-Hero",
371
+ "Lavender Haze",
372
+ "Midnight Rain",
373
+ "Question...?",
374
+ "Vigilante Shit",
375
+ ].map((track, index) => (
376
+ <div key={track} className="flex items-center gap-3">
377
+ <span className="text-fm-tertiary font-fm-text text-fm-sm leading-fm-sm w-5 text-right">
378
+ {index + 1}
379
+ </span>
380
+ <span className="text-fm-primary font-fm-text text-fm-md leading-fm-md">
381
+ {track}
382
+ </span>
383
+ </div>
384
+ ))}
385
+ </div>
386
+ ),
387
+ },
388
+ ]
303
389
 
304
- <Collapsible className="ml-4">
305
- <CollapsibleHeader title="Subcategory A" />
306
- <CollapsibleContent>
307
- <div className="pt-4">
308
- <p>Content for subcategory A with detailed information.</p>
309
- </div>
310
- </CollapsibleContent>
311
- </Collapsible>
390
+ const Demo = () => {
391
+ const [openMap, setOpenMap] = useState<Record<string, boolean>>({
392
+ track: true,
393
+ eq: false,
394
+ playlist: false,
395
+ })
312
396
 
313
- <Collapsible className="ml-4">
314
- <CollapsibleHeader title="Subcategory B" />
315
- <CollapsibleContent>
316
- <div className="pt-4">
317
- <p>Content for subcategory B with additional details.</p>
397
+ const allOpen = Object.values(openMap).every(Boolean)
398
+
399
+ const togglePanel = (id: string) => {
400
+ setOpenMap((current) => ({ ...current, [id]: !current[id] }))
401
+ }
402
+
403
+ const toggleAll = () => {
404
+ const nextValue = !allOpen
405
+ setOpenMap(
406
+ Object.fromEntries(sections.map(({ id }) => [id, nextValue]))
407
+ )
408
+ }
409
+
410
+ return (
411
+ <div className="w-full p-8">
412
+ <div className="mx-auto max-w-3xl space-y-6">
413
+ <div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
414
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary space-y-5 rounded-xl border p-5">
415
+ <p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
416
+ Panels
417
+ </p>
418
+ {sections.map(({ id, title }) => (
419
+ <button
420
+ key={id}
421
+ onClick={() => togglePanel(id)}
422
+ className="border-fm-divider-secondary bg-fm-surface-primary text-fm-primary font-fm-text text-fm-sm leading-fm-sm hover:border-fm-divider-primary w-full rounded-lg border px-4 py-2 text-left transition-colors"
423
+ >
424
+ {openMap[id] ? "Close" : "Open"} {title}
425
+ </button>
426
+ ))}
427
+ <div className="border-fm-divider-secondary border-t pt-4" />
428
+ <button
429
+ onClick={toggleAll}
430
+ className="border-fm-divider-primary text-fm-primary font-fm-text text-fm-sm leading-fm-sm hover:bg-fm-surface-primary w-full rounded-lg border px-4 py-2 transition-colors"
431
+ >
432
+ {allOpen ? "Collapse All" : "Expand All"}
433
+ </button>
434
+ </div>
435
+
436
+ <div className="flex flex-col gap-3 lg:col-span-2">
437
+ <div className="space-y-0">
438
+ {sections.map(({ id, title, content }) => (
439
+ <Collapsible
440
+ key={id}
441
+ open={openMap[id]}
442
+ onOpenChange={(open) =>
443
+ setOpenMap((current) => ({ ...current, [id]: open }))
444
+ }
445
+ >
446
+ <CollapsibleHeader title={title} />
447
+ <CollapsibleContent>{content}</CollapsibleContent>
448
+ </Collapsible>
449
+ ))}
318
450
  </div>
319
- </CollapsibleContent>
320
- </Collapsible>
451
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
452
+ <code className="text-fm-secondary text-fm-md leading-fm-md font-[var(--font-fm-mono)]">
453
+ {`openMap = ${JSON.stringify(openMap)}`}
454
+ </code>
455
+ </div>
456
+ </div>
457
+ </div>
321
458
  </div>
322
- </CollapsibleContent>
323
- </Collapsible>
324
- </div>
325
- ),
459
+ </div>
460
+ )
461
+ }
462
+
463
+ return <Demo />
464
+ },
326
465
  }