@otfdashkit/ui 0.1.4 → 0.1.6

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.4",
3
+ "version": "0.1.6",
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
  }
@@ -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'
@@ -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