@traky-app/ui 0.0.0-stage → 1.1.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 (195) hide show
  1. package/README.md +23 -2
  2. package/package.json +71 -4
  3. package/registry/registry.json +6 -0
  4. package/registry/traky/hooks/use-mobile.ts +19 -0
  5. package/registry/traky/json/accordion.json +18 -0
  6. package/registry/traky/json/alert-dialog.json +20 -0
  7. package/registry/traky/json/alert.json +17 -0
  8. package/registry/traky/json/animated-beam.json +14 -0
  9. package/registry/traky/json/animated-list.json +14 -0
  10. package/registry/traky/json/avatar.json +17 -0
  11. package/registry/traky/json/badge.json +18 -0
  12. package/registry/traky/json/bento-grid.json +20 -0
  13. package/registry/traky/json/breadcrumb.json +18 -0
  14. package/registry/traky/json/bubble.json +18 -0
  15. package/registry/traky/json/button-group.json +21 -0
  16. package/registry/traky/json/button.json +18 -0
  17. package/registry/traky/json/calendar.json +21 -0
  18. package/registry/traky/json/card.json +14 -0
  19. package/registry/traky/json/carousel.json +21 -0
  20. package/registry/traky/json/chart.json +17 -0
  21. package/registry/traky/json/checkbox.json +18 -0
  22. package/registry/traky/json/checkpoint.json +22 -0
  23. package/registry/traky/json/code-block.json +21 -0
  24. package/registry/traky/json/collapsible.json +17 -0
  25. package/registry/traky/json/command.json +22 -0
  26. package/registry/traky/json/context-menu.json +18 -0
  27. package/registry/traky/json/dialog.json +18 -0
  28. package/registry/traky/json/drawer.json +17 -0
  29. package/registry/traky/json/dropdown-menu.json +18 -0
  30. package/registry/traky/json/empty.json +17 -0
  31. package/registry/traky/json/field.json +21 -0
  32. package/registry/traky/json/globe.json +17 -0
  33. package/registry/traky/json/hook-use-mobile.json +14 -0
  34. package/registry/traky/json/hover-card.json +17 -0
  35. package/registry/traky/json/input-group.json +22 -0
  36. package/registry/traky/json/input-otp.json +18 -0
  37. package/registry/traky/json/input.json +17 -0
  38. package/registry/traky/json/item.json +21 -0
  39. package/registry/traky/json/kbd.json +14 -0
  40. package/registry/traky/json/label.json +17 -0
  41. package/registry/traky/json/lib-disable-input-autofill.json +14 -0
  42. package/registry/traky/json/lib-sidebar-persistence.json +14 -0
  43. package/registry/traky/json/lib-utils.json +18 -0
  44. package/registry/traky/json/map.json +19 -0
  45. package/registry/traky/json/marker.json +18 -0
  46. package/registry/traky/json/marquee.json +14 -0
  47. package/registry/traky/json/message.json +14 -0
  48. package/registry/traky/json/navigation-menu.json +19 -0
  49. package/registry/traky/json/number-ticker.json +14 -0
  50. package/registry/traky/json/pagination.json +20 -0
  51. package/registry/traky/json/popover.json +17 -0
  52. package/registry/traky/json/progress.json +17 -0
  53. package/registry/traky/json/select.json +18 -0
  54. package/registry/traky/json/separator.json +17 -0
  55. package/registry/traky/json/sheet.json +18 -0
  56. package/registry/traky/json/sidebar.json +29 -0
  57. package/registry/traky/json/skeleton.json +14 -0
  58. package/registry/traky/json/sonner.json +18 -0
  59. package/registry/traky/json/spinner.json +17 -0
  60. package/registry/traky/json/switch.json +17 -0
  61. package/registry/traky/json/table.json +14 -0
  62. package/registry/traky/json/tabs.json +17 -0
  63. package/registry/traky/json/textarea.json +17 -0
  64. package/registry/traky/json/toggle-group.json +21 -0
  65. package/registry/traky/json/toggle.json +18 -0
  66. package/registry/traky/json/tooltip.json +17 -0
  67. package/registry/traky/json/trace.json +20 -0
  68. package/registry/traky/lib/disable-input-autofill.ts +23 -0
  69. package/registry/traky/lib/sidebar-persistence.ts +76 -0
  70. package/registry/traky/lib/utils.ts +6 -0
  71. package/registry/traky/registry.json +637 -0
  72. package/registry/traky/ui/accordion.tsx +65 -0
  73. package/registry/traky/ui/alert-dialog.tsx +163 -0
  74. package/registry/traky/ui/alert.tsx +66 -0
  75. package/registry/traky/ui/animated-beam.tsx +185 -0
  76. package/registry/traky/ui/animated-list.tsx +71 -0
  77. package/registry/traky/ui/avatar.tsx +53 -0
  78. package/registry/traky/ui/badge.tsx +46 -0
  79. package/registry/traky/ui/bento-grid.tsx +109 -0
  80. package/registry/traky/ui/breadcrumb.tsx +108 -0
  81. package/registry/traky/ui/bubble.tsx +125 -0
  82. package/registry/traky/ui/button-group.tsx +83 -0
  83. package/registry/traky/ui/button.tsx +64 -0
  84. package/registry/traky/ui/calendar.tsx +213 -0
  85. package/registry/traky/ui/card.tsx +92 -0
  86. package/registry/traky/ui/carousel.tsx +240 -0
  87. package/registry/traky/ui/chart.tsx +380 -0
  88. package/registry/traky/ui/checkbox.tsx +31 -0
  89. package/registry/traky/ui/checkpoint.tsx +94 -0
  90. package/registry/traky/ui/code-block.tsx +232 -0
  91. package/registry/traky/ui/collapsible.tsx +33 -0
  92. package/registry/traky/ui/command.tsx +191 -0
  93. package/registry/traky/ui/context-menu.tsx +251 -0
  94. package/registry/traky/ui/dialog.tsx +142 -0
  95. package/registry/traky/ui/drawer.tsx +135 -0
  96. package/registry/traky/ui/dropdown-menu.tsx +256 -0
  97. package/registry/traky/ui/empty.tsx +104 -0
  98. package/registry/traky/ui/field.tsx +248 -0
  99. package/registry/traky/ui/globe.tsx +234 -0
  100. package/registry/traky/ui/hover-card.tsx +44 -0
  101. package/registry/traky/ui/input-group.tsx +170 -0
  102. package/registry/traky/ui/input-otp.tsx +77 -0
  103. package/registry/traky/ui/input.tsx +24 -0
  104. package/registry/traky/ui/item.tsx +193 -0
  105. package/registry/traky/ui/kbd.tsx +28 -0
  106. package/registry/traky/ui/label.tsx +24 -0
  107. package/registry/traky/ui/map.tsx +2226 -0
  108. package/registry/traky/ui/marker.tsx +69 -0
  109. package/registry/traky/ui/marquee.tsx +74 -0
  110. package/registry/traky/ui/message.tsx +92 -0
  111. package/registry/traky/ui/navigation-menu.tsx +169 -0
  112. package/registry/traky/ui/number-ticker.tsx +90 -0
  113. package/registry/traky/ui/pagination.tsx +122 -0
  114. package/registry/traky/ui/popover.tsx +48 -0
  115. package/registry/traky/ui/progress.tsx +31 -0
  116. package/registry/traky/ui/select.tsx +193 -0
  117. package/registry/traky/ui/separator.tsx +28 -0
  118. package/registry/traky/ui/sheet.tsx +143 -0
  119. package/registry/traky/ui/sidebar.tsx +764 -0
  120. package/registry/traky/ui/skeleton.tsx +13 -0
  121. package/registry/traky/ui/sonner.tsx +71 -0
  122. package/registry/traky/ui/spinner.tsx +15 -0
  123. package/registry/traky/ui/switch.tsx +41 -0
  124. package/registry/traky/ui/table.tsx +116 -0
  125. package/registry/traky/ui/tabs.tsx +66 -0
  126. package/registry/traky/ui/textarea.tsx +20 -0
  127. package/registry/traky/ui/toggle-group.tsx +83 -0
  128. package/registry/traky/ui/toggle.tsx +47 -0
  129. package/registry/traky/ui/tooltip.tsx +61 -0
  130. package/registry/traky/ui/trace.tsx +165 -0
  131. package/schema/components.json +24 -0
  132. package/src/hooks/use-document-theme.ts +25 -0
  133. package/src/hooks/use-mobile.ts +19 -0
  134. package/src/lib/disable-input-autofill.ts +23 -0
  135. package/src/lib/sidebar-persistence.ts +76 -0
  136. package/src/lib/utils.ts +30 -0
  137. package/src/ui/accordion.tsx +65 -0
  138. package/src/ui/alert-dialog.tsx +163 -0
  139. package/src/ui/alert.tsx +66 -0
  140. package/src/ui/animated-beam.tsx +185 -0
  141. package/src/ui/animated-list.tsx +71 -0
  142. package/src/ui/avatar.tsx +53 -0
  143. package/src/ui/badge.tsx +46 -0
  144. package/src/ui/bento-grid.tsx +109 -0
  145. package/src/ui/breadcrumb.tsx +108 -0
  146. package/src/ui/bubble.tsx +125 -0
  147. package/src/ui/button-group.tsx +83 -0
  148. package/src/ui/button.tsx +64 -0
  149. package/src/ui/calendar.tsx +213 -0
  150. package/src/ui/card.tsx +92 -0
  151. package/src/ui/carousel.tsx +240 -0
  152. package/src/ui/chart.tsx +380 -0
  153. package/src/ui/checkbox.tsx +31 -0
  154. package/src/ui/checkpoint.tsx +94 -0
  155. package/src/ui/code-block.tsx +232 -0
  156. package/src/ui/collapsible.tsx +33 -0
  157. package/src/ui/command.tsx +191 -0
  158. package/src/ui/context-menu.tsx +251 -0
  159. package/src/ui/dialog.tsx +142 -0
  160. package/src/ui/drawer.tsx +135 -0
  161. package/src/ui/dropdown-menu.tsx +256 -0
  162. package/src/ui/empty.tsx +104 -0
  163. package/src/ui/field.tsx +248 -0
  164. package/src/ui/globe.tsx +234 -0
  165. package/src/ui/hover-card.tsx +44 -0
  166. package/src/ui/input-group.tsx +170 -0
  167. package/src/ui/input-otp.tsx +77 -0
  168. package/src/ui/input.tsx +24 -0
  169. package/src/ui/item.tsx +193 -0
  170. package/src/ui/kbd.tsx +28 -0
  171. package/src/ui/label.tsx +24 -0
  172. package/src/ui/map.tsx +2226 -0
  173. package/src/ui/marker.tsx +69 -0
  174. package/src/ui/marquee.tsx +74 -0
  175. package/src/ui/message.tsx +92 -0
  176. package/src/ui/navigation-menu.tsx +169 -0
  177. package/src/ui/number-ticker.tsx +90 -0
  178. package/src/ui/pagination.tsx +122 -0
  179. package/src/ui/popover.tsx +48 -0
  180. package/src/ui/progress.tsx +31 -0
  181. package/src/ui/select.tsx +193 -0
  182. package/src/ui/separator.tsx +28 -0
  183. package/src/ui/sheet.tsx +143 -0
  184. package/src/ui/sidebar.tsx +764 -0
  185. package/src/ui/skeleton.tsx +13 -0
  186. package/src/ui/sonner.tsx +71 -0
  187. package/src/ui/spinner.tsx +15 -0
  188. package/src/ui/switch.tsx +41 -0
  189. package/src/ui/table.tsx +116 -0
  190. package/src/ui/tabs.tsx +66 -0
  191. package/src/ui/textarea.tsx +20 -0
  192. package/src/ui/toggle-group.tsx +83 -0
  193. package/src/ui/toggle.tsx +47 -0
  194. package/src/ui/tooltip.tsx +61 -0
  195. package/src/ui/trace.tsx +165 -0
@@ -0,0 +1,380 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as RechartsPrimitive from "recharts"
5
+ import type {
6
+ TooltipContentProps,
7
+ TooltipPayloadEntry,
8
+ LegendPayload,
9
+ } from "recharts"
10
+
11
+ import { cn } from "../lib/utils"
12
+
13
+ // Format: { THEME_NAME: CSS_SELECTOR }
14
+ const THEMES = { light: "", dark: ".dark" } as const
15
+
16
+ export type ChartConfig = {
17
+ [k in string]: {
18
+ label?: React.ReactNode
19
+ icon?: React.ComponentType
20
+ } & (
21
+ | { color?: string; theme?: never }
22
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
23
+ )
24
+ }
25
+
26
+ type ChartContextProps = {
27
+ config: ChartConfig
28
+ }
29
+
30
+ const ChartContext = React.createContext<ChartContextProps | null>(null)
31
+
32
+ function useChart() {
33
+ const context = React.useContext(ChartContext)
34
+
35
+ if (!context) {
36
+ throw new Error("useChart must be used within a <ChartContainer />")
37
+ }
38
+
39
+ return context
40
+ }
41
+
42
+ /** Color de la serie definido en el config. La leyenda y el tooltip usan el mismo token que el trazo. */
43
+ function seriesColor(config: ChartConfig, key: string, fallback?: string) {
44
+ if (key in config && config[key]?.color) {
45
+ return `var(--color-${key})`
46
+ }
47
+ return fallback
48
+ }
49
+
50
+ function ChartContainer({
51
+ id,
52
+ className,
53
+ children,
54
+ config,
55
+ ...props
56
+ }: React.ComponentProps<"div"> & {
57
+ config: ChartConfig
58
+ children: React.ComponentProps<
59
+ typeof RechartsPrimitive.ResponsiveContainer
60
+ >["children"]
61
+ }) {
62
+ const uniqueId = React.useId()
63
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
64
+
65
+ return (
66
+ <ChartContext.Provider value={{ config }}>
67
+ <div
68
+ data-slot="chart"
69
+ data-chart={chartId}
70
+ className={cn(
71
+ "[&_.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",
72
+ className
73
+ )}
74
+ {...props}
75
+ >
76
+ <ChartStyle id={chartId} config={config} />
77
+ <RechartsPrimitive.ResponsiveContainer
78
+ width="100%"
79
+ height="100%"
80
+ minWidth={0}
81
+ debounce={50}
82
+ >
83
+ {children}
84
+ </RechartsPrimitive.ResponsiveContainer>
85
+ </div>
86
+ </ChartContext.Provider>
87
+ )
88
+ }
89
+
90
+ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
91
+ const colorConfig = Object.entries(config).filter(
92
+ ([, config]) => config.theme || config.color
93
+ )
94
+
95
+ if (!colorConfig.length) {
96
+ return null
97
+ }
98
+
99
+ return (
100
+ <style
101
+ dangerouslySetInnerHTML={{
102
+ __html: Object.entries(THEMES)
103
+ .map(
104
+ ([theme, prefix]) => `
105
+ ${prefix} [data-chart=${id}] {
106
+ ${colorConfig
107
+ .map(([key, itemConfig]) => {
108
+ const color =
109
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
110
+ itemConfig.color
111
+ return color ? ` --color-${key}: ${color};` : null
112
+ })
113
+ .join("\n")}
114
+ }
115
+ `
116
+ )
117
+ .join("\n"),
118
+ }}
119
+ />
120
+ )
121
+ }
122
+
123
+ const ChartTooltip = RechartsPrimitive.Tooltip
124
+
125
+ function ChartTooltipContent({
126
+ active,
127
+ payload,
128
+ className,
129
+ indicator = "dot",
130
+ hideLabel = false,
131
+ hideIndicator = false,
132
+ label,
133
+ labelFormatter,
134
+ labelClassName,
135
+ formatter,
136
+ color,
137
+ nameKey,
138
+ labelKey,
139
+ }: Partial<TooltipContentProps> &
140
+ React.ComponentProps<"div"> & {
141
+ hideLabel?: boolean
142
+ hideIndicator?: boolean
143
+ indicator?: "line" | "dot" | "dashed"
144
+ nameKey?: string
145
+ labelKey?: string
146
+ }) {
147
+ const { config } = useChart()
148
+
149
+ const tooltipLabel = React.useMemo(() => {
150
+ if (hideLabel || !payload?.length) {
151
+ return null
152
+ }
153
+
154
+ const [item] = payload
155
+ const key = `${labelKey || item?.dataKey || item?.name || "value"}`
156
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
157
+ const value =
158
+ !labelKey && typeof label === "string"
159
+ ? config[label as keyof typeof config]?.label || label
160
+ : itemConfig?.label
161
+
162
+ if (labelFormatter) {
163
+ return (
164
+ <div className={cn("font-medium", labelClassName)}>
165
+ {labelFormatter(value, payload)}
166
+ </div>
167
+ )
168
+ }
169
+
170
+ if (!value) {
171
+ return null
172
+ }
173
+
174
+ return <div className={cn("font-medium", labelClassName)}>{value}</div>
175
+ }, [
176
+ label,
177
+ labelFormatter,
178
+ payload,
179
+ hideLabel,
180
+ labelClassName,
181
+ config,
182
+ labelKey,
183
+ ])
184
+
185
+ if (!active || !payload?.length) {
186
+ return null
187
+ }
188
+
189
+ const nestLabel = payload.length === 1 && indicator !== "dot"
190
+
191
+ return (
192
+ <div
193
+ className={cn(
194
+ "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",
195
+ className
196
+ )}
197
+ >
198
+ {!nestLabel ? tooltipLabel : null}
199
+ <div className="grid gap-1.5">
200
+ {payload
201
+ .filter((item: TooltipPayloadEntry) => item.type !== "none")
202
+ .map((item: TooltipPayloadEntry, index: number) => {
203
+ const key = `${item.dataKey || nameKey || item.name || "value"}`
204
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
205
+ const indicatorColor =
206
+ color || seriesColor(config, key, item.color || item.payload?.fill)
207
+
208
+ return (
209
+ <div
210
+ key={
211
+ item.graphicalItemId ??
212
+ `row-${index}-${String(item.name ?? item.dataKey ?? "")}`
213
+ }
214
+ className={cn(
215
+ "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
216
+ indicator === "dot" && "items-center"
217
+ )}
218
+ >
219
+ {formatter && item?.value !== undefined && item.name ? (
220
+ formatter(item.value, item.name, item, index, item.payload)
221
+ ) : (
222
+ <>
223
+ {itemConfig?.icon ? (
224
+ <itemConfig.icon />
225
+ ) : (
226
+ !hideIndicator && (
227
+ <div
228
+ className={cn(
229
+ "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
230
+ {
231
+ "h-2.5 w-2.5": indicator === "dot",
232
+ "w-1": indicator === "line",
233
+ "w-0 border-[1.5px] border-dashed bg-transparent":
234
+ indicator === "dashed",
235
+ "my-0.5": nestLabel && indicator === "dashed",
236
+ }
237
+ )}
238
+ style={
239
+ {
240
+ "--color-bg": indicatorColor,
241
+ "--color-border": indicatorColor,
242
+ } as React.CSSProperties
243
+ }
244
+ />
245
+ )
246
+ )}
247
+ <div
248
+ className={cn(
249
+ "flex flex-1 justify-between leading-none",
250
+ nestLabel ? "items-end" : "items-center"
251
+ )}
252
+ >
253
+ <div className="grid gap-1.5">
254
+ {nestLabel ? tooltipLabel : null}
255
+ <span className="text-muted-foreground">
256
+ {itemConfig?.label || item.name}
257
+ </span>
258
+ </div>
259
+ {item.value && (
260
+ <span className="text-foreground font-mono font-medium tabular-nums">
261
+ {item.value.toLocaleString()}
262
+ </span>
263
+ )}
264
+ </div>
265
+ </>
266
+ )}
267
+ </div>
268
+ )
269
+ })}
270
+ </div>
271
+ </div>
272
+ )
273
+ }
274
+
275
+ const ChartLegend = RechartsPrimitive.Legend
276
+
277
+ function ChartLegendContent({
278
+ className,
279
+ hideIcon = false,
280
+ payload,
281
+ verticalAlign = "bottom",
282
+ nameKey,
283
+ }: React.ComponentProps<"div"> & {
284
+ payload?: ReadonlyArray<LegendPayload>
285
+ verticalAlign?: "top" | "bottom";
286
+ hideIcon?: boolean
287
+ nameKey?: string
288
+ }) {
289
+ const { config } = useChart()
290
+
291
+ if (!payload?.length) {
292
+ return null
293
+ }
294
+
295
+ return (
296
+ <div
297
+ className={cn(
298
+ "flex items-center justify-center gap-4",
299
+ verticalAlign === "top" ? "pb-3" : "pt-3",
300
+ className
301
+ )}
302
+ >
303
+ {payload
304
+ .filter((item: LegendPayload) => item.type !== "none")
305
+ .map((item: LegendPayload) => {
306
+ const key = `${nameKey || item.dataKey || "value"}`
307
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
308
+
309
+ return (
310
+ <div
311
+ key={item.value}
312
+ className={cn(
313
+ "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3"
314
+ )}
315
+ >
316
+ {itemConfig?.icon && !hideIcon ? (
317
+ <itemConfig.icon />
318
+ ) : (
319
+ <div
320
+ className="h-2 w-2 shrink-0 rounded-[2px]"
321
+ style={{
322
+ backgroundColor: seriesColor(config, key, item.color),
323
+ }}
324
+ />
325
+ )}
326
+ {itemConfig?.label}
327
+ </div>
328
+ )
329
+ })}
330
+ </div>
331
+ )
332
+ }
333
+
334
+ // Helper to extract item config from a payload.
335
+ function getPayloadConfigFromPayload(
336
+ config: ChartConfig,
337
+ payload: unknown,
338
+ key: string
339
+ ) {
340
+ if (typeof payload !== "object" || payload === null) {
341
+ return undefined
342
+ }
343
+
344
+ const payloadPayload =
345
+ "payload" in payload &&
346
+ typeof payload.payload === "object" &&
347
+ payload.payload !== null
348
+ ? payload.payload
349
+ : undefined
350
+
351
+ let configLabelKey: string = key
352
+
353
+ if (
354
+ key in payload &&
355
+ typeof payload[key as keyof typeof payload] === "string"
356
+ ) {
357
+ configLabelKey = payload[key as keyof typeof payload] as string
358
+ } else if (
359
+ payloadPayload &&
360
+ key in payloadPayload &&
361
+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
362
+ ) {
363
+ configLabelKey = payloadPayload[
364
+ key as keyof typeof payloadPayload
365
+ ] as string
366
+ }
367
+
368
+ return configLabelKey in config
369
+ ? config[configLabelKey]
370
+ : config[key as keyof typeof config]
371
+ }
372
+
373
+ export {
374
+ ChartContainer,
375
+ ChartTooltip,
376
+ ChartTooltipContent,
377
+ ChartLegend,
378
+ ChartLegendContent,
379
+ ChartStyle,
380
+ }
@@ -0,0 +1,31 @@
1
+ "use client"
2
+
3
+ import { IconCheck } from "@tabler/icons-react";
4
+ import * as React from "react"
5
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
6
+ import { cn } from "../lib/utils"
7
+
8
+ function Checkbox({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
12
+ return (
13
+ <CheckboxPrimitive.Root
14
+ data-slot="checkbox"
15
+ className={cn(
16
+ "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",
17
+ className
18
+ )}
19
+ {...props}
20
+ >
21
+ <CheckboxPrimitive.Indicator
22
+ data-slot="checkbox-indicator"
23
+ className="grid place-content-center text-current transition-none"
24
+ >
25
+ <IconCheck className="size-3.5" />
26
+ </CheckboxPrimitive.Indicator>
27
+ </CheckboxPrimitive.Root>
28
+ )
29
+ }
30
+
31
+ export { Checkbox }
@@ -0,0 +1,94 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { IconBookmark } from "@tabler/icons-react"
5
+
6
+ import { Button } from "./button"
7
+ import { Marker, MarkerContent, MarkerIcon } from "./marker"
8
+ import {
9
+ Tooltip,
10
+ TooltipContent,
11
+ TooltipProvider,
12
+ TooltipTrigger,
13
+ } from "./tooltip"
14
+ import { cn } from "../lib/utils"
15
+
16
+ /**
17
+ * Checkpoint (AI Element): punto de restauración en un hilo.
18
+ * Compuesto con Marker + Button — https://ui.shadcn.com/docs/ai-elements/base/checkpoint
19
+ */
20
+ function Checkpoint({
21
+ className,
22
+ ...props
23
+ }: React.ComponentProps<typeof Marker>) {
24
+ return (
25
+ <Marker
26
+ data-slot="checkpoint"
27
+ variant="separator"
28
+ className={cn(className)}
29
+ {...props}
30
+ />
31
+ )
32
+ }
33
+
34
+ function CheckpointIcon({
35
+ className,
36
+ children,
37
+ ...props
38
+ }: React.ComponentProps<typeof MarkerIcon>) {
39
+ return (
40
+ <MarkerIcon data-slot="checkpoint-icon" className={className} {...props}>
41
+ {children ?? <IconBookmark />}
42
+ </MarkerIcon>
43
+ )
44
+ }
45
+
46
+ function CheckpointTrigger({
47
+ className,
48
+ children = "Restore",
49
+ tooltip,
50
+ ...props
51
+ }: React.ComponentProps<typeof Button> & {
52
+ tooltip?: string
53
+ }) {
54
+ const button = (
55
+ <Button
56
+ data-slot="checkpoint-trigger"
57
+ type="button"
58
+ size="xs"
59
+ variant="outline"
60
+ className={cn(className)}
61
+ {...props}
62
+ >
63
+ {children}
64
+ </Button>
65
+ )
66
+
67
+ if (!tooltip) {
68
+ return button
69
+ }
70
+
71
+ return (
72
+ <TooltipProvider>
73
+ <Tooltip>
74
+ <TooltipTrigger asChild>{button}</TooltipTrigger>
75
+ <TooltipContent>{tooltip}</TooltipContent>
76
+ </Tooltip>
77
+ </TooltipProvider>
78
+ )
79
+ }
80
+
81
+ function CheckpointContent({
82
+ className,
83
+ ...props
84
+ }: React.ComponentProps<typeof MarkerContent>) {
85
+ return (
86
+ <MarkerContent
87
+ data-slot="checkpoint-content"
88
+ className={cn("inline-flex items-center gap-2", className)}
89
+ {...props}
90
+ />
91
+ )
92
+ }
93
+
94
+ export { Checkpoint, CheckpointIcon, CheckpointContent, CheckpointTrigger }