@rovula/ui 0.0.76 → 0.0.77
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/dist/cjs/bundle.css +12 -0
- package/dist/cjs/bundle.js +3 -3
- package/dist/cjs/bundle.js.map +1 -1
- package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +7 -0
- package/dist/cjs/types/components/InputFilter/InputFilter.stories.d.ts +7 -0
- package/dist/cjs/types/components/NumberInput/NumberInput.d.ts +39 -0
- package/dist/cjs/types/components/NumberInput/NumberInput.stories.d.ts +18 -0
- package/dist/cjs/types/components/NumberInput/index.d.ts +2 -0
- package/dist/cjs/types/components/RadioGroup/RadioGroup.stories.d.ts +1 -1
- package/dist/cjs/types/components/Search/Search.stories.d.ts +7 -0
- package/dist/cjs/types/components/Slider/Slider.stories.d.ts +1 -1
- package/dist/cjs/types/components/TextInput/TextInput.d.ts +14 -0
- package/dist/cjs/types/components/TextInput/TextInput.stories.d.ts +14 -0
- package/dist/cjs/types/index.d.ts +2 -0
- package/dist/components/NumberInput/NumberInput.js +254 -0
- package/dist/components/NumberInput/NumberInput.stories.js +212 -0
- package/dist/components/NumberInput/index.js +1 -0
- package/dist/components/TextInput/TextInput.js +13 -11
- package/dist/esm/bundle.css +12 -0
- package/dist/esm/bundle.js +3 -3
- package/dist/esm/bundle.js.map +1 -1
- package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +7 -0
- package/dist/esm/types/components/InputFilter/InputFilter.stories.d.ts +7 -0
- package/dist/esm/types/components/NumberInput/NumberInput.d.ts +39 -0
- package/dist/esm/types/components/NumberInput/NumberInput.stories.d.ts +18 -0
- package/dist/esm/types/components/NumberInput/index.d.ts +2 -0
- package/dist/esm/types/components/RadioGroup/RadioGroup.stories.d.ts +1 -1
- package/dist/esm/types/components/Search/Search.stories.d.ts +7 -0
- package/dist/esm/types/components/Slider/Slider.stories.d.ts +1 -1
- package/dist/esm/types/components/TextInput/TextInput.d.ts +14 -0
- package/dist/esm/types/components/TextInput/TextInput.stories.d.ts +14 -0
- package/dist/esm/types/index.d.ts +2 -0
- package/dist/index.d.ts +52 -1
- package/dist/index.js +1 -0
- package/dist/src/theme/global.css +16 -0
- package/package.json +1 -1
- package/src/components/NumberInput/NumberInput.stories.tsx +350 -0
- package/src/components/NumberInput/NumberInput.tsx +428 -0
- package/src/components/NumberInput/index.ts +2 -0
- package/src/components/TextInput/TextInput.tsx +54 -12
- package/src/index.ts +2 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
import {
|
|
9
|
+
TextInput,
|
|
10
|
+
InputProps as TextInputProps,
|
|
11
|
+
} from "../TextInput/TextInput";
|
|
12
|
+
import { ChevronUpIcon, ChevronDownIcon } from "@heroicons/react/16/solid";
|
|
13
|
+
import { cn } from "@/utils/cn";
|
|
14
|
+
|
|
15
|
+
export type NumberInputProps = Omit<
|
|
16
|
+
TextInputProps,
|
|
17
|
+
"type" | "value" | "defaultValue" | "onChange"
|
|
18
|
+
> & {
|
|
19
|
+
value?: number | string;
|
|
20
|
+
defaultValue?: number | string;
|
|
21
|
+
min?: number;
|
|
22
|
+
max?: number;
|
|
23
|
+
step?: number;
|
|
24
|
+
precision?: number;
|
|
25
|
+
hideControls?: boolean;
|
|
26
|
+
allowDecimal?: boolean;
|
|
27
|
+
allowNegative?: boolean;
|
|
28
|
+
formatDisplay?: boolean;
|
|
29
|
+
thousandSeparator?: string;
|
|
30
|
+
decimalSeparator?: string;
|
|
31
|
+
prefix?: string;
|
|
32
|
+
suffix?: string;
|
|
33
|
+
onChange?: (value: number | undefined) => void;
|
|
34
|
+
onValueChange?: (value: string) => void;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
|
38
|
+
(
|
|
39
|
+
{
|
|
40
|
+
value,
|
|
41
|
+
defaultValue,
|
|
42
|
+
min,
|
|
43
|
+
max,
|
|
44
|
+
step = 1,
|
|
45
|
+
precision,
|
|
46
|
+
hideControls = false,
|
|
47
|
+
allowDecimal = true,
|
|
48
|
+
allowNegative = true,
|
|
49
|
+
formatDisplay = false,
|
|
50
|
+
thousandSeparator = ",",
|
|
51
|
+
decimalSeparator = ".",
|
|
52
|
+
prefix = "",
|
|
53
|
+
suffix = "",
|
|
54
|
+
disabled = false,
|
|
55
|
+
onChange,
|
|
56
|
+
onValueChange,
|
|
57
|
+
className,
|
|
58
|
+
size = "md",
|
|
59
|
+
...props
|
|
60
|
+
},
|
|
61
|
+
ref
|
|
62
|
+
) => {
|
|
63
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
64
|
+
const [internalValue, setInternalValue] = useState<string>(
|
|
65
|
+
(value ?? defaultValue ?? "").toString()
|
|
66
|
+
);
|
|
67
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
68
|
+
|
|
69
|
+
useImperativeHandle(ref, () => inputRef?.current as HTMLInputElement);
|
|
70
|
+
|
|
71
|
+
// Helper function to remove formatting
|
|
72
|
+
const unformatNumber = useCallback(
|
|
73
|
+
(formattedValue: string): string => {
|
|
74
|
+
let cleaned = formattedValue;
|
|
75
|
+
|
|
76
|
+
// Remove prefix and suffix
|
|
77
|
+
if (prefix) cleaned = cleaned.replace(prefix, "");
|
|
78
|
+
if (suffix) cleaned = cleaned.replace(suffix, "");
|
|
79
|
+
|
|
80
|
+
// Remove thousand separators
|
|
81
|
+
if (thousandSeparator) {
|
|
82
|
+
cleaned = cleaned.split(thousandSeparator).join("");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Replace decimal separator with standard dot
|
|
86
|
+
if (decimalSeparator !== ".") {
|
|
87
|
+
cleaned = cleaned.replace(decimalSeparator, ".");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return cleaned.trim();
|
|
91
|
+
},
|
|
92
|
+
[prefix, suffix, thousandSeparator, decimalSeparator]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Helper function to format number for display
|
|
96
|
+
const formatNumber = useCallback(
|
|
97
|
+
(numValue: string): string => {
|
|
98
|
+
if (!formatDisplay || numValue === "" || numValue === "-")
|
|
99
|
+
return numValue;
|
|
100
|
+
|
|
101
|
+
const num = parseFloat(numValue);
|
|
102
|
+
if (isNaN(num)) return numValue;
|
|
103
|
+
|
|
104
|
+
let formatted = num.toString();
|
|
105
|
+
|
|
106
|
+
// Apply precision formatting
|
|
107
|
+
if (precision !== undefined) {
|
|
108
|
+
formatted = num.toFixed(precision);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Split into integer and decimal parts
|
|
112
|
+
const parts = formatted.split(".");
|
|
113
|
+
let integerPart = parts[0];
|
|
114
|
+
const decimalPart = parts[1];
|
|
115
|
+
|
|
116
|
+
// Add thousand separators
|
|
117
|
+
if (thousandSeparator) {
|
|
118
|
+
integerPart = integerPart.replace(
|
|
119
|
+
/\B(?=(\d{3})+(?!\d))/g,
|
|
120
|
+
thousandSeparator
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Combine with decimal separator
|
|
125
|
+
formatted = decimalPart
|
|
126
|
+
? `${integerPart}${decimalSeparator}${decimalPart}`
|
|
127
|
+
: integerPart;
|
|
128
|
+
|
|
129
|
+
// Add prefix and suffix
|
|
130
|
+
if (prefix) formatted = `${prefix}${formatted}`;
|
|
131
|
+
if (suffix) formatted = `${formatted}${suffix}`;
|
|
132
|
+
|
|
133
|
+
return formatted;
|
|
134
|
+
},
|
|
135
|
+
[
|
|
136
|
+
formatDisplay,
|
|
137
|
+
precision,
|
|
138
|
+
thousandSeparator,
|
|
139
|
+
decimalSeparator,
|
|
140
|
+
prefix,
|
|
141
|
+
suffix,
|
|
142
|
+
]
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const validateAndFormat = useCallback(
|
|
146
|
+
(val: string): string => {
|
|
147
|
+
if (val === "" || val === "-") return val;
|
|
148
|
+
|
|
149
|
+
let numValue = parseFloat(val);
|
|
150
|
+
|
|
151
|
+
if (isNaN(numValue)) return internalValue;
|
|
152
|
+
|
|
153
|
+
// Apply min/max constraints
|
|
154
|
+
if (min !== undefined && numValue < min) numValue = min;
|
|
155
|
+
if (max !== undefined && numValue > max) numValue = max;
|
|
156
|
+
|
|
157
|
+
// Apply precision
|
|
158
|
+
if (precision !== undefined) {
|
|
159
|
+
numValue = parseFloat(numValue.toFixed(precision));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return numValue.toString();
|
|
163
|
+
},
|
|
164
|
+
[min, max, precision, internalValue]
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const handleInputChange = useCallback(
|
|
168
|
+
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
169
|
+
let inputValue = e.target.value;
|
|
170
|
+
|
|
171
|
+
// Unformat the input if formatting is enabled
|
|
172
|
+
if (formatDisplay) {
|
|
173
|
+
inputValue = unformatNumber(inputValue);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Allow empty input
|
|
177
|
+
if (inputValue === "") {
|
|
178
|
+
setInternalValue("");
|
|
179
|
+
onChange?.(undefined);
|
|
180
|
+
onValueChange?.("");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Allow negative sign at the start
|
|
185
|
+
if (allowNegative && inputValue === "-") {
|
|
186
|
+
setInternalValue("-");
|
|
187
|
+
onValueChange?.("-");
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Filter non-numeric characters based on settings
|
|
192
|
+
const regex = allowDecimal
|
|
193
|
+
? allowNegative
|
|
194
|
+
? /^-?\d*\.?\d*$/
|
|
195
|
+
: /^\d*\.?\d*$/
|
|
196
|
+
: allowNegative
|
|
197
|
+
? /^-?\d*$/
|
|
198
|
+
: /^\d*$/;
|
|
199
|
+
|
|
200
|
+
if (!regex.test(inputValue)) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
setInternalValue(inputValue);
|
|
205
|
+
onValueChange?.(inputValue);
|
|
206
|
+
|
|
207
|
+
// Only call onChange with a valid number
|
|
208
|
+
const numValue = parseFloat(inputValue);
|
|
209
|
+
if (!isNaN(numValue)) {
|
|
210
|
+
onChange?.(numValue);
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
[
|
|
214
|
+
allowDecimal,
|
|
215
|
+
allowNegative,
|
|
216
|
+
formatDisplay,
|
|
217
|
+
unformatNumber,
|
|
218
|
+
onChange,
|
|
219
|
+
onValueChange,
|
|
220
|
+
]
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const handleFocus = useCallback(
|
|
224
|
+
(e: React.FocusEvent<HTMLInputElement>) => {
|
|
225
|
+
setIsFocused(true);
|
|
226
|
+
props.onFocus?.(e);
|
|
227
|
+
},
|
|
228
|
+
[props]
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const handleBlur = useCallback(
|
|
232
|
+
(e: React.FocusEvent<HTMLInputElement>) => {
|
|
233
|
+
setIsFocused(false);
|
|
234
|
+
|
|
235
|
+
const formatted = validateAndFormat(internalValue);
|
|
236
|
+
setInternalValue(formatted);
|
|
237
|
+
|
|
238
|
+
const numValue = parseFloat(formatted);
|
|
239
|
+
if (!isNaN(numValue)) {
|
|
240
|
+
onChange?.(numValue);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
props.onBlur?.(e);
|
|
244
|
+
},
|
|
245
|
+
[internalValue, validateAndFormat, onChange, props]
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const increment = useCallback(
|
|
249
|
+
(
|
|
250
|
+
e:
|
|
251
|
+
| React.MouseEvent<HTMLButtonElement>
|
|
252
|
+
| React.KeyboardEvent<HTMLInputElement>
|
|
253
|
+
) => {
|
|
254
|
+
if (disabled) return;
|
|
255
|
+
|
|
256
|
+
e.stopPropagation();
|
|
257
|
+
e.preventDefault();
|
|
258
|
+
|
|
259
|
+
const currentValue = parseFloat(internalValue) || 0;
|
|
260
|
+
let newValue = currentValue + step;
|
|
261
|
+
|
|
262
|
+
if (max !== undefined && newValue > max) {
|
|
263
|
+
newValue = max;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const formatted = validateAndFormat(newValue.toString());
|
|
267
|
+
setInternalValue(formatted);
|
|
268
|
+
onChange?.(parseFloat(formatted));
|
|
269
|
+
onValueChange?.(formatted);
|
|
270
|
+
},
|
|
271
|
+
[
|
|
272
|
+
internalValue,
|
|
273
|
+
step,
|
|
274
|
+
max,
|
|
275
|
+
disabled,
|
|
276
|
+
validateAndFormat,
|
|
277
|
+
onChange,
|
|
278
|
+
onValueChange,
|
|
279
|
+
]
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const decrement = useCallback(
|
|
283
|
+
(
|
|
284
|
+
e:
|
|
285
|
+
| React.MouseEvent<HTMLButtonElement>
|
|
286
|
+
| React.KeyboardEvent<HTMLInputElement>
|
|
287
|
+
) => {
|
|
288
|
+
if (disabled) return;
|
|
289
|
+
|
|
290
|
+
e.stopPropagation();
|
|
291
|
+
e.preventDefault();
|
|
292
|
+
|
|
293
|
+
const currentValue = parseFloat(internalValue) || 0;
|
|
294
|
+
let newValue = currentValue - step;
|
|
295
|
+
|
|
296
|
+
if (min !== undefined && newValue < min) {
|
|
297
|
+
newValue = min;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const formatted = validateAndFormat(newValue.toString());
|
|
301
|
+
setInternalValue(formatted);
|
|
302
|
+
onChange?.(parseFloat(formatted));
|
|
303
|
+
onValueChange?.(formatted);
|
|
304
|
+
},
|
|
305
|
+
[
|
|
306
|
+
internalValue,
|
|
307
|
+
step,
|
|
308
|
+
min,
|
|
309
|
+
disabled,
|
|
310
|
+
validateAndFormat,
|
|
311
|
+
onChange,
|
|
312
|
+
onValueChange,
|
|
313
|
+
]
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
const handleKeyDown = useCallback(
|
|
317
|
+
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
318
|
+
switch (e.key) {
|
|
319
|
+
case "ArrowUp":
|
|
320
|
+
increment(e);
|
|
321
|
+
break;
|
|
322
|
+
case "ArrowDown":
|
|
323
|
+
decrement(e);
|
|
324
|
+
break;
|
|
325
|
+
case "Enter":
|
|
326
|
+
e.currentTarget.blur();
|
|
327
|
+
break;
|
|
328
|
+
default:
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
props.onKeyDown?.(e);
|
|
332
|
+
},
|
|
333
|
+
[increment, decrement, props]
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// Sync internal value with external value prop
|
|
337
|
+
React.useEffect(() => {
|
|
338
|
+
if (value !== undefined) {
|
|
339
|
+
setInternalValue(value.toString());
|
|
340
|
+
}
|
|
341
|
+
}, [value]);
|
|
342
|
+
|
|
343
|
+
// Get the display value (formatted when not focused)
|
|
344
|
+
const displayValue = React.useMemo(() => {
|
|
345
|
+
if (isFocused || !formatDisplay) {
|
|
346
|
+
return internalValue;
|
|
347
|
+
}
|
|
348
|
+
return formatNumber(internalValue);
|
|
349
|
+
}, [isFocused, formatDisplay, internalValue, formatNumber]);
|
|
350
|
+
|
|
351
|
+
const controlsSize = {
|
|
352
|
+
sm: "w-3 h-3",
|
|
353
|
+
md: "w-4 h-4",
|
|
354
|
+
lg: "w-6 h-6",
|
|
355
|
+
}[size];
|
|
356
|
+
|
|
357
|
+
const paddingSize = {
|
|
358
|
+
sm: "absolute top-0.5",
|
|
359
|
+
md: "absolute top-1",
|
|
360
|
+
lg: "absolute top-1",
|
|
361
|
+
}[size];
|
|
362
|
+
|
|
363
|
+
const renderControls = () => {
|
|
364
|
+
if (hideControls) return null;
|
|
365
|
+
|
|
366
|
+
return (
|
|
367
|
+
<div
|
|
368
|
+
className={cn(
|
|
369
|
+
"flex flex-col",
|
|
370
|
+
props.iconMode === "flat" && paddingSize
|
|
371
|
+
)}
|
|
372
|
+
>
|
|
373
|
+
<button
|
|
374
|
+
type="button"
|
|
375
|
+
onClick={increment}
|
|
376
|
+
disabled={
|
|
377
|
+
disabled ||
|
|
378
|
+
(max !== undefined && parseFloat(internalValue) >= max)
|
|
379
|
+
}
|
|
380
|
+
className={cn(
|
|
381
|
+
" hover:bg-input-active-stroke/10 disabled:opacity-30 disabled:cursor-not-allowed transition-colors rounded-full text-input-filled-text"
|
|
382
|
+
)}
|
|
383
|
+
tabIndex={-1}
|
|
384
|
+
>
|
|
385
|
+
<ChevronUpIcon className={controlsSize} />
|
|
386
|
+
</button>
|
|
387
|
+
<button
|
|
388
|
+
type="button"
|
|
389
|
+
onClick={decrement}
|
|
390
|
+
disabled={
|
|
391
|
+
disabled ||
|
|
392
|
+
(min !== undefined && parseFloat(internalValue) <= min)
|
|
393
|
+
}
|
|
394
|
+
className={cn(
|
|
395
|
+
" hover:bg-input-active-stroke/10 disabled:opacity-30 disabled:cursor-not-allowed transition-colors rounded-full text-input-filled-text"
|
|
396
|
+
)}
|
|
397
|
+
tabIndex={-1}
|
|
398
|
+
>
|
|
399
|
+
<ChevronDownIcon className={controlsSize} />
|
|
400
|
+
</button>
|
|
401
|
+
</div>
|
|
402
|
+
);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
return (
|
|
406
|
+
<TextInput
|
|
407
|
+
{...props}
|
|
408
|
+
ref={inputRef}
|
|
409
|
+
type="text"
|
|
410
|
+
inputMode="decimal"
|
|
411
|
+
value={displayValue}
|
|
412
|
+
onChange={handleInputChange as any}
|
|
413
|
+
onFocus={handleFocus}
|
|
414
|
+
onBlur={handleBlur}
|
|
415
|
+
onKeyDown={handleKeyDown}
|
|
416
|
+
disabled={disabled}
|
|
417
|
+
size={size}
|
|
418
|
+
hasClearIcon={false}
|
|
419
|
+
endIcon={renderControls()}
|
|
420
|
+
className={cn(className)}
|
|
421
|
+
/>
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
NumberInput.displayName = "NumberInput";
|
|
427
|
+
|
|
428
|
+
export default NumberInput;
|
|
@@ -45,6 +45,13 @@ export type InputProps = {
|
|
|
45
45
|
endIcon?: ReactNode;
|
|
46
46
|
className?: string;
|
|
47
47
|
labelClassName?: string;
|
|
48
|
+
classes?: {
|
|
49
|
+
iconWrapper?: string;
|
|
50
|
+
iconSearchWrapper?: string;
|
|
51
|
+
icon?: string;
|
|
52
|
+
startIconWrapper?: string;
|
|
53
|
+
endIconWrapper?: string;
|
|
54
|
+
};
|
|
48
55
|
onClickStartIcon?: () => void;
|
|
49
56
|
onClickEndIcon?: () => void;
|
|
50
57
|
renderStartIcon?: () => ReactNode;
|
|
@@ -78,6 +85,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
78
85
|
onClickEndIcon,
|
|
79
86
|
renderStartIcon,
|
|
80
87
|
renderEndIcon,
|
|
88
|
+
classes,
|
|
81
89
|
...props
|
|
82
90
|
},
|
|
83
91
|
ref
|
|
@@ -164,13 +172,30 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
164
172
|
const startIconElement = useMemo(() => {
|
|
165
173
|
if (!hasLeftSectionIcon) return;
|
|
166
174
|
|
|
167
|
-
if (renderStartIcon)
|
|
175
|
+
if (renderStartIcon) {
|
|
176
|
+
return (
|
|
177
|
+
<div
|
|
178
|
+
className={cn(
|
|
179
|
+
iconSearchWrapperClassname,
|
|
180
|
+
"flex",
|
|
181
|
+
classes?.iconSearchWrapper
|
|
182
|
+
)}
|
|
183
|
+
>
|
|
184
|
+
{renderStartIcon()}
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
168
188
|
|
|
169
189
|
if (iconMode === "flat") {
|
|
170
190
|
return (
|
|
171
|
-
<div
|
|
191
|
+
<div
|
|
192
|
+
className={cn(
|
|
193
|
+
iconSearchWrapperClassname,
|
|
194
|
+
classes?.iconSearchWrapper
|
|
195
|
+
)}
|
|
196
|
+
>
|
|
172
197
|
<div
|
|
173
|
-
className={iconClassname}
|
|
198
|
+
className={cn(iconClassname, classes?.icon)}
|
|
174
199
|
onClick={handleOnClickLeftSectionIcon}
|
|
175
200
|
>
|
|
176
201
|
{startIcon}
|
|
@@ -181,7 +206,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
181
206
|
|
|
182
207
|
return (
|
|
183
208
|
<div
|
|
184
|
-
className={startIconWrapperClassname}
|
|
209
|
+
className={cn(startIconWrapperClassname, classes?.startIconWrapper)}
|
|
185
210
|
onClick={handleOnClickLeftSectionIcon}
|
|
186
211
|
>
|
|
187
212
|
{startIcon}
|
|
@@ -201,13 +226,23 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
201
226
|
const endIconElement = useMemo(() => {
|
|
202
227
|
if (!hasRightSectionIcon) return;
|
|
203
228
|
|
|
204
|
-
if (renderEndIcon)
|
|
229
|
+
if (renderEndIcon) {
|
|
230
|
+
return (
|
|
231
|
+
<div
|
|
232
|
+
className={cn(iconWrapperClassname, "flex", classes?.iconWrapper)}
|
|
233
|
+
>
|
|
234
|
+
{renderEndIcon()}
|
|
235
|
+
</div>
|
|
236
|
+
);
|
|
237
|
+
}
|
|
205
238
|
|
|
206
239
|
if (iconMode === "flat") {
|
|
207
240
|
return (
|
|
208
|
-
<div
|
|
241
|
+
<div
|
|
242
|
+
className={cn(iconWrapperClassname, "flex", classes?.iconWrapper)}
|
|
243
|
+
>
|
|
209
244
|
<div
|
|
210
|
-
className={iconClassname}
|
|
245
|
+
className={cn(iconClassname, classes?.icon)}
|
|
211
246
|
onClick={handleOnClickRightSectionIcon}
|
|
212
247
|
>
|
|
213
248
|
{endIcon}
|
|
@@ -218,7 +253,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
218
253
|
|
|
219
254
|
return (
|
|
220
255
|
<div
|
|
221
|
-
className={endIconWrapperClassname}
|
|
256
|
+
className={cn(endIconWrapperClassname, classes?.endIconWrapper)}
|
|
222
257
|
onClick={handleOnClickRightSectionIcon}
|
|
223
258
|
>
|
|
224
259
|
{endIcon}
|
|
@@ -239,8 +274,15 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
239
274
|
<div className={`inline-flex flex-col ${fullwidth ? "w-full" : ""}`}>
|
|
240
275
|
<div className="relative">
|
|
241
276
|
{hasSearchIcon && !hasLeftSectionIcon && (
|
|
242
|
-
<div
|
|
243
|
-
|
|
277
|
+
<div
|
|
278
|
+
className={cn(
|
|
279
|
+
iconSearchWrapperClassname,
|
|
280
|
+
classes?.iconSearchWrapper
|
|
281
|
+
)}
|
|
282
|
+
>
|
|
283
|
+
<MagnifyingGlassIcon
|
|
284
|
+
className={cn(iconClassname, classes?.icon)}
|
|
285
|
+
/>
|
|
244
286
|
</div>
|
|
245
287
|
)}
|
|
246
288
|
<input
|
|
@@ -256,7 +298,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
256
298
|
|
|
257
299
|
{hasClearIcon && !hasRightSectionIcon && (
|
|
258
300
|
<div
|
|
259
|
-
className={iconWrapperClassname}
|
|
301
|
+
className={cn(iconWrapperClassname, classes?.iconWrapper)}
|
|
260
302
|
style={{
|
|
261
303
|
display:
|
|
262
304
|
keepCloseIconOnValue && props.value ? "flex" : undefined,
|
|
@@ -264,7 +306,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
264
306
|
>
|
|
265
307
|
<XCircleIcon
|
|
266
308
|
type="button"
|
|
267
|
-
className={iconClassname}
|
|
309
|
+
className={cn(iconClassname, classes?.icon)}
|
|
268
310
|
onMouseDown={handleClearInput}
|
|
269
311
|
/>
|
|
270
312
|
</div>
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import "./icons/iconConfig";
|
|
|
5
5
|
|
|
6
6
|
export { default as Button } from "./components/Button/Button";
|
|
7
7
|
export { default as TextInput } from "./components/TextInput/TextInput";
|
|
8
|
+
export { NumberInput } from "./components/NumberInput/NumberInput";
|
|
8
9
|
export { default as TextArea } from "./components/TextArea/TextArea";
|
|
9
10
|
export { default as Text } from "./components/Text/Text";
|
|
10
11
|
export { default as Tabs } from "./components/Tabs/Tabs";
|
|
@@ -47,6 +48,7 @@ export * from "./components/RadioGroup/RadioGroup";
|
|
|
47
48
|
// Export component types
|
|
48
49
|
export type { ButtonProps } from "./components/Button/Button";
|
|
49
50
|
export type { InputProps } from "./components/TextInput/TextInput";
|
|
51
|
+
export type { NumberInputProps } from "./components/NumberInput/NumberInput";
|
|
50
52
|
export type { TextAreaProps } from "./components/TextArea/TextArea";
|
|
51
53
|
export type { DropdownProps, Options } from "./components/Dropdown/Dropdown";
|
|
52
54
|
export type { NavbarProps } from "./components/Navbar/Navbar";
|