@luxfi/ui 5.6.0 → 6.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 (155) hide show
  1. package/dist/components/Badge.d.ts.map +1 -0
  2. package/dist/components/Badge.js +91 -0
  3. package/dist/components/Button.d.ts.map +1 -0
  4. package/dist/components/Button.js +137 -0
  5. package/dist/components/Card.d.ts.map +1 -0
  6. package/dist/components/Card.js +86 -0
  7. package/dist/components/IconButton.d.ts.map +1 -0
  8. package/dist/components/IconButton.js +64 -0
  9. package/dist/components/Input.d.ts.map +1 -0
  10. package/dist/components/Input.js +71 -0
  11. package/dist/components/Modal.d.ts.map +1 -0
  12. package/dist/components/Modal.js +98 -0
  13. package/dist/components/Skeleton.d.ts.map +1 -0
  14. package/dist/components/Skeleton.js +44 -0
  15. package/dist/components/Spinner.d.ts.map +1 -0
  16. package/dist/components/Spinner.js +42 -0
  17. package/dist/components/Switch.d.ts.map +1 -0
  18. package/dist/components/Switch.js +10 -0
  19. package/dist/components/TokenLogo.d.ts.map +1 -0
  20. package/dist/components/TokenLogo.js +57 -0
  21. package/dist/components/Tooltip.d.ts.map +1 -0
  22. package/dist/components/Tooltip.js +34 -0
  23. package/dist/components/index.d.ts.map +1 -0
  24. package/dist/components/index.js +19 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +35 -5618
  27. package/dist/theme/index.d.ts.map +1 -0
  28. package/dist/theme/index.js +6 -0
  29. package/dist/theme/themes.d.ts.map +1 -0
  30. package/dist/theme/themes.js +73 -0
  31. package/dist/theme/tokens.d.ts.map +1 -0
  32. package/dist/theme/tokens.js +101 -0
  33. package/package.json +81 -278
  34. package/dist/accordion.cjs +0 -213
  35. package/dist/accordion.js +0 -186
  36. package/dist/alert.cjs +0 -553
  37. package/dist/alert.js +0 -531
  38. package/dist/avatar.cjs +0 -149
  39. package/dist/avatar.js +0 -125
  40. package/dist/badge.cjs +0 -611
  41. package/dist/badge.js +0 -589
  42. package/dist/button.cjs +0 -689
  43. package/dist/button.js +0 -664
  44. package/dist/checkbox.cjs +0 -265
  45. package/dist/checkbox.js +0 -241
  46. package/dist/close-button.cjs +0 -73
  47. package/dist/close-button.js +0 -51
  48. package/dist/collapsible.cjs +0 -702
  49. package/dist/collapsible.js +0 -679
  50. package/dist/color-mode.cjs +0 -96
  51. package/dist/color-mode.js +0 -72
  52. package/dist/dialog.cjs +0 -279
  53. package/dist/dialog.js +0 -246
  54. package/dist/drawer.cjs +0 -207
  55. package/dist/drawer.js +0 -175
  56. package/dist/empty-state.cjs +0 -93
  57. package/dist/empty-state.js +0 -71
  58. package/dist/field.cjs +0 -183
  59. package/dist/field.js +0 -160
  60. package/dist/heading.cjs +0 -46
  61. package/dist/heading.js +0 -40
  62. package/dist/icon-button.cjs +0 -491
  63. package/dist/icon-button.js +0 -470
  64. package/dist/image.cjs +0 -572
  65. package/dist/image.js +0 -551
  66. package/dist/index.cjs +0 -5779
  67. package/dist/input-group.cjs +0 -155
  68. package/dist/input-group.js +0 -133
  69. package/dist/input.cjs +0 -65
  70. package/dist/input.js +0 -59
  71. package/dist/link.cjs +0 -630
  72. package/dist/link.js +0 -606
  73. package/dist/menu.cjs +0 -305
  74. package/dist/menu.js +0 -269
  75. package/dist/pin-input.cjs +0 -182
  76. package/dist/pin-input.js +0 -160
  77. package/dist/popover.cjs +0 -327
  78. package/dist/popover.js +0 -294
  79. package/dist/progress-circle.cjs +0 -152
  80. package/dist/progress-circle.js +0 -128
  81. package/dist/progress.cjs +0 -117
  82. package/dist/progress.js +0 -94
  83. package/dist/provider.cjs +0 -62
  84. package/dist/provider.js +0 -40
  85. package/dist/radio.cjs +0 -177
  86. package/dist/radio.js +0 -153
  87. package/dist/rating.cjs +0 -80
  88. package/dist/rating.js +0 -58
  89. package/dist/select.cjs +0 -791
  90. package/dist/select.js +0 -757
  91. package/dist/separator.cjs +0 -57
  92. package/dist/separator.js +0 -51
  93. package/dist/skeleton.cjs +0 -370
  94. package/dist/skeleton.js +0 -346
  95. package/dist/slider.cjs +0 -138
  96. package/dist/slider.js +0 -115
  97. package/dist/switch.cjs +0 -163
  98. package/dist/switch.js +0 -140
  99. package/dist/table.cjs +0 -1044
  100. package/dist/table.js +0 -1013
  101. package/dist/tabs.cjs +0 -240
  102. package/dist/tabs.js +0 -213
  103. package/dist/tag.cjs +0 -651
  104. package/dist/tag.js +0 -628
  105. package/dist/textarea.cjs +0 -65
  106. package/dist/textarea.js +0 -59
  107. package/dist/toaster.cjs +0 -99
  108. package/dist/toaster.js +0 -96
  109. package/dist/tooltip.cjs +0 -171
  110. package/dist/tooltip.js +0 -148
  111. package/dist/utils.cjs +0 -11
  112. package/dist/utils.js +0 -9
  113. package/src/accordion.tsx +0 -285
  114. package/src/alert.tsx +0 -221
  115. package/src/avatar.tsx +0 -174
  116. package/src/badge.tsx +0 -158
  117. package/src/button.tsx +0 -411
  118. package/src/checkbox.tsx +0 -307
  119. package/src/close-button.tsx +0 -51
  120. package/src/collapsible.tsx +0 -126
  121. package/src/color-mode.tsx +0 -125
  122. package/src/dialog.tsx +0 -356
  123. package/src/drawer.tsx +0 -186
  124. package/src/empty-state.tsx +0 -97
  125. package/src/field.tsx +0 -202
  126. package/src/heading.tsx +0 -55
  127. package/src/icon-button.tsx +0 -192
  128. package/src/image.tsx +0 -280
  129. package/src/index.ts +0 -192
  130. package/src/input-group.tsx +0 -159
  131. package/src/input.tsx +0 -60
  132. package/src/link.tsx +0 -326
  133. package/src/menu.tsx +0 -471
  134. package/src/pin-input.tsx +0 -187
  135. package/src/popover.tsx +0 -400
  136. package/src/progress-circle.tsx +0 -180
  137. package/src/progress.tsx +0 -109
  138. package/src/provider.tsx +0 -12
  139. package/src/radio.tsx +0 -175
  140. package/src/rating.tsx +0 -79
  141. package/src/select.tsx +0 -696
  142. package/src/separator.tsx +0 -59
  143. package/src/skeleton.tsx +0 -302
  144. package/src/slider.tsx +0 -152
  145. package/src/switch.tsx +0 -158
  146. package/src/table.tsx +0 -621
  147. package/src/tabs.tsx +0 -354
  148. package/src/tag.tsx +0 -159
  149. package/src/textarea.tsx +0 -60
  150. package/src/toaster.tsx +0 -117
  151. package/src/tokens.css +0 -438
  152. package/src/tooltip.tsx +0 -184
  153. package/src/utils/cn.ts +0 -7
  154. package/src/utils.ts +0 -6
  155. package/tokens.css +0 -438
package/src/popover.tsx DELETED
@@ -1,400 +0,0 @@
1
- import * as RadixPopover from '@radix-ui/react-popover';
2
- import * as React from 'react';
3
-
4
- import { cn } from './utils';
5
-
6
- import { CloseButton } from './close-button';
7
-
8
- // --- Utility: map Chakra-style placement string to Radix side + align ---
9
-
10
- type Side = 'top' | 'right' | 'bottom' | 'left';
11
- type Align = 'start' | 'center' | 'end';
12
-
13
- interface PlacementMapping {
14
- readonly side: Side;
15
- readonly align: Align;
16
- }
17
-
18
- function parsePlacement(placement: string | undefined): PlacementMapping {
19
- if (!placement) {
20
- return { side: 'bottom', align: 'start' };
21
- }
22
-
23
- const parts = placement.split('-');
24
- const side = (parts[0] as Side) ?? 'bottom';
25
- const alignPart = parts[1];
26
-
27
- let align: Align = 'center';
28
- if (alignPart === 'start') {
29
- align = 'start';
30
- } else if (alignPart === 'end') {
31
- align = 'end';
32
- }
33
-
34
- return { side, align };
35
- }
36
-
37
- // --- PopoverRoot ---
38
-
39
- interface Positioning {
40
- readonly placement?: string;
41
- readonly offset?: {
42
- readonly mainAxis?: number;
43
- readonly crossAxis?: number;
44
- };
45
- readonly overflowPadding?: number;
46
- }
47
-
48
- export interface PopoverRootProps {
49
- readonly children?: React.ReactNode;
50
- readonly open?: boolean;
51
- readonly defaultOpen?: boolean;
52
-
53
- /** Chakra-style callback: receives `{ open: boolean }` */
54
- readonly onOpenChange?: (details: { open: boolean }) => void;
55
- readonly positioning?: Positioning;
56
- readonly lazyMount?: boolean;
57
- readonly unmountOnExit?: boolean;
58
- readonly autoFocus?: boolean;
59
- readonly closeOnInteractOutside?: boolean;
60
- readonly modal?: boolean;
61
- }
62
-
63
- // Stash positioning info in context so PopoverContent can read it.
64
- interface PopoverPositioning {
65
- readonly side: Side;
66
- readonly align: Align;
67
- readonly sideOffset: number;
68
- readonly alignOffset: number;
69
- readonly collisionPadding: number;
70
- readonly autoFocus: boolean;
71
- readonly closeOnInteractOutside: boolean;
72
- }
73
-
74
- const PositioningContext = React.createContext<PopoverPositioning>({
75
- side: 'bottom',
76
- align: 'start',
77
- sideOffset: 4,
78
- alignOffset: 0,
79
- collisionPadding: 4,
80
- autoFocus: false,
81
- closeOnInteractOutside: true,
82
- });
83
-
84
- export const PopoverRoot = (props: PopoverRootProps): React.ReactElement => {
85
- const {
86
- children,
87
- open,
88
- defaultOpen,
89
- onOpenChange,
90
- positioning,
91
- autoFocus = false,
92
- closeOnInteractOutside = true,
93
- modal = false,
94
- // lazyMount and unmountOnExit are handled via forceMount on Portal/Content
95
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
96
- lazyMount: _lazyMount,
97
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
98
- unmountOnExit: _unmountOnExit,
99
- } = props;
100
-
101
- // Merge default positioning with user positioning
102
- const mergedPositioning: Positioning = {
103
- placement: 'bottom-start',
104
- overflowPadding: 4,
105
- ...positioning,
106
- offset: {
107
- mainAxis: 4,
108
- ...positioning?.offset,
109
- },
110
- };
111
-
112
- const { side, align } = parsePlacement(mergedPositioning.placement);
113
-
114
- const positioningValue = React.useMemo<PopoverPositioning>(() => ({
115
- side,
116
- align,
117
- sideOffset: mergedPositioning.offset?.mainAxis ?? 4,
118
- alignOffset: mergedPositioning.offset?.crossAxis ?? 0,
119
- collisionPadding: mergedPositioning.overflowPadding ?? 4,
120
- autoFocus,
121
- closeOnInteractOutside,
122
- }), [
123
- side, align,
124
- mergedPositioning.offset?.mainAxis, mergedPositioning.offset?.crossAxis,
125
- mergedPositioning.overflowPadding, autoFocus, closeOnInteractOutside,
126
- ]);
127
-
128
- // Bridge Chakra-style onOpenChange ({ open }) to Radix (open)
129
- const handleOpenChange = React.useCallback((isOpen: boolean) => {
130
- onOpenChange?.({ open: isOpen });
131
- }, [ onOpenChange ]);
132
-
133
- return (
134
- <PositioningContext.Provider value={ positioningValue }>
135
- <RadixPopover.Root
136
- open={ open }
137
- defaultOpen={ defaultOpen }
138
- onOpenChange={ handleOpenChange }
139
- modal={ modal }
140
- >
141
- { children }
142
- </RadixPopover.Root>
143
- </PositioningContext.Provider>
144
- );
145
- };
146
-
147
- // --- PopoverTrigger ---
148
-
149
- export interface PopoverTriggerProps extends React.ComponentPropsWithoutRef<'button'> {
150
- readonly asChild?: boolean;
151
- }
152
-
153
- export const PopoverTrigger = React.forwardRef<
154
- HTMLButtonElement,
155
- PopoverTriggerProps
156
- >(function PopoverTrigger(props, ref) {
157
- const { asChild = true, ...rest } = props;
158
- return <RadixPopover.Trigger asChild={ asChild } ref={ ref } { ...rest }/>;
159
- });
160
-
161
- // --- PopoverContent ---
162
-
163
- export interface PopoverContentProps extends React.ComponentPropsWithoutRef<'div'> {
164
- readonly portalled?: boolean;
165
- readonly portalRef?: React.RefObject<HTMLElement>;
166
- // Legacy Chakra style-prop shims
167
- readonly w?: string | Record<string, string>;
168
- readonly minW?: string;
169
- readonly maxW?: string;
170
- readonly paddingTop?: number | string;
171
- }
172
-
173
- export const PopoverContent = React.forwardRef<
174
- HTMLDivElement,
175
- PopoverContentProps
176
- >(function PopoverContent(props, ref) {
177
- const { portalled = true, portalRef, className, w, minW, maxW, paddingTop, style: styleProp, ...rest } = props;
178
- const resolvedW = typeof w === 'object' ? (w as Record<string, string>).base ?? (w as Record<string, string>).lg : w;
179
- const contentStyle: React.CSSProperties = {
180
- ...styleProp,
181
- ...(resolvedW ? { width: resolvedW } : {}),
182
- ...(minW ? { minWidth: minW } : {}),
183
- ...(maxW ? { maxWidth: maxW } : {}),
184
- ...(paddingTop !== undefined ? { paddingTop: typeof paddingTop === 'number' ? `${ paddingTop * 4 }px` : paddingTop } : {}),
185
- };
186
- const positioning = React.useContext(PositioningContext);
187
-
188
- const preventFocus = React.useCallback((e: Event) => e.preventDefault(), []);
189
- const preventInteract = React.useCallback((e: Event) => e.preventDefault(), []);
190
-
191
- const content = (
192
- <RadixPopover.Content
193
- ref={ ref }
194
- side={ positioning.side }
195
- align={ positioning.align }
196
- sideOffset={ positioning.sideOffset }
197
- alignOffset={ positioning.alignOffset }
198
- collisionPadding={ positioning.collisionPadding }
199
- onOpenAutoFocus={ positioning.autoFocus ? undefined : preventFocus }
200
- onInteractOutside={ positioning.closeOnInteractOutside ? undefined : preventInteract }
201
- className={ cn(
202
- 'z-50 rounded-lg border border-[var(--color-popover-border,var(--color-border-divider))]',
203
- 'bg-[var(--color-popover-bg,var(--color-dialog-bg))]',
204
- 'shadow-[var(--shadow-popover,var(--shadow-lg))]',
205
- 'outline-none',
206
- 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
207
- 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
208
- 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
209
- 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
210
- className,
211
- ) }
212
- style={ Object.keys(contentStyle).length > 0 ? contentStyle : undefined }
213
- { ...rest }
214
- />
215
- );
216
-
217
- if (!portalled) {
218
- return content;
219
- }
220
-
221
- return (
222
- <RadixPopover.Portal container={ portalRef?.current ?? undefined }>
223
- { content }
224
- </RadixPopover.Portal>
225
- );
226
- });
227
-
228
- // --- PopoverArrow ---
229
-
230
- export interface PopoverArrowProps extends React.ComponentPropsWithoutRef<'svg'> {}
231
-
232
- export const PopoverArrow = React.forwardRef<
233
- SVGSVGElement,
234
- PopoverArrowProps
235
- >(function PopoverArrow(props, ref) {
236
- const { className, ...rest } = props;
237
- return (
238
- <RadixPopover.Arrow
239
- ref={ ref }
240
- className={ cn('fill-[var(--color-popover-bg,var(--color-dialog-bg))]', className) }
241
- { ...rest }
242
- />
243
- );
244
- });
245
-
246
- // --- PopoverCloseTrigger ---
247
-
248
- export interface PopoverCloseTriggerProps extends React.ComponentPropsWithoutRef<'button'> {}
249
-
250
- export const PopoverCloseTrigger = React.forwardRef<
251
- HTMLButtonElement,
252
- PopoverCloseTriggerProps
253
- >(function PopoverCloseTrigger(props, ref) {
254
- const { className, ...rest } = props;
255
- return (
256
- <RadixPopover.Close
257
- className={ cn('absolute top-1 right-1', className) }
258
- { ...rest }
259
- asChild
260
- ref={ ref }
261
- >
262
- <CloseButton/>
263
- </RadixPopover.Close>
264
- );
265
- });
266
-
267
- // --- PopoverCloseTriggerWrapper ---
268
-
269
- export interface PopoverCloseTriggerWrapperProps extends React.ComponentPropsWithoutRef<'button'> {
270
- readonly disabled?: boolean;
271
- }
272
-
273
- export const PopoverCloseTriggerWrapper = React.forwardRef<
274
- HTMLButtonElement,
275
- PopoverCloseTriggerWrapperProps
276
- >(function PopoverCloseTriggerWrapper(props, ref) {
277
- const { disabled, children, ...rest } = props;
278
-
279
- if (disabled) {
280
- return children as React.ReactElement;
281
- }
282
-
283
- return (
284
- <RadixPopover.Close ref={ ref } { ...rest } asChild>
285
- { children }
286
- </RadixPopover.Close>
287
- );
288
- });
289
-
290
- // --- Simple wrapper components ---
291
-
292
- export interface PopoverBodyProps extends React.ComponentPropsWithoutRef<'div'> {
293
- // Legacy Chakra style-prop shims
294
- readonly display?: string;
295
- readonly flexDir?: string;
296
- readonly rowGap?: number | string;
297
- readonly px?: number | string;
298
- readonly py?: number | string;
299
- readonly textStyle?: string;
300
- readonly alignItems?: string;
301
- }
302
-
303
- export const PopoverBody = React.forwardRef<
304
- HTMLDivElement,
305
- PopoverBodyProps
306
- >(function PopoverBody(props, ref) {
307
- const {
308
- className, style: styleProp,
309
- display: _display, flexDir: _flexDir, rowGap: _rowGap, px: _px, py: _py,
310
- textStyle: _textStyle, alignItems: _alignItems,
311
- ...rest
312
- } = props;
313
- const bodyStyle: React.CSSProperties = {
314
- ...styleProp,
315
- ...(_display ? { display: _display } : {}),
316
- ...(_flexDir ? { flexDirection: _flexDir as React.CSSProperties['flexDirection'] } : {}),
317
- ...(_rowGap !== undefined ? { rowGap: typeof _rowGap === 'number' ? `${ _rowGap * 4 }px` : _rowGap } : {}),
318
- ...(_px !== undefined ? {
319
- paddingLeft: typeof _px === 'number' ? `${ _px * 4 }px` : _px,
320
- paddingRight: typeof _px === 'number' ? `${ _px * 4 }px` : _px,
321
- } : {}),
322
- ...(_py !== undefined ? {
323
- paddingTop: typeof _py === 'number' ? `${ _py * 4 }px` : _py,
324
- paddingBottom: typeof _py === 'number' ? `${ _py * 4 }px` : _py,
325
- } : {}),
326
- ...(_alignItems ? { alignItems: _alignItems } : {}),
327
- };
328
- return (
329
- <div
330
- ref={ ref }
331
- className={ cn('p-4', className) }
332
- style={ Object.keys(bodyStyle).length > 0 ? bodyStyle : undefined }
333
- { ...rest }
334
- />
335
- );
336
- });
337
-
338
- export interface PopoverHeaderProps extends React.ComponentPropsWithoutRef<'div'> {}
339
-
340
- export const PopoverHeader = React.forwardRef<
341
- HTMLDivElement,
342
- PopoverHeaderProps
343
- >(function PopoverHeader(props, ref) {
344
- const { className, ...rest } = props;
345
- return (
346
- <div
347
- ref={ ref }
348
- className={ cn('px-4 pt-4 pb-0 font-semibold', className) }
349
- { ...rest }
350
- />
351
- );
352
- });
353
-
354
- export interface PopoverFooterProps extends React.ComponentPropsWithoutRef<'div'> {}
355
-
356
- export const PopoverFooter = React.forwardRef<
357
- HTMLDivElement,
358
- PopoverFooterProps
359
- >(function PopoverFooter(props, ref) {
360
- const { className, ...rest } = props;
361
- return (
362
- <div
363
- ref={ ref }
364
- className={ cn('px-4 pb-4 pt-0', className) }
365
- { ...rest }
366
- />
367
- );
368
- });
369
-
370
- export interface PopoverTitleProps extends React.ComponentPropsWithoutRef<'h3'> {}
371
-
372
- export const PopoverTitle = React.forwardRef<
373
- HTMLHeadingElement,
374
- PopoverTitleProps
375
- >(function PopoverTitle(props, ref) {
376
- const { className, ...rest } = props;
377
- return (
378
- <h3
379
- ref={ ref }
380
- className={ cn('text-base font-semibold', className) }
381
- { ...rest }
382
- />
383
- );
384
- });
385
-
386
- export interface PopoverDescriptionProps extends React.ComponentPropsWithoutRef<'p'> {}
387
-
388
- export const PopoverDescription = React.forwardRef<
389
- HTMLParagraphElement,
390
- PopoverDescriptionProps
391
- >(function PopoverDescription(props, ref) {
392
- const { className, ...rest } = props;
393
- return (
394
- <p
395
- ref={ ref }
396
- className={ cn('text-sm', className) }
397
- { ...rest }
398
- />
399
- );
400
- });
@@ -1,180 +0,0 @@
1
- import * as React from 'react';
2
-
3
- import { cn } from './utils';
4
-
5
- // ---------------------------------------------------------------------------
6
- // Size presets matching the original Chakra progressCircle recipe
7
- // ---------------------------------------------------------------------------
8
-
9
- const SIZE_MAP = {
10
- xs: { size: 24, thickness: 4, textClass: 'text-[10px]' },
11
- sm: { size: 32, thickness: 5, textClass: 'text-[10px]' },
12
- md: { size: 40, thickness: 6, textClass: 'text-xs' },
13
- lg: { size: 48, thickness: 7, textClass: 'text-sm' },
14
- xl: { size: 64, thickness: 8, textClass: 'text-sm' },
15
- } as const;
16
-
17
- type Size = keyof typeof SIZE_MAP;
18
-
19
- // ---------------------------------------------------------------------------
20
- // Context
21
- // ---------------------------------------------------------------------------
22
-
23
- interface ProgressCircleCtx {
24
- readonly value: number | null;
25
- readonly size: Size;
26
- readonly colorPalette: string;
27
- }
28
-
29
- const Ctx = React.createContext<ProgressCircleCtx>({
30
- value: 0,
31
- size: 'md',
32
- colorPalette: 'blue',
33
- });
34
-
35
- // ---------------------------------------------------------------------------
36
- // Root
37
- // ---------------------------------------------------------------------------
38
-
39
- export interface ProgressCircleRootProps
40
- extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'> {
41
- readonly value?: number | null;
42
- readonly size?: Size;
43
- readonly colorPalette?: string;
44
- }
45
-
46
- export const ProgressCircleRoot = React.forwardRef<
47
- HTMLDivElement,
48
- ProgressCircleRootProps
49
- >(function ProgressCircleRoot(props, ref) {
50
- const {
51
- value = 0,
52
- size = 'md',
53
- colorPalette = 'blue',
54
- className,
55
- children,
56
- ...rest
57
- } = props;
58
-
59
- const ctx = React.useMemo<ProgressCircleCtx>(
60
- () => ({ value: value ?? null, size, colorPalette }),
61
- [ value, size, colorPalette ],
62
- );
63
-
64
- return (
65
- <Ctx.Provider value={ ctx }>
66
- <div
67
- ref={ ref }
68
- role="progressbar"
69
- aria-valuenow={ value ?? undefined }
70
- aria-valuemin={ 0 }
71
- aria-valuemax={ 100 }
72
- className={ cn('relative inline-flex text-sm', className) }
73
- { ...rest }
74
- >
75
- { children }
76
- </div>
77
- </Ctx.Provider>
78
- );
79
- });
80
-
81
- // ---------------------------------------------------------------------------
82
- // Ring (SVG track + range arc)
83
- // ---------------------------------------------------------------------------
84
-
85
- export interface ProgressCircleRingProps
86
- extends Omit<React.SVGAttributes<SVGSVGElement>, 'color'> {
87
- readonly trackColor?: string;
88
- readonly cap?: React.SVGAttributes<SVGCircleElement>['strokeLinecap'];
89
- readonly color?: string;
90
- }
91
-
92
- export const ProgressCircleRing = React.forwardRef<
93
- SVGSVGElement,
94
- ProgressCircleRingProps
95
- >(function ProgressCircleRing(props, ref) {
96
- const { trackColor, cap, color, className, ...rest } = props;
97
- const { value, size: sizeKey } = React.useContext(Ctx);
98
-
99
- const { size, thickness } = SIZE_MAP[sizeKey];
100
- const radius = size / 2 - thickness / 2;
101
- const circumference = 2 * Math.PI * radius;
102
- const percent = value ?? 0;
103
- const offset = circumference * ((100 - percent) / 100);
104
-
105
- return (
106
- <svg
107
- ref={ ref }
108
- width={ size }
109
- height={ size }
110
- viewBox={ `0 0 ${ size } ${ size }` }
111
- className={ cn(
112
- value === null && 'animate-spin',
113
- className,
114
- ) }
115
- { ...rest }
116
- >
117
- { /* Track */ }
118
- <circle
119
- cx={ size / 2 }
120
- cy={ size / 2 }
121
- r={ radius }
122
- fill="transparent"
123
- strokeWidth={ thickness }
124
- stroke={ trackColor ?? 'currentColor' }
125
- className={ !trackColor ? 'opacity-20' : undefined }
126
- />
127
- { /* Range */ }
128
- <circle
129
- cx={ size / 2 }
130
- cy={ size / 2 }
131
- r={ radius }
132
- fill="transparent"
133
- strokeWidth={ thickness }
134
- stroke={ color ?? 'currentColor' }
135
- strokeLinecap={ cap }
136
- strokeDasharray={ circumference }
137
- strokeDashoffset={ offset }
138
- style={{
139
- transformOrigin: 'center',
140
- transform: 'rotate(-90deg)',
141
- transition: 'stroke-dashoffset 0.6s, stroke-dasharray 0.6s',
142
- opacity: percent === 0 ? 0 : undefined,
143
- }}
144
- />
145
- </svg>
146
- );
147
- });
148
-
149
- // ---------------------------------------------------------------------------
150
- // Value text (centered number)
151
- // ---------------------------------------------------------------------------
152
-
153
- export interface ProgressCircleValueTextProps
154
- extends React.HTMLAttributes<HTMLDivElement> {}
155
-
156
- export const ProgressCircleValueText = React.forwardRef<
157
- HTMLDivElement,
158
- ProgressCircleValueTextProps
159
- >(function ProgressCircleValueText(props, ref) {
160
- const { className, children, ...rest } = props;
161
- const { value, size: sizeKey } = React.useContext(Ctx);
162
-
163
- const { textClass } = SIZE_MAP[sizeKey];
164
-
165
- return (
166
- <div
167
- ref={ ref }
168
- aria-live="polite"
169
- className={ cn(
170
- 'absolute inset-0 flex items-center justify-center',
171
- 'font-medium tracking-tight tabular-nums leading-none',
172
- textClass,
173
- className,
174
- ) }
175
- { ...rest }
176
- >
177
- { children ?? `${ Math.round(value ?? 0) }%` }
178
- </div>
179
- );
180
- });
package/src/progress.tsx DELETED
@@ -1,109 +0,0 @@
1
- import * as RadixProgress from '@radix-ui/react-progress';
2
- import * as React from 'react';
3
-
4
- import { cn } from './utils';
5
-
6
- const SIZE_CLASSES = {
7
- sm: 'h-1',
8
- md: 'h-2',
9
- lg: 'h-3',
10
- xl: 'h-4',
11
- } as const;
12
-
13
- type Size = keyof typeof SIZE_CLASSES;
14
-
15
- interface TrackProps {
16
- readonly borderStartRadius?: number | string;
17
- readonly borderEndRadius?: number | string;
18
- readonly className?: string;
19
- readonly style?: React.CSSProperties;
20
- }
21
-
22
- interface ProgressProps extends React.ComponentPropsWithoutRef<'div'> {
23
- readonly value?: number | null;
24
- readonly min?: number;
25
- readonly max?: number;
26
- readonly size?: Size;
27
- readonly color?: string;
28
- readonly trackProps?: TrackProps;
29
-
30
- // Common Chakra layout shorthands that consumers currently pass.
31
- readonly w?: string;
32
- readonly flex?: string | number;
33
- }
34
-
35
- function normalizeValue(value: number | null | undefined, min: number, max: number): number {
36
- if (value == null) return 0;
37
- if (max <= min) return 0;
38
- return Math.round(((value - min) / (max - min)) * 100);
39
- }
40
-
41
- export const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
42
- function Progress(props, ref) {
43
- const {
44
- value,
45
- min = 0,
46
- max = 100,
47
- size = 'md',
48
- color,
49
- trackProps,
50
- className,
51
- style,
52
- w,
53
- flex,
54
- ...rest
55
- } = props;
56
-
57
- const percent = normalizeValue(value, min, max);
58
-
59
- const trackStyle: React.CSSProperties = {
60
- ...trackProps?.style,
61
- ...(trackProps?.borderStartRadius !== undefined && {
62
- borderStartStartRadius: trackProps.borderStartRadius,
63
- borderEndStartRadius: trackProps.borderStartRadius,
64
- }),
65
- ...(trackProps?.borderEndRadius !== undefined && {
66
- borderStartEndRadius: trackProps.borderEndRadius,
67
- borderEndEndRadius: trackProps.borderEndRadius,
68
- }),
69
- };
70
-
71
- const rootStyle: React.CSSProperties = {
72
- ...style,
73
- ...(w !== undefined && { width: w === 'full' ? '100%' : w }),
74
- ...(flex !== undefined && { flex }),
75
- };
76
-
77
- return (
78
- <div
79
- ref={ ref }
80
- className={ cn('relative', className) }
81
- style={ rootStyle }
82
- { ...rest }
83
- >
84
- <RadixProgress.Root
85
- value={ percent }
86
- max={ 100 }
87
- className={ cn(
88
- 'w-full overflow-hidden rounded-full',
89
- 'bg-[var(--color-progress-track)]',
90
- SIZE_CLASSES[size],
91
- trackProps?.className,
92
- ) }
93
- style={ trackStyle }
94
- >
95
- <RadixProgress.Indicator
96
- className={ cn(
97
- 'h-full rounded-full transition-transform duration-500 ease-out',
98
- !color && 'bg-[var(--color-progress-indicator)]',
99
- ) }
100
- style={{
101
- width: `${ percent }%`,
102
- ...(color && { backgroundColor: `var(--color-${ color.replace('.', '-') }, ${ color })` }),
103
- }}
104
- />
105
- </RadixProgress.Root>
106
- </div>
107
- );
108
- },
109
- );
package/src/provider.tsx DELETED
@@ -1,12 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
-
5
- import {
6
- ColorModeProvider,
7
- type ColorModeProviderProps,
8
- } from './color-mode';
9
-
10
- export function Provider(props: ColorModeProviderProps) {
11
- return <ColorModeProvider { ...props }/>;
12
- }