@catalystsoftware/ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +7 -0
  2. package/components/catalyst-ui/buttons/burger.tsx +207 -0
  3. package/components/catalyst-ui/core/data-display/timeline.tsx +210 -0
  4. package/components/catalyst-ui/core/feedback/alert.tsx +491 -0
  5. package/components/catalyst-ui/core/feedback/spinner-1.tsx +65 -0
  6. package/components/catalyst-ui/core/feedback/toast.tsx +1857 -0
  7. package/components/catalyst-ui/core/navigation/menu.tsx +164 -0
  8. package/components/catalyst-ui/forms/toggle-class.tsx +176 -0
  9. package/components/catalyst-ui/hooks/use-copy-to-clipboard.tsx +419 -0
  10. package/components/catalyst-ui/hooks/use-counter.tsx +13 -0
  11. package/components/catalyst-ui/hooks/use-event-listener.tsx +23 -0
  12. package/components/catalyst-ui/hooks/use-export-markdown.tsx +47 -0
  13. package/components/catalyst-ui/hooks/use-focus.tsx +17 -0
  14. package/components/catalyst-ui/hooks/use-interval.tsx +23 -0
  15. package/components/catalyst-ui/hooks/use-is-client.tsx +16 -0
  16. package/components/catalyst-ui/hooks/use-media-query.tsx +19 -0
  17. package/components/catalyst-ui/hooks/use-mobile.tsx +19 -0
  18. package/components/catalyst-ui/hooks/use-resize-observer.tsx +81 -0
  19. package/components/catalyst-ui/hooks/use-timeout.tsx +21 -0
  20. package/components/catalyst-ui/hooks/use-timer.tsx +209 -0
  21. package/components/catalyst-ui/hooks/use-toggle.tsx +12 -0
  22. package/components/catalyst-ui/media/image.tsx +13 -0
  23. package/components/catalyst-ui/overlays/dual-sidebar.tsx +4142 -0
  24. package/components/catalyst-ui/overlays/sidebar-original.tsx +726 -0
  25. package/components/catalyst-ui/primitives/accordion.tsx +250 -0
  26. package/components/catalyst-ui/primitives/alert-dialog.tsx +126 -0
  27. package/components/catalyst-ui/primitives/aspect-ratio.tsx +9 -0
  28. package/components/catalyst-ui/primitives/avatar.tsx +296 -0
  29. package/components/catalyst-ui/primitives/badge.tsx +57 -0
  30. package/components/catalyst-ui/primitives/breadcrumb.tsx +101 -0
  31. package/components/catalyst-ui/primitives/button.tsx +265 -0
  32. package/components/catalyst-ui/primitives/calendar-v4.tsx +208 -0
  33. package/components/catalyst-ui/primitives/calendar.tsx +295 -0
  34. package/components/catalyst-ui/primitives/card.tsx +618 -0
  35. package/components/catalyst-ui/primitives/carousel.tsx +238 -0
  36. package/components/catalyst-ui/primitives/chart.tsx +347 -0
  37. package/components/catalyst-ui/primitives/checkbox.tsx +225 -0
  38. package/components/catalyst-ui/primitives/collapsible.tsx +212 -0
  39. package/components/catalyst-ui/primitives/command.tsx +393 -0
  40. package/components/catalyst-ui/primitives/context-menu.tsx +236 -0
  41. package/components/catalyst-ui/primitives/dialog.tsx +471 -0
  42. package/components/catalyst-ui/primitives/drawer.tsx +761 -0
  43. package/components/catalyst-ui/primitives/dropdown-menu.tsx +290 -0
  44. package/components/catalyst-ui/primitives/empty.tsx +104 -0
  45. package/components/catalyst-ui/primitives/field.tsx +244 -0
  46. package/components/catalyst-ui/primitives/hover-card.tsx +124 -0
  47. package/components/catalyst-ui/primitives/input-otp.tsx +76 -0
  48. package/components/catalyst-ui/primitives/input.tsx +64 -0
  49. package/components/catalyst-ui/primitives/item.tsx +196 -0
  50. package/components/catalyst-ui/primitives/kbd.tsx +75 -0
  51. package/components/catalyst-ui/primitives/label.tsx +24 -0
  52. package/components/catalyst-ui/primitives/navigation-menu.tsx +150 -0
  53. package/components/catalyst-ui/primitives/pagination.tsx +198 -0
  54. package/components/catalyst-ui/primitives/popover.tsx +232 -0
  55. package/components/catalyst-ui/primitives/progress.tsx +34 -0
  56. package/components/catalyst-ui/primitives/radio-group.tsx +43 -0
  57. package/components/catalyst-ui/primitives/resizable.tsx +56 -0
  58. package/components/catalyst-ui/primitives/select.tsx +155 -0
  59. package/components/catalyst-ui/primitives/separator.tsx +74 -0
  60. package/components/catalyst-ui/primitives/sheet.tsx +126 -0
  61. package/components/catalyst-ui/primitives/skeleton.tsx +15 -0
  62. package/components/catalyst-ui/primitives/slider.tsx +27 -0
  63. package/components/catalyst-ui/primitives/switch.tsx +187 -0
  64. package/components/catalyst-ui/primitives/tabs.tsx +335 -0
  65. package/components/catalyst-ui/primitives/textarea.tsx +24 -0
  66. package/components/catalyst-ui/primitives/toggle-group.tsx +55 -0
  67. package/components/catalyst-ui/primitives/toggle.tsx +42 -0
  68. package/components/catalyst-ui/primitives/tooltip.tsx +116 -0
  69. package/components/catalyst-ui/utils/basic-auth.tsx +40 -0
  70. package/components/catalyst-ui/utils/context-storage.tsx +19 -0
  71. package/components/catalyst-ui/utils/cors-middleware.tsx +71 -0
  72. package/components/catalyst-ui/utils/deferred-content.tsx +595 -0
  73. package/components/catalyst-ui/utils/honeypot-middleware.tsx +38 -0
  74. package/components/catalyst-ui/utils/incId.tsx +75 -0
  75. package/components/catalyst-ui/utils/jwk-auth.tsx +36 -0
  76. package/components/catalyst-ui/utils/request-id.tsx +14 -0
  77. package/components/catalyst-ui/utils/secure-headers.tsx +37 -0
  78. package/components/catalyst-ui/utils/server-timing.tsx +23 -0
  79. package/components/catalyst-ui/utils/utils.ts +43 -0
  80. package/components/catalyst-ui/utils/with-cookie.tsx +43 -0
  81. package/components/catalyst-ui/x/accordian-x.tsx +428 -0
  82. package/components/catalyst-ui/x/alert-x.tsx +413 -0
  83. package/components/catalyst-ui/x/animated-text-x.tsx +2242 -0
  84. package/components/catalyst-ui/x/avatar-x.tsx +515 -0
  85. package/components/catalyst-ui/x/badge-x.tsx +670 -0
  86. package/components/catalyst-ui/x/button-X.tsx +2857 -0
  87. package/components/catalyst-ui/x/button-group-x.tsx +847 -0
  88. package/components/catalyst-ui/x/calendar-x.tsx +1910 -0
  89. package/components/catalyst-ui/x/card-x.tsx +2597 -0
  90. package/components/catalyst-ui/x/checkbox-x.tsx +656 -0
  91. package/components/catalyst-ui/x/collapsible-x.tsx +1360 -0
  92. package/components/catalyst-ui/x/combobox-x.tsx +911 -0
  93. package/components/catalyst-ui/x/data-table-x.tsx +1753 -0
  94. package/components/catalyst-ui/x/date-picker-x.tsx +648 -0
  95. package/components/catalyst-ui/x/dialog-x.tsx +659 -0
  96. package/components/catalyst-ui/x/dropdown-menu-x.tsx +612 -0
  97. package/components/catalyst-ui/x/hover-card-x.tsx +375 -0
  98. package/components/catalyst-ui/x/icon-x.tsx +840 -0
  99. package/components/catalyst-ui/x/input-mask-x.tsx +981 -0
  100. package/components/catalyst-ui/x/input-otp-x.tsx +659 -0
  101. package/components/catalyst-ui/x/loader-x.tsx +1757 -0
  102. package/components/catalyst-ui/x/pagination-x.tsx +622 -0
  103. package/components/catalyst-ui/x/popover-x.tsx +744 -0
  104. package/components/catalyst-ui/x/radio-group-x.tsx +499 -0
  105. package/components/catalyst-ui/x/select-x.tsx +1127 -0
  106. package/components/catalyst-ui/x/sheet-x.tsx +668 -0
  107. package/components/catalyst-ui/x/switch-x.tsx +681 -0
  108. package/components/catalyst-ui/x/table-x.tsx +574 -0
  109. package/components/catalyst-ui/x/tabs-x.tsx +839 -0
  110. package/components/catalyst-ui/x/textarea-x.tsx +1263 -0
  111. package/components/catalyst-ui/x/tooltip-x.tsx +396 -0
  112. package/components/catalyst-ui/x/tracker-x.tsx +560 -0
  113. package/data/bg-data.tsx +901 -0
  114. package/data/buttons-data.tsx +2327 -0
  115. package/data/charts-data.tsx +102 -0
  116. package/data/chat-data.tsx +83 -0
  117. package/data/code-data.tsx +1040 -0
  118. package/data/comboboxes-data.tsx +1843 -0
  119. package/data/command-data.tsx +1381 -0
  120. package/data/core-data.tsx +15953 -0
  121. package/data/crm-data.tsx +47 -0
  122. package/data/data.tsx +159 -0
  123. package/data/date-and-time-data.tsx +554 -0
  124. package/data/dependencies.tsx +7 -0
  125. package/data/ecommerce-data.tsx +1387 -0
  126. package/data/forms-data.tsx +7890 -0
  127. package/data/hooks-data.tsx +5487 -0
  128. package/data/index.ts +34 -0
  129. package/data/inputs-data.tsx +557 -0
  130. package/data/interactive-data.tsx +5394 -0
  131. package/data/lofi-data.tsx +18295 -0
  132. package/data/marketing-data.tsx +2546 -0
  133. package/data/media-data.tsx +1510 -0
  134. package/data/motion-data.tsx +5801 -0
  135. package/data/overlay-data.tsx +4136 -0
  136. package/data/pdf-data.tsx +124 -0
  137. package/data/pos-data.tsx +213 -0
  138. package/data/postcss.config.js +6 -0
  139. package/data/primitive-data.tsx +5170 -0
  140. package/data/prompt-data.tsx +1226 -0
  141. package/data/requiredLibs.ts +4 -0
  142. package/data/sandbox-data.tsx +1 -0
  143. package/data/sidebars-data.tsx +5421 -0
  144. package/data/stacks-data.tsx +32 -0
  145. package/data/table-data.tsx +706 -0
  146. package/data/tailwind.config.js +3830 -0
  147. package/data/tailwind.config.ngin.js +3830 -0
  148. package/data/tailwind.css +431 -0
  149. package/data/tools-data.tsx +6910 -0
  150. package/data/typography-data.tsx +2050 -0
  151. package/data/utils-data.tsx +6500 -0
  152. package/data/x-data.tsx +1171 -0
  153. package/dist/index.d.ts +3 -0
  154. package/dist/index.d.ts.map +1 -0
  155. package/dist/index.js +30245 -0
  156. package/dist/index.js.map +362 -0
  157. package/package.json +50 -0
@@ -0,0 +1,1360 @@
1
+ import React, { useId } from 'react'
2
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
3
+ import {
4
+ ChevronRightIcon,
5
+ ChevronsUpDownIcon,
6
+ ChevronDownIcon,
7
+ ChevronUpIcon,
8
+ FileIcon,
9
+ FolderIcon,
10
+ FolderOpenIcon,
11
+ UserIcon,
12
+ PanelsTopLeftIcon,
13
+ PlusIcon,
14
+ StarIcon,
15
+ CircleSmallIcon,
16
+ SettingsIcon,
17
+ UsersIcon,
18
+ LogOutIcon
19
+ } from "lucide-react"
20
+ import { cn } from "~/components/catalyst-ui"
21
+ import {
22
+ Collapsible,
23
+ CollapsibleTrigger,
24
+ CollapsibleContent,
25
+ collapsibleTriggerVariants,
26
+ collapsibleContentVariants,
27
+ collapsibleHeaderVariants
28
+ } from "~/components/catalyst-ui"
29
+ import { Button } from "~/components/catalyst-ui"
30
+ import { Avatar, AvatarImage, AvatarFallback } from "~/components/catalyst-ui"
31
+ import { Card, CardTitle, CardAction, CardContent } from "~/components/catalyst-ui"
32
+ import { Checkbox } from "~/components/catalyst-ui"
33
+ import { Label } from "~/components/catalyst-ui"
34
+ import { Input } from "~/components/catalyst-ui"
35
+ import { Separator } from "~/components/catalyst-ui"
36
+ import { Textarea } from "~/components/catalyst-ui"
37
+ import { RadioGroup, RadioGroupItem } from "~/components/catalyst-ui"
38
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuGroup } from "~/components/catalyst-ui"
39
+
40
+ /**
41
+ * ★ ━━━━ ☆ ━━━━ CollapsibleX ━━━━ ☆ ━━━━ ★
42
+ * type CollapsibleType =
43
+ * | 'default' | 'list' | 'profile' | 'filter' | 'faq' // Basic & Interactive
44
+ * | 'card' | 'dropdown' | 'form' | 'animated' // Specialized
45
+ *
46
+ * ★ ━━━━ ☆ ━━━━ USAGE ━━━━ ☆ ━━━━ ★
47
+ *
48
+ * ★ ━━━━━━━━━ Default Collapsible ━━━━━━━━━ ★
49
+ * ```jsx
50
+ * <CollapsibleX className='flex w-full max-w-[350px] flex-col gap-2'>
51
+ * <CollapsibleXHeader>@peduarte starred 3 repositories</CollapsibleXHeader>
52
+ * <div className='rounded-md border px-4 py-2 font-mono text-sm'>@radix-ui/primitives</div>
53
+ * <CollapsibleXContent>
54
+ * <div className='rounded-md border px-4 py-2 font-mono text-sm'>@radix-ui/colors</div>
55
+ * <div className='rounded-md border px-4 py-2 font-mono text-sm'>@stitches/react</div>
56
+ * </CollapsibleXContent>
57
+ * </CollapsibleX>
58
+ * ```
59
+ *
60
+ * ★ ━━━━━━━━━ List Collapsible ━━━━━━━━━ ★
61
+ * ```jsx
62
+ * <CollapsibleX collapsible='list' className='flex w-full max-w-[350px] flex-col items-start gap-4'>
63
+ * <div className='font-medium'>Today&apos;s task completion</div>
64
+ * <ul className='flex w-full flex-col gap-2'>
65
+ * {tasks.slice(0, 2).map(task => (
66
+ * <li key={task.name} className='flex items-start gap-4'>
67
+ * <Avatar>
68
+ * <AvatarImage src={task.image} alt={task.name} />
69
+ * <AvatarFallback>{task.fallback}</AvatarFallback>
70
+ * </Avatar>
71
+ * <div className='flex flex-1 flex-col'>
72
+ * <div className='text-sm font-medium'>{task.name}</div>
73
+ * <p className='text-muted-foreground text-xs'>{task.designation}</p>
74
+ * </div>
75
+ * <span className='text-muted-foreground text-sm'>{`${task.percentage}%`}</span>
76
+ * </li>
77
+ * ))}
78
+ * <CollapsibleXContent>
79
+ * {tasks.slice(2).map(task => (
80
+ * <li key={task.name} className='flex items-start gap-4'>
81
+ * <Avatar>
82
+ * <AvatarImage src={task.image} alt={task.name} />
83
+ * <AvatarFallback>{task.fallback}</AvatarFallback>
84
+ * </Avatar>
85
+ * <div className='flex flex-1 flex-col'>
86
+ * <div className='text-sm font-medium'>{task.name}</div>
87
+ * <p className='text-muted-foreground text-xs'>{task.designation}</p>
88
+ * </div>
89
+ * <span className='text-muted-foreground text-sm'>{`${task.percentage}%`}</span>
90
+ * </li>
91
+ * ))}
92
+ * </CollapsibleXContent>
93
+ * </ul>
94
+ * <CollapsibleXTrigger />
95
+ * </CollapsibleX>
96
+ * ```
97
+ *
98
+ * ★ ━━━━━━━━━ Profile Collapsible ━━━━━━━━━ ★
99
+ * ```jsx
100
+ * <ul className='flex w-full max-w-[350px] flex-col gap-4'>
101
+ * {users.map(user => (
102
+ * <CollapsibleX collapsible='profile' key={user.name} asChild>
103
+ * <li className='flex flex-col gap-2'>
104
+ * <CollapsibleXTrigger
105
+ * name={user.name}
106
+ * avatar={user.image}
107
+ * fallback={user.fallback}
108
+ * />
109
+ * <CollapsibleXContent
110
+ * bio={user.bio}
111
+ * followers={user.followers}
112
+ * projects={user.projects}
113
+ * followed={user.followed}
114
+ * />
115
+ * </li>
116
+ * </CollapsibleX>
117
+ * ))}
118
+ * </ul>
119
+ * ```
120
+ *
121
+ * ★ ━━━━━━━━━ Filter Collapsible ━━━━━━━━━ ★
122
+ * ```jsx
123
+ * <div className='w-full max-w-[350px] space-y-3'>
124
+ * <CollapsibleX collapsible='filter' className='flex flex-col gap-2'>
125
+ * <CollapsibleXHeader>Price Range</CollapsibleXHeader>
126
+ * <CollapsibleXContent>
127
+ * <div className='flex items-center justify-between gap-4 px-4'>
128
+ * <Label htmlFor='min-price' className='shrink-0 text-sm font-medium'>Min Price</Label>
129
+ * <Input id='min-price' type='number' placeholder='0' className='max-w-58' />
130
+ * </div>
131
+ * <div className='flex items-center justify-between gap-4 px-4'>
132
+ * <Label htmlFor='max-price' className='shrink-0 text-sm font-medium'>Max Price</Label>
133
+ * <Input id='max-price' type='number' placeholder='1000' className='max-w-58' />
134
+ * </div>
135
+ * </CollapsibleXContent>
136
+ * </CollapsibleX>
137
+ * </div>
138
+ * ```
139
+ *
140
+ * ★ ━━━━━━━━━ FAQ Collapsible ━━━━━━━━━ ★
141
+ * ```jsx
142
+ * <div className='space-y-2'>
143
+ * <p className='font-medium'>How can I track my order?</p>
144
+ * <CollapsibleX collapsible='faq' open={isOpen1} onOpenChange={setIsOpen1} className='space-y-2'>
145
+ * <CollapsibleXContent>
146
+ * To track your order, simply log in to your account and navigate to the order history section.
147
+ * </CollapsibleXContent>
148
+ * <CollapsibleXTrigger isOpen={isOpen1} />
149
+ * </CollapsibleX>
150
+ * </div>
151
+ * ```
152
+ *
153
+ * ★ ━━━━━━━━━ Card Collapsible ━━━━━━━━━ ★
154
+ * ```jsx
155
+ * <Card className='w-full max-w-md pb-0'>
156
+ * <CollapsibleX collapsible='card'>
157
+ * <div className='flex items-center justify-between px-6 pb-6'>
158
+ * <CardTitle>How do I track my order?</CardTitle>
159
+ * <CardAction>
160
+ * <CollapsibleXTrigger />
161
+ * </CardAction>
162
+ * </div>
163
+ * <CollapsibleXContent>
164
+ * <p className='px-6'>You&apos;ll receive tracking information via email once your order ships.</p>
165
+ * <img
166
+ * src='https://example.com/image.jpg'
167
+ * alt='Banner'
168
+ * className='aspect-video h-70 rounded-b-xl object-cover'
169
+ * />
170
+ * </CollapsibleXContent>
171
+ * </CollapsibleX>
172
+ * </Card>
173
+ * ```
174
+ *
175
+ * ★ ━━━━━━━━━ Animated Collapsible ━━━━━━━━━ ★
176
+ * ```jsx
177
+ * <CollapsibleX collapsible='animated' className='flex w-full max-w-[350px] flex-col gap-2'>
178
+ * <CollapsibleXHeader>@peduarte starred 3 repositories</CollapsibleXHeader>
179
+ * <div className='rounded-md border px-4 py-2 font-mono text-sm'>@radix-ui/primitives</div>
180
+ * <CollapsibleXContent>
181
+ * <div className='rounded-md border px-4 py-2 font-mono text-sm'>@radix-ui/colors</div>
182
+ * <div className='rounded-md border px-4 py-2 font-mono text-sm'>@stitches/react</div>
183
+ * </CollapsibleXContent>
184
+ * </CollapsibleX>
185
+ * ```
186
+ *
187
+ * ★ ━━━━━━━━━ Props ━━━━━━━━━ ★
188
+ *
189
+ * CollapsibleX Props:
190
+ * - `collapsible`: CollapsibleType - Specifies which variant to use
191
+ * - `variant`: string - Catalyst UI variant
192
+ * - `spacing`: string - Spacing between items
193
+ * - `layout`: string - Layout direction
194
+ * - `padding`: string - Padding size
195
+ * - `animated`: boolean - Enable animations
196
+ * - `size`: string - Component size
197
+ * - `align`: string - Content alignment
198
+ * - `indent`: string - Indentation level
199
+ * - `className`: string - Additional CSS classes
200
+ * - `open`: boolean - Controlled open state
201
+ * - `onOpenChange`: (open: boolean) => void - Open state change callback
202
+ *
203
+ * CollapsibleXTrigger Props:
204
+ * - `name`: string - Display name (for profile variant)
205
+ * - `avatar`: string - Avatar image URL (for profile variant)
206
+ * - `fallback`: string - Avatar fallback text (for profile variant)
207
+ * - `isOpen`: boolean - Current open state (for faq variant)
208
+ * - `icon`: IconComponent - Icon component (for dropdown variant)
209
+ * - `children`: ReactNode - Trigger content
210
+ * - `className`: string - Additional CSS classes
211
+ *
212
+ * CollapsibleXContent Props:
213
+ * - `bio`: string - Biography text (for profile variant)
214
+ * - `followers`: string - Number of followers (for profile variant)
215
+ * - `projects`: number - Number of projects (for profile variant)
216
+ * - `followed`: boolean - Follow status (for profile variant)
217
+ * - `children`: ReactNode - Content to show when expanded
218
+ * - `className`: string - Additional CSS classes
219
+ *
220
+ * CollapsibleXHeader Props:
221
+ * - `children`: ReactNode - Header content
222
+ * - `className`: string - Additional CSS classes
223
+ *
224
+ * ★ ━━━━━━━━━ Import ━━━━━━━━━ ★
225
+ * ```javascript
226
+ * import { CollapsibleX, CollapsibleXTrigger, CollapsibleXContent, CollapsibleXHeader } from "./CollapsibleX";
227
+ *
228
+ * // Required dependencies
229
+ * import { Avatar, AvatarImage, AvatarFallback } from "~/components/catalyst-ui";
230
+ * import { Button } from "~/components/catalyst-ui";
231
+ * import { Card, CardTitle, CardAction, CardContent } from "~/components/catalyst-ui";
232
+ * import { Input } from "~/components/catalyst-ui";
233
+ * import { Label } from "~/components/catalyst-ui";
234
+ * ```
235
+ *
236
+ * ★ ━━━━━━━━━ Data Structure Examples ━━━━━━━━━ ★
237
+ * ```typescript
238
+ * // List items structure
239
+ * const tasks = [
240
+ * {
241
+ * name: 'John Doe',
242
+ * designation: 'Designer',
243
+ * percentage: 85,
244
+ * image: 'https://example.com/avatar.jpg',
245
+ * fallback: 'JD'
246
+ * }
247
+ * ];
248
+ *
249
+ * // Profile items structure
250
+ * const users = [
251
+ * {
252
+ * name: 'Alice Johnson',
253
+ * bio: 'Frontend developer passionate about React',
254
+ * followers: '1.2k',
255
+ * projects: 24,
256
+ * image: 'https://example.com/avatar.jpg',
257
+ * fallback: 'AJ',
258
+ * followed: false
259
+ * }
260
+ * ];
261
+ *
262
+ * // State management
263
+ * const [isOpen, setIsOpen] = useState(false);
264
+ * ```
265
+ *
266
+ */
267
+
268
+ type CollapsibleType =
269
+ | 'default'
270
+ | 'fileTree'
271
+ | 'list'
272
+ | 'profile'
273
+ | 'filter'
274
+ | 'faq'
275
+ | 'card'
276
+ | 'dropdown'
277
+ | 'form'
278
+ | 'animated'
279
+
280
+ const CollapsibleXContext = React.createContext<CollapsibleType>('default')
281
+ const CollapsibleContext = React.createContext<CollapsibleContextValue>({});
282
+
283
+ export function CollapsibleX({ collapsible = 'default', variant, spacing, layout, padding, animated, size, align, indent, children, ...props }: any) {
284
+ const contextValue: CollapsibleContextValue = { variant, spacing, layout, padding, animated, size, align, indent, }
285
+ return (
286
+ <CollapsibleXContext.Provider value={collapsible}>
287
+ <CollapsibleContext.Provider value={contextValue}>
288
+ <Collapsible {...props} >
289
+ {children}
290
+ </Collapsible>
291
+ </CollapsibleContext.Provider>
292
+ </CollapsibleXContext.Provider>
293
+ )
294
+ }
295
+
296
+ export function CollapsibleXTrigger({ children, ...props }: any) {
297
+ const collapsibleType = React.useContext(CollapsibleXContext)
298
+ const {variant, size, align} = useCollapsibleContext()
299
+ switch (collapsibleType) {
300
+ case 'fileTree':
301
+ return (
302
+ <CollapsibleTrigger className={cn('focus-visible:ring-ring/50 flex items-center gap-2 rounded-md p-1 outline-none focus-visible:ring-[3px]', collapsibleTriggerVariants({ variant, size, align }), props.className)} {...props}>
303
+ <ChevronRightIcon className='size-4 shrink-0 transition-transform [[data-state="open"]>&]:rotate-90' />
304
+ <FolderIcon className='size-4 shrink-0 [[data-state=open]>&]:hidden' />
305
+ <FolderOpenIcon className='size-4 shrink-0 [[data-state=closed]>&]:hidden' />
306
+ <span className='text-sm'>{children}</span>
307
+ </CollapsibleTrigger>
308
+ )
309
+
310
+ case 'list':
311
+ return (
312
+ <CollapsibleTrigger className={cn(collapsibleTriggerVariants({ variant, size, align }), props.className)} asChild {...props}>
313
+ <Button variant='outline' size='sm'>
314
+ <span className='[[data-state=open]>&]:hidden'>Show more</span>
315
+ <span className='[[data-state=closed]>&]:hidden'>Show less</span>
316
+ <ChevronUpIcon className='[[data-state=closed]>&]:rotate-180' />
317
+ </Button>
318
+ </CollapsibleTrigger>
319
+ )
320
+
321
+ case 'profile':
322
+ return (
323
+ <CollapsibleTrigger className={cn(collapsibleTriggerVariants({ variant, size, align }), 'flex w-full items-center justify-between gap-4', props.className)} {...props}>
324
+ <div className='flex items-center gap-2'>
325
+ <Avatar>
326
+ <AvatarImage src={props.avatar} alt={props.name} />
327
+ <AvatarFallback>{props.fallback}</AvatarFallback>
328
+ </Avatar>
329
+ <span className='font-medium'>{props.name}</span>
330
+ </div>
331
+ <ChevronRightIcon className='size-4 transition-transform [[data-state=open]_&]:rotate-90' />
332
+ </CollapsibleTrigger>
333
+ )
334
+
335
+ case 'filter':
336
+ return (
337
+ <CollapsibleTrigger asChild className={cn(collapsibleTriggerVariants({ variant, size, align }), 'group', props.className)} {...props}>
338
+ <Button variant='ghost' size='icon-sm'>
339
+ <ChevronDownIcon className='text-muted-foreground transition-transform group-data-[state=open]:rotate-180' />
340
+ <span className='sr-only'>Toggle</span>
341
+ </Button>
342
+ </CollapsibleTrigger>
343
+ )
344
+
345
+ case 'faq':
346
+ return (
347
+ <CollapsibleTrigger className={cn(collapsibleTriggerVariants({ variant, size, align }), '', props.className)} {...props}>
348
+ <span className='text-muted-foreground text-sm underline'>
349
+ {props.isOpen ? 'Hide answer' : 'Show answer'}
350
+ </span>
351
+ </CollapsibleTrigger>
352
+ )
353
+
354
+ case 'card':
355
+ return (
356
+ <CollapsibleTrigger className={cn(collapsibleTriggerVariants({ variant, size, align }), '', props.className)} asChild {...props}>
357
+ <Button variant='outline' size='sm'>
358
+ <span className='[[data-state=open]>&]:hidden'>Show</span>
359
+ <span className='[[data-state=closed]>&]:hidden'>Hide</span>
360
+ <ChevronUpIcon className='[[data-state=closed]>&]:rotate-180' />
361
+ </Button>
362
+ </CollapsibleTrigger>
363
+ )
364
+
365
+ case 'dropdown':
366
+ return (
367
+ <CollapsibleTrigger className={cn(collapsibleTriggerVariants({ variant, size, align }), '', props.className)} asChild {...props}>
368
+ <DropdownMenuItem onSelect={event => event.preventDefault()} className='justify-between'>
369
+ <div className='flex items-center gap-2'>
370
+ {props.icon && <props.icon />}
371
+ <span>{props.children}</span>
372
+ </div>
373
+ <ChevronRightIcon className='shrink-0 transition-transform [[data-state="open"]>&]:rotate-90' />
374
+ </DropdownMenuItem>
375
+ </CollapsibleTrigger>
376
+ )
377
+
378
+ case 'form':
379
+ return (
380
+ <CollapsibleTrigger className={cn(collapsibleTriggerVariants({ variant, size, align }), 'group', props.className)} asChild {...props}>
381
+ <Button variant='ghost' size='icon-sm'>
382
+ <ChevronDownIcon className='text-muted-foreground transition-transform group-data-[state=open]:rotate-180' />
383
+ <span className='sr-only'>Toggle</span>
384
+ </Button>
385
+ </CollapsibleTrigger>
386
+ )
387
+
388
+ case 'animated':
389
+ return (
390
+ <CollapsibleTrigger className={cn(collapsibleTriggerVariants({ variant, size, align }), '', props.className)} asChild {...props}>
391
+ <Button variant='ghost' size='icon-sm'>
392
+ <ChevronsUpDownIcon />
393
+ <span className='sr-only'>Toggle</span>
394
+ </Button>
395
+ </CollapsibleTrigger>
396
+ )
397
+
398
+ default:
399
+ return (
400
+ <CollapsibleTrigger className={cn(collapsibleTriggerVariants({ variant, size, align }), 'flex items-center justify-between', props.className)} asChild {...props}>
401
+ <span>{children}</span>
402
+ <Button variant='ghost' size='icon-sm'>
403
+ <ChevronsUpDownIcon />
404
+ <span className='sr-only'>Toggle</span>
405
+ </Button>
406
+ </CollapsibleTrigger>
407
+ )
408
+ }
409
+ }
410
+
411
+ export function CollapsibleXContent({children, ...props }: any) {
412
+ const collapsibleType = React.useContext(CollapsibleXContext)
413
+ const {spacing, layout, padding, animated, indent} = React.useContext(CollapsibleContext);
414
+ switch (collapsibleType) {
415
+ case 'fileTree':
416
+ return (
417
+ <CollapsibleContent
418
+ className={cn( collapsibleContentVariants({ spacing, layout, padding, animated, indent }), 'flex flex-col gap-1.5',props.className )}
419
+ {...props} />
420
+ )
421
+
422
+ case 'list':
423
+ return (
424
+ <CollapsibleContent
425
+ className={cn( collapsibleContentVariants({ spacing, layout, padding, animated, indent }), 'flex flex-col gap-2',props.className )}
426
+ {...props} />
427
+ )
428
+
429
+ case 'profile':
430
+ return (
431
+ <CollapsibleContent
432
+ className={cn( collapsibleContentVariants({ spacing, layout, padding, animated, indent }), '',props.className )}
433
+ {...props}>
434
+ <div className='flex flex-col gap-2'>
435
+ <p className='text-muted-foreground text-sm'>{props.bio}</p>
436
+ <div className='flex items-center justify-between gap-2'>
437
+ <div className='flex items-center gap-4'>
438
+ <span className='flex items-center gap-2'>
439
+ <UserIcon className='size-4' />
440
+ <span className='text-sm'>{props.followers}</span>
441
+ </span>
442
+ <span className='flex items-center gap-2'>
443
+ <PanelsTopLeftIcon className='size-4' />
444
+ <span className='text-sm'>{props.projects}</span>
445
+ </span>
446
+ </div>
447
+ {props.followed ? (
448
+ <Button variant='outline' className='h-7 rounded-full px-3 py-1 text-xs'>
449
+ Following
450
+ </Button>
451
+ ) : (
452
+ <Button className='h-7 rounded-full px-3 py-1 text-xs'>
453
+ Follow
454
+ <PlusIcon />
455
+ </Button>
456
+ )}
457
+ </div>
458
+ {children && children}
459
+ </div>
460
+ </CollapsibleContent>
461
+ )
462
+
463
+ case 'filter':
464
+ return (
465
+ <CollapsibleContent
466
+ className={cn( collapsibleContentVariants({ spacing, layout, padding, animated, indent }), 'flex flex-col gap-2',props.className )}
467
+ {...props} />
468
+ )
469
+
470
+ case 'faq':
471
+ return (
472
+ <CollapsibleContent className={cn( collapsibleContentVariants({ spacing, layout, padding, animated, indent }), '',props.className )} {...props}>
473
+ <p className='text-sm'>{children}</p>
474
+ </CollapsibleContent>
475
+ )
476
+
477
+ case 'card':
478
+ return (
479
+ <CollapsibleContent className={cn( collapsibleContentVariants({ spacing, layout, padding, animated, indent }), '',props.className )} {...props}>
480
+ <CardContent className='space-y-2 px-0'>
481
+ {children}
482
+ </CardContent>
483
+ </CollapsibleContent>
484
+ )
485
+
486
+ case 'dropdown':
487
+ return (
488
+ <CollapsibleContent
489
+ className={cn( collapsibleContentVariants({ spacing, layout, padding, animated, indent }), 'pl-4',props.className )}
490
+ {...props} />
491
+ )
492
+
493
+ case 'form':
494
+ return (
495
+ <CollapsibleContent
496
+ className={cn( collapsibleContentVariants({ spacing, layout, padding, animated, indent }), 'flex flex-col gap-3 px-4 pt-3',props.className )}
497
+ {...props} />
498
+ )
499
+
500
+ case 'animated':
501
+ return (
502
+ <CollapsibleContent
503
+ className={cn( collapsibleContentVariants({ spacing, layout, padding, animated, indent }), 'data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down flex flex-col gap-2 overflow-hidden transition-all duration-300',props.className )}
504
+ {...props} />
505
+ )
506
+
507
+ default:
508
+ return (
509
+ <CollapsibleContent
510
+ className={cn( collapsibleContentVariants({ spacing, layout, padding, animated, indent }), 'flex flex-col gap-2',props.className )}
511
+ {...props} />
512
+ )
513
+ }
514
+ }
515
+
516
+ export function CollapsibleXHeader({ children,...props }: any) {
517
+ const collapsibleType = React.useContext(CollapsibleXContext)
518
+ const {spacing, align, padding,} = React.useContext(CollapsibleContext);
519
+ switch (collapsibleType) {
520
+ case 'filter':
521
+ return (
522
+ <div
523
+ className={cn(collapsibleHeaderVariants({ spacing, padding, align, }), 'flex items-center justify-between gap-4 px-4', className)} {...props}>
524
+ <div className='text-sm font-semibold'>{children}</div>
525
+ <CollapsibleXTrigger />
526
+ </div>
527
+ )
528
+
529
+ case 'form':
530
+ return (
531
+ <div className={cn(collapsibleHeaderVariants({ spacing, padding, align, }),'flex items-center justify-between gap-4 px-4', className)} {...props}>
532
+ <div className='text-sm font-semibold'>{children}</div>
533
+ <CollapsibleXTrigger />
534
+ </div>
535
+ )
536
+
537
+ default:
538
+ return (
539
+ <div className={cn(collapsibleHeaderVariants({ spacing, padding, align, }),'flex items-center justify-between gap-4 px-4', className)} {...props}>
540
+ <div className='text-sm font-semibold'>{children}</div>
541
+ <CollapsibleXTrigger />
542
+ </div>
543
+ )
544
+ }
545
+ }
546
+
547
+ export function CollapsibleXDemo() {
548
+ const [isOpen1, setIsOpen1] = React.useState(true)
549
+ const [isOpen2, setIsOpen2] = React.useState(false)
550
+
551
+ const fileTree = [
552
+ {
553
+ name: 'src', type: 'folder', children: [
554
+ {
555
+ name: 'components', type: 'folder', children: [
556
+ { name: 'Button.tsx', type: 'file' },
557
+ { name: 'Card.tsx', type: 'file' }
558
+ ]
559
+ },
560
+ {
561
+ name: 'utils', type: 'folder', children: [
562
+ { name: 'helpers.ts', type: 'file' }
563
+ ]
564
+ }
565
+ ]
566
+ },
567
+ { name: 'package.json', type: 'file' }
568
+ ]
569
+
570
+ const tasks = [
571
+ { name: 'John Doe', designation: 'Designer', percentage: 85, image: 'https://api.dicebear.com/7.x/avataaars/svg?seed=John', fallback: 'JD' },
572
+ { name: 'Jane Smith', designation: 'Developer', percentage: 92, image: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Jane', fallback: 'JS' },
573
+ { name: 'Bob Wilson', designation: 'Manager', percentage: 78, image: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Bob', fallback: 'BW' }
574
+ ]
575
+
576
+ const users = [
577
+ { name: 'Alice Johnson', bio: 'Frontend developer passionate about React', followers: '1.2k', projects: 24, image: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Alice', fallback: 'AJ', followed: false },
578
+ { name: 'Charlie Brown', bio: 'Full-stack engineer and open source contributor', followers: '856', projects: 18, image: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Charlie', fallback: 'CB', followed: true }
579
+ ]
580
+
581
+ return (
582
+ <div className="space-y-12 p-6 max-w-3xl mx-auto">
583
+ <div>
584
+ <h3 className="text-lg font-semibold mb-4">Default</h3>
585
+ <CollapsibleX className='flex w-full max-w-[350px] flex-col gap-2'>
586
+ <CollapsibleXHeader>@peduarte starred 3 repositories</CollapsibleXHeader>
587
+ <div className='rounded-md border px-4 py-2 font-mono text-sm'>@radix-ui/primitives</div>
588
+ <CollapsibleXContent>
589
+ <div className='rounded-md border px-4 py-2 font-mono text-sm'>@radix-ui/colors</div>
590
+ <div className='rounded-md border px-4 py-2 font-mono text-sm'>@stitches/react</div>
591
+ </CollapsibleXContent>
592
+ </CollapsibleX>
593
+ </div>
594
+
595
+ <div>
596
+ <h3 className="text-lg font-semibold mb-4">List</h3>
597
+ <CollapsibleX collapsible='list' className='flex w-full max-w-[350px] flex-col items-start gap-4'>
598
+ <div className='font-medium'>Today&apos;s task completion</div>
599
+ <ul className='flex w-full flex-col gap-2'>
600
+ {tasks.slice(0, 2).map(task => (
601
+ <li key={task.name} className='flex items-start gap-4'>
602
+ <Avatar>
603
+ <AvatarImage src={task.image} alt={task.name} />
604
+ <AvatarFallback>{task.fallback}</AvatarFallback>
605
+ </Avatar>
606
+ <div className='flex flex-1 flex-col'>
607
+ <div className='text-sm font-medium'>{task.name}</div>
608
+ <p className='text-muted-foreground text-xs'>{task.designation}</p>
609
+ </div>
610
+ <span className='text-muted-foreground text-sm'>{`${task.percentage}%`}</span>
611
+ </li>
612
+ ))}
613
+ <CollapsibleXContent>
614
+ {tasks.slice(2).map(task => (
615
+ <li key={task.name} className='flex items-start gap-4'>
616
+ <Avatar>
617
+ <AvatarImage src={task.image} alt={task.name} />
618
+ <AvatarFallback>{task.fallback}</AvatarFallback>
619
+ </Avatar>
620
+ <div className='flex flex-1 flex-col'>
621
+ <div className='text-sm font-medium'>{task.name}</div>
622
+ <p className='text-muted-foreground text-xs'>{task.designation}</p>
623
+ </div>
624
+ <span className='text-muted-foreground text-sm'>{`${task.percentage}%`}</span>
625
+ </li>
626
+ ))}
627
+ </CollapsibleXContent>
628
+ </ul>
629
+ <CollapsibleXTrigger />
630
+ </CollapsibleX>
631
+ </div>
632
+
633
+ <div>
634
+ <h3 className="text-lg font-semibold mb-4">Profile</h3>
635
+ <ul className='flex w-full max-w-[350px] flex-col gap-4'>
636
+ {users.map(user => (
637
+ <CollapsibleX collapsible='profile' key={user.name} asChild>
638
+ <li className='flex flex-col gap-2'>
639
+ <CollapsibleXTrigger name={user.name} avatar={user.image} fallback={user.fallback} />
640
+ <CollapsibleXContent bio={user.bio} followers={user.followers} projects={user.projects} followed={user.followed} />
641
+ </li>
642
+ </CollapsibleX>
643
+ ))}
644
+ </ul>
645
+ </div>
646
+
647
+ <div>
648
+ <h3 className="text-lg font-semibold mb-4">Filter</h3>
649
+ <div className='w-full max-w-[350px] space-y-3'>
650
+ <CollapsibleX collapsible='filter' className='flex flex-col gap-2'>
651
+ <CollapsibleXHeader>Price Range</CollapsibleXHeader>
652
+ <CollapsibleXContent>
653
+ <div className='flex items-center justify-between gap-4 px-4'>
654
+ <Label htmlFor='min-price' className='shrink-0 text-sm font-medium'>Min Price</Label>
655
+ <Input id='min-price' type='number' placeholder='0' className='max-w-58' />
656
+ </div>
657
+ <div className='flex items-center justify-between gap-4 px-4'>
658
+ <Label htmlFor='max-price' className='shrink-0 text-sm font-medium'>Max Price</Label>
659
+ <Input id='max-price' type='number' placeholder='1000' className='max-w-58' />
660
+ </div>
661
+ </CollapsibleXContent>
662
+ </CollapsibleX>
663
+ </div>
664
+ </div>
665
+
666
+ <div>
667
+ <h3 className="text-lg font-semibold mb-4">FAQ</h3>
668
+ <div className='w-full space-y-4'>
669
+ <div className='space-y-2'>
670
+ <p className='font-medium'>How can I track my order?</p>
671
+ <CollapsibleX collapsible='faq' open={isOpen1} onOpenChange={setIsOpen1} className='space-y-2'>
672
+ <CollapsibleXContent>
673
+ To track your order, simply log in to your account and navigate to the order history section.
674
+ </CollapsibleXContent>
675
+ <CollapsibleXTrigger isOpen={isOpen1} />
676
+ </CollapsibleX>
677
+ </div>
678
+ </div>
679
+ </div>
680
+
681
+ <div>
682
+ <h3 className="text-lg font-semibold mb-4">Card</h3>
683
+ <Card className='w-full max-w-md pb-0'>
684
+ <CollapsibleX collapsible='card'>
685
+ <div className='flex items-center justify-between px-6 pb-6'>
686
+ <CardTitle>How do I track my order?</CardTitle>
687
+ <CardAction>
688
+ <CollapsibleXTrigger />
689
+ </CardAction>
690
+ </div>
691
+ <CollapsibleXContent>
692
+ <p className='px-6'>You&apos;ll receive tracking information via email once your order ships.</p>
693
+ <img
694
+ src='https://cdn.shadcnstudio.com/ss-assets/components/accordion/image-1.jpg?width=446&format=auto'
695
+ alt='Banner'
696
+ className='aspect-video h-70 rounded-b-xl object-cover'
697
+ />
698
+ </CollapsibleXContent>
699
+ </CollapsibleX>
700
+ </Card>
701
+ </div>
702
+
703
+ <div>
704
+ <h3 className="text-lg font-semibold mb-4">Animated</h3>
705
+ <CollapsibleX collapsible='animated' className='flex w-full max-w-[350px] flex-col gap-2'>
706
+ <CollapsibleXHeader>@peduarte starred 3 repositories</CollapsibleXHeader>
707
+ <div className='rounded-md border px-4 py-2 font-mono text-sm'>@radix-ui/primitives</div>
708
+ <CollapsibleXContent>
709
+ <div className='rounded-md border px-4 py-2 font-mono text-sm'>@radix-ui/colors</div>
710
+ <div className='rounded-md border px-4 py-2 font-mono text-sm'>@stitches/react</div>
711
+ </CollapsibleXContent>
712
+ </CollapsibleX>
713
+ </div>
714
+ </div>
715
+ )
716
+ }
717
+
718
+ const FileTree = ({ item, level }: { level: number; item: FileTreeItem }) => {
719
+ if (item.type === 'file') {
720
+ return (
721
+ <div
722
+ className='focus-visible:ring-ring/50 flex items-center gap-2 rounded-md p-1 outline-none focus-visible:ring-[3px]'
723
+ style={{ paddingLeft: `${level === 0 ? 1.75 : 3.25}rem` }}
724
+ >
725
+ <FileIcon className='size-4 shrink-0' />
726
+ <span className='text-muted-foreground text-sm'>{item.name}</span>
727
+ </div>
728
+ )
729
+ }
730
+
731
+ return (
732
+ <Collapsible className='flex flex-col gap-1.5' style={{ paddingLeft: `${level === 0 ? 0 : 1.5}rem` }}>
733
+ <CollapsibleTrigger className='focus-visible:ring-ring/50 flex items-center gap-2 rounded-md p-1 outline-none focus-visible:ring-[3px]'>
734
+ <ChevronRightIcon className='size-4 shrink-0 transition-transform [[data-state="open"]>&]:rotate-90' />
735
+ <FolderIcon className='size-4 shrink-0 [[data-state=open]>&]:hidden' />
736
+ <FolderOpenIcon className='size-4 shrink-0 [[data-state=closed]>&]:hidden' />
737
+ <span className='text-sm'>{item.name}</span>
738
+ </CollapsibleTrigger>
739
+ <CollapsibleContent className='flex flex-col gap-1.5'>
740
+ {item.children.map(item => (
741
+ <FileTree key={item.name} item={item} level={level + 1} />
742
+ ))}
743
+ </CollapsibleContent>
744
+ </Collapsible>
745
+ )
746
+ }
747
+
748
+ const CollapsibleTreeDemo = () => {
749
+ return (
750
+ <div className='flex w-full max-w-48 flex-col gap-2'>
751
+ {fileTree.map(item => (
752
+ <FileTree key={item.name} item={item} level={0} />
753
+ ))}
754
+ </div>
755
+ )
756
+ }
757
+
758
+ const CollapsibleListDemo = () => {
759
+ return (
760
+ <Collapsible className='flex w-full max-w-[350px] flex-col items-start gap-4'>
761
+ <div className='font-medium'>Today&apos;s task completion</div>
762
+ <ul className='flex w-full flex-col gap-2'>
763
+ {tasks.slice(0, 2).map(task => (
764
+ <li key={task.name} className='flex items-start gap-4'>
765
+ <Avatar>
766
+ <AvatarImage src={task.image} alt={task.name} />
767
+ <AvatarFallback>{task.fallback}</AvatarFallback>
768
+ </Avatar>
769
+ <div className='flex flex-1 flex-col'>
770
+ <div className='text-sm font-medium'>{task.name}</div>
771
+ <p className='text-muted-foreground text-xs'>{task.designation}</p>
772
+ </div>
773
+ <span className='text-muted-foreground text-sm'>{`${task.percentage}%`}</span>
774
+ </li>
775
+ ))}
776
+ <CollapsibleContent className='flex flex-col gap-2'>
777
+ {tasks.slice(2).map(task => (
778
+ <li key={task.name} className='flex items-start gap-4'>
779
+ <Avatar>
780
+ <AvatarImage src={task.image} alt={task.name} />
781
+ <AvatarFallback>{task.fallback}</AvatarFallback>
782
+ </Avatar>
783
+ <div className='flex flex-1 flex-col'>
784
+ <div className='text-sm font-medium'>{task.name}</div>
785
+ <p className='text-muted-foreground text-xs'>{task.designation}</p>
786
+ </div>
787
+ <span className='text-muted-foreground text-sm'>{`${task.percentage}%`}</span>
788
+ </li>
789
+ ))}
790
+ </CollapsibleContent>
791
+ </ul>
792
+ <CollapsibleTrigger asChild>
793
+ <Button variant='outline' size='sm'>
794
+ <span className='[[data-state=open]>&]:hidden'>Show more</span>
795
+ <span className='[[data-state=closed]>&]:hidden'>Show less</span>
796
+ <ChevronUpIcon className='[[data-state=closed]>&]:rotate-180' />
797
+ </Button>
798
+ </CollapsibleTrigger>
799
+ </Collapsible>
800
+ )
801
+ }
802
+
803
+ const CollapsibleProfileDemo = () => {
804
+ return (
805
+ <ul className='flex w-full max-w-[350px] flex-col gap-4'>
806
+ {users.map(user => (
807
+ <Collapsible key={user.name} asChild>
808
+ <li className='flex flex-col gap-2'>
809
+ <CollapsibleTrigger className='flex w-full items-center justify-between gap-4'>
810
+ <div className='flex items-center gap-2'>
811
+ <Avatar>
812
+ <AvatarImage src={user.image} alt={user.fallback} />
813
+ <AvatarFallback>{user.fallback}</AvatarFallback>
814
+ </Avatar>
815
+ <span className='font-medium'>{user.name}</span>
816
+ </div>
817
+ <ChevronRightIcon className='size-4 transition-transform [[data-state=open]_&]:rotate-90' />
818
+ </CollapsibleTrigger>
819
+ <CollapsibleContent>
820
+ <div className='flex flex-col gap-2'>
821
+ <p className='text-muted-foreground text-sm'>{user.bio}</p>
822
+ <div className='flex items-center justify-between gap-2'>
823
+ <div className='flex items-center gap-4'>
824
+ <span className='flex items-center gap-2'>
825
+ <UserIcon className='size-4' />
826
+ <span className='text-sm'>{user.followers}</span>
827
+ </span>
828
+ <span className='flex items-center gap-2'>
829
+ <PanelsTopLeftIcon className='size-4' />
830
+ <span className='text-sm'>{user.projects}</span>
831
+ </span>
832
+ </div>
833
+ {user.followed ? (
834
+ <Button variant='outline' className='h-7 rounded-full px-3 py-1 text-xs'>
835
+ Following
836
+ </Button>
837
+ ) : (
838
+ <Button className='h-7 rounded-full px-3 py-1 text-xs'>
839
+ Follow
840
+ <PlusIcon />
841
+ </Button>
842
+ )}
843
+ </div>
844
+ </div>
845
+ </CollapsibleContent>
846
+ </li>
847
+ </Collapsible>
848
+ ))}
849
+ </ul>
850
+ )
851
+ }
852
+
853
+ const CollapsibleFilterDemo = () => {
854
+ return (
855
+ <div className='w-full max-w-[350px] space-y-3'>
856
+ <Collapsible className='flex flex-col gap-2'>
857
+ <div className='flex items-center justify-between gap-4 px-4'>
858
+ <div className='text-sm font-semibold'>Price Range</div>
859
+ <CollapsibleTrigger asChild className='group'>
860
+ <Button variant='ghost' size='icon-sm'>
861
+ <ChevronDownIcon className='text-muted-foreground transition-transform group-data-[state=open]:rotate-180' />
862
+ <span className='sr-only'>Toggle</span>
863
+ </Button>
864
+ </CollapsibleTrigger>
865
+ </div>
866
+ <CollapsibleContent className='flex flex-col gap-2'>
867
+ <div className='flex items-center justify-between gap-4 px-4'>
868
+ <Label htmlFor='min-price' className='shrink-0 text-sm font-medium'>
869
+ Min Price
870
+ </Label>
871
+ <Input id='min-price' type='number' placeholder='0' className='max-w-58' />
872
+ </div>
873
+ <div className='flex items-center justify-between gap-4 px-4'>
874
+ <Label htmlFor='max-price' className='shrink-0 text-sm font-medium'>
875
+ Max Price
876
+ </Label>
877
+ <Input id='max-price' type='number' placeholder='1000' className='max-w-58' />
878
+ </div>
879
+ </CollapsibleContent>
880
+ </Collapsible>
881
+ <Separator />
882
+ <Collapsible className='flex w-full max-w-[350px] flex-col gap-2'>
883
+ <div className='flex items-center justify-between gap-4 px-4'>
884
+ <div className='text-sm font-semibold'>Customer Ratings</div>
885
+ <CollapsibleTrigger asChild className='group'>
886
+ <Button variant='ghost' size='icon-sm'>
887
+ <ChevronDownIcon className='text-muted-foreground transition-transform group-data-[state=open]:rotate-180' />
888
+ <span className='sr-only'>Toggle</span>
889
+ </Button>
890
+ </CollapsibleTrigger>
891
+ </div>
892
+ <CollapsibleContent className='flex flex-col gap-2'>
893
+ <div className='flex items-center gap-2 px-4'>
894
+ <Checkbox id='rating-4' />
895
+ <Label htmlFor='rating-4' className='flex shrink-0 items-center gap-1 text-sm font-medium'>
896
+ <span className='flex items-center gap-1'>
897
+ 4
898
+ <StarIcon className='size-4 fill-amber-500 stroke-amber-500 dark:fill-amber-400 dark:stroke-amber-400' />
899
+ </span>
900
+ & Up
901
+ </Label>
902
+ </div>
903
+ <div className='flex items-center gap-2 px-4'>
904
+ <Checkbox id='rating-3' />
905
+ <Label htmlFor='rating-3' className='flex shrink-0 items-center gap-1 text-sm font-medium'>
906
+ <span className='flex items-center gap-1'>
907
+ 3
908
+ <StarIcon className='size-4 fill-amber-500 stroke-amber-500 dark:fill-amber-400 dark:stroke-amber-400' />
909
+ </span>
910
+ & Up
911
+ </Label>
912
+ </div>
913
+ <div className='flex items-center gap-2 px-4'>
914
+ <Checkbox id='rating-2' />
915
+ <Label htmlFor='rating-2' className='flex shrink-0 items-center gap-1 text-sm font-medium'>
916
+ <span className='flex items-center gap-1'>
917
+ 2
918
+ <StarIcon className='size-4 fill-amber-500 stroke-amber-500 dark:fill-amber-400 dark:stroke-amber-400' />
919
+ </span>
920
+ & Up
921
+ </Label>
922
+ </div>
923
+ </CollapsibleContent>
924
+ </Collapsible>
925
+ <Separator />
926
+ <Collapsible className='flex w-full max-w-[350px] flex-col gap-2'>
927
+ <div className='flex items-center justify-between gap-4 px-4'>
928
+ <div className='text-sm font-semibold'>Brand</div>
929
+ <CollapsibleTrigger asChild className='group'>
930
+ <Button variant='ghost' size='icon-sm'>
931
+ <ChevronDownIcon className='text-muted-foreground transition-transform group-data-[state=open]:rotate-180' />
932
+ <span className='sr-only'>Toggle</span>
933
+ </Button>
934
+ </CollapsibleTrigger>
935
+ </div>
936
+ <CollapsibleContent className='flex flex-col gap-2'>
937
+ <div className='flex items-center gap-2 px-4'>
938
+ <Checkbox id='brand-apple' />
939
+ <Label htmlFor='brand-apple' className='shrink-0 text-sm font-medium'>
940
+ Apple
941
+ </Label>
942
+ </div>
943
+ <div className='flex items-center gap-2 px-4'>
944
+ <Checkbox id='brand-samsung' />
945
+ <Label htmlFor='brand-samsung' className='shrink-0 text-sm font-medium'>
946
+ Samsung
947
+ </Label>
948
+ </div>
949
+ <div className='flex items-center gap-2 px-4'>
950
+ <Checkbox id='brand-google' />
951
+ <Label htmlFor='brand-google' className='shrink-0 text-sm font-medium'>
952
+ Google
953
+ </Label>
954
+ </div>
955
+ <div className='flex items-center gap-2 px-4'>
956
+ <Checkbox id='brand-oneplus' />
957
+ <Label htmlFor='brand-oneplus' className='shrink-0 text-sm font-medium'>
958
+ OnePlus
959
+ </Label>
960
+ </div>
961
+ <div className='flex items-center gap-2 px-4'>
962
+ <Checkbox id='brand-xiaomi' />
963
+ <Label htmlFor='brand-xiaomi' className='shrink-0 text-sm font-medium'>
964
+ Xiaomi
965
+ </Label>
966
+ </div>
967
+ </CollapsibleContent>
968
+ </Collapsible>
969
+ <Separator />
970
+ <Collapsible className='flex w-full max-w-[350px] flex-col gap-2'>
971
+ <div className='flex items-center justify-between gap-4 px-4'>
972
+ <div className='text-sm font-semibold'>Battery</div>
973
+ <CollapsibleTrigger asChild className='group'>
974
+ <Button variant='ghost' size='icon-sm'>
975
+ <ChevronDownIcon className='text-muted-foreground transition-transform group-data-[state=open]:rotate-180' />
976
+ <span className='sr-only'>Toggle</span>
977
+ </Button>
978
+ </CollapsibleTrigger>
979
+ </div>
980
+ <CollapsibleContent className='flex flex-col gap-2'>
981
+ <div className='flex items-center gap-2 px-4'>
982
+ <Checkbox id='battery-3500' />
983
+ <Label htmlFor='battery-3500' className='shrink-0 text-sm font-medium'>
984
+ 3500mAh
985
+ </Label>
986
+ </div>
987
+ <div className='flex items-center gap-2 px-4'>
988
+ <Checkbox id='battery-4000' />
989
+ <Label htmlFor='battery-4000' className='shrink-0 text-sm font-medium'>
990
+ 4000mAh
991
+ </Label>
992
+ </div>
993
+ <div className='flex items-center gap-2 px-4'>
994
+ <Checkbox id='battery-5000' />
995
+ <Label htmlFor='battery-5000' className='shrink-0 text-sm font-medium'>
996
+ 5000mAh
997
+ </Label>
998
+ </div>
999
+ <div className='flex items-center gap-2 px-4'>
1000
+ <Checkbox id='battery-6000' />
1001
+ <Label htmlFor='battery-6000' className='shrink-0 text-sm font-medium'>
1002
+ 6000mAh
1003
+ </Label>
1004
+ </div>
1005
+ </CollapsibleContent>
1006
+ </Collapsible>
1007
+ </div>
1008
+ )
1009
+ }
1010
+
1011
+ const CollapsibleShowMoreDemo = () => {
1012
+ const [isTrackOrderOpen, setIsTrackOrderOpen] = useState(true)
1013
+ const [isCancelOrderOpen, setIsCancelOrderOpen] = useState(false)
1014
+
1015
+ return (
1016
+ <div className='w-full space-y-4'>
1017
+ <div className='space-y-2'>
1018
+ <p className='font-medium'>How can I track my order?</p>
1019
+ <Collapsible open={isTrackOrderOpen} onOpenChange={setIsTrackOrderOpen} className='space-y-2'>
1020
+ <CollapsibleContent>
1021
+ <p className='text-sm'>
1022
+ To track your order, simply log in to your account and navigate to the order history section. You&apos;ll
1023
+ find detailed information about your order status and tracking number there.
1024
+ </p>
1025
+ </CollapsibleContent>
1026
+ <CollapsibleTrigger>
1027
+ <span className='text-muted-foreground text-sm underline'>
1028
+ {isTrackOrderOpen ? 'Hide answer' : 'Show answer'}
1029
+ </span>
1030
+ </CollapsibleTrigger>
1031
+ </Collapsible>
1032
+ </div>
1033
+ <div className='space-y-2'>
1034
+ <p className='font-medium'>Can I cancel my order?</p>
1035
+ <Collapsible open={isCancelOrderOpen} onOpenChange={setIsCancelOrderOpen} className='space-y-2'>
1036
+ <CollapsibleContent>
1037
+ <p className='text-sm'>
1038
+ Scheduled delivery orders can be cancelled 72 hours prior to your selected delivery date for full refund.
1039
+ </p>
1040
+ </CollapsibleContent>
1041
+ <CollapsibleTrigger>
1042
+ <span className='text-muted-foreground text-sm underline'>
1043
+ {isCancelOrderOpen ? 'Hide answer' : 'Show answer'}
1044
+ </span>
1045
+ </CollapsibleTrigger>
1046
+ </Collapsible>
1047
+ </div>
1048
+ </div>
1049
+ )
1050
+ }
1051
+
1052
+ const CollapsibleCardDemo = () => {
1053
+ return (
1054
+ <Card className='w-full max-w-md pb-0'>
1055
+ <Collapsible>
1056
+ <div className='flex items-center justify-between px-6 pb-6'>
1057
+ <CardTitle>How do I track my order?</CardTitle>
1058
+ <CardAction>
1059
+ <CollapsibleTrigger asChild>
1060
+ <Button variant='outline' size='sm'>
1061
+ <span className='[[data-state=open]>&]:hidden'>Show</span>
1062
+ <span className='[[data-state=closed]>&]:hidden'>Hide</span>
1063
+ <ChevronUpIcon className='[[data-state=closed]>&]:rotate-180' />
1064
+ </Button>
1065
+ </CollapsibleTrigger>
1066
+ </CardAction>
1067
+ </div>
1068
+ <CollapsibleContent>
1069
+ <CardContent className='space-y-2 px-0'>
1070
+ <p className='px-6'>You&apos;ll receive tracking information via email once your order ships.</p>
1071
+ <img
1072
+ src='https://cdn.shadcnstudio.com/ss-assets/components/accordion/image-1.jpg?width=446&format=auto'
1073
+ alt='Banner'
1074
+ className='aspect-video h-70 rounded-b-xl object-cover'
1075
+ />
1076
+ </CardContent>
1077
+ </CollapsibleContent>
1078
+ </Collapsible>
1079
+ </Card>
1080
+ )
1081
+ }
1082
+
1083
+ const CollapsibleDropdownMenuDemo = () => {
1084
+ return (
1085
+ <DropdownMenu>
1086
+ <DropdownMenuTrigger asChild>
1087
+ <Button variant='outline'>Dropdown with collapsible</Button>
1088
+ </DropdownMenuTrigger>
1089
+ <DropdownMenuContent className='w-56'>
1090
+ <DropdownMenuItem>
1091
+ <UserIcon />
1092
+ <span>Profile</span>
1093
+ </DropdownMenuItem>
1094
+ <Collapsible asChild>
1095
+ <DropdownMenuGroup>
1096
+ <CollapsibleTrigger asChild>
1097
+ <DropdownMenuItem onSelect={event => event.preventDefault()} className='justify-between'>
1098
+ <div className='flex items-center gap-2'>
1099
+ <SettingsIcon />
1100
+ <span>Settings</span>
1101
+ </div>
1102
+ <ChevronRightIcon className='shrink-0 transition-transform [[data-state="open"]>&]:rotate-90' />
1103
+ </DropdownMenuItem>
1104
+ </CollapsibleTrigger>
1105
+ <CollapsibleContent className='pl-4'>
1106
+ <DropdownMenuItem>
1107
+ <CircleSmallIcon />
1108
+ <span>Account</span>
1109
+ </DropdownMenuItem>
1110
+ <DropdownMenuItem>
1111
+ <CircleSmallIcon />
1112
+ <span>Security</span>
1113
+ </DropdownMenuItem>
1114
+ <DropdownMenuItem>
1115
+ <CircleSmallIcon />
1116
+ <span>Billing & plans</span>
1117
+ </DropdownMenuItem>
1118
+ </CollapsibleContent>
1119
+ </DropdownMenuGroup>
1120
+ </Collapsible>
1121
+ <Collapsible asChild>
1122
+ <DropdownMenuGroup>
1123
+ <CollapsibleTrigger asChild>
1124
+ <DropdownMenuItem onSelect={event => event.preventDefault()} className='justify-between'>
1125
+ <div className='flex items-center gap-2'>
1126
+ <UsersIcon />
1127
+ <span>Users</span>
1128
+ </div>
1129
+ <ChevronRightIcon className='shrink-0 transition-transform [[data-state="open"]>&]:rotate-90' />
1130
+ </DropdownMenuItem>
1131
+ </CollapsibleTrigger>
1132
+ <CollapsibleContent className='pl-4'>
1133
+ <DropdownMenuItem>
1134
+ <CircleSmallIcon />
1135
+ <span>Teams</span>
1136
+ </DropdownMenuItem>
1137
+ <DropdownMenuItem>
1138
+ <CircleSmallIcon />
1139
+ <span>Projects</span>
1140
+ </DropdownMenuItem>
1141
+ <DropdownMenuItem>
1142
+ <CircleSmallIcon />
1143
+ <span>Connections</span>
1144
+ </DropdownMenuItem>
1145
+ </CollapsibleContent>
1146
+ </DropdownMenuGroup>
1147
+ </Collapsible>
1148
+ <DropdownMenuItem>
1149
+ <LogOutIcon />
1150
+ <span>Log out</span>
1151
+ </DropdownMenuItem>
1152
+ </DropdownMenuContent>
1153
+ </DropdownMenu>
1154
+ )
1155
+ }
1156
+
1157
+ const CollapsibleFormDemo = () => {
1158
+ const id = useId()
1159
+ const { meta, getCardNumberProps, getExpiryDateProps, getCVCProps, getCardImageProps } = usePaymentInputs()
1160
+
1161
+ const items = [
1162
+ { value: '1', label: 'Standard 3-5 Days', description: 'Friday, 15 June - Tuesday, 19 June', price: 'Free' },
1163
+ { value: '2', label: 'Express', description: 'Friday, 15 June - Sunday, 17 June', price: '$5.00' },
1164
+ { value: '3', label: 'Overnight', description: 'Tomorrow', price: '$10.00' }
1165
+ ]
1166
+
1167
+ return (
1168
+ <div className='w-full space-y-3'>
1169
+ <div className='w-full max-w-md space-y-3 rounded-md border py-4'>
1170
+ <Collapsible className='flex flex-col gap-2'>
1171
+ <div className='flex items-center justify-between gap-4 px-4'>
1172
+ <div className='text-sm font-semibold'>Delivery Address</div>
1173
+ <CollapsibleTrigger asChild className='group'>
1174
+ <Button variant='ghost' size='icon-sm'>
1175
+ <ChevronDownIcon className='text-muted-foreground transition-transform group-data-[state=open]:rotate-180' />
1176
+ <span className='sr-only'>Toggle</span>
1177
+ </Button>
1178
+ </CollapsibleTrigger>
1179
+ </div>
1180
+ <CollapsibleContent className='flex flex-col gap-3 px-4 pt-3'>
1181
+ <div className='group relative w-full'>
1182
+ <label
1183
+ htmlFor='full-name'
1184
+ className='origin-start text-muted-foreground group-focus-within:text-foreground has-[+input:not(:placeholder-shown)]:text-foreground absolute top-1/2 block -translate-y-1/2 cursor-text px-2 text-sm transition-all group-focus-within:pointer-events-none group-focus-within:top-0 group-focus-within:cursor-default group-focus-within:text-xs group-focus-within:font-medium has-[+input:not(:placeholder-shown)]:pointer-events-none has-[+input:not(:placeholder-shown)]:top-0 has-[+input:not(:placeholder-shown)]:cursor-default has-[+input:not(:placeholder-shown)]:text-xs has-[+input:not(:placeholder-shown)]:font-medium'
1185
+ >
1186
+ <span className='bg-background inline-flex px-1'>Full Name</span>
1187
+ </label>
1188
+ <Input id='full-name' type='text' placeholder=' ' className='dark:bg-background' />
1189
+ </div>
1190
+ <div className='group relative w-full space-y-2'>
1191
+ <label
1192
+ htmlFor='address'
1193
+ className='origin-start text-muted-foreground group-focus-within:text-foreground has-[+textarea:not(:placeholder-shown)]:text-foreground has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive absolute top-0 block translate-y-2 cursor-text px-2 text-sm transition-all group-focus-within:pointer-events-none group-focus-within:-translate-y-1/2 group-focus-within:cursor-default group-focus-within:text-xs group-focus-within:font-medium has-[+textarea:not(:placeholder-shown)]:pointer-events-none has-[+textarea:not(:placeholder-shown)]:-translate-y-1/2 has-[+textarea:not(:placeholder-shown)]:cursor-default has-[+textarea:not(:placeholder-shown)]:text-xs has-[+textarea:not(:placeholder-shown)]:font-medium'
1194
+ >
1195
+ <span className='bg-background inline-flex px-1'>Address</span>
1196
+ </label>
1197
+ <Textarea id='address' placeholder=' ' className='!bg-background' />
1198
+ </div>
1199
+ <div className='group relative w-full'>
1200
+ <label
1201
+ htmlFor='pin-code'
1202
+ className='origin-start text-muted-foreground group-focus-within:text-foreground has-[+input:not(:placeholder-shown)]:text-foreground absolute top-1/2 block -translate-y-1/2 cursor-text px-2 text-sm transition-all group-focus-within:pointer-events-none group-focus-within:top-0 group-focus-within:cursor-default group-focus-within:text-xs group-focus-within:font-medium has-[+input:not(:placeholder-shown)]:pointer-events-none has-[+input:not(:placeholder-shown)]:top-0 has-[+input:not(:placeholder-shown)]:cursor-default has-[+input:not(:placeholder-shown)]:text-xs has-[+input:not(:placeholder-shown)]:font-medium'
1203
+ >
1204
+ <span className='bg-background inline-flex px-1'>Pin Code</span>
1205
+ </label>
1206
+ <Input id='pin-code' type='number' placeholder=' ' className='dark:bg-background' />
1207
+ </div>
1208
+ <div className='group relative w-full'>
1209
+ <label
1210
+ htmlFor='city-name'
1211
+ className='origin-start text-muted-foreground group-focus-within:text-foreground has-[+input:not(:placeholder-shown)]:text-foreground absolute top-1/2 block -translate-y-1/2 cursor-text px-2 text-sm transition-all group-focus-within:pointer-events-none group-focus-within:top-0 group-focus-within:cursor-default group-focus-within:text-xs group-focus-within:font-medium has-[+input:not(:placeholder-shown)]:pointer-events-none has-[+input:not(:placeholder-shown)]:top-0 has-[+input:not(:placeholder-shown)]:cursor-default has-[+input:not(:placeholder-shown)]:text-xs has-[+input:not(:placeholder-shown)]:font-medium'
1212
+ >
1213
+ <span className='bg-background inline-flex px-1'>City</span>
1214
+ </label>
1215
+ <Input id='city-name' type='text' placeholder=' ' className='dark:bg-background' />
1216
+ </div>
1217
+ <div className='group relative w-full'>
1218
+ <label
1219
+ htmlFor='landmark'
1220
+ className='origin-start text-muted-foreground group-focus-within:text-foreground has-[+input:not(:placeholder-shown)]:text-foreground absolute top-1/2 block -translate-y-1/2 cursor-text px-2 text-sm transition-all group-focus-within:pointer-events-none group-focus-within:top-0 group-focus-within:cursor-default group-focus-within:text-xs group-focus-within:font-medium has-[+input:not(:placeholder-shown)]:pointer-events-none has-[+input:not(:placeholder-shown)]:top-0 has-[+input:not(:placeholder-shown)]:cursor-default has-[+input:not(:placeholder-shown)]:text-xs has-[+input:not(:placeholder-shown)]:font-medium'
1221
+ >
1222
+ <span className='bg-background inline-flex px-1'>Landmark</span>
1223
+ </label>
1224
+ <Input id='landmark' type='text' placeholder=' ' className='dark:bg-background' />
1225
+ </div>
1226
+ </CollapsibleContent>
1227
+ </Collapsible>
1228
+ <Separator />
1229
+ <Collapsible className='flex flex-col gap-2'>
1230
+ <div className='flex items-center justify-between gap-4 px-4'>
1231
+ <div className='text-sm font-semibold'>Delivery Options</div>
1232
+ <CollapsibleTrigger asChild className='group'>
1233
+ <Button variant='ghost' size='icon-sm'>
1234
+ <ChevronDownIcon className='text-muted-foreground transition-transform group-data-[state=open]:rotate-180' />
1235
+ <span className='sr-only'>Toggle</span>
1236
+ </Button>
1237
+ </CollapsibleTrigger>
1238
+ </div>
1239
+ <CollapsibleContent className='flex flex-col gap-2 px-4'>
1240
+ <RadioGroup className='w-full gap-0 -space-y-px rounded-md pt-3 shadow-xs' defaultValue='2'>
1241
+ {items.map(item => (
1242
+ <div
1243
+ key={`${id}-${item.value}`}
1244
+ className='border-input has-data-[state=checked]:border-primary/50 has-data-[state=checked]:bg-accent relative flex flex-col gap-4 border p-4 outline-none first:rounded-t-md last:rounded-b-md has-data-[state=checked]:z-10'
1245
+ >
1246
+ <div className='flex items-center justify-between gap-1.5'>
1247
+ <div className='flex items-center gap-2'>
1248
+ <RadioGroupItem
1249
+ id={`${id}-${item.value}`}
1250
+ value={item.value}
1251
+ className='after:absolute after:inset-0'
1252
+ aria-label={`plan-radio-${item.value}`}
1253
+ aria-describedby={`${`${id}-${item.value}`}-price`}
1254
+ />
1255
+ <div className='space-y-1'>
1256
+ <Label className='inline-flex items-center' htmlFor={`${id}-${item.value}`}>
1257
+ {item.label}
1258
+ </Label>
1259
+ <p className='text-muted-foreground text-sm'>{item.description}</p>
1260
+ </div>
1261
+ </div>
1262
+ <div
1263
+ id={`${`${id}-${item.value}`}-price`}
1264
+ className='text-muted-foreground text-xs leading-[inherit]'
1265
+ >
1266
+ {item.price}
1267
+ </div>
1268
+ </div>
1269
+ </div>
1270
+ ))}
1271
+ </RadioGroup>
1272
+ </CollapsibleContent>
1273
+ </Collapsible>
1274
+ <Separator />
1275
+ <Collapsible className='flex flex-col gap-2'>
1276
+ <div className='flex items-center justify-between gap-4 px-4'>
1277
+ <div className='text-sm font-semibold'>Payment</div>
1278
+ <CollapsibleTrigger asChild className='group'>
1279
+ <Button variant='ghost' size='icon-sm'>
1280
+ <ChevronDownIcon className='text-muted-foreground transition-transform group-data-[state=open]:rotate-180' />
1281
+ <span className='sr-only'>Toggle</span>
1282
+ </Button>
1283
+ </CollapsibleTrigger>
1284
+ </div>
1285
+ <CollapsibleContent className='flex flex-col gap-2'>
1286
+ <div className='w-full space-y-2 px-4 pt-2'>
1287
+ <Label>Card details</Label>
1288
+ <div>
1289
+ <div className='relative focus-within:z-1'>
1290
+ <Input
1291
+ {...getCardNumberProps()}
1292
+ id={`number-${id}`}
1293
+ className='peer rounded-b-none pr-9 shadow-none'
1294
+ />
1295
+ <div className='text-muted-foreground pointer-events-none absolute inset-y-0 right-0 flex items-center justify-center pr-3 peer-disabled:opacity-50'>
1296
+ {meta.cardType ? (
1297
+ <svg
1298
+ className='w-6 overflow-hidden'
1299
+ {...getCardImageProps({
1300
+ images: images as unknown as CardImages
1301
+ })}
1302
+ />
1303
+ ) : (
1304
+ <CreditCardIcon className='size-4' />
1305
+ )}
1306
+ <span className='sr-only'>Card Provider</span>
1307
+ </div>
1308
+ </div>
1309
+ <div className='-mt-px flex'>
1310
+ <div className='min-w-0 flex-1 focus-within:z-1'>
1311
+ <Input
1312
+ {...getExpiryDateProps()}
1313
+ id={`expiry-${id}`}
1314
+ className='rounded-t-none rounded-r-none shadow-none'
1315
+ />
1316
+ </div>
1317
+ <div className='-ms-px min-w-0 flex-1 focus-within:z-1'>
1318
+ <Input {...getCVCProps()} id={`cvc-${id}`} className='rounded-t-none rounded-l-none shadow-none' />
1319
+ </div>
1320
+ </div>
1321
+ </div>
1322
+ </div>
1323
+ </CollapsibleContent>
1324
+ </Collapsible>
1325
+ </div>
1326
+ <p className='text-muted-foreground text-xs'>
1327
+ Built with{' '}
1328
+ <a
1329
+ className='hover:text-foreground underline'
1330
+ href='https://github.com/medipass/react-payment-inputs'
1331
+ target='_blank'
1332
+ rel='noopener noreferrer'
1333
+ >
1334
+ React Payment Inputs
1335
+ </a>
1336
+ </p>
1337
+ </div>
1338
+ )
1339
+ }
1340
+
1341
+ const CollapsibleAnimatedDemo = () => {
1342
+ return (
1343
+ <Collapsible className='flex w-full max-w-[350px] flex-col gap-2'>
1344
+ <div className='flex items-center justify-between gap-4 px-4'>
1345
+ <div className='text-sm font-semibold'>@peduarte starred 3 repositories</div>
1346
+ <CollapsibleTrigger asChild>
1347
+ <Button variant='ghost' size='icon-sm'>
1348
+ <ChevronsUpDownIcon />
1349
+ <span className='sr-only'>Toggle</span>
1350
+ </Button>
1351
+ </CollapsibleTrigger>
1352
+ </div>
1353
+ <div className='rounded-md border px-4 py-2 font-mono text-sm'>@radix-ui/primitives</div>
1354
+ <CollapsibleContent className='data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down flex flex-col gap-2 overflow-hidden transition-all duration-300'>
1355
+ <div className='rounded-md border px-4 py-2 font-mono text-sm'>@radix-ui/colors</div>
1356
+ <div className='rounded-md border px-4 py-2 font-mono text-sm'>@stitches/react</div>
1357
+ </CollapsibleContent>
1358
+ </Collapsible>
1359
+ )
1360
+ }