@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.
- package/CHANGELOG.md +21 -0
- package/README.md +1 -1
- package/dist/components/accordion.mjs +2 -2
- package/dist/components/alert-dialog.d.mts +1 -1
- package/dist/components/alert-dialog.mjs +4 -4
- package/dist/components/alert.d.mts +3 -13
- package/dist/components/alert.mjs +3 -23
- package/dist/components/badge.d.mts +3 -15
- package/dist/components/badge.mjs +2 -44
- package/dist/components/breadcrumb.mjs +1 -1
- package/dist/components/button-group.d.mts +3 -13
- package/dist/components/button-group.mjs +3 -24
- package/dist/components/button.d.mts +3 -25
- package/dist/components/button.mjs +2 -72
- package/dist/components/calendar.mjs +2 -1
- package/dist/components/carousel.d.mts +1 -2
- package/dist/components/chart.d.mts +2 -4
- package/dist/components/checkbox.mjs +2 -2
- package/dist/components/context-menu.mjs +2 -2
- package/dist/components/dialog.d.mts +1 -1
- package/dist/components/dialog.mjs +4 -4
- package/dist/components/drawer.d.mts +1 -1
- package/dist/components/drawer.mjs +2 -2
- package/dist/components/dropdown-menu.mjs +2 -2
- package/dist/components/empty.d.mts +3 -13
- package/dist/components/empty.mjs +3 -18
- package/dist/components/field.d.mts +3 -14
- package/dist/components/field.mjs +3 -32
- package/dist/components/form.d.mts +2 -4
- package/dist/components/hover-card.mjs +1 -1
- package/dist/components/input-group.d.mts +4 -31
- package/dist/components/input-group.mjs +3 -90
- package/dist/components/input-number.mjs +4 -4
- package/dist/components/input-otp.mjs +2 -2
- package/dist/components/input.mjs +1 -1
- package/dist/components/item.d.mts +4 -29
- package/dist/components/item.mjs +3 -56
- package/dist/components/menubar.mjs +2 -2
- package/dist/components/native-select.mjs +1 -1
- package/dist/components/navigation-menu.d.mts +1 -6
- package/dist/components/navigation-menu.mjs +8 -15
- package/dist/components/pagination.d.mts +1 -1
- package/dist/components/pagination.mjs +1 -1
- package/dist/components/popover.mjs +1 -1
- package/dist/components/progress-circle.d.mts +3 -47
- package/dist/components/progress-circle.mjs +2 -47
- package/dist/components/progress.mjs +1 -1
- package/dist/components/radio-group.mjs +1 -1
- package/dist/components/radio.mjs +1 -1
- package/dist/components/scroll-area.d.mts +3 -19
- package/dist/components/scroll-area.mjs +4 -61
- package/dist/components/select.d.mts +1 -1
- package/dist/components/select.mjs +3 -3
- package/dist/components/separator.d.mts +3 -18
- package/dist/components/separator.mjs +3 -23
- package/dist/components/sheet.d.mts +6 -18
- package/dist/components/sheet.mjs +6 -49
- package/dist/components/sidebar.d.mts +4 -19
- package/dist/components/sidebar.mjs +10 -46
- package/dist/components/skeleton.mjs +1 -1
- package/dist/components/slider.mjs +1 -1
- package/dist/components/spinner.mjs +1 -1
- package/dist/components/switch.mjs +2 -2
- package/dist/components/table.mjs +1 -1
- package/dist/components/tabs.mjs +1 -1
- package/dist/components/textarea.mjs +1 -1
- package/dist/components/toggle-group.d.mts +3 -2
- package/dist/components/toggle-group.mjs +1 -1
- package/dist/components/toggle.d.mts +2 -21
- package/dist/components/toggle.mjs +2 -39
- package/dist/components/tooltip.mjs +1 -1
- package/dist/index.d.mts +31 -16
- package/dist/index.mjs +30 -15
- package/dist/lib/utils.d.mts +1 -12
- package/dist/lib/utils.mjs +1 -9
- package/dist/primitives/checkbox-group.d.mts +1 -2
- package/dist/primitives/input-number.d.mts +1 -2
- package/dist/primitives/input.d.mts +1 -2
- package/dist/primitives/progress-circle.d.mts +1 -2
- package/dist/variants/alert.d.mts +18 -0
- package/dist/variants/alert.mjs +25 -0
- package/dist/variants/badge.d.mts +20 -0
- package/dist/variants/badge.mjs +46 -0
- package/dist/variants/button-group.d.mts +18 -0
- package/dist/variants/button-group.mjs +26 -0
- package/dist/variants/button.d.mts +30 -0
- package/dist/variants/button.mjs +76 -0
- package/dist/variants/empty.d.mts +18 -0
- package/dist/variants/empty.mjs +20 -0
- package/dist/variants/field.d.mts +19 -0
- package/dist/variants/field.mjs +34 -0
- package/dist/variants/input-group.d.mts +43 -0
- package/dist/variants/input-group.mjs +93 -0
- package/dist/variants/item.d.mts +37 -0
- package/dist/variants/item.mjs +60 -0
- package/dist/variants/navigation-menu.d.mts +13 -0
- package/dist/variants/navigation-menu.mjs +12 -0
- package/dist/variants/progress-circle.d.mts +52 -0
- package/dist/variants/progress-circle.mjs +49 -0
- package/dist/variants/scroll-area.d.mts +24 -0
- package/dist/variants/scroll-area.mjs +63 -0
- package/dist/variants/separator.d.mts +23 -0
- package/dist/variants/separator.mjs +25 -0
- package/dist/variants/sheet.d.mts +20 -0
- package/dist/variants/sheet.mjs +50 -0
- package/dist/variants/sidebar.d.mts +23 -0
- package/dist/variants/sidebar.mjs +42 -0
- package/dist/variants/toggle.d.mts +23 -0
- package/dist/variants/toggle.mjs +43 -0
- package/package.json +168 -20
- package/src/components/accordion.tsx +156 -0
- package/src/components/alert-dialog.tsx +314 -0
- package/src/components/alert.tsx +86 -0
- package/src/components/aspect-ratio.tsx +28 -0
- package/src/components/avatar.tsx +84 -0
- package/src/components/badge.tsx +38 -0
- package/src/components/breadcrumb.tsx +197 -0
- package/src/components/button-group.tsx +107 -0
- package/src/components/button.tsx +66 -0
- package/src/components/calendar.tsx +277 -0
- package/src/components/card.tsx +175 -0
- package/src/components/carousel.tsx +367 -0
- package/src/components/chart.tsx +587 -0
- package/src/components/checkbox-cards.tsx +92 -0
- package/src/components/checkbox-group.tsx +83 -0
- package/src/components/checkbox.tsx +65 -0
- package/src/components/collapsible.tsx +60 -0
- package/src/components/command.tsx +311 -0
- package/src/components/context-menu.tsx +489 -0
- package/src/components/dialog.tsx +295 -0
- package/src/components/drawer.tsx +271 -0
- package/src/components/dropdown-menu.tsx +498 -0
- package/src/components/empty.tsx +169 -0
- package/src/components/field.tsx +362 -0
- package/src/components/form.tsx +300 -0
- package/src/components/hover-card.tsx +116 -0
- package/src/components/input-group.tsx +224 -0
- package/src/components/input-number.tsx +161 -0
- package/src/components/input-otp.tsx +151 -0
- package/src/components/input-password.tsx +74 -0
- package/src/components/input-search.tsx +98 -0
- package/src/components/input.tsx +52 -0
- package/src/components/item.tsx +280 -0
- package/src/components/kbd.tsx +59 -0
- package/src/components/label.tsx +44 -0
- package/src/components/menubar.tsx +531 -0
- package/src/components/native-select.tsx +96 -0
- package/src/components/navigation-menu.tsx +295 -0
- package/src/components/pagination.tsx +204 -0
- package/src/components/popover.tsx +139 -0
- package/src/components/progress-circle.tsx +203 -0
- package/src/components/progress.tsx +54 -0
- package/src/components/radio-cards.tsx +85 -0
- package/src/components/radio-group.tsx +79 -0
- package/src/components/radio.tsx +61 -0
- package/src/components/resizable.tsx +99 -0
- package/src/components/scroll-area.tsx +115 -0
- package/src/components/select.tsx +319 -0
- package/src/components/separator.tsx +74 -0
- package/src/components/sheet.tsx +278 -0
- package/src/components/sidebar.tsx +1056 -0
- package/src/components/skeleton.tsx +37 -0
- package/src/components/slider.tsx +95 -0
- package/src/components/sonner.tsx +47 -0
- package/src/components/spinner.tsx +75 -0
- package/src/components/switch.tsx +66 -0
- package/src/components/table.tsx +200 -0
- package/src/components/tabs.tsx +128 -0
- package/src/components/textarea.tsx +49 -0
- package/src/components/toggle-group.tsx +141 -0
- package/src/components/toggle.tsx +39 -0
- package/src/components/tooltip.tsx +141 -0
- package/src/css/amber.css +59 -22
- package/src/css/blue.css +59 -22
- package/src/css/cyan.css +59 -22
- package/src/css/emerald.css +59 -22
- package/src/css/fuchsia.css +59 -22
- package/src/css/gray.css +59 -22
- package/src/css/green.css +59 -22
- package/src/css/indigo.css +59 -22
- package/src/css/lime.css +59 -22
- package/src/css/neutral.css +59 -22
- package/src/css/orange.css +59 -22
- package/src/css/pink.css +59 -22
- package/src/css/preset.css +32 -13
- package/src/css/purple.css +59 -22
- package/src/css/red.css +59 -22
- package/src/css/rose.css +59 -22
- package/src/css/sky.css +59 -22
- package/src/css/slate.css +59 -22
- package/src/css/stone.css +59 -22
- package/src/css/teal.css +59 -22
- package/src/css/violet.css +59 -22
- package/src/css/yellow.css +59 -22
- package/src/css/zinc.css +59 -22
- package/src/hooks/use-animated-value.ts +97 -0
- package/src/hooks/use-copy-to-clipboard.ts +63 -0
- package/src/hooks/use-is-mobile.ts +27 -0
- package/src/hooks/use-media-query.ts +71 -0
- package/src/hooks/use-mutation-observer.ts +54 -0
- package/src/hooks/use-pagination.ts +166 -0
- package/src/index.ts +720 -0
- package/src/lib/utils.ts +5 -0
- package/src/primitives/checkbox-group.tsx +360 -0
- package/src/primitives/input-number.tsx +1013 -0
- package/src/primitives/input.tsx +243 -0
- package/src/primitives/progress-circle.tsx +537 -0
- package/src/variants/alert.ts +45 -0
- package/src/variants/badge.ts +66 -0
- package/src/variants/button-group.ts +49 -0
- package/src/variants/button.ts +93 -0
- package/src/variants/empty.ts +43 -0
- package/src/variants/field.ts +50 -0
- package/src/variants/input-group.ts +132 -0
- package/src/variants/item.ts +90 -0
- package/src/variants/navigation-menu.ts +32 -0
- package/src/variants/progress-circle.ts +47 -0
- package/src/variants/scroll-area.ts +79 -0
- package/src/variants/separator.ts +41 -0
- package/src/variants/sheet.ts +70 -0
- package/src/variants/sidebar.ts +61 -0
- package/src/variants/toggle.ts +59 -0
- 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 };
|