@codefast/ui 0.3.16-canary.2 → 0.3.16-canary.3

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 (223) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +1 -1
  3. package/dist/components/accordion.mjs +2 -2
  4. package/dist/components/alert-dialog.d.mts +1 -1
  5. package/dist/components/alert-dialog.mjs +4 -4
  6. package/dist/components/alert.d.mts +3 -13
  7. package/dist/components/alert.mjs +3 -23
  8. package/dist/components/badge.d.mts +3 -15
  9. package/dist/components/badge.mjs +2 -44
  10. package/dist/components/breadcrumb.mjs +1 -1
  11. package/dist/components/button-group.d.mts +3 -13
  12. package/dist/components/button-group.mjs +3 -24
  13. package/dist/components/button.d.mts +3 -25
  14. package/dist/components/button.mjs +2 -72
  15. package/dist/components/calendar.mjs +2 -1
  16. package/dist/components/carousel.d.mts +1 -2
  17. package/dist/components/chart.d.mts +2 -4
  18. package/dist/components/checkbox.mjs +2 -2
  19. package/dist/components/context-menu.mjs +2 -2
  20. package/dist/components/dialog.d.mts +1 -1
  21. package/dist/components/dialog.mjs +4 -4
  22. package/dist/components/drawer.d.mts +1 -1
  23. package/dist/components/drawer.mjs +2 -2
  24. package/dist/components/dropdown-menu.mjs +2 -2
  25. package/dist/components/empty.d.mts +3 -13
  26. package/dist/components/empty.mjs +3 -18
  27. package/dist/components/field.d.mts +3 -14
  28. package/dist/components/field.mjs +3 -32
  29. package/dist/components/form.d.mts +2 -4
  30. package/dist/components/hover-card.mjs +1 -1
  31. package/dist/components/input-group.d.mts +4 -31
  32. package/dist/components/input-group.mjs +3 -90
  33. package/dist/components/input-number.mjs +4 -4
  34. package/dist/components/input-otp.mjs +2 -2
  35. package/dist/components/input.mjs +1 -1
  36. package/dist/components/item.d.mts +4 -29
  37. package/dist/components/item.mjs +3 -56
  38. package/dist/components/menubar.mjs +2 -2
  39. package/dist/components/native-select.mjs +1 -1
  40. package/dist/components/navigation-menu.d.mts +1 -6
  41. package/dist/components/navigation-menu.mjs +8 -15
  42. package/dist/components/pagination.d.mts +1 -1
  43. package/dist/components/pagination.mjs +1 -1
  44. package/dist/components/popover.mjs +1 -1
  45. package/dist/components/progress-circle.d.mts +3 -47
  46. package/dist/components/progress-circle.mjs +2 -47
  47. package/dist/components/progress.mjs +1 -1
  48. package/dist/components/radio-group.mjs +1 -1
  49. package/dist/components/radio.mjs +1 -1
  50. package/dist/components/scroll-area.d.mts +3 -19
  51. package/dist/components/scroll-area.mjs +4 -61
  52. package/dist/components/select.d.mts +1 -1
  53. package/dist/components/select.mjs +3 -3
  54. package/dist/components/separator.d.mts +3 -18
  55. package/dist/components/separator.mjs +3 -23
  56. package/dist/components/sheet.d.mts +6 -18
  57. package/dist/components/sheet.mjs +6 -49
  58. package/dist/components/sidebar.d.mts +4 -19
  59. package/dist/components/sidebar.mjs +10 -46
  60. package/dist/components/skeleton.mjs +1 -1
  61. package/dist/components/slider.mjs +1 -1
  62. package/dist/components/spinner.mjs +1 -1
  63. package/dist/components/switch.mjs +2 -2
  64. package/dist/components/table.mjs +1 -1
  65. package/dist/components/tabs.mjs +1 -1
  66. package/dist/components/textarea.mjs +1 -1
  67. package/dist/components/toggle-group.d.mts +3 -2
  68. package/dist/components/toggle-group.mjs +1 -1
  69. package/dist/components/toggle.d.mts +2 -21
  70. package/dist/components/toggle.mjs +2 -39
  71. package/dist/components/tooltip.mjs +1 -1
  72. package/dist/index.d.mts +31 -16
  73. package/dist/index.mjs +30 -15
  74. package/dist/lib/utils.d.mts +1 -12
  75. package/dist/lib/utils.mjs +1 -9
  76. package/dist/primitives/checkbox-group.d.mts +1 -2
  77. package/dist/primitives/input-number.d.mts +1 -2
  78. package/dist/primitives/input.d.mts +1 -2
  79. package/dist/primitives/progress-circle.d.mts +1 -2
  80. package/dist/variants/alert.d.mts +18 -0
  81. package/dist/variants/alert.mjs +25 -0
  82. package/dist/variants/badge.d.mts +20 -0
  83. package/dist/variants/badge.mjs +46 -0
  84. package/dist/variants/button-group.d.mts +18 -0
  85. package/dist/variants/button-group.mjs +26 -0
  86. package/dist/variants/button.d.mts +30 -0
  87. package/dist/variants/button.mjs +76 -0
  88. package/dist/variants/empty.d.mts +18 -0
  89. package/dist/variants/empty.mjs +20 -0
  90. package/dist/variants/field.d.mts +19 -0
  91. package/dist/variants/field.mjs +34 -0
  92. package/dist/variants/input-group.d.mts +43 -0
  93. package/dist/variants/input-group.mjs +93 -0
  94. package/dist/variants/item.d.mts +37 -0
  95. package/dist/variants/item.mjs +60 -0
  96. package/dist/variants/navigation-menu.d.mts +13 -0
  97. package/dist/variants/navigation-menu.mjs +12 -0
  98. package/dist/variants/progress-circle.d.mts +52 -0
  99. package/dist/variants/progress-circle.mjs +49 -0
  100. package/dist/variants/scroll-area.d.mts +24 -0
  101. package/dist/variants/scroll-area.mjs +63 -0
  102. package/dist/variants/separator.d.mts +23 -0
  103. package/dist/variants/separator.mjs +25 -0
  104. package/dist/variants/sheet.d.mts +20 -0
  105. package/dist/variants/sheet.mjs +50 -0
  106. package/dist/variants/sidebar.d.mts +23 -0
  107. package/dist/variants/sidebar.mjs +42 -0
  108. package/dist/variants/toggle.d.mts +23 -0
  109. package/dist/variants/toggle.mjs +43 -0
  110. package/package.json +168 -20
  111. package/src/components/accordion.tsx +156 -0
  112. package/src/components/alert-dialog.tsx +314 -0
  113. package/src/components/alert.tsx +86 -0
  114. package/src/components/aspect-ratio.tsx +28 -0
  115. package/src/components/avatar.tsx +84 -0
  116. package/src/components/badge.tsx +38 -0
  117. package/src/components/breadcrumb.tsx +197 -0
  118. package/src/components/button-group.tsx +107 -0
  119. package/src/components/button.tsx +66 -0
  120. package/src/components/calendar.tsx +277 -0
  121. package/src/components/card.tsx +175 -0
  122. package/src/components/carousel.tsx +367 -0
  123. package/src/components/chart.tsx +587 -0
  124. package/src/components/checkbox-cards.tsx +92 -0
  125. package/src/components/checkbox-group.tsx +83 -0
  126. package/src/components/checkbox.tsx +65 -0
  127. package/src/components/collapsible.tsx +60 -0
  128. package/src/components/command.tsx +311 -0
  129. package/src/components/context-menu.tsx +489 -0
  130. package/src/components/dialog.tsx +295 -0
  131. package/src/components/drawer.tsx +271 -0
  132. package/src/components/dropdown-menu.tsx +498 -0
  133. package/src/components/empty.tsx +169 -0
  134. package/src/components/field.tsx +362 -0
  135. package/src/components/form.tsx +300 -0
  136. package/src/components/hover-card.tsx +116 -0
  137. package/src/components/input-group.tsx +224 -0
  138. package/src/components/input-number.tsx +161 -0
  139. package/src/components/input-otp.tsx +151 -0
  140. package/src/components/input-password.tsx +74 -0
  141. package/src/components/input-search.tsx +98 -0
  142. package/src/components/input.tsx +52 -0
  143. package/src/components/item.tsx +280 -0
  144. package/src/components/kbd.tsx +59 -0
  145. package/src/components/label.tsx +44 -0
  146. package/src/components/menubar.tsx +531 -0
  147. package/src/components/native-select.tsx +96 -0
  148. package/src/components/navigation-menu.tsx +295 -0
  149. package/src/components/pagination.tsx +204 -0
  150. package/src/components/popover.tsx +139 -0
  151. package/src/components/progress-circle.tsx +203 -0
  152. package/src/components/progress.tsx +54 -0
  153. package/src/components/radio-cards.tsx +85 -0
  154. package/src/components/radio-group.tsx +79 -0
  155. package/src/components/radio.tsx +61 -0
  156. package/src/components/resizable.tsx +99 -0
  157. package/src/components/scroll-area.tsx +115 -0
  158. package/src/components/select.tsx +319 -0
  159. package/src/components/separator.tsx +74 -0
  160. package/src/components/sheet.tsx +278 -0
  161. package/src/components/sidebar.tsx +1056 -0
  162. package/src/components/skeleton.tsx +37 -0
  163. package/src/components/slider.tsx +95 -0
  164. package/src/components/sonner.tsx +47 -0
  165. package/src/components/spinner.tsx +75 -0
  166. package/src/components/switch.tsx +66 -0
  167. package/src/components/table.tsx +200 -0
  168. package/src/components/tabs.tsx +128 -0
  169. package/src/components/textarea.tsx +49 -0
  170. package/src/components/toggle-group.tsx +141 -0
  171. package/src/components/toggle.tsx +39 -0
  172. package/src/components/tooltip.tsx +141 -0
  173. package/src/css/amber.css +59 -22
  174. package/src/css/blue.css +59 -22
  175. package/src/css/cyan.css +59 -22
  176. package/src/css/emerald.css +59 -22
  177. package/src/css/fuchsia.css +59 -22
  178. package/src/css/gray.css +59 -22
  179. package/src/css/green.css +59 -22
  180. package/src/css/indigo.css +59 -22
  181. package/src/css/lime.css +59 -22
  182. package/src/css/neutral.css +59 -22
  183. package/src/css/orange.css +59 -22
  184. package/src/css/pink.css +59 -22
  185. package/src/css/preset.css +32 -13
  186. package/src/css/purple.css +59 -22
  187. package/src/css/red.css +59 -22
  188. package/src/css/rose.css +59 -22
  189. package/src/css/sky.css +59 -22
  190. package/src/css/slate.css +59 -22
  191. package/src/css/stone.css +59 -22
  192. package/src/css/teal.css +59 -22
  193. package/src/css/violet.css +59 -22
  194. package/src/css/yellow.css +59 -22
  195. package/src/css/zinc.css +59 -22
  196. package/src/hooks/use-animated-value.ts +97 -0
  197. package/src/hooks/use-copy-to-clipboard.ts +63 -0
  198. package/src/hooks/use-is-mobile.ts +27 -0
  199. package/src/hooks/use-media-query.ts +71 -0
  200. package/src/hooks/use-mutation-observer.ts +54 -0
  201. package/src/hooks/use-pagination.ts +166 -0
  202. package/src/index.ts +720 -0
  203. package/src/lib/utils.ts +5 -0
  204. package/src/primitives/checkbox-group.tsx +360 -0
  205. package/src/primitives/input-number.tsx +1013 -0
  206. package/src/primitives/input.tsx +243 -0
  207. package/src/primitives/progress-circle.tsx +537 -0
  208. package/src/variants/alert.ts +45 -0
  209. package/src/variants/badge.ts +66 -0
  210. package/src/variants/button-group.ts +49 -0
  211. package/src/variants/button.ts +93 -0
  212. package/src/variants/empty.ts +43 -0
  213. package/src/variants/field.ts +50 -0
  214. package/src/variants/input-group.ts +132 -0
  215. package/src/variants/item.ts +90 -0
  216. package/src/variants/navigation-menu.ts +32 -0
  217. package/src/variants/progress-circle.ts +47 -0
  218. package/src/variants/scroll-area.ts +79 -0
  219. package/src/variants/separator.ts +41 -0
  220. package/src/variants/sheet.ts +70 -0
  221. package/src/variants/sidebar.ts +61 -0
  222. package/src/variants/toggle.ts +59 -0
  223. package/dist/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/clsx.d.mts +0 -6
@@ -0,0 +1,537 @@
1
+ "use client";
2
+
3
+ import type { Scope } from "@radix-ui/react-context";
4
+ import type { ComponentProps, ReactNode } from "react";
5
+
6
+ import { createContextScope } from "@radix-ui/react-context";
7
+ import { useId, useMemo } from "react";
8
+
9
+ /* -----------------------------------------------------------------------------
10
+ * Context: ProgressCircleProvider
11
+ * --------------------------------------------------------------------------- */
12
+
13
+ const PROGRESS_CIRCLE_PROVIDER_NAME = "ProgressCircleProvider";
14
+
15
+ type ScopedProps<P> = P & { __scopeProgressCircle?: Scope };
16
+
17
+ /**
18
+ * Defines color thresholds based on progress values
19
+ */
20
+ interface Threshold {
21
+ /**
22
+ * Background color to be applied
23
+ */
24
+ background: string;
25
+
26
+ /**
27
+ * Foreground color to be applied
28
+ */
29
+ color: string;
30
+
31
+ /**
32
+ * The value at which this threshold becomes active
33
+ */
34
+ value: number;
35
+ }
36
+
37
+ /**
38
+ * Props for the ProgressCircleProvider context
39
+ */
40
+ interface ProgressCircleContextValue {
41
+ /**
42
+ * Center coordinate of the circle (half of size)
43
+ */
44
+ center: number;
45
+
46
+ /**
47
+ * Total circumference of the circle for stroke calculations
48
+ */
49
+ circumference: number;
50
+
51
+ /**
52
+ * Clamped progress value, undefined for indeterminate state
53
+ */
54
+ clampedValue: number | undefined;
55
+
56
+ /**
57
+ * Unique identifier for the progress circle
58
+ */
59
+ id: string;
60
+
61
+ /**
62
+ * Maximum progress value
63
+ */
64
+ max: number;
65
+
66
+ /**
67
+ * Minimum progress value
68
+ */
69
+ min: number;
70
+
71
+ /**
72
+ * Radius of the circle for SVG rendering
73
+ */
74
+ radius: number;
75
+
76
+ /**
77
+ * CSS transform for rotating the progress indicator
78
+ */
79
+ rotationTransform: string;
80
+
81
+ /**
82
+ * Size of the progress circle in pixels
83
+ */
84
+ size: number;
85
+
86
+ /**
87
+ * Stroke dash offset for progress visualization
88
+ */
89
+ strokeDashoffset: number;
90
+
91
+ /**
92
+ * Width of the stroke for the progress circle
93
+ */
94
+ strokeWidth: number;
95
+
96
+ /**
97
+ * Threshold configuration for color changes
98
+ */
99
+ threshold: Threshold | undefined;
100
+
101
+ /**
102
+ * Raw progress value (can be null/undefined for indeterminate)
103
+ */
104
+ value: null | number | undefined;
105
+
106
+ /**
107
+ * Text representation of the current value for accessibility
108
+ */
109
+ valueText: string;
110
+ }
111
+
112
+ const [createProgressCircleContext, createProgressCircleScope] = createContextScope(
113
+ PROGRESS_CIRCLE_PROVIDER_NAME,
114
+ );
115
+
116
+ const [ProgressCircleContextProvider, useProgressCircleContext] =
117
+ createProgressCircleContext<ProgressCircleContextValue>(PROGRESS_CIRCLE_PROVIDER_NAME);
118
+
119
+ /* -----------------------------------------------------------------------------
120
+ * Component: ProgressCircleProvider
121
+ * --------------------------------------------------------------------------- */
122
+
123
+ /**
124
+ * @since 0.3.16-canary.0
125
+ */
126
+ interface ProgressCircleProviderProps {
127
+ /**
128
+ * React children to be rendered inside the progress circle
129
+ */
130
+ children: ReactNode;
131
+
132
+ /**
133
+ * Custom function to format the numeric value for display
134
+ */
135
+ formatValue?: (value: number) => string;
136
+
137
+ /**
138
+ * Unique identifier for the progress circle component
139
+ */
140
+ id?: string;
141
+
142
+ /**
143
+ * Maximum value of the progress (defaults to 100)
144
+ */
145
+ max?: number;
146
+
147
+ /**
148
+ * Minimum value of the progress (defaults to 0)
149
+ */
150
+ min?: number;
151
+
152
+ /**
153
+ * Size of the progress circle in pixels
154
+ */
155
+ size?: number;
156
+
157
+ /**
158
+ * Starting angle of the progress circle in degrees (0 = top)
159
+ */
160
+ startAngle?: number;
161
+
162
+ /**
163
+ * Width of the progress circle's stroke in pixels
164
+ */
165
+ strokeWidth?: number;
166
+
167
+ /**
168
+ * Array of threshold configurations for different value ranges
169
+ */
170
+ thresholds?: Array<Threshold>;
171
+
172
+ /**
173
+ * Current progress value (null for indeterminate state)
174
+ */
175
+ value?: null | number;
176
+ }
177
+
178
+ /**
179
+ * Provides context for the ProgressCircle component
180
+ *
181
+ * Manages calculations for rendering the circular progress indicator,
182
+ * including value clamping, sizing, thresholds, and indeterminate state.
183
+ *
184
+ * @example
185
+ * ```tsx
186
+ * <ProgressCircleProvider
187
+ * value={75}
188
+ * min={0}
189
+ * max={100}
190
+ * size={64}
191
+ * thresholds={[
192
+ * { value: 30, color: 'red', background: 'pink' },
193
+ * { value: 70, color: 'yellow', background: 'lightyellow' },
194
+ * { value: 100, color: 'green', background: 'lightgreen' }
195
+ * ]}
196
+ * >
197
+ * <ProgressCircleSVG>
198
+ * <ProgressCircleIndicator />
199
+ * <ProgressCircleTrack />
200
+ * </ProgressCircleSVG>
201
+ * <ProgressCircleValue />
202
+ * </ProgressCircleProvider>
203
+ * ```
204
+ *
205
+ * @since 0.3.16-canary.0
206
+ */
207
+ function ProgressCircleProvider({
208
+ __scopeProgressCircle,
209
+ children,
210
+ formatValue,
211
+ id: propertyId,
212
+ max = 100,
213
+ min = 0,
214
+ size = 48,
215
+ startAngle = -90,
216
+ strokeWidth = 4,
217
+ thresholds,
218
+ value,
219
+ }: ScopedProps<ProgressCircleProviderProps>): ReactNode {
220
+ const uniqueId = useId();
221
+ const id = propertyId ?? uniqueId;
222
+
223
+ // Ensure size and stroke width are non-negative
224
+ const validSize = Math.max(0, size);
225
+ const validStrokeWidth = Math.max(0, strokeWidth);
226
+ const validStartAngle = startAngle % 360;
227
+
228
+ // Validate min and max, swap if min > max
229
+ let validMin = min;
230
+ let validMax = max;
231
+
232
+ if (validMin > validMax) {
233
+ [validMin, validMax] = [validMax, validMin];
234
+ }
235
+
236
+ // Handle indeterminate state
237
+ const isIndeterminate = value === null || value === undefined;
238
+ const clampedValue = isIndeterminate ? undefined : clamp(validMin, validMax, value);
239
+ const range = validMax - validMin;
240
+ const percentage =
241
+ clampedValue !== undefined && range > 0 ? ((clampedValue - validMin) / range) * 100 : 0;
242
+ const valueText =
243
+ clampedValue !== undefined && formatValue
244
+ ? formatValue(clampedValue)
245
+ : `${Math.round(percentage).toString()}%`;
246
+
247
+ // Sort thresholds by value
248
+ const sortedThresholds = useMemo(
249
+ () =>
250
+ thresholds && thresholds.length > 0
251
+ ? [...thresholds].toSorted((a, b) => a.value - b.value)
252
+ : [],
253
+ [thresholds],
254
+ );
255
+
256
+ // Determine an active threshold based on a clamped value
257
+ const threshold = useMemo(() => {
258
+ if (clampedValue === undefined) {
259
+ return;
260
+ }
261
+
262
+ for (const sortedThreshold of sortedThresholds) {
263
+ if (clampedValue <= sortedThreshold.value) {
264
+ return sortedThreshold;
265
+ }
266
+ }
267
+
268
+ return sortedThresholds.at(-1);
269
+ }, [sortedThresholds, clampedValue]);
270
+
271
+ // Calculate circle properties
272
+ const center = validSize / 2;
273
+ const radius = Math.max(0, center - validStrokeWidth / 2);
274
+ const circumference = 2 * Math.PI * radius;
275
+ const strokeDashoffset = circumference - (percentage / 100) * circumference;
276
+ const rotationTransform = `rotate(${validStartAngle.toString()}, 0, 0)`;
277
+
278
+ return (
279
+ <ProgressCircleContextProvider
280
+ center={center}
281
+ circumference={circumference}
282
+ clampedValue={clampedValue} // Undefined for indeterminate
283
+ id={id}
284
+ max={validMax}
285
+ min={validMin}
286
+ radius={radius}
287
+ rotationTransform={rotationTransform}
288
+ scope={__scopeProgressCircle}
289
+ size={validSize}
290
+ strokeDashoffset={strokeDashoffset}
291
+ strokeWidth={validStrokeWidth}
292
+ threshold={threshold}
293
+ value={value ?? 0}
294
+ valueText={valueText}
295
+ >
296
+ {children}
297
+ </ProgressCircleContextProvider>
298
+ );
299
+ }
300
+
301
+ /* -----------------------------------------------------------------------------
302
+ * Component: ProgressCircle
303
+ * --------------------------------------------------------------------------- */
304
+
305
+ /**
306
+ * @since 0.3.16-canary.0
307
+ */
308
+ type ProgressCircleProps = ComponentProps<"div">;
309
+
310
+ /**
311
+ * Root component for the progress circle
312
+ *
313
+ * Serves as a wrapper for other progress circle components.
314
+ *
315
+ * @since 0.3.16-canary.0
316
+ */
317
+ function ProgressCircle({
318
+ __scopeProgressCircle,
319
+ ...props
320
+ }: ScopedProps<ProgressCircleProps>): ReactNode {
321
+ return <div {...props} />;
322
+ }
323
+
324
+ /* -----------------------------------------------------------------------------
325
+ * Component: ProgressCircleSVG
326
+ * --------------------------------------------------------------------------- */
327
+
328
+ const PROGRESS_CIRCLE_SVG_NAME = "ProgressCircleSVG";
329
+
330
+ /**
331
+ * @since 0.3.16-canary.0
332
+ */
333
+ type ProgressCircleSVGProps = ComponentProps<"svg">;
334
+
335
+ /**
336
+ * SVG container for the progress circle
337
+ *
338
+ * Renders the SVG with accessibility attributes and supports indeterminate state.
339
+ *
340
+ * @since 0.3.16-canary.0
341
+ */
342
+ function ProgressCircleSVG({
343
+ __scopeProgressCircle,
344
+ ...props
345
+ }: ScopedProps<ProgressCircleSVGProps>): ReactNode {
346
+ const { clampedValue, id, max, min, size, valueText } = useProgressCircleContext(
347
+ PROGRESS_CIRCLE_SVG_NAME,
348
+ __scopeProgressCircle,
349
+ );
350
+
351
+ return (
352
+ <svg
353
+ aria-label="Progress"
354
+ aria-valuemax={max}
355
+ aria-valuemin={min}
356
+ aria-valuenow={clampedValue} // Undefined for indeterminate state
357
+ aria-valuetext={clampedValue === undefined ? undefined : valueText}
358
+ height={size}
359
+ id={id}
360
+ role="progressbar"
361
+ viewBox={`0 0 ${size.toString()} ${size.toString()}`}
362
+ width={size}
363
+ {...props}
364
+ />
365
+ );
366
+ }
367
+
368
+ /* -----------------------------------------------------------------------------
369
+ * Component: ProgressCircleTrack
370
+ * --------------------------------------------------------------------------- */
371
+
372
+ const PROGRESS_CIRCLE_TRACK_NAME = "ProgressCircleTrack";
373
+
374
+ /**
375
+ * @since 0.3.16-canary.0
376
+ */
377
+ type ProgressCircleTrackProps = ComponentProps<"circle">;
378
+
379
+ /**
380
+ * Background circle for the progress indicator
381
+ *
382
+ * Renders the static track of the progress circle.
383
+ *
384
+ * @since 0.3.16-canary.0
385
+ */
386
+ function ProgressCircleTrack({
387
+ __scopeProgressCircle,
388
+ ...props
389
+ }: ScopedProps<ProgressCircleTrackProps>): ReactNode {
390
+ const { center, radius, strokeWidth, threshold } = useProgressCircleContext(
391
+ PROGRESS_CIRCLE_TRACK_NAME,
392
+ __scopeProgressCircle,
393
+ );
394
+
395
+ return (
396
+ <circle
397
+ cx={center}
398
+ cy={center}
399
+ fill="transparent"
400
+ r={radius}
401
+ stroke={threshold?.background ?? "currentColor"}
402
+ strokeWidth={strokeWidth}
403
+ {...props}
404
+ />
405
+ );
406
+ }
407
+
408
+ /* -----------------------------------------------------------------------------
409
+ * Component: ProgressCircleIndicator
410
+ * --------------------------------------------------------------------------- */
411
+
412
+ const PROGRESS_CIRCLE_INDICATOR_NAME = "ProgressCircleIndicator";
413
+
414
+ /**
415
+ * @since 0.3.16-canary.0
416
+ */
417
+ type ProgressCircleIndicatorProps = ComponentProps<"circle">;
418
+
419
+ /**
420
+ * Foreground circle showing progress
421
+ *
422
+ * Renders the dynamic progress indicator with stroke dash properties.
423
+ *
424
+ * @since 0.3.16-canary.0
425
+ */
426
+ function ProgressCircleIndicator({
427
+ __scopeProgressCircle,
428
+ ...props
429
+ }: ScopedProps<ProgressCircleIndicatorProps>): ReactNode {
430
+ const {
431
+ center,
432
+ circumference,
433
+ radius,
434
+ rotationTransform,
435
+ strokeDashoffset,
436
+ strokeWidth,
437
+ threshold,
438
+ } = useProgressCircleContext(PROGRESS_CIRCLE_INDICATOR_NAME, __scopeProgressCircle);
439
+
440
+ return (
441
+ <circle
442
+ cx={center}
443
+ cy={center}
444
+ fill="transparent"
445
+ r={radius}
446
+ stroke={threshold?.color ?? "currentColor"}
447
+ strokeDasharray={circumference}
448
+ strokeDashoffset={strokeDashoffset}
449
+ strokeLinecap="round"
450
+ strokeWidth={strokeWidth}
451
+ transform={rotationTransform}
452
+ {...props}
453
+ />
454
+ );
455
+ }
456
+
457
+ /* -----------------------------------------------------------------------------
458
+ * Component: ProgressCircleValue
459
+ * --------------------------------------------------------------------------- */
460
+
461
+ const PROGRESS_CIRCLE_VALUE_NAME = "ProgressCircleValue";
462
+
463
+ /**
464
+ * @since 0.3.16-canary.0
465
+ */
466
+ interface ProgressCircleValueProps extends Omit<ComponentProps<"div">, "children"> {
467
+ children?: ((context: { value: number | undefined; valueText: string }) => ReactNode) | ReactNode;
468
+ }
469
+
470
+ /**
471
+ * Displays the current progress value
472
+ *
473
+ * Supports custom content or default value text rendering.
474
+ *
475
+ * @since 0.3.16-canary.0
476
+ */
477
+ function ProgressCircleValue({
478
+ __scopeProgressCircle,
479
+ children,
480
+ ...props
481
+ }: ScopedProps<ProgressCircleValueProps>): ReactNode {
482
+ const { clampedValue, valueText } = useProgressCircleContext(
483
+ PROGRESS_CIRCLE_VALUE_NAME,
484
+ __scopeProgressCircle,
485
+ );
486
+
487
+ if (typeof children === "function") {
488
+ return children({ value: clampedValue, valueText });
489
+ }
490
+
491
+ return <div {...props}>{children ?? valueText}</div>;
492
+ }
493
+
494
+ /* -----------------------------------------------------------------------------
495
+ * Helpers
496
+ * -------------------------------------------------------------------------- */
497
+
498
+ /**
499
+ * Clamps a value within a specified min/max range
500
+ *
501
+ * @param min - Minimum value
502
+ * @param max - Maximum value
503
+ * @param value - Value to clamp
504
+ * @returns Clamped value
505
+ */
506
+ function clamp(min: number, max: number, value: number): number {
507
+ return Math.min(max, Math.max(min, value));
508
+ }
509
+
510
+ /* -----------------------------------------------------------------------------
511
+ * Exports
512
+ * -------------------------------------------------------------------------- */
513
+
514
+ export {
515
+ createProgressCircleScope,
516
+ ProgressCircleIndicator as Indicator,
517
+ ProgressCircle,
518
+ ProgressCircleIndicator,
519
+ ProgressCircleProvider,
520
+ ProgressCircleSVG,
521
+ ProgressCircleTrack,
522
+ ProgressCircleValue,
523
+ ProgressCircleProvider as Provider,
524
+ ProgressCircle as Root,
525
+ ProgressCircleSVG as SVG,
526
+ ProgressCircleTrack as Track,
527
+ ProgressCircleValue as Value,
528
+ };
529
+
530
+ export type {
531
+ ProgressCircleIndicatorProps,
532
+ ProgressCircleProps,
533
+ ProgressCircleProviderProps,
534
+ ProgressCircleSVGProps,
535
+ ProgressCircleTrackProps,
536
+ ProgressCircleValueProps,
537
+ };
@@ -0,0 +1,45 @@
1
+ import type { VariantProps } from "#/lib/utils";
2
+
3
+ import { tv } from "#/lib/utils";
4
+
5
+ /* -----------------------------------------------------------------------------
6
+ * Variant: Alert
7
+ * -------------------------------------------------------------------------- */
8
+
9
+ /**
10
+ * @since 0.3.16-canary.0
11
+ */
12
+ const alertVariants = tv({
13
+ base: [
14
+ "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 px-4 py-3",
15
+ "rounded-xl border",
16
+ "bg-card text-sm",
17
+ "has-[>svg]:grid-cols-[--spacing(4)_1fr] has-[>svg]:gap-x-3",
18
+ "[&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
19
+ ],
20
+ defaultVariants: {
21
+ variant: "default",
22
+ },
23
+ variants: {
24
+ variant: {
25
+ default: "text-card-foreground",
26
+ destructive: [
27
+ "text-destructive",
28
+ "*:data-[slot=alert-description]:text-destructive/90",
29
+ "[&>svg]:text-current",
30
+ ],
31
+ },
32
+ },
33
+ });
34
+
35
+ /**
36
+ * @since 0.3.16-canary.0
37
+ */
38
+ type AlertVariants = VariantProps<typeof alertVariants>;
39
+
40
+ /* -----------------------------------------------------------------------------
41
+ * Exports
42
+ * -------------------------------------------------------------------------- */
43
+
44
+ export { alertVariants };
45
+ export type { AlertVariants };
@@ -0,0 +1,66 @@
1
+ import type { VariantProps } from "#/lib/utils";
2
+
3
+ import { tv } from "#/lib/utils";
4
+
5
+ /* -----------------------------------------------------------------------------
6
+ * Variant: Badge
7
+ * -------------------------------------------------------------------------- */
8
+
9
+ /**
10
+ * @since 0.3.16-canary.0
11
+ */
12
+ const badgeVariants = tv({
13
+ base: [
14
+ "inline-flex w-fit shrink-0 items-center justify-center gap-2 px-1.5 py-0.5",
15
+ "rounded-md border outline-hidden",
16
+ "text-xs font-medium whitespace-nowrap",
17
+ "transition",
18
+ "focus-visible:ring-3 focus-visible:ring-ring/50",
19
+ "[&>svg]:size-3 [&>svg]:shrink-0",
20
+ ],
21
+ defaultVariants: {
22
+ variant: "default",
23
+ },
24
+ variants: {
25
+ variant: {
26
+ default: [
27
+ "border-transparent",
28
+ "bg-primary text-primary-foreground",
29
+ "focus-visible:ring-primary/20",
30
+ "dark:focus-visible:ring-primary/40",
31
+ "[a&]:hover:bg-primary/80",
32
+ ],
33
+ destructive: [
34
+ "border-transparent",
35
+ "bg-destructive text-white",
36
+ "focus-visible:ring-destructive/20",
37
+ "dark:bg-destructive/60",
38
+ "dark:focus-visible:ring-destructive/40",
39
+ "[a&]:hover:bg-destructive/90",
40
+ ],
41
+ outline: [
42
+ "border-input",
43
+ "bg-background",
44
+ "focus-visible:border-ring",
45
+ "[a&]:hover:border-ring/60 [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
46
+ ],
47
+ secondary: [
48
+ "border-transparent",
49
+ "bg-secondary text-secondary-foreground",
50
+ "[a&]:hover:bg-secondary/80",
51
+ ],
52
+ },
53
+ },
54
+ });
55
+
56
+ /**
57
+ * @since 0.3.16-canary.0
58
+ */
59
+ type BadgeVariants = VariantProps<typeof badgeVariants>;
60
+
61
+ /* -----------------------------------------------------------------------------
62
+ * Exports
63
+ * -------------------------------------------------------------------------- */
64
+
65
+ export { badgeVariants };
66
+ export type { BadgeVariants };
@@ -0,0 +1,49 @@
1
+ import type { VariantProps } from "#/lib/utils";
2
+
3
+ import { tv } from "#/lib/utils";
4
+
5
+ /* -----------------------------------------------------------------------------
6
+ * Variant: ButtonGroup
7
+ * -------------------------------------------------------------------------- */
8
+
9
+ /**
10
+ * @since 0.3.16-canary.0
11
+ */
12
+ const buttonGroupVariants = tv({
13
+ base: [
14
+ "flex w-fit items-stretch",
15
+ "has-[>[data-slot=button-group]]:gap-2",
16
+ "[&>*]:focus-visible:relative [&>*]:focus-visible:z-10",
17
+ "has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg",
18
+ "[&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit",
19
+ "[&>input]:flex-1",
20
+ ],
21
+ defaultVariants: {
22
+ orientation: "horizontal",
23
+ },
24
+ variants: {
25
+ orientation: {
26
+ horizontal: [
27
+ "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0",
28
+ "[&>*:not(:last-child)]:rounded-r-none",
29
+ ],
30
+ vertical: [
31
+ "flex-col",
32
+ "[&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0",
33
+ "[&>*:not(:last-child)]:rounded-b-none",
34
+ ],
35
+ },
36
+ },
37
+ });
38
+
39
+ /**
40
+ * @since 0.3.16-canary.0
41
+ */
42
+ type ButtonGroupVariants = VariantProps<typeof buttonGroupVariants>;
43
+
44
+ /* -----------------------------------------------------------------------------
45
+ * Exports
46
+ * -------------------------------------------------------------------------- */
47
+
48
+ export { buttonGroupVariants };
49
+ export type { ButtonGroupVariants };