@otfdashkit/ui 0.1.3 → 0.1.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@otfdashkit/ui",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "OTF UI — production-quality React component library. 98 primitives + blocks + layouts + charts on Radix + Tailwind v4. Cross-platform parity API with @otfdashkit/ui-native.",
5
5
  "keywords": [
6
6
  "otfdashkit",
@@ -33,9 +33,14 @@
33
33
  "types": "dist/index.d.ts",
34
34
  "exports": {
35
35
  ".": {
36
- "types": "./dist/index.d.ts",
37
- "import": "./dist/index.mjs",
38
- "require": "./dist/index.js"
36
+ "import": {
37
+ "types": "./dist/index.d.mts",
38
+ "default": "./dist/index.mjs"
39
+ },
40
+ "require": {
41
+ "types": "./dist/index.d.ts",
42
+ "default": "./dist/index.js"
43
+ }
39
44
  },
40
45
  "./styles": "./src/styles.css",
41
46
  "./themes/*": "./src/themes/*.css"
@@ -44,6 +49,7 @@
44
49
  "dist/**/*.js",
45
50
  "dist/**/*.mjs",
46
51
  "dist/**/*.d.ts",
52
+ "dist/**/*.d.mts",
47
53
  "src/**/*.tsx",
48
54
  "src/**/*.ts",
49
55
  "src/themes/*.css",
@@ -138,6 +144,7 @@
138
144
  "build": "tsup",
139
145
  "dev": "tsup --watch",
140
146
  "type-check": "tsc --noEmit",
147
+ "check:types-consumer": "bash scripts/check-types-consumer.sh",
141
148
  "clean": "rm -rf dist"
142
149
  }
143
150
  }
package/src/index.ts CHANGED
@@ -167,7 +167,8 @@ export * from './text'
167
167
  export * from './backgrounds'
168
168
  export * from './headers'
169
169
  export * from './motion'
170
- // export * from './data' // conflicts with components/data-grid (DataGrid)
170
+ // './data' is not star-exported: its DataGrid name collides with components/data-grid.
171
+ export { DataGrid as AdvancedDataGrid, type DataGridProps as AdvancedDataGridProps } from './data/DataGrid'
171
172
  export * from './data/DataList'
172
173
  export * from './data/EventCalendar'
173
174
  export * from './data/GridList'
@@ -1,131 +1,2 @@
1
- import React from 'react'
2
- import { Slot } from '@radix-ui/react-slot'
3
- import { cva, type VariantProps } from 'class-variance-authority'
4
- import { cn } from '../utils/cn'
5
-
6
- const buttonVariants = cva(
7
- [
8
- 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium font-sans',
9
- 'transition-colors duration-[var(--duration-fast,100ms)]',
10
- 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
11
- 'disabled:pointer-events-none disabled:opacity-50',
12
- 'select-none',
13
- ].join(' '),
14
- {
15
- variants: {
16
- variant: {
17
- default:
18
- 'bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80',
19
- secondary:
20
- 'bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70',
21
- outline:
22
- 'border border-border bg-transparent hover:bg-accent hover:text-accent-foreground active:bg-accent/70',
23
- ghost:
24
- 'hover:bg-accent hover:text-accent-foreground active:bg-accent/70',
25
- destructive:
26
- 'bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80',
27
- link:
28
- 'text-primary underline-offset-4 hover:underline',
29
- /** Gradient stack — premium variant */
30
- gradient: [
31
- 'bg-gradient-to-b from-primary to-[hsl(var(--primary)/0.8)]',
32
- 'text-primary-foreground',
33
- 'shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_-1px_0_0_rgba(0,0,0,0.25)_inset]',
34
- 'hover:from-[hsl(var(--primary)/0.9)] hover:to-[hsl(var(--primary)/0.7)]',
35
- 'active:from-[hsl(var(--primary)/0.8)] active:to-[hsl(var(--primary)/0.6)]',
36
- ].join(' '),
37
- /** Texture — nested ring surface as a button */
38
- texture: [
39
- 'relative border border-border/40',
40
- 'bg-gradient-to-b from-card to-[hsl(var(--card)/0.9)]',
41
- 'text-foreground',
42
- 'shadow-[0_0_0_1px_hsl(var(--border)/0.2)_inset]',
43
- 'hover:border-border/60 hover:bg-accent',
44
- 'active:shadow-none',
45
- ].join(' '),
46
- /** Shimmer — animated conic gradient sweep with a hover highlight.
47
- * Loud + premium; reserve for primary CTAs and pricing pages, not
48
- * for in-product UI (gets tiring fast). Cross-platform parity with
49
- * @otfdashkit/ui-native via the same `shimmer` variant key. */
50
- shimmer: [
51
- 'relative isolate overflow-hidden',
52
- 'bg-primary text-primary-foreground',
53
- 'shadow-[0_1px_0_rgba(255,255,255,0.18)_inset,0_-1px_0_rgba(0,0,0,0.25)_inset]',
54
- 'hover:bg-primary/95',
55
- // Continuous conic-gradient sweep behind the surface.
56
- 'before:absolute before:inset-[-1px] before:rounded-[inherit] before:-z-10',
57
- 'before:bg-[conic-gradient(from_0deg,transparent_0deg,transparent_70deg,rgba(255,255,255,0.55)_90deg,transparent_110deg,transparent_360deg)]',
58
- 'before:animate-[shimmer-rotate_2.6s_linear_infinite]',
59
- // Diagonal gloss that sweeps on hover.
60
- 'after:absolute after:inset-0 after:rounded-[inherit] after:pointer-events-none',
61
- 'after:bg-[linear-gradient(115deg,transparent_30%,rgba(255,255,255,0.22)_50%,transparent_70%)]',
62
- 'after:translate-x-[-120%] hover:after:translate-x-[120%] after:transition-transform after:duration-[900ms] after:ease-out',
63
- ].join(' '),
64
- },
65
- size: {
66
- sm: 'h-8 px-3 text-xs rounded-md',
67
- default: 'h-9 px-4 py-2',
68
- lg: 'h-11 px-6 text-base',
69
- xl: 'h-12 px-8 text-base',
70
- icon: 'h-9 w-9 p-0',
71
- 'icon-sm': 'h-7 w-7 p-0 text-xs',
72
- },
73
- },
74
- defaultVariants: { variant: 'default', size: 'default' },
75
- }
76
- )
77
-
78
- export interface ButtonProps
79
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
80
- VariantProps<typeof buttonVariants> {
81
- asChild?: boolean
82
- }
83
-
84
- export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
85
- ({ className, variant, size, asChild = false, ...props }, ref) => {
86
- const Comp = asChild ? Slot : 'button'
87
- return (
88
- <Comp
89
- data-slot="button"
90
- className={cn(buttonVariants({ variant, size }), className)}
91
- ref={ref}
92
- {...props}
93
- />
94
- )
95
- }
96
- )
97
- Button.displayName = 'Button'
98
-
99
- export interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
100
- icon: React.ReactNode
101
- label: string
102
- size?: 'sm' | 'default' | 'lg'
103
- variant?: VariantProps<typeof buttonVariants>['variant']
104
- asChild?: boolean
105
- }
106
-
107
- export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
108
- ({ icon, label, size = 'default', variant = 'ghost', className, asChild = false, ...props }, ref) => {
109
- const Comp = asChild ? Slot : 'button'
110
- const sizeClass = size === 'sm' ? 'h-7 w-7' : size === 'lg' ? 'h-10 w-10' : 'h-9 w-9'
111
- return (
112
- <Comp
113
- ref={ref}
114
- aria-label={label}
115
- data-slot="icon-button"
116
- className={cn(
117
- buttonVariants({ variant }),
118
- sizeClass,
119
- 'p-0 shrink-0',
120
- className
121
- )}
122
- {...props}
123
- >
124
- {icon}
125
- </Comp>
126
- )
127
- }
128
- )
129
- IconButton.displayName = 'IconButton'
130
-
131
- export { buttonVariants }
1
+ // Single source of truth lives in ../button; this path stays so existing imports keep working.
2
+ export * from '../button'
@@ -1,368 +1,2 @@
1
- // @ts-nocheck
2
- import * as React from "react"
3
- import * as RechartsPrimitive from "recharts"
4
-
5
- import { cn } from "../utils/cn"
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-muted-foreground [&_.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-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
55
- className
56
- )}
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
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
- const ChartTooltipContent = React.forwardRef<
105
- HTMLDivElement,
106
- React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
107
- React.ComponentProps<"div"> & {
108
- hideLabel?: boolean
109
- hideIndicator?: boolean
110
- indicator?: "line" | "dot" | "dashed"
111
- nameKey?: string
112
- labelKey?: string
113
- }
114
- >(
115
- (
116
- {
117
- active,
118
- payload,
119
- className,
120
- indicator = "dot",
121
- hideLabel = false,
122
- hideIndicator = false,
123
- label,
124
- labelFormatter,
125
- labelClassName,
126
- formatter,
127
- color,
128
- nameKey,
129
- labelKey,
130
- },
131
- ref
132
- ) => {
133
- const { config } = useChart()
134
-
135
- const tooltipLabel = React.useMemo(() => {
136
- if (hideLabel || !payload?.length) {
137
- return null
138
- }
139
-
140
- const [item] = payload
141
- const key = `${labelKey || item?.dataKey || item?.name || "value"}`
142
- const itemConfig = getPayloadConfigFromPayload(config, item, key)
143
- const value =
144
- !labelKey && typeof label === "string"
145
- ? config[label as keyof typeof config]?.label || label
146
- : itemConfig?.label
147
-
148
- if (labelFormatter) {
149
- return (
150
- <div className={cn("font-medium", labelClassName)}>
151
- {labelFormatter(value, payload)}
152
- </div>
153
- )
154
- }
155
-
156
- if (!value) {
157
- return null
158
- }
159
-
160
- return <div className={cn("font-medium", labelClassName)}>{value}</div>
161
- }, [
162
- label,
163
- labelFormatter,
164
- payload,
165
- hideLabel,
166
- labelClassName,
167
- config,
168
- labelKey,
169
- ])
170
-
171
- if (!active || !payload?.length) {
172
- return null
173
- }
174
-
175
- const nestLabel = payload.length === 1 && indicator !== "dot"
176
-
177
- return (
178
- <div
179
- ref={ref}
180
- className={cn(
181
- "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
182
- className
183
- )}
184
- >
185
- {!nestLabel ? tooltipLabel : null}
186
- <div className="grid gap-1.5">
187
- {payload
188
- .filter((item) => item.type !== "none")
189
- .map((item, index) => {
190
- const key = `${nameKey || item.name || item.dataKey || "value"}`
191
- const itemConfig = getPayloadConfigFromPayload(config, item, key)
192
- const indicatorColor = color || item.payload.fill || item.color
193
-
194
- return (
195
- <div
196
- key={item.dataKey}
197
- className={cn(
198
- "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
199
- indicator === "dot" && "items-center"
200
- )}
201
- >
202
- {formatter && item?.value !== undefined && item.name ? (
203
- formatter(item.value, item.name, item, index, item.payload)
204
- ) : (
205
- <>
206
- {itemConfig?.icon ? (
207
- <itemConfig.icon />
208
- ) : (
209
- !hideIndicator && (
210
- <div
211
- className={cn(
212
- "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
213
- {
214
- "h-2.5 w-2.5": indicator === "dot",
215
- "w-1": indicator === "line",
216
- "w-0 border-[1.5px] border-dashed bg-transparent":
217
- indicator === "dashed",
218
- "my-0.5": nestLabel && indicator === "dashed",
219
- }
220
- )}
221
- style={
222
- {
223
- "--color-bg": indicatorColor,
224
- "--color-border": indicatorColor,
225
- } as React.CSSProperties
226
- }
227
- />
228
- )
229
- )}
230
- <div
231
- className={cn(
232
- "flex flex-1 justify-between leading-none",
233
- nestLabel ? "items-end" : "items-center"
234
- )}
235
- >
236
- <div className="grid gap-1.5">
237
- {nestLabel ? tooltipLabel : null}
238
- <span className="text-muted-foreground">
239
- {itemConfig?.label || item.name}
240
- </span>
241
- </div>
242
- {item.value && (
243
- <span className="font-mono font-medium tabular-nums text-foreground">
244
- {item.value.toLocaleString()}
245
- </span>
246
- )}
247
- </div>
248
- </>
249
- )}
250
- </div>
251
- )
252
- })}
253
- </div>
254
- </div>
255
- )
256
- }
257
- )
258
- ChartTooltipContent.displayName = "ChartTooltip"
259
-
260
- const ChartLegend = RechartsPrimitive.Legend
261
-
262
- const ChartLegendContent = React.forwardRef<
263
- HTMLDivElement,
264
- React.ComponentProps<"div"> &
265
- Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
266
- hideIcon?: boolean
267
- nameKey?: string
268
- }
269
- >(
270
- (
271
- { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
272
- ref
273
- ) => {
274
- const { config } = useChart()
275
-
276
- if (!payload?.length) {
277
- return null
278
- }
279
-
280
- return (
281
- <div
282
- ref={ref}
283
- className={cn(
284
- "flex items-center justify-center gap-4",
285
- verticalAlign === "top" ? "pb-3" : "pt-3",
286
- className
287
- )}
288
- >
289
- {payload
290
- .filter((item) => item.type !== "none")
291
- .map((item) => {
292
- const key = `${nameKey || item.dataKey || "value"}`
293
- const itemConfig = getPayloadConfigFromPayload(config, item, key)
294
-
295
- return (
296
- <div
297
- key={item.value}
298
- className={cn(
299
- "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
300
- )}
301
- >
302
- {itemConfig?.icon && !hideIcon ? (
303
- <itemConfig.icon />
304
- ) : (
305
- <div
306
- className="h-2 w-2 shrink-0 rounded-[2px]"
307
- style={{
308
- backgroundColor: item.color,
309
- }}
310
- />
311
- )}
312
- {itemConfig?.label}
313
- </div>
314
- )
315
- })}
316
- </div>
317
- )
318
- }
319
- )
320
- ChartLegendContent.displayName = "ChartLegend"
321
-
322
- // Helper to extract item config from a payload.
323
- function getPayloadConfigFromPayload(
324
- config: ChartConfig,
325
- payload: unknown,
326
- key: string
327
- ) {
328
- if (typeof payload !== "object" || payload === null) {
329
- return undefined
330
- }
331
-
332
- const payloadPayload =
333
- "payload" in payload &&
334
- typeof payload.payload === "object" &&
335
- payload.payload !== null
336
- ? payload.payload
337
- : undefined
338
-
339
- let configLabelKey: string = key
340
-
341
- if (
342
- key in payload &&
343
- typeof payload[key as keyof typeof payload] === "string"
344
- ) {
345
- configLabelKey = payload[key as keyof typeof payload] as string
346
- } else if (
347
- payloadPayload &&
348
- key in payloadPayload &&
349
- typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
350
- ) {
351
- configLabelKey = payloadPayload[
352
- key as keyof typeof payloadPayload
353
- ] as string
354
- }
355
-
356
- return configLabelKey in config
357
- ? config[configLabelKey]
358
- : config[key as keyof typeof config]
359
- }
360
-
361
- export {
362
- ChartContainer,
363
- ChartTooltip,
364
- ChartTooltipContent,
365
- ChartLegend,
366
- ChartLegendContent,
367
- ChartStyle,
368
- }
1
+ // Single source of truth lives in ../chart; this path stays so existing imports keep working.
2
+ export * from '../chart'
@@ -14,12 +14,42 @@ const buttonVariants = cva(
14
14
  ghost: 'hover:bg-accent hover:text-accent-foreground',
15
15
  destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
16
16
  link: 'text-primary underline-offset-4 hover:underline',
17
+ gradient: [
18
+ 'bg-gradient-to-b from-primary to-[hsl(var(--primary)/0.8)]',
19
+ 'text-primary-foreground',
20
+ 'shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_-1px_0_0_rgba(0,0,0,0.25)_inset]',
21
+ 'hover:from-[hsl(var(--primary)/0.9)] hover:to-[hsl(var(--primary)/0.7)]',
22
+ 'active:from-[hsl(var(--primary)/0.8)] active:to-[hsl(var(--primary)/0.6)]',
23
+ ].join(' '),
24
+ texture: [
25
+ 'relative border border-border/40',
26
+ 'bg-gradient-to-b from-card to-[hsl(var(--card)/0.9)]',
27
+ 'text-foreground',
28
+ 'shadow-[0_0_0_1px_hsl(var(--border)/0.2)_inset]',
29
+ 'hover:border-border/60 hover:bg-accent',
30
+ 'active:shadow-none',
31
+ ].join(' '),
32
+ // Loud by design: keep for primary CTAs. The sweep keyframe ships in themes/base.css.
33
+ shimmer: [
34
+ 'relative isolate overflow-hidden',
35
+ 'bg-primary text-primary-foreground',
36
+ 'shadow-[0_1px_0_rgba(255,255,255,0.18)_inset,0_-1px_0_rgba(0,0,0,0.25)_inset]',
37
+ 'hover:bg-primary/95',
38
+ 'before:absolute before:inset-[-1px] before:rounded-[inherit] before:-z-10',
39
+ 'before:bg-[conic-gradient(from_0deg,transparent_0deg,transparent_70deg,rgba(255,255,255,0.55)_90deg,transparent_110deg,transparent_360deg)]',
40
+ 'before:animate-[shimmer-rotate_2.6s_linear_infinite] motion-reduce:before:animate-none',
41
+ 'after:absolute after:inset-0 after:rounded-[inherit] after:pointer-events-none',
42
+ 'after:bg-[linear-gradient(115deg,transparent_30%,rgba(255,255,255,0.22)_50%,transparent_70%)]',
43
+ 'after:translate-x-[-120%] hover:after:translate-x-[120%] after:transition-transform after:duration-[900ms] after:ease-out',
44
+ ].join(' '),
17
45
  },
18
46
  size: {
19
47
  sm: 'h-8 px-3 text-xs',
20
48
  default: 'h-9 px-4 py-2',
21
49
  lg: 'h-11 px-6 text-base',
50
+ xl: 'h-12 px-8 text-base',
22
51
  icon: 'h-9 w-9',
52
+ 'icon-sm': 'h-7 w-7 p-0 text-xs',
23
53
  },
24
54
  },
25
55
  defaultVariants: { variant: 'default', size: 'default' },
@@ -40,4 +70,26 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
40
70
  )
41
71
  Button.displayName = 'Button'
42
72
 
73
+ export interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
74
+ icon: React.ReactNode
75
+ /** Accessible name; an icon-only button has no visible text. */
76
+ label: string
77
+ size?: 'sm' | 'default' | 'lg'
78
+ variant?: VariantProps<typeof buttonVariants>['variant']
79
+ asChild?: boolean
80
+ }
81
+
82
+ export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
83
+ ({ icon, label, size = 'default', variant = 'ghost', className, asChild = false, ...props }, ref) => {
84
+ const Comp = asChild ? Slot : 'button'
85
+ const sizeClass = size === 'sm' ? 'h-7 w-7' : size === 'lg' ? 'h-10 w-10' : 'h-9 w-9'
86
+ return (
87
+ <Comp ref={ref} aria-label={label} className={cn(buttonVariants({ variant }), sizeClass, 'p-0 shrink-0', className)} {...props}>
88
+ {icon}
89
+ </Comp>
90
+ )
91
+ }
92
+ )
93
+ IconButton.displayName = 'IconButton'
94
+
43
95
  export { buttonVariants }
@@ -101,17 +101,20 @@ ${colorConfig
101
101
 
102
102
  const ChartTooltip = RechartsPrimitive.Tooltip
103
103
 
104
- const ChartTooltipContent = React.forwardRef<
105
- HTMLDivElement,
106
- React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
107
- React.ComponentProps<"div"> & {
108
- hideLabel?: boolean
109
- hideIndicator?: boolean
110
- indicator?: "line" | "dot" | "dashed"
111
- nameKey?: string
112
- labelKey?: string
113
- }
114
- >(
104
+ // Named and annotated so the emitted declarations reference recharts' public types by name;
105
+ // an inferred type expands into recharts' internal file paths, which consumers cannot resolve.
106
+ export type ChartTooltipContentProps = React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
107
+ React.ComponentProps<"div"> & {
108
+ hideLabel?: boolean
109
+ hideIndicator?: boolean
110
+ indicator?: "line" | "dot" | "dashed"
111
+ nameKey?: string
112
+ labelKey?: string
113
+ }
114
+
115
+ const ChartTooltipContent: React.ForwardRefExoticComponent<
116
+ React.PropsWithoutRef<ChartTooltipContentProps> & React.RefAttributes<HTMLDivElement>
117
+ > = React.forwardRef<HTMLDivElement, ChartTooltipContentProps>(
115
118
  (
116
119
  {
117
120
  active,
@@ -259,14 +262,17 @@ ChartTooltipContent.displayName = "ChartTooltip"
259
262
 
260
263
  const ChartLegend = RechartsPrimitive.Legend
261
264
 
262
- const ChartLegendContent = React.forwardRef<
263
- HTMLDivElement,
264
- React.ComponentProps<"div"> &
265
- Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
266
- hideIcon?: boolean
267
- nameKey?: string
268
- }
269
- >(
265
+ // Declared here, not picked from LegendProps: newer recharts releases drop `payload` from it.
266
+ export type ChartLegendContentProps = React.ComponentProps<"div"> & {
267
+ payload?: ReadonlyArray<RechartsPrimitive.LegendPayload>
268
+ verticalAlign?: "top" | "middle" | "bottom"
269
+ hideIcon?: boolean
270
+ nameKey?: string
271
+ }
272
+
273
+ const ChartLegendContent: React.ForwardRefExoticComponent<
274
+ React.PropsWithoutRef<ChartLegendContentProps> & React.RefAttributes<HTMLDivElement>
275
+ > = React.forwardRef<HTMLDivElement, ChartLegendContentProps>(
270
276
  (
271
277
  { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
272
278
  ref