@auto-engineer/generate-react-client 1.64.0 → 1.66.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 (145) hide show
  1. package/CHANGELOG.md +140 -0
  2. package/dist/starter/.storybook/main.ts +17 -22
  3. package/dist/starter/.storybook/manager-head.html +31 -31
  4. package/dist/starter/.storybook/manager.ts +133 -133
  5. package/dist/starter/.storybook/preview-head.html +12 -12
  6. package/dist/starter/.storybook/preview.tsx +79 -79
  7. package/dist/starter/biome.json +126 -0
  8. package/dist/starter/codegen.ts +11 -11
  9. package/dist/starter/components.json +27 -27
  10. package/dist/starter/package.json +86 -80
  11. package/dist/starter/public/mockServiceWorker.js +261 -261
  12. package/dist/starter/scripts/build-component-db.ts +17 -20
  13. package/dist/starter/src/App.tsx +15 -17
  14. package/dist/starter/src/components/ui/Accordion.stories.tsx +35 -35
  15. package/dist/starter/src/components/ui/Accordion.tsx +33 -33
  16. package/dist/starter/src/components/ui/Alert.stories.tsx +15 -15
  17. package/dist/starter/src/components/ui/Alert.tsx +32 -32
  18. package/dist/starter/src/components/ui/AlertDialog.stories.tsx +50 -50
  19. package/dist/starter/src/components/ui/AlertDialog.tsx +114 -115
  20. package/dist/starter/src/components/ui/AspectRatio.stories.tsx +20 -20
  21. package/dist/starter/src/components/ui/AspectRatio.tsx +1 -1
  22. package/dist/starter/src/components/ui/Avatar.stories.tsx +27 -27
  23. package/dist/starter/src/components/ui/Avatar.tsx +63 -63
  24. package/dist/starter/src/components/ui/Badge.stories.tsx +14 -14
  25. package/dist/starter/src/components/ui/Badge.tsx +27 -27
  26. package/dist/starter/src/components/ui/Breadcrumb.stories.tsx +38 -38
  27. package/dist/starter/src/components/ui/Breadcrumb.tsx +63 -62
  28. package/dist/starter/src/components/ui/Button.stories.tsx +55 -55
  29. package/dist/starter/src/components/ui/Button.tsx +49 -49
  30. package/dist/starter/src/components/ui/ButtonGroup.stories.tsx +17 -17
  31. package/dist/starter/src/components/ui/ButtonGroup.tsx +52 -53
  32. package/dist/starter/src/components/ui/Calendar.stories.tsx +20 -19
  33. package/dist/starter/src/components/ui/Calendar.tsx +142 -143
  34. package/dist/starter/src/components/ui/Card.stories.tsx +29 -29
  35. package/dist/starter/src/components/ui/Card.tsx +31 -31
  36. package/dist/starter/src/components/ui/Carousel.stories.tsx +41 -41
  37. package/dist/starter/src/components/ui/Carousel.tsx +171 -172
  38. package/dist/starter/src/components/ui/Chart.stories.tsx +21 -21
  39. package/dist/starter/src/components/ui/Chart.tsx +244 -247
  40. package/dist/starter/src/components/ui/Checkbox.stories.tsx +11 -11
  41. package/dist/starter/src/components/ui/Checkbox.tsx +18 -18
  42. package/dist/starter/src/components/ui/Collapsible.stories.tsx +40 -40
  43. package/dist/starter/src/components/ui/Collapsible.tsx +3 -3
  44. package/dist/starter/src/components/ui/Combobox.stories.tsx +48 -48
  45. package/dist/starter/src/components/ui/Combobox.tsx +204 -205
  46. package/dist/starter/src/components/ui/Command.stories.tsx +55 -55
  47. package/dist/starter/src/components/ui/Command.tsx +102 -103
  48. package/dist/starter/src/components/ui/ContextMenu.stories.tsx +52 -52
  49. package/dist/starter/src/components/ui/ContextMenu.tsx +151 -151
  50. package/dist/starter/src/components/ui/DesignSystem-Colors.stories.tsx +92 -92
  51. package/dist/starter/src/components/ui/DesignSystem-Layout.stories.tsx +139 -139
  52. package/dist/starter/src/components/ui/DesignSystem-Overview.stories.tsx +676 -657
  53. package/dist/starter/src/components/ui/DesignSystem-Typography.stories.tsx +59 -59
  54. package/dist/starter/src/components/ui/Dialog.stories.tsx +56 -56
  55. package/dist/starter/src/components/ui/Dialog.tsx +97 -98
  56. package/dist/starter/src/components/ui/Direction.stories.tsx +20 -20
  57. package/dist/starter/src/components/ui/Direction.tsx +7 -7
  58. package/dist/starter/src/components/ui/Drawer.stories.tsx +54 -54
  59. package/dist/starter/src/components/ui/Drawer.tsx +70 -70
  60. package/dist/starter/src/components/ui/DropdownMenu.stories.tsx +58 -58
  61. package/dist/starter/src/components/ui/DropdownMenu.tsx +157 -157
  62. package/dist/starter/src/components/ui/Empty.stories.tsx +22 -22
  63. package/dist/starter/src/components/ui/Empty.tsx +58 -58
  64. package/dist/starter/src/components/ui/Field.stories.tsx +31 -31
  65. package/dist/starter/src/components/ui/Field.tsx +180 -181
  66. package/dist/starter/src/components/ui/Form.stories.tsx +29 -29
  67. package/dist/starter/src/components/ui/Form.tsx +93 -96
  68. package/dist/starter/src/components/ui/HoverCard.stories.tsx +34 -34
  69. package/dist/starter/src/components/ui/HoverCard.tsx +21 -21
  70. package/dist/starter/src/components/ui/Input.stories.tsx +18 -18
  71. package/dist/starter/src/components/ui/Input.tsx +14 -14
  72. package/dist/starter/src/components/ui/InputGroup.stories.tsx +34 -34
  73. package/dist/starter/src/components/ui/InputGroup.tsx +110 -111
  74. package/dist/starter/src/components/ui/InputOTP.stories.tsx +28 -28
  75. package/dist/starter/src/components/ui/InputOTP.tsx +43 -43
  76. package/dist/starter/src/components/ui/Item.stories.tsx +45 -45
  77. package/dist/starter/src/components/ui/Item.tsx +113 -114
  78. package/dist/starter/src/components/ui/Kbd.stories.tsx +31 -31
  79. package/dist/starter/src/components/ui/Kbd.tsx +11 -11
  80. package/dist/starter/src/components/ui/Label.stories.tsx +62 -62
  81. package/dist/starter/src/components/ui/Label.tsx +26 -25
  82. package/dist/starter/src/components/ui/Menubar.stories.tsx +62 -62
  83. package/dist/starter/src/components/ui/Menubar.tsx +173 -173
  84. package/dist/starter/src/components/ui/NativeSelect.stories.tsx +26 -26
  85. package/dist/starter/src/components/ui/NativeSelect.tsx +29 -29
  86. package/dist/starter/src/components/ui/NavigationMenu.stories.tsx +64 -64
  87. package/dist/starter/src/components/ui/NavigationMenu.tsx +103 -103
  88. package/dist/starter/src/components/ui/Pagination.stories.tsx +61 -61
  89. package/dist/starter/src/components/ui/Pagination.tsx +69 -71
  90. package/dist/starter/src/components/ui/Popover.stories.tsx +38 -38
  91. package/dist/starter/src/components/ui/Popover.tsx +25 -25
  92. package/dist/starter/src/components/ui/Progress.stories.tsx +9 -9
  93. package/dist/starter/src/components/ui/Progress.tsx +14 -14
  94. package/dist/starter/src/components/ui/RadioGroup.stories.tsx +35 -35
  95. package/dist/starter/src/components/ui/RadioGroup.tsx +19 -19
  96. package/dist/starter/src/components/ui/Resizable.stories.tsx +54 -54
  97. package/dist/starter/src/components/ui/Resizable.tsx +29 -29
  98. package/dist/starter/src/components/ui/ScrollArea.stories.tsx +27 -27
  99. package/dist/starter/src/components/ui/ScrollArea.tsx +34 -34
  100. package/dist/starter/src/components/ui/Select.stories.tsx +43 -43
  101. package/dist/starter/src/components/ui/Select.tsx +120 -120
  102. package/dist/starter/src/components/ui/Separator.stories.tsx +27 -27
  103. package/dist/starter/src/components/ui/Separator.tsx +17 -17
  104. package/dist/starter/src/components/ui/Sheet.stories.tsx +53 -53
  105. package/dist/starter/src/components/ui/Sheet.tsx +69 -69
  106. package/dist/starter/src/components/ui/Sidebar.stories.tsx +77 -77
  107. package/dist/starter/src/components/ui/Sidebar.tsx +563 -564
  108. package/dist/starter/src/components/ui/Skeleton.stories.tsx +25 -25
  109. package/dist/starter/src/components/ui/Skeleton.tsx +1 -1
  110. package/dist/starter/src/components/ui/Slider.stories.tsx +5 -5
  111. package/dist/starter/src/components/ui/Slider.tsx +45 -44
  112. package/dist/starter/src/components/ui/Sonner.stories.tsx +32 -32
  113. package/dist/starter/src/components/ui/Sonner.tsx +23 -23
  114. package/dist/starter/src/components/ui/Spinner.stories.tsx +8 -8
  115. package/dist/starter/src/components/ui/Spinner.tsx +1 -1
  116. package/dist/starter/src/components/ui/Switch.stories.tsx +16 -17
  117. package/dist/starter/src/components/ui/Switch.tsx +24 -24
  118. package/dist/starter/src/components/ui/Table.stories.tsx +50 -50
  119. package/dist/starter/src/components/ui/Table.tsx +45 -45
  120. package/dist/starter/src/components/ui/Tabs.stories.tsx +39 -39
  121. package/dist/starter/src/components/ui/Tabs.tsx +47 -47
  122. package/dist/starter/src/components/ui/Textarea.stories.tsx +9 -9
  123. package/dist/starter/src/components/ui/Textarea.tsx +11 -11
  124. package/dist/starter/src/components/ui/Toast.stories.tsx +77 -77
  125. package/dist/starter/src/components/ui/Toast.tsx +75 -75
  126. package/dist/starter/src/components/ui/Toaster.tsx +17 -19
  127. package/dist/starter/src/components/ui/Toggle.stories.tsx +20 -20
  128. package/dist/starter/src/components/ui/Toggle.tsx +26 -26
  129. package/dist/starter/src/components/ui/ToggleGroup.stories.tsx +41 -41
  130. package/dist/starter/src/components/ui/ToggleGroup.tsx +61 -62
  131. package/dist/starter/src/components/ui/Tooltip.stories.tsx +26 -26
  132. package/dist/starter/src/components/ui/Tooltip.tsx +24 -24
  133. package/dist/starter/src/gql/execute.ts +1 -1
  134. package/dist/starter/src/gql/fragment-masking.ts +1 -1
  135. package/dist/starter/src/gql/graphql.ts +3 -0
  136. package/dist/starter/src/hooks/use-mobile.ts +11 -11
  137. package/dist/starter/src/hooks/use-toast.ts +135 -135
  138. package/dist/starter/src/index.css +105 -105
  139. package/dist/starter/src/lib/utils.ts +1 -1
  140. package/dist/starter/src/main.tsx +4 -1
  141. package/dist/starter/tsconfig.app.json +24 -24
  142. package/dist/starter/tsconfig.json +8 -8
  143. package/dist/starter/vite.config.ts +38 -37
  144. package/dist/tsconfig.tsbuildinfo +1 -1
  145. package/package.json +3 -3
@@ -7,26 +7,26 @@ import { cn } from '@/lib/utils';
7
7
  const THEMES = { light: '', dark: '.dark' } as const;
8
8
 
9
9
  export type ChartConfig = {
10
- [k in string]: {
11
- label?: React.ReactNode;
12
- icon?: React.ComponentType;
13
- } & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> });
10
+ [k in string]: {
11
+ label?: React.ReactNode;
12
+ icon?: React.ComponentType;
13
+ } & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> });
14
14
  };
15
15
 
16
16
  type ChartContextProps = {
17
- config: ChartConfig;
17
+ config: ChartConfig;
18
18
  };
19
19
 
20
20
  const ChartContext = React.createContext<ChartContextProps | null>(null);
21
21
 
22
22
  function useChart() {
23
- const context = React.useContext(ChartContext);
23
+ const context = React.useContext(ChartContext);
24
24
 
25
- if (!context) {
26
- throw new Error('useChart must be used within a <ChartContainer />');
27
- }
25
+ if (!context) {
26
+ throw new Error('useChart must be used within a <ChartContainer />');
27
+ }
28
28
 
29
- return context;
29
+ return context;
30
30
  }
31
31
 
32
32
  /**
@@ -34,63 +34,64 @@ function useChart() {
34
34
  * Pass a `config` object mapping data keys to labels, colors, and optional icons.
35
35
  */
36
36
  function ChartContainer({
37
- id,
38
- className,
39
- children,
40
- config,
41
- ...props
37
+ id,
38
+ className,
39
+ children,
40
+ config,
41
+ ...props
42
42
  }: React.ComponentProps<'div'> & {
43
- config: ChartConfig;
44
- children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>['children'];
43
+ config: ChartConfig;
44
+ children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>['children'];
45
45
  }) {
46
- const uniqueId = React.useId();
47
- const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`;
48
-
49
- return (
50
- <ChartContext.Provider value={{ config }}>
51
- <div
52
- data-slot="chart"
53
- data-chart={chartId}
54
- className={cn(
55
- "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
56
- className,
57
- )}
58
- {...props}
59
- >
60
- <ChartStyle id={chartId} config={config} />
61
- <RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>
62
- </div>
63
- </ChartContext.Provider>
64
- );
46
+ const uniqueId = React.useId();
47
+ const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`;
48
+
49
+ return (
50
+ <ChartContext.Provider value={{ config }}>
51
+ <div
52
+ data-slot="chart"
53
+ data-chart={chartId}
54
+ className={cn(
55
+ "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
56
+ className,
57
+ )}
58
+ {...props}
59
+ >
60
+ <ChartStyle id={chartId} config={config} />
61
+ <RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>
62
+ </div>
63
+ </ChartContext.Provider>
64
+ );
65
65
  }
66
66
 
67
67
  const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
68
- const colorConfig = Object.entries(config).filter(([, config]) => config.theme || config.color);
69
-
70
- if (!colorConfig.length) {
71
- return null;
72
- }
73
-
74
- return (
75
- <style
76
- dangerouslySetInnerHTML={{
77
- __html: Object.entries(THEMES)
78
- .map(
79
- ([theme, prefix]) => `
68
+ const colorConfig = Object.entries(config).filter(([, config]) => config.theme || config.color);
69
+
70
+ if (!colorConfig.length) {
71
+ return null;
72
+ }
73
+
74
+ return (
75
+ <style
76
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: Dynamic CSS for chart theme colors - content derived from config, not user input
77
+ dangerouslySetInnerHTML={{
78
+ __html: Object.entries(THEMES)
79
+ .map(
80
+ ([theme, prefix]) => `
80
81
  ${prefix} [data-chart=${id}] {
81
82
  ${colorConfig
82
- .map(([key, itemConfig]) => {
83
- const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;
84
- return color ? ` --color-${key}: ${color};` : null;
85
- })
86
- .join('\n')}
83
+ .map(([key, itemConfig]) => {
84
+ const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;
85
+ return color ? ` --color-${key}: ${color};` : null;
86
+ })
87
+ .join('\n')}
87
88
  }
88
89
  `,
89
- )
90
- .join('\n'),
91
- }}
92
- />
93
- );
90
+ )
91
+ .join('\n'),
92
+ }}
93
+ />
94
+ );
94
95
  };
95
96
 
96
97
  const ChartTooltip = RechartsPrimitive.Tooltip;
@@ -100,206 +101,202 @@ const ChartTooltip = RechartsPrimitive.Tooltip;
100
101
  * Supports dot, line, and dashed indicator styles.
101
102
  */
102
103
  function ChartTooltipContent({
103
- active,
104
- payload,
105
- className,
106
- indicator = 'dot',
107
- hideLabel = false,
108
- hideIndicator = false,
109
- label,
110
- labelFormatter,
111
- labelClassName,
112
- formatter,
113
- color,
114
- nameKey,
115
- labelKey,
104
+ active,
105
+ payload,
106
+ className,
107
+ indicator = 'dot',
108
+ hideLabel = false,
109
+ hideIndicator = false,
110
+ label,
111
+ labelFormatter,
112
+ labelClassName,
113
+ formatter,
114
+ color,
115
+ nameKey,
116
+ labelKey,
116
117
  }: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
117
- React.ComponentProps<'div'> & {
118
- hideLabel?: boolean;
119
- hideIndicator?: boolean;
120
- indicator?: 'line' | 'dot' | 'dashed';
121
- nameKey?: string;
122
- labelKey?: string;
123
- }) {
124
- const { config } = useChart();
125
-
126
- const tooltipLabel = React.useMemo(() => {
127
- if (hideLabel || !payload?.length) {
128
- return null;
129
- }
130
-
131
- const [item] = payload;
132
- const key = `${labelKey || item?.dataKey || item?.name || 'value'}`;
133
- const itemConfig = getPayloadConfigFromPayload(config, item, key);
134
- const value =
135
- !labelKey && typeof label === 'string' ? config[label as keyof typeof config]?.label || label : itemConfig?.label;
136
-
137
- if (labelFormatter) {
138
- return <div className={cn('font-medium', labelClassName)}>{labelFormatter(value, payload)}</div>;
139
- }
140
-
141
- if (!value) {
142
- return null;
143
- }
144
-
145
- return <div className={cn('font-medium', labelClassName)}>{value}</div>;
146
- }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
147
-
148
- if (!active || !payload?.length) {
149
- return null;
150
- }
151
-
152
- const nestLabel = payload.length === 1 && indicator !== 'dot';
153
-
154
- return (
155
- <div
156
- className={cn(
157
- 'border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl',
158
- className,
159
- )}
160
- >
161
- {!nestLabel ? tooltipLabel : null}
162
- <div className="grid gap-1.5">
163
- {payload
164
- .filter((item) => item.type !== 'none')
165
- .map((item, index) => {
166
- const key = `${nameKey || item.name || item.dataKey || 'value'}`;
167
- const itemConfig = getPayloadConfigFromPayload(config, item, key);
168
- const indicatorColor = color || item.payload.fill || item.color;
169
-
170
- return (
171
- <div
172
- key={item.dataKey}
173
- className={cn(
174
- '[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5',
175
- indicator === 'dot' && 'items-center',
176
- )}
177
- >
178
- {formatter && item?.value !== undefined && item.name ? (
179
- formatter(item.value, item.name, item, index, item.payload)
180
- ) : (
181
- <>
182
- {itemConfig?.icon ? (
183
- <itemConfig.icon />
184
- ) : (
185
- !hideIndicator && (
186
- <div
187
- className={cn('shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)', {
188
- 'h-2.5 w-2.5': indicator === 'dot',
189
- 'w-1': indicator === 'line',
190
- 'w-0 border-[1.5px] border-dashed bg-transparent': indicator === 'dashed',
191
- 'my-0.5': nestLabel && indicator === 'dashed',
192
- })}
193
- style={
194
- {
195
- '--color-bg': indicatorColor,
196
- '--color-border': indicatorColor,
197
- } as React.CSSProperties
198
- }
199
- />
200
- )
201
- )}
202
- <div
203
- className={cn(
204
- 'flex flex-1 justify-between leading-none',
205
- nestLabel ? 'items-end' : 'items-center',
206
- )}
207
- >
208
- <div className="grid gap-1.5">
209
- {nestLabel ? tooltipLabel : null}
210
- <span className="text-muted-foreground">{itemConfig?.label || item.name}</span>
211
- </div>
212
- {item.value && (
213
- <span className="text-foreground font-mono font-medium tabular-nums">
214
- {item.value.toLocaleString()}
215
- </span>
216
- )}
217
- </div>
218
- </>
219
- )}
220
- </div>
221
- );
222
- })}
223
- </div>
224
- </div>
225
- );
118
+ React.ComponentProps<'div'> & {
119
+ hideLabel?: boolean;
120
+ hideIndicator?: boolean;
121
+ indicator?: 'line' | 'dot' | 'dashed';
122
+ nameKey?: string;
123
+ labelKey?: string;
124
+ }) {
125
+ const { config } = useChart();
126
+
127
+ const tooltipLabel = React.useMemo(() => {
128
+ if (hideLabel || !payload?.length) {
129
+ return null;
130
+ }
131
+
132
+ const [item] = payload;
133
+ const key = `${labelKey || item?.dataKey || item?.name || 'value'}`;
134
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
135
+ const value =
136
+ !labelKey && typeof label === 'string' ? config[label as keyof typeof config]?.label || label : itemConfig?.label;
137
+
138
+ if (labelFormatter) {
139
+ return <div className={cn('font-medium', labelClassName)}>{labelFormatter(value, payload)}</div>;
140
+ }
141
+
142
+ if (!value) {
143
+ return null;
144
+ }
145
+
146
+ return <div className={cn('font-medium', labelClassName)}>{value}</div>;
147
+ }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
148
+
149
+ if (!active || !payload?.length) {
150
+ return null;
151
+ }
152
+
153
+ const nestLabel = payload.length === 1 && indicator !== 'dot';
154
+
155
+ return (
156
+ <div
157
+ className={cn(
158
+ 'border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl',
159
+ className,
160
+ )}
161
+ >
162
+ {!nestLabel ? tooltipLabel : null}
163
+ <div className="grid gap-1.5">
164
+ {payload
165
+ .filter((item) => item.type !== 'none')
166
+ .map((item, index) => {
167
+ const key = `${nameKey || item.name || item.dataKey || 'value'}`;
168
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
169
+ const indicatorColor = color || item.payload.fill || item.color;
170
+
171
+ return (
172
+ <div
173
+ key={item.dataKey}
174
+ className={cn(
175
+ '[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5',
176
+ indicator === 'dot' && 'items-center',
177
+ )}
178
+ >
179
+ {formatter && item?.value !== undefined && item.name ? (
180
+ formatter(item.value, item.name, item, index, item.payload)
181
+ ) : (
182
+ <>
183
+ {itemConfig?.icon ? (
184
+ <itemConfig.icon />
185
+ ) : (
186
+ !hideIndicator && (
187
+ <div
188
+ className={cn('shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)', {
189
+ 'h-2.5 w-2.5': indicator === 'dot',
190
+ 'w-1': indicator === 'line',
191
+ 'w-0 border-[1.5px] border-dashed bg-transparent': indicator === 'dashed',
192
+ 'my-0.5': nestLabel && indicator === 'dashed',
193
+ })}
194
+ style={
195
+ {
196
+ '--color-bg': indicatorColor,
197
+ '--color-border': indicatorColor,
198
+ } as React.CSSProperties
199
+ }
200
+ />
201
+ )
202
+ )}
203
+ <div
204
+ className={cn(
205
+ 'flex flex-1 justify-between leading-none',
206
+ nestLabel ? 'items-end' : 'items-center',
207
+ )}
208
+ >
209
+ <div className="grid gap-1.5">
210
+ {nestLabel ? tooltipLabel : null}
211
+ <span className="text-muted-foreground">{itemConfig?.label || item.name}</span>
212
+ </div>
213
+ {item.value && (
214
+ <span className="text-foreground font-mono font-medium tabular-nums">
215
+ {item.value.toLocaleString()}
216
+ </span>
217
+ )}
218
+ </div>
219
+ </>
220
+ )}
221
+ </div>
222
+ );
223
+ })}
224
+ </div>
225
+ </div>
226
+ );
226
227
  }
227
228
 
228
229
  const ChartLegend = RechartsPrimitive.Legend;
229
230
 
230
231
  /** A styled legend content component for Recharts that reads labels, colors, and icons from ChartConfig. */
231
232
  function ChartLegendContent({
232
- className,
233
- hideIcon = false,
234
- payload,
235
- verticalAlign = 'bottom',
236
- nameKey,
233
+ className,
234
+ hideIcon = false,
235
+ payload,
236
+ verticalAlign = 'bottom',
237
+ nameKey,
237
238
  }: React.ComponentProps<'div'> &
238
- Pick<RechartsPrimitive.LegendProps, 'payload' | 'verticalAlign'> & {
239
- hideIcon?: boolean;
240
- nameKey?: string;
241
- }) {
242
- const { config } = useChart();
243
-
244
- if (!payload?.length) {
245
- return null;
246
- }
247
-
248
- return (
249
- <div className={cn('flex items-center justify-center gap-4', verticalAlign === 'top' ? 'pb-3' : 'pt-3', className)}>
250
- {payload
251
- .filter((item) => item.type !== 'none')
252
- .map((item) => {
253
- const key = `${nameKey || item.dataKey || 'value'}`;
254
- const itemConfig = getPayloadConfigFromPayload(config, item, key);
255
-
256
- return (
257
- <div
258
- key={item.value}
259
- className={cn('[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3')}
260
- >
261
- {itemConfig?.icon && !hideIcon ? (
262
- <itemConfig.icon />
263
- ) : (
264
- <div
265
- className="h-2 w-2 shrink-0 rounded-[2px]"
266
- style={{
267
- backgroundColor: item.color,
268
- }}
269
- />
270
- )}
271
- {itemConfig?.label}
272
- </div>
273
- );
274
- })}
275
- </div>
276
- );
239
+ Pick<RechartsPrimitive.LegendProps, 'payload' | 'verticalAlign'> & {
240
+ hideIcon?: boolean;
241
+ nameKey?: string;
242
+ }) {
243
+ const { config } = useChart();
244
+
245
+ if (!payload?.length) {
246
+ return null;
247
+ }
248
+
249
+ return (
250
+ <div className={cn('flex items-center justify-center gap-4', verticalAlign === 'top' ? 'pb-3' : 'pt-3', className)}>
251
+ {payload
252
+ .filter((item) => item.type !== 'none')
253
+ .map((item) => {
254
+ const key = `${nameKey || item.dataKey || 'value'}`;
255
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
256
+
257
+ return (
258
+ <div
259
+ key={item.value}
260
+ className={cn('[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3')}
261
+ >
262
+ {itemConfig?.icon && !hideIcon ? (
263
+ <itemConfig.icon />
264
+ ) : (
265
+ <div
266
+ className="h-2 w-2 shrink-0 rounded-[2px]"
267
+ style={{
268
+ backgroundColor: item.color,
269
+ }}
270
+ />
271
+ )}
272
+ {itemConfig?.label}
273
+ </div>
274
+ );
275
+ })}
276
+ </div>
277
+ );
277
278
  }
278
279
 
279
280
  // Helper to extract item config from a payload.
280
- function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {
281
- if (typeof payload !== 'object' || payload === null) {
282
- return undefined;
283
- }
284
-
285
- const payloadPayload =
286
- 'payload' in payload && typeof payload.payload === 'object' && payload.payload !== null
287
- ? payload.payload
288
- : undefined;
289
-
290
- let configLabelKey: string = key;
291
-
292
- if (key in payload && typeof payload[key as keyof typeof payload] === 'string') {
293
- configLabelKey = payload[key as keyof typeof payload] as string;
294
- } else if (
295
- payloadPayload &&
296
- key in payloadPayload &&
297
- typeof payloadPayload[key as keyof typeof payloadPayload] === 'string'
298
- ) {
299
- configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;
300
- }
301
-
302
- return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
281
+ function getPayloadConfigFromPayload(
282
+ config: ChartConfig,
283
+ payload: Record<string, string | number | Record<string, string>>,
284
+ key: string,
285
+ ) {
286
+ const payloadPayload =
287
+ 'payload' in payload && typeof payload.payload === 'object' && payload.payload !== null
288
+ ? (payload.payload as Record<string, string>)
289
+ : undefined;
290
+
291
+ let configLabelKey: string = key;
292
+
293
+ if (key in payload && typeof payload[key] === 'string') {
294
+ configLabelKey = payload[key] as string;
295
+ } else if (payloadPayload && key in payloadPayload && typeof payloadPayload[key] === 'string') {
296
+ configLabelKey = payloadPayload[key] as string;
297
+ }
298
+
299
+ return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
303
300
  }
304
301
 
305
302
  export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle };
@@ -3,33 +3,33 @@ import { Checkbox } from '@/components/ui/Checkbox';
3
3
  import { Label } from '@/components/ui/Label';
4
4
 
5
5
  const meta: Meta<typeof Checkbox> = {
6
- title: 'UI Components/Checkbox',
7
- component: Checkbox,
6
+ title: 'UI Components/Checkbox',
7
+ component: Checkbox,
8
8
  };
9
9
  export default meta;
10
10
  type Story = StoryObj<typeof Checkbox>;
11
11
 
12
12
  /** Shows an unchecked checkbox in its default state. */
13
13
  export const Default: Story = {
14
- render: () => <Checkbox />,
14
+ render: () => <Checkbox />,
15
15
  };
16
16
 
17
17
  /** Shows a checkbox that starts in the checked state. */
18
18
  export const Checked: Story = {
19
- render: () => <Checkbox defaultChecked />,
19
+ render: () => <Checkbox defaultChecked />,
20
20
  };
21
21
 
22
22
  /** Shows a disabled checkbox that cannot be interacted with. */
23
23
  export const Disabled: Story = {
24
- render: () => <Checkbox disabled />,
24
+ render: () => <Checkbox disabled />,
25
25
  };
26
26
 
27
27
  /** Shows a checkbox paired with a Label component for accessible form usage. */
28
28
  export const WithLabel: Story = {
29
- render: () => (
30
- <div className="flex items-center space-x-2">
31
- <Checkbox id="terms" />
32
- <Label htmlFor="terms">Accept terms and conditions</Label>
33
- </div>
34
- ),
29
+ render: () => (
30
+ <div className="flex items-center space-x-2">
31
+ <Checkbox id="terms" />
32
+ <Label htmlFor="terms">Accept terms and conditions</Label>
33
+ </div>
34
+ ),
35
35
  };
@@ -1,30 +1,30 @@
1
1
  'use client';
2
2
 
3
- import * as React from 'react';
4
3
  import { CheckIcon } from 'lucide-react';
5
4
  import { Checkbox as CheckboxPrimitive } from 'radix-ui';
5
+ import type * as React from 'react';
6
6
 
7
7
  import { cn } from '@/lib/utils';
8
8
 
9
9
  /** A toggleable boolean input with check icon. Supports checked, unchecked, and indeterminate states. Pair with Label for forms. */
10
10
  function Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
11
- return (
12
- <CheckboxPrimitive.Root
13
- data-slot="checkbox"
14
- className={cn(
15
- 'peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
16
- className,
17
- )}
18
- {...props}
19
- >
20
- <CheckboxPrimitive.Indicator
21
- data-slot="checkbox-indicator"
22
- className="grid place-content-center text-current transition-none"
23
- >
24
- <CheckIcon className="size-3.5" />
25
- </CheckboxPrimitive.Indicator>
26
- </CheckboxPrimitive.Root>
27
- );
11
+ return (
12
+ <CheckboxPrimitive.Root
13
+ data-slot="checkbox"
14
+ className={cn(
15
+ 'peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
16
+ className,
17
+ )}
18
+ {...props}
19
+ >
20
+ <CheckboxPrimitive.Indicator
21
+ data-slot="checkbox-indicator"
22
+ className="grid place-content-center text-current transition-none"
23
+ >
24
+ <CheckIcon className="size-3.5" />
25
+ </CheckboxPrimitive.Indicator>
26
+ </CheckboxPrimitive.Root>
27
+ );
28
28
  }
29
29
 
30
30
  export { Checkbox };