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
@@ -0,0 +1,384 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as RechartsPrimitive from "recharts";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ // Format: { THEME_NAME: CSS_SELECTOR }
8
+ const THEMES = { light: "", dark: ".dark" } as const;
9
+
10
+ export type ChartConfig = {
11
+ [k in string]: {
12
+ label?: React.ReactNode;
13
+ icon?: React.ComponentType;
14
+ } & (
15
+ | { color?: string; theme?: never }
16
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
17
+ );
18
+ };
19
+
20
+ type ChartContextProps = {
21
+ config: ChartConfig;
22
+ };
23
+
24
+ const ChartContext = React.createContext<ChartContextProps | null>(null);
25
+
26
+ function useChart() {
27
+ const context = React.useContext(ChartContext);
28
+
29
+ if (!context) {
30
+ throw new Error("useChart must be used within a <ChartContainer />");
31
+ }
32
+
33
+ return context;
34
+ }
35
+
36
+ const ChartContainer = React.forwardRef<
37
+ HTMLDivElement,
38
+ React.ComponentProps<"div"> & {
39
+ config: ChartConfig;
40
+ children: React.ComponentProps<
41
+ typeof RechartsPrimitive.ResponsiveContainer
42
+ >["children"];
43
+ }
44
+ >(({ id, className, children, config, ...props }, ref) => {
45
+ const uniqueId = React.useId();
46
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
47
+
48
+ return (
49
+ <ChartContext.Provider value={{ config }}>
50
+ <div
51
+ data-chart={chartId}
52
+ ref={ref}
53
+ className={cn(
54
+ "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-foreground-muted [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-secondary-hover [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-secondary-hover [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
55
+ className
56
+ )} data-sonance-name="chart"
57
+ {...props}
58
+ >
59
+ <ChartStyle id={chartId} config={config} />
60
+ <RechartsPrimitive.ResponsiveContainer>
61
+ {children}
62
+ </RechartsPrimitive.ResponsiveContainer>
63
+ </div>
64
+ </ChartContext.Provider>
65
+ );
66
+ });
67
+ ChartContainer.displayName = "Chart";
68
+
69
+ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
70
+ const colorConfig = Object.entries(config).filter(
71
+ ([, config]) => config.theme || config.color
72
+ );
73
+
74
+ if (!colorConfig.length) {
75
+ return null;
76
+ }
77
+
78
+ return (
79
+ <style data-sonance-name="chart"
80
+ dangerouslySetInnerHTML={{
81
+ __html: Object.entries(THEMES)
82
+ .map(
83
+ ([theme, prefix]) => `
84
+ ${prefix} [data-chart=${id}] {
85
+ ${colorConfig
86
+ .map(([key, itemConfig]) => {
87
+ const color =
88
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
89
+ itemConfig.color;
90
+ return color ? ` --color-${key}: ${color};` : null;
91
+ })
92
+ .join("\n")}
93
+ }
94
+ `
95
+ )
96
+ .join("\n"),
97
+ }}
98
+ />
99
+ );
100
+ };
101
+
102
+ const ChartTooltip = RechartsPrimitive.Tooltip;
103
+
104
+ interface ChartTooltipContentProps extends React.HTMLAttributes<HTMLDivElement> {
105
+ active?: boolean;
106
+ payload?: Array<{
107
+ name?: string;
108
+ value?: number | string;
109
+ dataKey?: string | number;
110
+ color?: string;
111
+ payload?: Record<string, unknown>;
112
+ fill?: string;
113
+ }>;
114
+ label?: string;
115
+ hideLabel?: boolean;
116
+ hideIndicator?: boolean;
117
+ indicator?: "line" | "dot" | "dashed";
118
+ nameKey?: string;
119
+ labelKey?: string;
120
+ labelFormatter?: (value: unknown, payload: unknown[]) => React.ReactNode;
121
+ labelClassName?: string;
122
+ formatter?: (
123
+ value: unknown,
124
+ name: unknown,
125
+ item: unknown,
126
+ index: number,
127
+ payload: unknown
128
+ ) => React.ReactNode;
129
+ color?: string;
130
+ }
131
+
132
+ const ChartTooltipContent = React.forwardRef<HTMLDivElement, ChartTooltipContentProps>(
133
+ (
134
+ {
135
+ active,
136
+ payload,
137
+ className,
138
+ indicator = "dot",
139
+ hideLabel = false,
140
+ hideIndicator = false,
141
+ label,
142
+ labelFormatter,
143
+ labelClassName,
144
+ formatter,
145
+ color,
146
+ nameKey,
147
+ labelKey,
148
+ },
149
+ ref
150
+ ) => {
151
+ const { config } = useChart();
152
+
153
+ const tooltipLabel = React.useMemo(() => {
154
+ if (hideLabel || !payload?.length) {
155
+ return null;
156
+ }
157
+
158
+ const [item] = payload;
159
+ const key = `${labelKey || item.dataKey || item.name || "value"}`;
160
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
161
+ const value =
162
+ !labelKey && typeof label === "string"
163
+ ? config[label as keyof typeof config]?.label || label
164
+ : itemConfig?.label;
165
+
166
+ if (labelFormatter) {
167
+ return (
168
+ <div data-sonance-name="chart" className={cn("font-medium", labelClassName)}>
169
+ {labelFormatter(value, payload)}
170
+ </div>
171
+ );
172
+ }
173
+
174
+ if (!value) {
175
+ return null;
176
+ }
177
+
178
+ return <div className={cn("font-medium", labelClassName)}>{value}</div>;
179
+ }, [
180
+ label,
181
+ labelFormatter,
182
+ payload,
183
+ hideLabel,
184
+ labelClassName,
185
+ config,
186
+ labelKey,
187
+ ]);
188
+
189
+ if (!active || !payload?.length) {
190
+ return null;
191
+ }
192
+
193
+ const nestLabel = payload.length === 1 && indicator !== "dot";
194
+
195
+ return (
196
+ <div
197
+ ref={ref}
198
+ className={cn(
199
+ "grid min-w-[8rem] items-start gap-1.5 rounded-sm border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
200
+ className
201
+ )}
202
+ >
203
+ {!nestLabel ? tooltipLabel : null}
204
+ <div className="grid gap-1.5">
205
+ {payload.map((item, index) => {
206
+ const key = `${nameKey || item.name || item.dataKey || "value"}`;
207
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
208
+ const indicatorColor = color || (item.payload as Record<string, unknown>)?.fill as string || item.color;
209
+
210
+ return (
211
+ <div
212
+ key={String(item.dataKey)}
213
+ className={cn(
214
+ "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-foreground-muted",
215
+ indicator === "dot" && "items-center"
216
+ )}
217
+ >
218
+ {formatter && item?.value !== undefined && item.name ? (
219
+ formatter(item.value, item.name, item, index, item.payload)
220
+ ) : (
221
+ <>
222
+ {itemConfig?.icon ? (
223
+ <itemConfig.icon />
224
+ ) : (
225
+ !hideIndicator && (
226
+ <div
227
+ className={cn(
228
+ "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
229
+ {
230
+ "h-2.5 w-2.5": indicator === "dot",
231
+ "w-1": indicator === "line",
232
+ "w-0 border-[1.5px] border-dashed bg-transparent":
233
+ indicator === "dashed",
234
+ "my-0.5": nestLabel && indicator === "dashed",
235
+ }
236
+ )}
237
+ style={
238
+ {
239
+ "--color-bg": indicatorColor,
240
+ "--color-border": indicatorColor,
241
+ } as React.CSSProperties
242
+ }
243
+ />
244
+ )
245
+ )}
246
+ <div
247
+ className={cn(
248
+ "flex flex-1 justify-between leading-none",
249
+ nestLabel ? "items-end" : "items-center"
250
+ )}
251
+ >
252
+ <div className="grid gap-1.5">
253
+ {nestLabel ? tooltipLabel : null}
254
+ <span id="span-itemconfiglabel-item" className="text-foreground-muted">
255
+ {itemConfig?.label || item.name}
256
+ </span>
257
+ </div>
258
+ {item.value && (
259
+ <span id="span-typeof-itemvalue-num" className="font-mono font-medium tabular-nums text-foreground">
260
+ {typeof item.value === 'number' ? item.value.toLocaleString() : item.value}
261
+ </span>
262
+ )}
263
+ </div>
264
+ </>
265
+ )}
266
+ </div>
267
+ );
268
+ })}
269
+ </div>
270
+ </div>
271
+ );
272
+ }
273
+ );
274
+ ChartTooltipContent.displayName = "ChartTooltip";
275
+
276
+ const ChartLegend = RechartsPrimitive.Legend;
277
+
278
+ interface ChartLegendContentProps extends React.HTMLAttributes<HTMLDivElement> {
279
+ payload?: Array<{
280
+ value?: string;
281
+ dataKey?: string | number;
282
+ color?: string;
283
+ }>;
284
+ verticalAlign?: "top" | "bottom";
285
+ hideIcon?: boolean;
286
+ nameKey?: string;
287
+ }
288
+
289
+ const ChartLegendContent = React.forwardRef<HTMLDivElement, ChartLegendContentProps>(
290
+ (
291
+ { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
292
+ ref
293
+ ) => {
294
+ const { config } = useChart();
295
+
296
+ if (!payload?.length) {
297
+ return null;
298
+ }
299
+
300
+ return (
301
+ <div
302
+ ref={ref}
303
+ className={cn(
304
+ "flex items-center justify-center gap-4",
305
+ verticalAlign === "top" ? "pb-3" : "pt-3",
306
+ className
307
+ )}
308
+ >
309
+ {payload.map((item) => {
310
+ const key = `${nameKey || item.dataKey || "value"}`;
311
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
312
+
313
+ return (
314
+ <div
315
+ key={item.value}
316
+ className={cn(
317
+ "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-foreground-muted"
318
+ )}
319
+ >
320
+ {itemConfig?.icon && !hideIcon ? (
321
+ <itemConfig.icon />
322
+ ) : (
323
+ <div
324
+ className="h-2 w-2 shrink-0 rounded-[2px]"
325
+ style={{
326
+ backgroundColor: item.color,
327
+ }}
328
+ />
329
+ )}
330
+ {itemConfig?.label}
331
+ </div>
332
+ );
333
+ })}
334
+ </div>
335
+ );
336
+ }
337
+ );
338
+ ChartLegendContent.displayName = "ChartLegend";
339
+
340
+ // Helper to extract item config from a payload.
341
+ function getPayloadConfigFromPayload(
342
+ config: ChartConfig,
343
+ payload: unknown,
344
+ key: string
345
+ ) {
346
+ if (typeof payload !== "object" || payload === null) {
347
+ return undefined;
348
+ }
349
+
350
+ const payloadPayload =
351
+ "payload" in payload &&
352
+ typeof payload.payload === "object" &&
353
+ payload.payload !== null
354
+ ? payload.payload
355
+ : undefined;
356
+
357
+ let configLabelKey: string = key;
358
+
359
+ if (
360
+ key in payload &&
361
+ typeof (payload as Record<string, unknown>)[key] === "string"
362
+ ) {
363
+ configLabelKey = (payload as Record<string, unknown>)[key] as string;
364
+ } else if (
365
+ payloadPayload &&
366
+ key in payloadPayload &&
367
+ typeof (payloadPayload as Record<string, unknown>)[key] === "string"
368
+ ) {
369
+ configLabelKey = (payloadPayload as Record<string, unknown>)[key] as string;
370
+ }
371
+
372
+ return configLabelKey in config
373
+ ? config[configLabelKey]
374
+ : config[key as keyof typeof config];
375
+ }
376
+
377
+ export {
378
+ ChartContainer,
379
+ ChartTooltip,
380
+ ChartTooltipContent,
381
+ ChartLegend,
382
+ ChartLegendContent,
383
+ ChartStyle,
384
+ };
@@ -45,7 +45,7 @@ export const Default: Story = {
45
45
  render: () => {
46
46
  const [values, setValues] = useState<string[]>([]);
47
47
  return (
48
- <CheckboxGroup value={values} onValueChange={setValues}>
48
+ <CheckboxGroup data-sonance-name="checkbox-group.stories" value={values} onValueChange={setValues}>
49
49
  <CheckboxGroupItem value="option1" label="Option 1" />
50
50
  <CheckboxGroupItem value="option2" label="Option 2" />
51
51
  <CheckboxGroupItem value="option3" label="Option 3" />
@@ -224,8 +224,8 @@ export const FormExample: Story = {
224
224
  </CheckboxGroup>
225
225
 
226
226
  <div className="text-sm text-foreground-muted">
227
- <p>Interests: {interests.join(', ') || 'None selected'}</p>
228
- <p>Agreements: {agreements.join(', ') || 'None selected'}</p>
227
+ <p id="form-example-p-interests-interestsj">Interests: {interests.join(', ') || 'None selected'}</p>
228
+ <p id="form-example-p-agreements-agreement">Agreements: {agreements.join(', ') || 'None selected'}</p>
229
229
  </div>
230
230
  </div>
231
231
  );
@@ -238,7 +238,7 @@ export const ResponsiveMatrix: Story = {
238
238
  <div className="space-y-8">
239
239
  {/* Mobile */}
240
240
  <div>
241
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
241
+ <h4 id="responsive-matrix-h4-mobile-375px" className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
242
242
  <div className="w-[375px] border border-dashed border-border p-4">
243
243
  <CheckboxGroup label="Select Options" defaultValue={['option1']}>
244
244
  <CheckboxGroupItem value="option1" label="Option 1" />
@@ -249,7 +249,7 @@ export const ResponsiveMatrix: Story = {
249
249
  </div>
250
250
  {/* Tablet */}
251
251
  <div>
252
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
252
+ <h4 id="responsive-matrix-h4-tablet-768px" className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
253
253
  <div className="w-[768px] border border-dashed border-border p-4">
254
254
  <CheckboxGroup label="Select Sizes" orientation="horizontal" defaultValue={['medium']}>
255
255
  <CheckboxGroupItem value="small" label="Small" />
@@ -261,7 +261,7 @@ export const ResponsiveMatrix: Story = {
261
261
  </div>
262
262
  {/* Desktop */}
263
263
  <div>
264
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
264
+ <h4 id="responsive-matrix-h4-desktop-1280px" className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
265
265
  <div className="w-[1280px] border border-dashed border-border p-4">
266
266
  <div className="grid grid-cols-2 gap-8">
267
267
  <CheckboxGroup label="Features" defaultValue={['basic']}>
@@ -79,7 +79,7 @@ export function CheckboxGroup({
79
79
  >
80
80
  {children}
81
81
  </div>
82
- {error && <p className="mt-2 text-sm text-error">{error}</p>}
82
+ {error && <p id="checkbox-group-p-error" className="mt-2 text-sm text-error">{error}</p>}
83
83
  </div>
84
84
  </CheckboxGroupContext.Provider>
85
85
  );
@@ -109,7 +109,7 @@ export function CheckboxGroupItem({
109
109
  };
110
110
 
111
111
  return (
112
- <Checkbox
112
+ <Checkbox id="checkbox-group-item-checkbox-checkboxgroup" data-sonance-name="checkbox-group"
113
113
  checked={isChecked}
114
114
  onChange={handleChange}
115
115
  disabled={isDisabled}
@@ -145,7 +145,7 @@ export function CheckboxList({
145
145
  className,
146
146
  }: CheckboxListProps) {
147
147
  return (
148
- <CheckboxGroup
148
+ <CheckboxGroup data-sonance-name="checkbox-group"
149
149
  value={value}
150
150
  defaultValue={defaultValue}
151
151
  onValueChange={onValueChange}
@@ -84,11 +84,11 @@ export const DisabledChecked: Story = {
84
84
  export const AllStates: Story = {
85
85
  render: () => (
86
86
  <div className="space-y-4">
87
- <Checkbox label="Unchecked" />
88
- <Checkbox label="Checked" defaultChecked />
89
- <Checkbox label="With description" description="Additional information about this option" />
90
- <Checkbox label="Disabled" disabled />
91
- <Checkbox label="Disabled checked" disabled defaultChecked />
87
+ <Checkbox id="all-states-checkbox" label="Unchecked" />
88
+ <Checkbox id="all-states-checkbox" label="Checked" defaultChecked />
89
+ <Checkbox id="all-states-checkbox" label="With description" description="Additional information about this option" />
90
+ <Checkbox id="all-states-checkbox" label="Disabled" disabled />
91
+ <Checkbox id="all-states-checkbox" label="Disabled checked" disabled defaultChecked />
92
92
  </div>
93
93
  ),
94
94
  };
@@ -98,17 +98,20 @@ export const FormExample: Story = {
98
98
  render: () => (
99
99
  <div className="space-y-6 w-80">
100
100
  <div className="space-y-4">
101
- <h3 className="text-sm font-medium text-foreground">Notification Preferences</h3>
101
+ <h3 id="form-example-h3-notification-prefere" className="text-sm font-medium text-foreground">Notification Preferences</h3>
102
102
  <Checkbox
103
+ id="form-example-checkbox"
103
104
  label="Email notifications"
104
105
  description="Get notified about account activity via email"
105
106
  defaultChecked
106
107
  />
107
108
  <Checkbox
109
+ id="form-example-checkbox"
108
110
  label="SMS notifications"
109
111
  description="Get notified via text message"
110
112
  />
111
113
  <Checkbox
114
+ id="form-example-checkbox"
112
115
  label="Push notifications"
113
116
  description="Get notified on your mobile device"
114
117
  defaultChecked
@@ -124,34 +127,34 @@ export const ResponsiveMatrix: Story = {
124
127
  <div className="space-y-8">
125
128
  {/* Mobile */}
126
129
  <div>
127
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
130
+ <h4 id="responsive-matrix-h4-mobile-375px" className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
128
131
  <div className="w-[375px] border border-dashed border-border p-4 space-y-4">
129
- <Checkbox label="Accept terms and conditions" />
130
- <Checkbox label="Subscribe to newsletter" description="Receive updates about new features" />
131
- <Checkbox label="Remember me" defaultChecked />
132
+ <Checkbox id="responsive-matrix-checkbox" label="Accept terms and conditions" />
133
+ <Checkbox id="responsive-matrix-checkbox" label="Subscribe to newsletter" description="Receive updates about new features" />
134
+ <Checkbox id="responsive-matrix-checkbox" label="Remember me" defaultChecked />
132
135
  </div>
133
136
  </div>
134
137
  {/* Tablet */}
135
138
  <div>
136
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
139
+ <h4 id="responsive-matrix-h4-tablet-768px" className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
137
140
  <div className="w-[768px] border border-dashed border-border p-4">
138
141
  <div className="grid grid-cols-2 gap-4">
139
- <Checkbox label="Email notifications" description="Get notified via email" defaultChecked />
140
- <Checkbox label="SMS notifications" description="Get notified via text" />
141
- <Checkbox label="Push notifications" description="Mobile app notifications" defaultChecked />
142
- <Checkbox label="Marketing emails" description="Product updates and offers" />
142
+ <Checkbox id="responsive-matrix-checkbox" label="Email notifications" description="Get notified via email" defaultChecked />
143
+ <Checkbox id="responsive-matrix-checkbox" label="SMS notifications" description="Get notified via text" />
144
+ <Checkbox id="responsive-matrix-checkbox" label="Push notifications" description="Mobile app notifications" defaultChecked />
145
+ <Checkbox id="responsive-matrix-checkbox" label="Marketing emails" description="Product updates and offers" />
143
146
  </div>
144
147
  </div>
145
148
  </div>
146
149
  {/* Desktop */}
147
150
  <div>
148
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
151
+ <h4 id="responsive-matrix-h4-desktop-1280px" className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
149
152
  <div className="w-[1280px] border border-dashed border-border p-4">
150
153
  <div className="grid grid-cols-4 gap-4">
151
- <Checkbox label="Unchecked" />
152
- <Checkbox label="Checked" defaultChecked />
153
- <Checkbox label="Disabled" disabled />
154
- <Checkbox label="Disabled checked" disabled defaultChecked />
154
+ <Checkbox id="responsive-matrix-checkbox" label="Unchecked" />
155
+ <Checkbox id="responsive-matrix-checkbox" label="Checked" defaultChecked />
156
+ <Checkbox id="responsive-matrix-checkbox" label="Disabled" disabled />
157
+ <Checkbox id="responsive-matrix-checkbox" label="Disabled checked" disabled defaultChecked />
155
158
  </div>
156
159
  </div>
157
160
  </div>
@@ -28,21 +28,28 @@ const getStateStyles = (state?: CheckboxState) => {
28
28
  };
29
29
 
30
30
  export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
31
- ({ className, label, description, id, state, disabled, checked, ...props }, ref) => {
31
+ ({ className, label, description, id, state, disabled, checked, defaultChecked, onChange, style, ...props }, ref) => {
32
32
  const uniqueId = useId();
33
33
  const inputId = id || `checkbox-${uniqueId}`;
34
34
  const isDisabled = disabled || state === "disabled";
35
- const isChecked = checked || state === "checked";
35
+
36
+ // Determine if we're in controlled mode
37
+ const isControlled = checked !== undefined || onChange !== undefined;
38
+ const isCheckedForState = state === "checked";
36
39
 
37
40
  return (
38
- <div className="flex items-start gap-3">
41
+ <div data-sonance-name="checkbox" className="flex items-start gap-3">
39
42
  <div className="relative flex items-center justify-center">
40
43
  <input
41
44
  type="checkbox"
42
45
  id={inputId}
43
46
  ref={ref}
44
47
  disabled={isDisabled}
45
- checked={isChecked}
48
+ style={style}
49
+ {...(isControlled
50
+ ? { checked: checked || isCheckedForState, onChange }
51
+ : { defaultChecked: defaultChecked || isCheckedForState }
52
+ )}
46
53
  className={cn(
47
54
  "peer h-5 w-5 shrink-0 appearance-none border border-border bg-input",
48
55
  "hover:border-border-hover",
@@ -52,7 +59,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
52
59
  "transition-colors duration-150",
53
60
  getStateStyles(state),
54
61
  className
55
- )}
62
+ )} data-sonance-name="checkbox"
56
63
  {...props}
57
64
  />
58
65
  <Check
@@ -73,7 +80,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
73
80
  </label>
74
81
  )}
75
82
  {description && (
76
- <p className="text-xs text-foreground-muted">{description}</p>
83
+ <p id="p-description" className="text-xs text-foreground-muted">{description}</p>
77
84
  )}
78
85
  </div>
79
86
  )}