sonance-brand-mcp 1.2.5 → 1.3.2

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 (189) hide show
  1. package/dist/assets/api/sonance-analyze/route.ts +1116 -0
  2. package/dist/assets/api/sonance-assets/route.ts +113 -0
  3. package/dist/assets/api/sonance-components/route.ts +41 -0
  4. package/dist/assets/api/sonance-inject-id/route.ts +363 -0
  5. package/dist/assets/api/sonance-save-logo/route.ts +426 -0
  6. package/dist/assets/api/sonance-theme/route.ts +106 -0
  7. package/dist/assets/brand-system.ts +1265 -0
  8. package/dist/assets/components/accordion.stories.tsx +26 -26
  9. package/dist/assets/components/accordion.tsx +3 -3
  10. package/dist/assets/components/alert-dialog.stories.tsx +142 -0
  11. package/dist/assets/components/alert-dialog.tsx +143 -0
  12. package/dist/assets/components/alert.stories.tsx +3 -3
  13. package/dist/assets/components/alert.tsx +4 -3
  14. package/dist/assets/components/aspect-ratio.stories.tsx +70 -0
  15. package/dist/assets/components/aspect-ratio.tsx +8 -0
  16. package/dist/assets/components/autocomplete.stories.tsx +9 -9
  17. package/dist/assets/components/autocomplete.tsx +3 -3
  18. package/dist/assets/components/avatar.stories.tsx +5 -5
  19. package/dist/assets/components/avatar.tsx +67 -23
  20. package/dist/assets/components/badge.stories.tsx +10 -10
  21. package/dist/assets/components/badge.tsx +3 -3
  22. package/dist/assets/components/breadcrumbs.stories.tsx +7 -7
  23. package/dist/assets/components/breadcrumbs.tsx +13 -8
  24. package/dist/assets/components/button.stories.tsx +74 -74
  25. package/dist/assets/components/button.tsx +2 -0
  26. package/dist/assets/components/calendar.stories.tsx +11 -11
  27. package/dist/assets/components/calendar.tsx +4 -4
  28. package/dist/assets/components/card.stories.tsx +22 -22
  29. package/dist/assets/components/card.tsx +7 -3
  30. package/dist/assets/components/carousel.stories.tsx +158 -0
  31. package/dist/assets/components/carousel.tsx +264 -0
  32. package/dist/assets/components/chart.stories.tsx +376 -0
  33. package/dist/assets/components/chart.tsx +384 -0
  34. package/dist/assets/components/checkbox-group.stories.tsx +6 -6
  35. package/dist/assets/components/checkbox-group.tsx +3 -3
  36. package/dist/assets/components/checkbox.stories.tsx +23 -20
  37. package/dist/assets/components/checkbox.tsx +13 -6
  38. package/dist/assets/components/code.stories.tsx +24 -24
  39. package/dist/assets/components/code.tsx +22 -27
  40. package/dist/assets/components/collapsible.stories.tsx +128 -0
  41. package/dist/assets/components/collapsible.tsx +10 -0
  42. package/dist/assets/components/command.stories.tsx +183 -0
  43. package/dist/assets/components/command.tsx +171 -0
  44. package/dist/assets/components/context-menu.stories.tsx +159 -0
  45. package/dist/assets/components/context-menu.tsx +214 -0
  46. package/dist/assets/components/date-input.stories.tsx +9 -9
  47. package/dist/assets/components/date-input.tsx +2 -2
  48. package/dist/assets/components/date-picker.stories.tsx +9 -9
  49. package/dist/assets/components/date-picker.tsx +3 -3
  50. package/dist/assets/components/date-range-picker.stories.tsx +12 -12
  51. package/dist/assets/components/date-range-picker.tsx +3 -3
  52. package/dist/assets/components/dialog.stories.tsx +40 -40
  53. package/dist/assets/components/dialog.tsx +8 -12
  54. package/dist/assets/components/divider.stories.tsx +30 -30
  55. package/dist/assets/components/divider.tsx +34 -35
  56. package/dist/assets/components/drawer.stories.tsx +32 -31
  57. package/dist/assets/components/drawer.tsx +7 -6
  58. package/dist/assets/components/dropdown-menu.tsx +213 -0
  59. package/dist/assets/components/dropdown.stories.tsx +12 -12
  60. package/dist/assets/components/dropdown.tsx +5 -5
  61. package/dist/assets/components/form.stories.tsx +30 -29
  62. package/dist/assets/components/form.tsx +5 -5
  63. package/dist/assets/components/hover-card.stories.tsx +115 -0
  64. package/dist/assets/components/hover-card.tsx +35 -0
  65. package/dist/assets/components/image.stories.tsx +48 -25
  66. package/dist/assets/components/image.tsx +8 -5
  67. package/dist/assets/components/input-otp.stories.tsx +15 -15
  68. package/dist/assets/components/input-otp.tsx +5 -5
  69. package/dist/assets/components/input.stories.tsx +30 -25
  70. package/dist/assets/components/input.tsx +7 -4
  71. package/dist/assets/components/kbd.stories.tsx +34 -34
  72. package/dist/assets/components/kbd.tsx +9 -9
  73. package/dist/assets/components/link.stories.tsx +36 -36
  74. package/dist/assets/components/link.tsx +4 -0
  75. package/dist/assets/components/listbox.stories.tsx +5 -5
  76. package/dist/assets/components/listbox.tsx +4 -4
  77. package/dist/assets/components/menubar.stories.tsx +208 -0
  78. package/dist/assets/components/menubar.tsx +247 -0
  79. package/dist/assets/components/navbar.stories.tsx +24 -24
  80. package/dist/assets/components/navbar.tsx +8 -14
  81. package/dist/assets/components/navigation-menu.stories.tsx +239 -0
  82. package/dist/assets/components/navigation-menu.tsx +135 -0
  83. package/dist/assets/components/number-input.stories.tsx +11 -11
  84. package/dist/assets/components/number-input.tsx +3 -3
  85. package/dist/assets/components/pagination.stories.tsx +13 -13
  86. package/dist/assets/components/pagination.tsx +6 -6
  87. package/dist/assets/components/popover.stories.tsx +35 -35
  88. package/dist/assets/components/popover.tsx +98 -15
  89. package/dist/assets/components/progress.stories.tsx +5 -5
  90. package/dist/assets/components/progress.tsx +5 -5
  91. package/dist/assets/components/radio-group.stories.tsx +7 -7
  92. package/dist/assets/components/radio-group.tsx +3 -3
  93. package/dist/assets/components/range-calendar.stories.tsx +18 -18
  94. package/dist/assets/components/range-calendar.tsx +3 -3
  95. package/dist/assets/components/resizable.stories.tsx +197 -0
  96. package/dist/assets/components/resizable.tsx +47 -0
  97. package/dist/assets/components/scroll-area.stories.tsx +123 -0
  98. package/dist/assets/components/scroll-area.tsx +48 -0
  99. package/dist/assets/components/scroll-shadow.stories.tsx +17 -17
  100. package/dist/assets/components/scroll-shadow.tsx +31 -9
  101. package/dist/assets/components/select.stories.tsx +20 -19
  102. package/dist/assets/components/select.tsx +10 -6
  103. package/dist/assets/components/separator.tsx +32 -0
  104. package/dist/assets/components/sheet.tsx +137 -0
  105. package/dist/assets/components/sidebar.stories.tsx +351 -0
  106. package/dist/assets/components/sidebar.tsx +757 -0
  107. package/dist/assets/components/skeleton.stories.tsx +3 -3
  108. package/dist/assets/components/skeleton.tsx +2 -2
  109. package/dist/assets/components/slider.stories.tsx +6 -6
  110. package/dist/assets/components/slider.tsx +3 -3
  111. package/dist/assets/components/spacer.stories.tsx +11 -11
  112. package/dist/assets/components/spacer.tsx +2 -2
  113. package/dist/assets/components/spinner.stories.tsx +8 -8
  114. package/dist/assets/components/spinner.tsx +5 -5
  115. package/dist/assets/components/switch.stories.tsx +24 -20
  116. package/dist/assets/components/switch.tsx +14 -6
  117. package/dist/assets/components/table.stories.tsx +7 -7
  118. package/dist/assets/components/table.tsx +8 -8
  119. package/dist/assets/components/tabs.stories.tsx +37 -37
  120. package/dist/assets/components/tabs.tsx +3 -3
  121. package/dist/assets/components/textarea.stories.tsx +13 -12
  122. package/dist/assets/components/textarea.tsx +3 -3
  123. package/dist/assets/components/theme-toggle.stories.tsx +31 -30
  124. package/dist/assets/components/theme-toggle.tsx +2 -2
  125. package/dist/assets/components/time-input.stories.tsx +16 -16
  126. package/dist/assets/components/time-input.tsx +2 -2
  127. package/dist/assets/components/toast.stories.tsx +8 -5
  128. package/dist/assets/components/toast.tsx +6 -6
  129. package/dist/assets/components/toggle-group.stories.tsx +153 -0
  130. package/dist/assets/components/toggle-group.tsx +61 -0
  131. package/dist/assets/components/toggle.stories.tsx +77 -0
  132. package/dist/assets/components/toggle.tsx +46 -0
  133. package/dist/assets/components/tooltip.stories.tsx +49 -27
  134. package/dist/assets/components/tooltip.tsx +23 -90
  135. package/dist/assets/components/user.stories.tsx +23 -23
  136. package/dist/assets/components/user.tsx +7 -4
  137. package/dist/assets/dev-tools/SonanceDevTools.tsx +4201 -0
  138. package/dist/assets/dev-tools/index.ts +10 -0
  139. package/dist/assets/globals.css +39 -0
  140. package/dist/assets/logos/40th-anniversary/Sonance_40_Logo_CMYK_BEAM_BLUE_40_AND_BEAM_DARK.png +0 -0
  141. package/dist/assets/logos/Sonance logo dark mode.png +0 -0
  142. package/dist/assets/logos/Sonance logo light mode.png +0 -0
  143. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_2C_Light_RGB_05162025.png +0 -0
  144. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_3C_Dark_RGB_05162025.png +0 -0
  145. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_White_RGB_05162025.png +0 -0
  146. package/dist/assets/logos/iport/IPORT_Sonance_LockUp_2C_Dark_RGB.png +0 -0
  147. package/dist/assets/logos/iport/IPORT_Sonance_LockUp_2C_Light_RGB.png +0 -0
  148. package/dist/assets/logos/james/James_Logo_Black_CMYK.png +0 -0
  149. package/dist/assets/logos/james/James_Logo_Black_RGB.png +0 -0
  150. package/dist/assets/logos/james/James_Logo_LtGray_CMYK.png +0 -0
  151. package/dist/assets/logos/james/James_Logo_LtGray_RGB.png +0 -0
  152. package/dist/assets/logos/james/James_Logo_Polished_RGB.png +0 -0
  153. package/dist/assets/logos/james/James_Logo_Reverse_CMYK.png +0 -0
  154. package/dist/assets/logos/james/James_Logo_Reverse_RGB.png +0 -0
  155. package/dist/assets/logos/james/James_Logo_White_CMYK.png +0 -0
  156. package/dist/assets/logos/life-is-better/Sonance_LifeisBetter_Dark_RGB.png +0 -0
  157. package/dist/assets/logos/life-is-better/Sonance_LifeisBetter_Light_RGB.png +0 -0
  158. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Dark_RGB.png +0 -0
  159. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Light_RGB.png +0 -0
  160. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Reverse_RGB.png +0 -0
  161. package/dist/assets/logos/my-sonance/My.Sonance_Logo_Black_RGB.png +0 -0
  162. package/dist/assets/logos/my-sonance/My.Sonance_Logo_Reverse_RGB.png +0 -0
  163. package/dist/assets/logos/sonance/Sonance_Logo_2C_Dark_RGB.png +0 -0
  164. package/dist/assets/logos/sonance/Sonance_Logo_2C_Light_RGB.png +0 -0
  165. package/dist/assets/logos/sonance/Sonance_Logo_2C_Reverse_RGB.png +0 -0
  166. package/dist/assets/logos/sonance/Sonance_Logo_Black_RGB.png +0 -0
  167. package/dist/assets/logos/sonance/Sonance_Logo_Grayscale_RGB.png +0 -0
  168. package/dist/assets/logos/sonance/Sonance_Logo_Reverse_RGB.png +0 -0
  169. package/dist/assets/logos/sonance-academy/SonanceAcademy_Logo_Dark_CMYK.png +0 -0
  170. package/dist/assets/logos/sonance-academy/SonanceAcademy_Logo_Light_CMYK.png +0 -0
  171. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Dark_RGB.png +0 -0
  172. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Light_RGB.png +0 -0
  173. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Reverse_RGB.png +0 -0
  174. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Black_RGB.png +0 -0
  175. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Grayscale_RGB.png +0 -0
  176. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Reverse_RGB.png +0 -0
  177. package/dist/assets/logos/sonance-james/Sonance_James_Lockup_Dark.png +0 -0
  178. package/dist/assets/logos/sonance-james/Sonance_James_Lockup_Light.png +0 -0
  179. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_LockupStacked_Dark.png +0 -0
  180. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_LockupStacked_Light.png +0 -0
  181. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Dark.png +0 -0
  182. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Light.png +0 -0
  183. package/dist/assets/logos/trufig/TrufigLogo_Black.png +0 -0
  184. package/dist/assets/logos/trufig/TrufigLogo_Light.png +0 -0
  185. package/dist/assets/logos/trufig/TrufigWatermark_Black.png +0 -0
  186. package/dist/assets/logos/trufig/TrufigWatermark_Light.png +0 -0
  187. package/dist/assets/styles/brand-overrides.css +37 -0
  188. package/dist/index.js +2055 -15
  189. package/package.json +1 -1
@@ -38,15 +38,15 @@ export const Default: Story = {
38
38
  render: () => {
39
39
  const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
40
40
  return (
41
- <div className="space-y-4">
41
+ <div data-sonance-name="range-calendar.stories" className="space-y-4">
42
42
  <RangeCalendar
43
43
  value={range}
44
44
  onValueChange={setRange}
45
45
  />
46
46
  <div className="text-sm text-foreground-muted">
47
- {range.start && <span>Start: {range.start.toLocaleDateString()}</span>}
48
- {range.start && range.end && <span> | </span>}
49
- {range.end && <span>End: {range.end.toLocaleDateString()}</span>}
47
+ {range.start && <span id="default-span-start-rangestarttolo">Start: {range.start.toLocaleDateString()}</span>}
48
+ {range.start && range.end && <span id="default-span"> | </span>}
49
+ {range.end && <span id="default-span-end-rangeendtolocale">End: {range.end.toLocaleDateString()}</span>}
50
50
  </div>
51
51
  </div>
52
52
  );
@@ -105,7 +105,7 @@ export const WithMinMaxDates: Story = {
105
105
  const today = new Date();
106
106
  return (
107
107
  <div className="space-y-4">
108
- <p className="text-sm text-foreground-muted">
108
+ <p id="with-min-max-dates-p-selection-limited-to" className="text-sm text-foreground-muted">
109
109
  Selection limited to next 60 days
110
110
  </p>
111
111
  <RangeCalendar
@@ -129,7 +129,7 @@ export const DisabledWeekends: Story = {
129
129
  };
130
130
  return (
131
131
  <div className="space-y-4">
132
- <p className="text-sm text-foreground-muted">
132
+ <p id="disabled-weekends-p-weekends-are-disable" className="text-sm text-foreground-muted">
133
133
  Weekends are disabled (business days only)
134
134
  </p>
135
135
  <RangeCalendar
@@ -170,15 +170,15 @@ export const HotelBookingExample: Story = {
170
170
  <div className="flex items-center gap-4 text-sm">
171
171
  <div className="flex items-center gap-2">
172
172
  <div className="w-4 h-4 bg-primary rounded-sm" />
173
- <span>Selected</span>
173
+ <span id="hotel-booking-example-span-selected">Selected</span>
174
174
  </div>
175
175
  <div className="flex items-center gap-2">
176
176
  <div className="w-4 h-4 bg-primary/20 rounded-sm" />
177
- <span>In Range</span>
177
+ <span id="hotel-booking-example-span-in-range">In Range</span>
178
178
  </div>
179
179
  <div className="flex items-center gap-2">
180
180
  <div className="w-4 h-4 bg-secondary text-foreground-subtle text-xs flex items-center justify-center">x</div>
181
- <span>Unavailable</span>
181
+ <span id="hotel-booking-example-span-unavailable">Unavailable</span>
182
182
  </div>
183
183
  </div>
184
184
  <RangeCalendar
@@ -191,8 +191,8 @@ export const HotelBookingExample: Story = {
191
191
  {nights > 0 && (
192
192
  <div className="p-4 border border-border rounded-sm">
193
193
  <div className="flex justify-between">
194
- <span className="text-foreground-muted">Duration</span>
195
- <span className="font-medium">{nights} night{nights !== 1 ? 's' : ''}</span>
194
+ <span id="hotel-booking-example-span-duration" className="text-foreground-muted">Duration</span>
195
+ <span id="hotel-booking-example-span-nights-nightnights-1" className="font-medium">{nights} night{nights !== 1 ? 's' : ''}</span>
196
196
  </div>
197
197
  </div>
198
198
  )}
@@ -239,7 +239,7 @@ export const ReportDateRangeExample: Story = {
239
239
  maxDate={today}
240
240
  />
241
241
  {range.start && range.end && (
242
- <p className="text-sm">
242
+ <p id="report-date-range-example-p-selected-rangestartt" className="text-sm">
243
243
  Selected: {range.start.toLocaleDateString()} - {range.end.toLocaleDateString()}
244
244
  </p>
245
245
  )}
@@ -258,7 +258,7 @@ export const AllConfigurations: Story = {
258
258
  return (
259
259
  <div className="space-y-8">
260
260
  <div>
261
- <h4 className="text-sm font-medium mb-2">Single Month</h4>
261
+ <h4 id="all-configurations-h4-single-month" className="text-sm font-medium mb-2">Single Month</h4>
262
262
  <RangeCalendar
263
263
  value={range1}
264
264
  onValueChange={setRange1}
@@ -266,7 +266,7 @@ export const AllConfigurations: Story = {
266
266
  />
267
267
  </div>
268
268
  <div>
269
- <h4 className="text-sm font-medium mb-2">Two Months (Default)</h4>
269
+ <h4 id="all-configurations-h4-two-months-default" className="text-sm font-medium mb-2">Two Months (Default)</h4>
270
270
  <RangeCalendar
271
271
  value={range2}
272
272
  onValueChange={setRange2}
@@ -274,7 +274,7 @@ export const AllConfigurations: Story = {
274
274
  />
275
275
  </div>
276
276
  <div>
277
- <h4 className="text-sm font-medium mb-2">Three Months</h4>
277
+ <h4 id="all-configurations-h4-three-months" className="text-sm font-medium mb-2">Three Months</h4>
278
278
  <RangeCalendar
279
279
  value={range3}
280
280
  onValueChange={setRange3}
@@ -297,7 +297,7 @@ export const ResponsiveMatrix: Story = {
297
297
  <div className="space-y-8">
298
298
  {/* Mobile */}
299
299
  <div>
300
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
300
+ <h4 id="responsive-matrix-h4-mobile-375px" className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
301
301
  <div className="w-[375px] border border-dashed border-border p-4 overflow-x-auto">
302
302
  <RangeCalendar
303
303
  value={mobile}
@@ -308,7 +308,7 @@ export const ResponsiveMatrix: Story = {
308
308
  </div>
309
309
  {/* Tablet */}
310
310
  <div>
311
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
311
+ <h4 id="responsive-matrix-h4-tablet-768px" className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
312
312
  <div className="w-[768px] border border-dashed border-border p-4">
313
313
  <RangeCalendar
314
314
  value={tablet}
@@ -319,7 +319,7 @@ export const ResponsiveMatrix: Story = {
319
319
  </div>
320
320
  {/* Desktop */}
321
321
  <div>
322
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
322
+ <h4 id="responsive-matrix-h4-desktop-1280px" className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
323
323
  <div className="w-[1280px] border border-dashed border-border p-4">
324
324
  <RangeCalendar
325
325
  value={desktop}
@@ -112,8 +112,8 @@ export function RangeCalendar({
112
112
  const days = eachDayOfInterval({ start: calendarStart, end: calendarEnd });
113
113
 
114
114
  return (
115
- <div key={monthDate.toISOString()}>
116
- <h3 className="text-sm font-medium text-foreground text-center mb-4">
115
+ <div data-sonance-name="range-calendar" key={monthDate.toISOString()}>
116
+ <h3 id="h3-formatmonthdate-mmmm" className="text-sm font-medium text-foreground text-center mb-4">
117
117
  {format(monthDate, "MMMM yyyy")}
118
118
  </h3>
119
119
 
@@ -140,7 +140,7 @@ export function RangeCalendar({
140
140
  const isEnd = isRangeEnd(day);
141
141
 
142
142
  return (
143
- <button
143
+ <button data-sonance-name="range-calendar"
144
144
  key={day.toISOString()}
145
145
  type="button"
146
146
  onClick={() => handleSelectDate(day)}
@@ -0,0 +1,197 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import {
3
+ ResizableHandle,
4
+ ResizablePanel,
5
+ ResizablePanelGroup,
6
+ } from "./resizable";
7
+
8
+ const meta: Meta<typeof ResizablePanelGroup> = {
9
+ title: "Components/Layout/Resizable",
10
+ component: ResizablePanelGroup,
11
+ parameters: {
12
+ layout: "centered",
13
+ },
14
+ tags: ["autodocs"],
15
+ };
16
+
17
+ export default meta;
18
+ type Story = StoryObj<typeof ResizablePanelGroup>;
19
+
20
+ export const Default: Story = {
21
+ render: () => (
22
+ <ResizablePanelGroup
23
+ direction="horizontal"
24
+ className="max-w-md rounded-sm border border-border"
25
+ >
26
+ <ResizablePanel defaultSize={50}>
27
+ <div className="flex h-[200px] items-center justify-center p-6">
28
+ <span id="default-span-one" className="font-medium text-foreground">One</span>
29
+ </div>
30
+ </ResizablePanel>
31
+ <ResizableHandle />
32
+ <ResizablePanel defaultSize={50}>
33
+ <div className="flex h-[200px] items-center justify-center p-6">
34
+ <span id="default-span-two" className="font-medium text-foreground">Two</span>
35
+ </div>
36
+ </ResizablePanel>
37
+ </ResizablePanelGroup>
38
+ ),
39
+ };
40
+
41
+ export const Vertical: Story = {
42
+ render: () => (
43
+ <ResizablePanelGroup
44
+ direction="vertical"
45
+ className="min-h-[200px] max-w-md rounded-sm border border-border"
46
+ >
47
+ <ResizablePanel defaultSize={25}>
48
+ <div className="flex h-full items-center justify-center p-6">
49
+ <span id="vertical-span-header" className="font-medium text-foreground">Header</span>
50
+ </div>
51
+ </ResizablePanel>
52
+ <ResizableHandle />
53
+ <ResizablePanel defaultSize={75}>
54
+ <div className="flex h-full items-center justify-center p-6">
55
+ <span id="vertical-span-content" className="font-medium text-foreground">Content</span>
56
+ </div>
57
+ </ResizablePanel>
58
+ </ResizablePanelGroup>
59
+ ),
60
+ };
61
+
62
+ export const WithHandle: Story = {
63
+ render: () => (
64
+ <ResizablePanelGroup
65
+ direction="horizontal"
66
+ className="min-h-[200px] max-w-md rounded-sm border border-border"
67
+ >
68
+ <ResizablePanel defaultSize={25}>
69
+ <div className="flex h-full items-center justify-center p-6">
70
+ <span id="with-handle-span-sidebar" className="font-medium text-foreground">Sidebar</span>
71
+ </div>
72
+ </ResizablePanel>
73
+ <ResizableHandle withHandle />
74
+ <ResizablePanel defaultSize={75}>
75
+ <div className="flex h-full items-center justify-center p-6">
76
+ <span id="with-handle-span-content" className="font-medium text-foreground">Content</span>
77
+ </div>
78
+ </ResizablePanel>
79
+ </ResizablePanelGroup>
80
+ ),
81
+ };
82
+
83
+ export const ThreePanels: Story = {
84
+ render: () => (
85
+ <ResizablePanelGroup
86
+ direction="horizontal"
87
+ className="min-h-[200px] max-w-lg rounded-sm border border-border"
88
+ >
89
+ <ResizablePanel defaultSize={20} minSize={15}>
90
+ <div className="flex h-full items-center justify-center p-4">
91
+ <span id="three-panels-span-nav" className="text-sm font-medium text-foreground">Nav</span>
92
+ </div>
93
+ </ResizablePanel>
94
+ <ResizableHandle withHandle />
95
+ <ResizablePanel defaultSize={60}>
96
+ <div className="flex h-full items-center justify-center p-4">
97
+ <span id="three-panels-span-main-content" className="font-medium text-foreground">Main Content</span>
98
+ </div>
99
+ </ResizablePanel>
100
+ <ResizableHandle withHandle />
101
+ <ResizablePanel defaultSize={20} minSize={15}>
102
+ <div className="flex h-full items-center justify-center p-4">
103
+ <span id="three-panels-span-panel" className="text-sm font-medium text-foreground">Panel</span>
104
+ </div>
105
+ </ResizablePanel>
106
+ </ResizablePanelGroup>
107
+ ),
108
+ };
109
+
110
+ export const NestedPanels: Story = {
111
+ render: () => (
112
+ <ResizablePanelGroup
113
+ direction="horizontal"
114
+ className="min-h-[300px] max-w-lg rounded-sm border border-border"
115
+ >
116
+ <ResizablePanel defaultSize={25}>
117
+ <div className="flex h-full items-center justify-center p-6">
118
+ <span id="nested-panels-span-sidebar" className="font-medium text-foreground">Sidebar</span>
119
+ </div>
120
+ </ResizablePanel>
121
+ <ResizableHandle withHandle />
122
+ <ResizablePanel defaultSize={75}>
123
+ <ResizablePanelGroup direction="vertical">
124
+ <ResizablePanel defaultSize={50}>
125
+ <div className="flex h-full items-center justify-center p-6">
126
+ <span id="nested-panels-span-top-panel" className="font-medium text-foreground">Top Panel</span>
127
+ </div>
128
+ </ResizablePanel>
129
+ <ResizableHandle />
130
+ <ResizablePanel defaultSize={50}>
131
+ <div className="flex h-full items-center justify-center p-6">
132
+ <span id="nested-panels-span-bottom-panel" className="font-medium text-foreground">Bottom Panel</span>
133
+ </div>
134
+ </ResizablePanel>
135
+ </ResizablePanelGroup>
136
+ </ResizablePanel>
137
+ </ResizablePanelGroup>
138
+ ),
139
+ };
140
+
141
+ export const IDELayout: Story = {
142
+ render: () => (
143
+ <ResizablePanelGroup
144
+ direction="horizontal"
145
+ className="min-h-[400px] max-w-2xl rounded-sm border border-border"
146
+ >
147
+ <ResizablePanel defaultSize={20} minSize={10}>
148
+ <div className="flex h-full flex-col bg-background p-2">
149
+ <p id="i-d-e-layout-p-explorer" className="text-xs font-medium uppercase tracking-widest text-foreground-muted mb-2">
150
+ Explorer
151
+ </p>
152
+ <div className="space-y-1 text-sm text-foreground-secondary">
153
+ <p id="i-d-e-layout-p--src">📁 src</p>
154
+ <p id="i-d-e-layout-p--components" className="pl-4">📁 components</p>
155
+ <p id="i-d-e-layout-p--lib" className="pl-4">📁 lib</p>
156
+ <p id="i-d-e-layout-p--packagejson">📄 package.json</p>
157
+ </div>
158
+ </div>
159
+ </ResizablePanel>
160
+ <ResizableHandle withHandle />
161
+ <ResizablePanel defaultSize={60}>
162
+ <ResizablePanelGroup direction="vertical">
163
+ <ResizablePanel defaultSize={70}>
164
+ <div className="flex h-full items-center justify-center bg-card p-6">
165
+ <span id="i-d-e-layout-span-editor" className="font-medium text-foreground">Editor</span>
166
+ </div>
167
+ </ResizablePanel>
168
+ <ResizableHandle />
169
+ <ResizablePanel defaultSize={30} minSize={15}>
170
+ <div className="flex h-full flex-col bg-background p-2">
171
+ <p id="i-d-e-layout-p-terminal" className="text-xs font-medium uppercase tracking-widest text-foreground-muted mb-2">
172
+ Terminal
173
+ </p>
174
+ <code className="text-sm text-foreground-secondary">
175
+ $ npm run dev
176
+ </code>
177
+ </div>
178
+ </ResizablePanel>
179
+ </ResizablePanelGroup>
180
+ </ResizablePanel>
181
+ <ResizableHandle withHandle />
182
+ <ResizablePanel defaultSize={20} minSize={10}>
183
+ <div className="flex h-full flex-col bg-background p-2">
184
+ <p id="i-d-e-layout-p-outline" className="text-xs font-medium uppercase tracking-widest text-foreground-muted mb-2">
185
+ Outline
186
+ </p>
187
+ <div className="space-y-1 text-sm text-foreground-secondary">
188
+ <p id="button-p--function-button">• function Button</p>
189
+ <p id="card-p--function-card">• function Card</p>
190
+ <p id="dialog-p--function-dialog">• function Dialog</p>
191
+ </div>
192
+ </div>
193
+ </ResizablePanel>
194
+ </ResizablePanelGroup>
195
+ ),
196
+ };
197
+
@@ -0,0 +1,47 @@
1
+ "use client";
2
+
3
+ import { GripVertical } from "lucide-react";
4
+ import * as ResizablePrimitive from "react-resizable-panels";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const ResizablePanelGroup = ({
8
+ className,
9
+ ...props
10
+ }: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
11
+ <ResizablePrimitive.PanelGroup data-sonance-name="resizable"
12
+ className={cn(
13
+ "flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
14
+ className
15
+ )}
16
+ {...props}
17
+ />
18
+ );
19
+
20
+ const ResizablePanel = ResizablePrimitive.Panel;
21
+
22
+ const ResizableHandle = ({
23
+ withHandle,
24
+ className,
25
+ ...props
26
+ }: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
27
+ withHandle?: boolean;
28
+ }) => (
29
+ <ResizablePrimitive.PanelResizeHandle
30
+ className={cn(
31
+ "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary focus-visible:ring-offset-1",
32
+ "data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0",
33
+ "[&[data-panel-group-direction=vertical]>div]:rotate-90",
34
+ className
35
+ )}
36
+ {...props}
37
+ >
38
+ {withHandle && (
39
+ <div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border border-border bg-card">
40
+ <GripVertical className="h-2.5 w-2.5" />
41
+ </div>
42
+ )}
43
+ </ResizablePrimitive.PanelResizeHandle>
44
+ );
45
+
46
+ export { ResizablePanelGroup, ResizablePanel, ResizableHandle };
47
+
@@ -0,0 +1,123 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { ScrollArea, ScrollBar } from "./scroll-area";
3
+ import { Divider } from "./divider";
4
+
5
+ const meta: Meta<typeof ScrollArea> = {
6
+ title: "Components/Data Display/ScrollArea",
7
+ component: ScrollArea,
8
+ parameters: {
9
+ layout: "centered",
10
+ },
11
+ tags: ["autodocs"],
12
+ };
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof ScrollArea>;
16
+
17
+ const tags = Array.from({ length: 50 }).map(
18
+ (_, i, a) => `v1.2.0-beta.${a.length - i}`
19
+ );
20
+
21
+ export const Default: Story = {
22
+ render: () => (
23
+ <ScrollArea className="h-72 w-48 rounded-sm border border-border">
24
+ <div className="p-4">
25
+ <h4 id="default-h4-tags" className="mb-4 text-sm font-medium leading-none text-foreground">Tags</h4>
26
+ {tags.map((tag) => (
27
+ <div key={tag}>
28
+ <div className="text-sm text-foreground-secondary">{tag}</div>
29
+ <Divider className="my-2" />
30
+ </div>
31
+ ))}
32
+ </div>
33
+ </ScrollArea>
34
+ ),
35
+ };
36
+
37
+ const works = [
38
+ {
39
+ artist: "Ornella Binni",
40
+ art: "Reflection",
41
+ },
42
+ {
43
+ artist: "Tom Byrom",
44
+ art: "Living Room",
45
+ },
46
+ {
47
+ artist: "Vladimir Malyavko",
48
+ art: "Sunrise",
49
+ },
50
+ {
51
+ artist: "Anton Repponen",
52
+ art: "Mountains",
53
+ },
54
+ {
55
+ artist: "Sead Dedić",
56
+ art: "Coastline",
57
+ },
58
+ ];
59
+
60
+ export const Horizontal: Story = {
61
+ render: () => (
62
+ <ScrollArea className="w-96 whitespace-nowrap rounded-sm border border-border">
63
+ <div className="flex w-max space-x-4 p-4">
64
+ {works.map((artwork) => (
65
+ <figure key={artwork.artist} className="shrink-0">
66
+ <div className="overflow-hidden rounded-sm">
67
+ <div className="h-[150px] w-[200px] bg-secondary-hover flex items-center justify-center">
68
+ <span id="horizontal-span-artworkart" className="text-foreground-muted text-xs">{artwork.art}</span>
69
+ </div>
70
+ </div>
71
+ <figcaption className="pt-2 text-xs text-foreground-muted">
72
+ Photo by{" "}
73
+ <span id="horizontal-span-artworkartist" className="font-medium text-foreground">
74
+ {artwork.artist}
75
+ </span>
76
+ </figcaption>
77
+ </figure>
78
+ ))}
79
+ </div>
80
+ <ScrollBar orientation="horizontal" />
81
+ </ScrollArea>
82
+ ),
83
+ };
84
+
85
+ export const LargeContent: Story = {
86
+ render: () => (
87
+ <ScrollArea className="h-[400px] w-[350px] rounded-sm border border-border p-4">
88
+ <div className="space-y-4">
89
+ <h4 id="large-content-h4-sonance-brand-guidel" className="text-lg font-medium text-foreground">Sonance Brand Guidelines</h4>
90
+ <p id="large-content-p-welcome-to-the-sonan" className="text-sm text-foreground-secondary leading-relaxed">
91
+ Welcome to the Sonance Brand Guidelines. This document outlines the visual
92
+ identity and design standards for the Sonance family of brands.
93
+ </p>
94
+ <h5 id="large-content-h5-typography" className="text-sm font-medium text-foreground mt-6">Typography</h5>
95
+ <p id="large-content-p-all-text-should-use-" className="text-sm text-foreground-secondary leading-relaxed">
96
+ All text should use the Montserrat font family. Headlines use font-weight 300
97
+ (Light) or 500 (Medium), while body text uses 400 (Regular).
98
+ </p>
99
+ <h5 id="large-content-h5-colors" className="text-sm font-medium text-foreground mt-6">Colors</h5>
100
+ <p id="large-content-p-the-sonance-brand-us" className="text-sm text-foreground-secondary leading-relaxed">
101
+ The Sonance brand uses a sophisticated palette centered around Charcoal
102
+ (#333F48) as the primary color, with Sonance Blue (#00D3C8) as the accent.
103
+ </p>
104
+ <h5 id="large-content-h5-logo-usage" className="text-sm font-medium text-foreground mt-6">Logo Usage</h5>
105
+ <p id="large-content-p-the-sonance-logo-sho" className="text-sm text-foreground-secondary leading-relaxed">
106
+ The Sonance logo should always have adequate clear space and never be
107
+ stretched, rotated, or modified in any way.
108
+ </p>
109
+ <h5 id="large-content-h5-photography" className="text-sm font-medium text-foreground mt-6">Photography</h5>
110
+ <p id="large-content-p-use-photorealistic-i" className="text-sm text-foreground-secondary leading-relaxed">
111
+ Use photo-realistic imagery only. Avoid cartoons, illustrations, or
112
+ AI-generated images that don&apos;t meet our quality standards.
113
+ </p>
114
+ <h5 id="large-content-h5-design-principles" className="text-sm font-medium text-foreground mt-6">Design Principles</h5>
115
+ <p id="large-content-p-embrace-generous-whi" className="text-sm text-foreground-secondary leading-relaxed">
116
+ Embrace generous whitespace, minimal borders, refined shadows, and clean
117
+ backgrounds. Every element should feel premium and intentional.
118
+ </p>
119
+ </div>
120
+ </ScrollArea>
121
+ ),
122
+ };
123
+
@@ -0,0 +1,48 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const ScrollArea = React.forwardRef<
8
+ React.ElementRef<typeof ScrollAreaPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
10
+ >(({ className, children, ...props }, ref) => (
11
+ <ScrollAreaPrimitive.Root data-sonance-name="scroll-area"
12
+ ref={ref}
13
+ className={cn("relative overflow-hidden", className)}
14
+ {...props}
15
+ >
16
+ <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
17
+ {children}
18
+ </ScrollAreaPrimitive.Viewport>
19
+ <ScrollBar />
20
+ <ScrollAreaPrimitive.Corner />
21
+ </ScrollAreaPrimitive.Root>
22
+ ));
23
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
24
+
25
+ const ScrollBar = React.forwardRef<
26
+ React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
27
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
28
+ >(({ className, orientation = "vertical", ...props }, ref) => (
29
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
30
+ ref={ref}
31
+ orientation={orientation}
32
+ className={cn(
33
+ "flex touch-none select-none transition-colors",
34
+ orientation === "vertical" &&
35
+ "h-full w-2.5 border-l border-l-transparent p-[1px]",
36
+ orientation === "horizontal" &&
37
+ "h-2.5 flex-col border-t border-t-transparent p-[1px]",
38
+ className
39
+ )}
40
+ {...props}
41
+ >
42
+ <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
43
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
44
+ ));
45
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
46
+
47
+ export { ScrollArea, ScrollBar };
48
+