@luxfi/ui 1.0.1

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 (124) hide show
  1. package/dist/accordion.cjs +213 -0
  2. package/dist/accordion.js +186 -0
  3. package/dist/alert.cjs +553 -0
  4. package/dist/alert.js +531 -0
  5. package/dist/avatar.cjs +149 -0
  6. package/dist/avatar.js +125 -0
  7. package/dist/badge.cjs +611 -0
  8. package/dist/badge.js +589 -0
  9. package/dist/button.cjs +689 -0
  10. package/dist/button.js +664 -0
  11. package/dist/checkbox.cjs +265 -0
  12. package/dist/checkbox.js +241 -0
  13. package/dist/close-button.cjs +73 -0
  14. package/dist/close-button.js +51 -0
  15. package/dist/collapsible.cjs +711 -0
  16. package/dist/collapsible.js +685 -0
  17. package/dist/color-mode.cjs +36 -0
  18. package/dist/color-mode.js +32 -0
  19. package/dist/dialog.cjs +279 -0
  20. package/dist/dialog.js +246 -0
  21. package/dist/drawer.cjs +207 -0
  22. package/dist/drawer.js +175 -0
  23. package/dist/empty-state.cjs +93 -0
  24. package/dist/empty-state.js +71 -0
  25. package/dist/field.cjs +183 -0
  26. package/dist/field.js +160 -0
  27. package/dist/heading.cjs +46 -0
  28. package/dist/heading.js +40 -0
  29. package/dist/icon-button.cjs +491 -0
  30. package/dist/icon-button.js +470 -0
  31. package/dist/image.cjs +572 -0
  32. package/dist/image.js +551 -0
  33. package/dist/index.cjs +5749 -0
  34. package/dist/index.js +5586 -0
  35. package/dist/input-group.cjs +155 -0
  36. package/dist/input-group.js +133 -0
  37. package/dist/input.cjs +65 -0
  38. package/dist/input.js +59 -0
  39. package/dist/link.cjs +639 -0
  40. package/dist/link.js +612 -0
  41. package/dist/menu.cjs +305 -0
  42. package/dist/menu.js +269 -0
  43. package/dist/pin-input.cjs +182 -0
  44. package/dist/pin-input.js +160 -0
  45. package/dist/popover.cjs +327 -0
  46. package/dist/popover.js +294 -0
  47. package/dist/progress-circle.cjs +152 -0
  48. package/dist/progress-circle.js +128 -0
  49. package/dist/progress.cjs +117 -0
  50. package/dist/progress.js +94 -0
  51. package/dist/provider.cjs +22 -0
  52. package/dist/provider.js +20 -0
  53. package/dist/radio.cjs +177 -0
  54. package/dist/radio.js +153 -0
  55. package/dist/rating.cjs +80 -0
  56. package/dist/rating.js +58 -0
  57. package/dist/select.cjs +791 -0
  58. package/dist/select.js +757 -0
  59. package/dist/separator.cjs +57 -0
  60. package/dist/separator.js +51 -0
  61. package/dist/skeleton.cjs +370 -0
  62. package/dist/skeleton.js +346 -0
  63. package/dist/slider.cjs +138 -0
  64. package/dist/slider.js +115 -0
  65. package/dist/switch.cjs +163 -0
  66. package/dist/switch.js +140 -0
  67. package/dist/table.cjs +1053 -0
  68. package/dist/table.js +1019 -0
  69. package/dist/tabs.cjs +240 -0
  70. package/dist/tabs.js +213 -0
  71. package/dist/tag.cjs +651 -0
  72. package/dist/tag.js +628 -0
  73. package/dist/textarea.cjs +65 -0
  74. package/dist/textarea.js +59 -0
  75. package/dist/toaster.cjs +99 -0
  76. package/dist/toaster.js +96 -0
  77. package/dist/tooltip.cjs +171 -0
  78. package/dist/tooltip.js +148 -0
  79. package/dist/utils.cjs +11 -0
  80. package/dist/utils.js +9 -0
  81. package/package.json +296 -0
  82. package/src/accordion.tsx +285 -0
  83. package/src/alert.tsx +221 -0
  84. package/src/avatar.tsx +174 -0
  85. package/src/badge.tsx +158 -0
  86. package/src/button.tsx +411 -0
  87. package/src/checkbox.tsx +307 -0
  88. package/src/close-button.tsx +51 -0
  89. package/src/collapsible.tsx +126 -0
  90. package/src/color-mode.tsx +37 -0
  91. package/src/dialog.tsx +356 -0
  92. package/src/drawer.tsx +186 -0
  93. package/src/empty-state.tsx +97 -0
  94. package/src/field.tsx +202 -0
  95. package/src/heading.tsx +55 -0
  96. package/src/icon-button.tsx +192 -0
  97. package/src/image.tsx +280 -0
  98. package/src/index.ts +192 -0
  99. package/src/input-group.tsx +159 -0
  100. package/src/input.tsx +60 -0
  101. package/src/link.tsx +333 -0
  102. package/src/menu.tsx +471 -0
  103. package/src/pin-input.tsx +187 -0
  104. package/src/popover.tsx +400 -0
  105. package/src/progress-circle.tsx +180 -0
  106. package/src/progress.tsx +109 -0
  107. package/src/provider.tsx +12 -0
  108. package/src/radio.tsx +175 -0
  109. package/src/rating.tsx +79 -0
  110. package/src/select.tsx +696 -0
  111. package/src/separator.tsx +59 -0
  112. package/src/skeleton.tsx +302 -0
  113. package/src/slider.tsx +152 -0
  114. package/src/switch.tsx +158 -0
  115. package/src/table.tsx +621 -0
  116. package/src/tabs.tsx +354 -0
  117. package/src/tag.tsx +159 -0
  118. package/src/textarea.tsx +60 -0
  119. package/src/toaster.tsx +117 -0
  120. package/src/tokens.css +438 -0
  121. package/src/tooltip.tsx +184 -0
  122. package/src/utils/cn.ts +7 -0
  123. package/src/utils.ts +6 -0
  124. package/tokens.css +438 -0
package/src/radio.tsx ADDED
@@ -0,0 +1,175 @@
1
+ import * as RadixRadioGroup from '@radix-ui/react-radio-group';
2
+ // chakra() HOC removed — pure Radix + Tailwind
3
+ import * as React from 'react';
4
+
5
+ import { cn } from './utils';
6
+
7
+ // ─── Size classes ───────────────────────────────────────────────────
8
+ const SIZE_CLASSES = {
9
+ xs: {
10
+ root: 'gap-1.5',
11
+ control: 'h-3 w-3',
12
+ indicator: 'h-1.5 w-1.5',
13
+ label: 'text-xs',
14
+ },
15
+ sm: {
16
+ root: 'gap-1.5',
17
+ control: 'h-3.5 w-3.5',
18
+ indicator: 'h-1.5 w-1.5',
19
+ label: 'text-xs',
20
+ },
21
+ md: {
22
+ root: 'gap-2',
23
+ control: 'h-4 w-4',
24
+ indicator: 'h-2 w-2',
25
+ label: 'text-sm',
26
+ },
27
+ lg: {
28
+ root: 'gap-2.5',
29
+ control: 'h-5 w-5',
30
+ indicator: 'h-2.5 w-2.5',
31
+ label: 'text-base',
32
+ },
33
+ } as const;
34
+
35
+ type RadioSize = keyof typeof SIZE_CLASSES;
36
+
37
+ // ─── Size context ───────────────────────────────────────────────────
38
+ const RadioSizeContext = React.createContext<RadioSize>('md');
39
+
40
+ // ─── RadioGroup ─────────────────────────────────────────────────────
41
+ export interface RadioGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'defaultValue' | 'dir'> {
42
+ name?: string;
43
+ required?: boolean;
44
+ disabled?: boolean;
45
+ readOnly?: boolean;
46
+ orientation?: 'horizontal' | 'vertical';
47
+ loop?: boolean;
48
+ defaultValue?: string;
49
+ value?: string | null;
50
+ onValueChange?: (details: { value: string | null }) => void;
51
+ size?: RadioSize;
52
+ }
53
+
54
+ const RadioGroupBase = React.forwardRef<HTMLDivElement, RadioGroupProps>(
55
+ function RadioGroup(props, ref) {
56
+ const {
57
+ children,
58
+ name,
59
+ required,
60
+ disabled = false,
61
+ readOnly = false,
62
+ orientation = 'vertical',
63
+ loop = true,
64
+ defaultValue,
65
+ value,
66
+ onValueChange,
67
+ size = 'md',
68
+ className,
69
+ ...rest
70
+ } = props;
71
+
72
+ const handleValueChange = React.useCallback(
73
+ (nextValue: string) => {
74
+ if (readOnly) return;
75
+ onValueChange?.({ value: nextValue });
76
+ },
77
+ [ readOnly, onValueChange ],
78
+ );
79
+
80
+ return (
81
+ <RadioSizeContext.Provider value={ size }>
82
+ <RadixRadioGroup.Root
83
+ ref={ ref }
84
+ name={ name }
85
+ required={ required }
86
+ disabled={ disabled || readOnly }
87
+ orientation={ orientation }
88
+ loop={ loop }
89
+ defaultValue={ defaultValue }
90
+ value={ value ?? undefined }
91
+ onValueChange={ handleValueChange }
92
+ className={ cn(
93
+ 'flex',
94
+ orientation === 'vertical' ? 'flex-col gap-3' : 'flex-row gap-6',
95
+ className,
96
+ ) }
97
+ { ...rest }
98
+ >
99
+ { children }
100
+ </RadixRadioGroup.Root>
101
+ </RadioSizeContext.Provider>
102
+ );
103
+ },
104
+ );
105
+
106
+ export const RadioGroup = RadioGroupBase;
107
+
108
+ const NOOP = () => { /* noop */ };
109
+
110
+ // ─── Radio ──────────────────────────────────────────────────────────
111
+ export interface RadioProps extends Omit<React.ComponentPropsWithoutRef<'label'>, 'onChange'> {
112
+ rootRef?: React.Ref<HTMLLabelElement>;
113
+ inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
114
+ value: string;
115
+ disabled?: boolean;
116
+ }
117
+
118
+ const RadioBase = React.forwardRef<HTMLInputElement, RadioProps>(
119
+ function Radio(props, ref) {
120
+ const { children, inputProps, rootRef, value, disabled, className, ...rest } = props;
121
+ const size = React.useContext(RadioSizeContext);
122
+ const sizeClasses = SIZE_CLASSES[size];
123
+
124
+ return (
125
+ <label
126
+ ref={ rootRef }
127
+ className={ cn(
128
+ 'inline-flex items-center cursor-pointer select-none',
129
+ sizeClasses.root,
130
+ disabled && 'opacity-50 cursor-not-allowed',
131
+ className,
132
+ ) }
133
+ data-disabled={ disabled || undefined }
134
+ { ...rest }
135
+ >
136
+ <RadixRadioGroup.Item
137
+ value={ value }
138
+ disabled={ disabled }
139
+ className={ cn(
140
+ 'inline-flex items-center justify-center shrink-0 rounded-full',
141
+ 'border-2 border-current/30',
142
+ 'data-[state=checked]:border-gray-800 dark:border-white',
143
+ 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500',
144
+ 'transition-colors duration-150',
145
+ sizeClasses.control,
146
+ ) }
147
+ >
148
+ <RadixRadioGroup.Indicator
149
+ className={ cn(
150
+ 'block rounded-full bg-gray-800 dark:bg-white',
151
+ sizeClasses.indicator,
152
+ ) }
153
+ />
154
+ </RadixRadioGroup.Item>
155
+ { /* Hidden native input for form compatibility and ref forwarding */ }
156
+ <input
157
+ ref={ ref }
158
+ type="radio"
159
+ className="sr-only"
160
+ value={ value }
161
+ disabled={ disabled }
162
+ tabIndex={ -1 }
163
+ aria-hidden
164
+ onChange={ NOOP }
165
+ { ...inputProps }
166
+ />
167
+ { children != null && (
168
+ <span className={ sizeClasses.label }>{ children }</span>
169
+ ) }
170
+ </label>
171
+ );
172
+ },
173
+ );
174
+
175
+ export const Radio = RadioBase;
package/src/rating.tsx ADDED
@@ -0,0 +1,79 @@
1
+ import * as React from 'react';
2
+
3
+ import { cn } from './utils';
4
+
5
+ // Inline star icons
6
+ const StarFilledIcon = ({ className }: { readonly className?: string }) => (
7
+ <svg className={ className } viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
8
+ <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/>
9
+ </svg>
10
+ );
11
+ const StarOutlineIcon = ({ className }: { readonly className?: string }) => (
12
+ <svg className={ className } viewBox="0 0 20 20" fill="none" stroke="currentColor" xmlns="http://www.w3.org/2000/svg">
13
+ <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" strokeWidth="1.5"/>
14
+ </svg>
15
+ );
16
+
17
+ export interface RatingProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'defaultValue' | 'onChange'> {
18
+ count?: number;
19
+ label?: string | Array<string>;
20
+ defaultValue?: number;
21
+ onValueChange?: ({ value }: { value: number }) => void;
22
+ readOnly?: boolean;
23
+ }
24
+
25
+ export const Rating = React.forwardRef<HTMLDivElement, RatingProps>(
26
+ function Rating(props, ref) {
27
+ const { count = 5, label: labelProp, defaultValue = 0, onValueChange, readOnly, className, ...rest } = props;
28
+
29
+ const [ value, setValue ] = React.useState(defaultValue);
30
+ const [ hoveredIndex, setHoveredIndex ] = React.useState(-1);
31
+
32
+ const highlightedIndex = hoveredIndex >= 0 && !readOnly ? hoveredIndex + 1 : value;
33
+ const label = Array.isArray(labelProp) ? labelProp[highlightedIndex - 1] : labelProp;
34
+
35
+ const handleClick = React.useCallback((index: number) => () => {
36
+ if (readOnly) return;
37
+ setValue(index);
38
+ onValueChange?.({ value: index });
39
+ }, [ readOnly, onValueChange ]);
40
+
41
+ const handleMouseEnter = React.useCallback((index: number) => () => {
42
+ if (readOnly) return;
43
+ setHoveredIndex(index);
44
+ }, [ readOnly ]);
45
+
46
+ const handleMouseLeave = React.useCallback(() => {
47
+ setHoveredIndex(-1);
48
+ }, []);
49
+
50
+ return (
51
+ <div ref={ ref } className={ cn('inline-flex items-center gap-1', className) } { ...rest }>
52
+ <div className="inline-flex items-center" onMouseLeave={ handleMouseLeave }>
53
+ { Array.from({ length: count }).map((_, index) => {
54
+ const filled = index < highlightedIndex;
55
+ const starIndex = index + 1;
56
+
57
+ return (
58
+ <button
59
+ key={ index }
60
+ type="button"
61
+ tabIndex={ readOnly ? -1 : 0 }
62
+ aria-label={ `Rate ${ starIndex } of ${ count }` }
63
+ className={ cn(
64
+ 'inline-flex items-center justify-center w-5 h-5 text-current',
65
+ readOnly ? 'cursor-default' : 'cursor-pointer',
66
+ ) }
67
+ onClick={ handleClick(starIndex) }
68
+ onMouseEnter={ handleMouseEnter(index) }
69
+ >
70
+ { filled ? <StarFilledIcon className="w-5 h-5"/> : <StarOutlineIcon className="w-5 h-5"/> }
71
+ </button>
72
+ );
73
+ }) }
74
+ </div>
75
+ { label && <span className="text-sm">{ label }</span> }
76
+ </div>
77
+ );
78
+ },
79
+ );