@rovula/ui 0.1.9 → 0.1.11
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.js +1 -1
- package/dist/cjs/bundle.js.map +1 -1
- package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +4 -0
- package/dist/cjs/types/components/InputFilter/InputFilter.stories.d.ts +4 -0
- package/dist/cjs/types/components/MaskedTextInput/MaskedTextInput.d.ts +4 -0
- package/dist/cjs/types/components/MaskedTextInput/MaskedTextInput.stories.d.ts +8 -0
- package/dist/cjs/types/components/PasswordInput/PasswordInput.stories.d.ts +4 -0
- package/dist/cjs/types/components/Search/Search.stories.d.ts +4 -0
- package/dist/cjs/types/components/TextArea/TextArea.d.ts +8 -0
- package/dist/cjs/types/components/TextArea/TextArea.stories.d.ts +4 -0
- package/dist/cjs/types/components/TextInput/TextInput.d.ts +8 -0
- package/dist/cjs/types/components/TextInput/TextInput.stories.d.ts +20 -0
- package/dist/components/TextArea/TextArea.js +32 -3
- package/dist/components/TextArea/TextArea.stories.js +29 -0
- package/dist/components/TextInput/TextInput.js +38 -2
- package/dist/components/TextInput/TextInput.stories.js +28 -0
- package/dist/esm/bundle.js +1 -1
- package/dist/esm/bundle.js.map +1 -1
- package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +4 -0
- package/dist/esm/types/components/InputFilter/InputFilter.stories.d.ts +4 -0
- package/dist/esm/types/components/MaskedTextInput/MaskedTextInput.d.ts +4 -0
- package/dist/esm/types/components/MaskedTextInput/MaskedTextInput.stories.d.ts +8 -0
- package/dist/esm/types/components/PasswordInput/PasswordInput.stories.d.ts +4 -0
- package/dist/esm/types/components/Search/Search.stories.d.ts +4 -0
- package/dist/esm/types/components/TextArea/TextArea.d.ts +8 -0
- package/dist/esm/types/components/TextArea/TextArea.stories.d.ts +4 -0
- package/dist/esm/types/components/TextInput/TextInput.d.ts +8 -0
- package/dist/esm/types/components/TextInput/TextInput.stories.d.ts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/src/theme/global.css +29 -29
- package/package.json +1 -1
- package/src/components/TextArea/TextArea.stories.tsx +108 -0
- package/src/components/TextArea/TextArea.tsx +52 -1
- package/src/components/TextInput/TextInput.stories.tsx +120 -5
- package/src/components/TextInput/TextInput.tsx +65 -0
- package/src/theme/themes/variable.css +29 -29
|
@@ -237,7 +237,9 @@ const KeepFooterSpaceDemo = () => {
|
|
|
237
237
|
keepFooterSpace
|
|
238
238
|
size="lg"
|
|
239
239
|
error={hasError}
|
|
240
|
-
errorMessage={
|
|
240
|
+
errorMessage={
|
|
241
|
+
hasError ? "Please enter a valid email address" : undefined
|
|
242
|
+
}
|
|
241
243
|
/>
|
|
242
244
|
</div>
|
|
243
245
|
<div>
|
|
@@ -249,7 +251,9 @@ const KeepFooterSpaceDemo = () => {
|
|
|
249
251
|
label="Email"
|
|
250
252
|
size="lg"
|
|
251
253
|
error={hasError}
|
|
252
|
-
errorMessage={
|
|
254
|
+
errorMessage={
|
|
255
|
+
hasError ? "Please enter a valid email address" : undefined
|
|
256
|
+
}
|
|
253
257
|
/>
|
|
254
258
|
</div>
|
|
255
259
|
<div></div>
|
|
@@ -315,7 +319,9 @@ const FeedbackApiDemo = () => {
|
|
|
315
319
|
error={legacyError}
|
|
316
320
|
errorMessage={legacyError ? "Invalid email format." : undefined}
|
|
317
321
|
warning={legacyWarning}
|
|
318
|
-
warningMessage={
|
|
322
|
+
warningMessage={
|
|
323
|
+
legacyWarning ? "Please verify this email." : undefined
|
|
324
|
+
}
|
|
319
325
|
/>
|
|
320
326
|
</div>
|
|
321
327
|
|
|
@@ -336,8 +342,8 @@ const FeedbackApiDemo = () => {
|
|
|
336
342
|
useStatusOverride
|
|
337
343
|
? "Status explicitly sets warning."
|
|
338
344
|
: legacyWarning
|
|
339
|
-
|
|
340
|
-
|
|
345
|
+
? "Please verify this email."
|
|
346
|
+
: undefined
|
|
341
347
|
}
|
|
342
348
|
/>
|
|
343
349
|
</div>
|
|
@@ -349,3 +355,112 @@ const FeedbackApiDemo = () => {
|
|
|
349
355
|
export const FeedbackApiCompatibility = {
|
|
350
356
|
render: () => <FeedbackApiDemo />,
|
|
351
357
|
} satisfies StoryObj;
|
|
358
|
+
|
|
359
|
+
const NormalizeDemo = () => {
|
|
360
|
+
const [value, setValue] = useState("");
|
|
361
|
+
return (
|
|
362
|
+
<div className="flex flex-col gap-6 w-full max-w-md">
|
|
363
|
+
<p className="text-sm text-text-g-contrast-low">
|
|
364
|
+
<code>normalize</code> strips disallowed characters on every keystroke.
|
|
365
|
+
This example removes backslash and special path characters in real-time.
|
|
366
|
+
</p>
|
|
367
|
+
<TextInput
|
|
368
|
+
id="normalize-demo"
|
|
369
|
+
label="Device Name"
|
|
370
|
+
size="lg"
|
|
371
|
+
keepFooterSpace
|
|
372
|
+
helperText={`Stored value: "${value}"`}
|
|
373
|
+
value={value}
|
|
374
|
+
normalize={(v) => v.trimStart().replace(/[\\/:*?"'<>|]/g, "")}
|
|
375
|
+
onChange={(e) => setValue(e.target.value)}
|
|
376
|
+
/>
|
|
377
|
+
</div>
|
|
378
|
+
);
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
export const Normalize = {
|
|
382
|
+
render: () => <NormalizeDemo />,
|
|
383
|
+
} satisfies StoryObj;
|
|
384
|
+
|
|
385
|
+
const FormatDemo = () => {
|
|
386
|
+
const [value, setValue] = useState("1000000");
|
|
387
|
+
return (
|
|
388
|
+
<div className="flex flex-col gap-6 w-full max-w-md">
|
|
389
|
+
<p className="text-sm text-text-g-contrast-low">
|
|
390
|
+
<code>format</code> transforms the displayed value without changing the
|
|
391
|
+
stored value. This example displays numbers with comma separators.
|
|
392
|
+
</p>
|
|
393
|
+
<TextInput
|
|
394
|
+
id="format-demo"
|
|
395
|
+
label="Amount"
|
|
396
|
+
size="lg"
|
|
397
|
+
keepFooterSpace
|
|
398
|
+
helperText={`Stored value: "${value}"`}
|
|
399
|
+
value={value}
|
|
400
|
+
format={(v) => Number(v.replace(/,/g, "") || 0).toLocaleString()}
|
|
401
|
+
normalize={(v) => v.replace(/[^0-9]/g, "")}
|
|
402
|
+
onChange={(e) => setValue(e.target.value)}
|
|
403
|
+
/>
|
|
404
|
+
</div>
|
|
405
|
+
);
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
export const Format = {
|
|
409
|
+
render: () => <FormatDemo />,
|
|
410
|
+
} satisfies StoryObj;
|
|
411
|
+
|
|
412
|
+
const TrimOnCommitDemo = () => {
|
|
413
|
+
const [value, setValue] = useState("");
|
|
414
|
+
return (
|
|
415
|
+
<div className="flex flex-col gap-6 w-full max-w-md">
|
|
416
|
+
<p className="text-sm text-text-g-contrast-low">
|
|
417
|
+
<code>trimOnCommit</code> trims leading and trailing whitespace when the
|
|
418
|
+
user blurs the input or presses Enter. Try typing{" "}
|
|
419
|
+
<code>"hello "</code> then click outside or press Enter.
|
|
420
|
+
</p>
|
|
421
|
+
<TextInput
|
|
422
|
+
id="trim-commit-demo"
|
|
423
|
+
label="Name"
|
|
424
|
+
size="lg"
|
|
425
|
+
keepFooterSpace
|
|
426
|
+
helperText={`Stored value: "${value}"`}
|
|
427
|
+
value={value}
|
|
428
|
+
trimOnCommit
|
|
429
|
+
onChange={(e) => setValue(e.target.value)}
|
|
430
|
+
/>
|
|
431
|
+
</div>
|
|
432
|
+
);
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
export const TrimOnCommit = {
|
|
436
|
+
render: () => <TrimOnCommitDemo />,
|
|
437
|
+
} satisfies StoryObj;
|
|
438
|
+
|
|
439
|
+
const NormalizeOnCommitDemo = () => {
|
|
440
|
+
const [value, setValue] = useState("");
|
|
441
|
+
return (
|
|
442
|
+
<div className="flex flex-col gap-6 w-full max-w-md">
|
|
443
|
+
<p className="text-sm text-text-g-contrast-low">
|
|
444
|
+
<code>normalizeOnCommit</code> applies a custom transform when the user
|
|
445
|
+
blurs or presses Enter. Use with <code>trimOnCommit</code> to compose —
|
|
446
|
+
trim runs first, then <code>normalizeOnCommit</code>. This example trims
|
|
447
|
+
and uppercases on commit.
|
|
448
|
+
</p>
|
|
449
|
+
<TextInput
|
|
450
|
+
id="normalize-on-commit-demo"
|
|
451
|
+
label="Code"
|
|
452
|
+
size="lg"
|
|
453
|
+
keepFooterSpace
|
|
454
|
+
helperText={`Stored value: "${value}"`}
|
|
455
|
+
value={value}
|
|
456
|
+
trimOnCommit
|
|
457
|
+
normalizeOnCommit={(v) => v.toUpperCase()}
|
|
458
|
+
onChange={(e) => setValue(e.target.value)}
|
|
459
|
+
/>
|
|
460
|
+
</div>
|
|
461
|
+
);
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
export const NormalizeOnCommit = {
|
|
465
|
+
render: () => <NormalizeOnCommitDemo />,
|
|
466
|
+
} satisfies StoryObj;
|
|
@@ -5,6 +5,9 @@ import React, {
|
|
|
5
5
|
useImperativeHandle,
|
|
6
6
|
useMemo,
|
|
7
7
|
useRef,
|
|
8
|
+
FocusEvent,
|
|
9
|
+
KeyboardEvent,
|
|
10
|
+
ChangeEvent,
|
|
8
11
|
} from "react";
|
|
9
12
|
import {
|
|
10
13
|
helperTextVariant,
|
|
@@ -60,6 +63,10 @@ export type InputProps = {
|
|
|
60
63
|
onClickEndIcon?: () => void;
|
|
61
64
|
renderStartIcon?: () => ReactNode;
|
|
62
65
|
renderEndIcon?: () => ReactNode;
|
|
66
|
+
normalize?: (value: string) => string;
|
|
67
|
+
format?: (value: string) => string;
|
|
68
|
+
trimOnCommit?: boolean;
|
|
69
|
+
normalizeOnCommit?: (value: string) => string;
|
|
63
70
|
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;
|
|
64
71
|
|
|
65
72
|
export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
@@ -94,6 +101,10 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
94
101
|
renderStartIcon,
|
|
95
102
|
renderEndIcon,
|
|
96
103
|
classes,
|
|
104
|
+
normalize,
|
|
105
|
+
format,
|
|
106
|
+
trimOnCommit,
|
|
107
|
+
normalizeOnCommit,
|
|
97
108
|
...props
|
|
98
109
|
},
|
|
99
110
|
ref
|
|
@@ -174,6 +185,56 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
174
185
|
|
|
175
186
|
useImperativeHandle(ref, () => inputRef?.current as HTMLInputElement);
|
|
176
187
|
|
|
188
|
+
const handleChange = useCallback(
|
|
189
|
+
(e: ChangeEvent<HTMLInputElement>) => {
|
|
190
|
+
if (normalize) {
|
|
191
|
+
e.target.value = normalize(e.target.value);
|
|
192
|
+
}
|
|
193
|
+
props.onChange?.(e);
|
|
194
|
+
},
|
|
195
|
+
[normalize, props.onChange]
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const commitValue = useCallback(
|
|
199
|
+
(e: FocusEvent<HTMLInputElement> | KeyboardEvent<HTMLInputElement>) => {
|
|
200
|
+
const input = e.currentTarget;
|
|
201
|
+
let committed = input.value;
|
|
202
|
+
if (trimOnCommit) committed = committed.trim();
|
|
203
|
+
if (normalizeOnCommit) committed = normalizeOnCommit(committed);
|
|
204
|
+
if (committed !== input.value) {
|
|
205
|
+
input.value = committed;
|
|
206
|
+
props.onChange?.({
|
|
207
|
+
...e,
|
|
208
|
+
target: input,
|
|
209
|
+
} as unknown as ChangeEvent<HTMLInputElement>);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
[trimOnCommit, normalizeOnCommit, props.onChange]
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const handleBlur = useCallback(
|
|
216
|
+
(e: FocusEvent<HTMLInputElement>) => {
|
|
217
|
+
if (trimOnCommit || normalizeOnCommit) commitValue(e);
|
|
218
|
+
props.onBlur?.(e);
|
|
219
|
+
},
|
|
220
|
+
[trimOnCommit, normalizeOnCommit, commitValue, props.onBlur]
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const handleKeyDown = useCallback(
|
|
224
|
+
(e: KeyboardEvent<HTMLInputElement>) => {
|
|
225
|
+
if ((trimOnCommit || normalizeOnCommit) && e.key === "Enter") {
|
|
226
|
+
commitValue(e);
|
|
227
|
+
}
|
|
228
|
+
props.onKeyDown?.(e);
|
|
229
|
+
},
|
|
230
|
+
[trimOnCommit, normalizeOnCommit, commitValue, props.onKeyDown]
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const displayValue =
|
|
234
|
+
format && typeof props.value === "string"
|
|
235
|
+
? format(props.value)
|
|
236
|
+
: props.value;
|
|
237
|
+
|
|
177
238
|
const handleClearInput = useCallback(() => {
|
|
178
239
|
if (inputRef.current) {
|
|
179
240
|
inputRef.current.value = "";
|
|
@@ -325,7 +386,11 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
|
|
|
325
386
|
type={type}
|
|
326
387
|
id={_id}
|
|
327
388
|
disabled={disabled}
|
|
389
|
+
value={displayValue}
|
|
328
390
|
className={cn(inputClassname, props.className)}
|
|
391
|
+
onChange={normalize ? handleChange : props.onChange}
|
|
392
|
+
onBlur={trimOnCommit || normalizeOnCommit ? handleBlur : props.onBlur}
|
|
393
|
+
onKeyDown={trimOnCommit || normalizeOnCommit ? handleKeyDown : props.onKeyDown}
|
|
329
394
|
/>
|
|
330
395
|
{hasSearchIcon && !hasLeftSectionIcon && (
|
|
331
396
|
<div
|
|
@@ -197,34 +197,34 @@
|
|
|
197
197
|
--state-success-text-pressed-skyller: #166534;
|
|
198
198
|
--state-warning-default-xspector: #ffc107;
|
|
199
199
|
--state-warning-default-report-xspector-light-mode: #ffc107;
|
|
200
|
-
--state-warning-default-skyller: #
|
|
200
|
+
--state-warning-default-skyller: #f59e0b;
|
|
201
201
|
--state-warning-hover-xspector: #ffdf1b;
|
|
202
202
|
--state-warning-hover-report-xspector-light-mode: #ffdf1b;
|
|
203
|
-
--state-warning-hover-skyller: #
|
|
203
|
+
--state-warning-hover-skyller: #fbbf24;
|
|
204
204
|
--state-warning-stroke-xspector: rgba(255 193 7 / 0.48);
|
|
205
205
|
--state-warning-stroke-report-xspector-light-mode: rgba(255 223 27 / 0.48);
|
|
206
|
-
--state-warning-stroke-skyller: rgba(
|
|
206
|
+
--state-warning-stroke-skyller: rgba(245 158 11 / 0.48);
|
|
207
207
|
--state-warning-hover-bg-xspector: rgba(255 193 7 / 0.08);
|
|
208
208
|
--state-warning-hover-bg-report-xspector-light-mode: rgba(255 193 7 / 0.08);
|
|
209
|
-
--state-warning-hover-bg-skyller: rgba(
|
|
209
|
+
--state-warning-hover-bg-skyller: rgba(251 191 36 / 0.08);
|
|
210
210
|
--state-warning-pressed-xspector: #985108;
|
|
211
211
|
--state-warning-pressed-report-xspector-light-mode: #985108;
|
|
212
|
-
--state-warning-pressed-skyller: #
|
|
212
|
+
--state-warning-pressed-skyller: #d97706;
|
|
213
213
|
--state-warning-active-xspector: #ffc107;
|
|
214
214
|
--state-warning-active-report-xspector-light-mode: #ffc107;
|
|
215
|
-
--state-warning-active-skyller: #
|
|
215
|
+
--state-warning-active-skyller: #b45309;
|
|
216
216
|
--state-warning-text-solid-xspector: #212b36;
|
|
217
217
|
--state-warning-text-solid-report-xspector-light-mode: #ffffff;
|
|
218
218
|
--state-warning-text-solid-skyller: #ffffff;
|
|
219
219
|
--state-warning-text-outline-xspector: #ffc107;
|
|
220
220
|
--state-warning-text-outline-report-xspector-light-mode: #ffc107;
|
|
221
|
-
--state-warning-text-outline-skyller: #
|
|
221
|
+
--state-warning-text-outline-skyller: #f59e0b;
|
|
222
222
|
--state-warning-text-hover-xspector: #ffdf1b;
|
|
223
223
|
--state-warning-text-hover-report-xspector-light-mode: #ffdf1b;
|
|
224
|
-
--state-warning-text-hover-skyller: #
|
|
224
|
+
--state-warning-text-hover-skyller: #fbbf24;
|
|
225
225
|
--state-warning-text-pressed-xspector: #985108;
|
|
226
226
|
--state-warning-text-pressed-report-xspector-light-mode: #985108;
|
|
227
|
-
--state-warning-text-pressed-skyller: #
|
|
227
|
+
--state-warning-text-pressed-skyller: #d97706;
|
|
228
228
|
--state-error-default-xspector: #ff4d35;
|
|
229
229
|
--state-error-default-report-xspector-light-mode: #ed2f15;
|
|
230
230
|
--state-error-default-skyller: #ef4444;
|
|
@@ -827,19 +827,19 @@
|
|
|
827
827
|
--modal-overlay-skyller: rgba(0 0 0 / 0.64);
|
|
828
828
|
--bg-bg1-xspector: #030c15;
|
|
829
829
|
--bg-bg1-report-xspector-light-mode: #ffffff;
|
|
830
|
-
--bg-bg1-skyller: #
|
|
830
|
+
--bg-bg1-skyller: #ffffff;
|
|
831
831
|
--bg-bg2-xspector: #0a1b2e;
|
|
832
832
|
--bg-bg2-report-xspector-light-mode: #ffffff;
|
|
833
833
|
--bg-bg2-skyller: #f8fbff;
|
|
834
|
-
--bg-bg3-xspector: #
|
|
835
|
-
--bg-bg3-report-xspector-light-mode: #
|
|
836
|
-
--bg-bg3-skyller: #
|
|
837
|
-
--bg-stroke1-xspector: #
|
|
834
|
+
--bg-bg3-xspector: #0e2841;
|
|
835
|
+
--bg-bg3-report-xspector-light-mode: #ffffff;
|
|
836
|
+
--bg-bg3-skyller: #f8fbff;
|
|
837
|
+
--bg-stroke1-xspector: #1c3955;
|
|
838
838
|
--bg-stroke1-report-xspector-light-mode: #e2e2e2;
|
|
839
|
-
--bg-stroke1-skyller: #
|
|
840
|
-
--bg-stroke2-xspector: #
|
|
841
|
-
--bg-stroke2-report-xspector-light-mode: #
|
|
842
|
-
--bg-stroke2-skyller: #
|
|
839
|
+
--bg-stroke1-skyller: #ececec;
|
|
840
|
+
--bg-stroke2-xspector: #294664;
|
|
841
|
+
--bg-stroke2-report-xspector-light-mode: #e5e5e5;
|
|
842
|
+
--bg-stroke2-skyller: #e5e5e5;
|
|
843
843
|
--input-default-text-xspector: #9e9e9e;
|
|
844
844
|
--input-default-text-report-xspector-light-mode: #9e9e9e;
|
|
845
845
|
--input-default-text-skyller: #737373;
|
|
@@ -1038,21 +1038,21 @@
|
|
|
1038
1038
|
--modal-line-xspector: rgba(158 158 158 / 0.24);
|
|
1039
1039
|
--modal-line-report-xspector-light-mode: #cfcfcf;
|
|
1040
1040
|
--modal-line-skyller: #d4d4d4;
|
|
1041
|
-
--bg-bg4-xspector: #
|
|
1042
|
-
--bg-bg4-report-xspector-light-mode: #
|
|
1043
|
-
--bg-bg4-skyller: #
|
|
1041
|
+
--bg-bg4-xspector: #0f2a46;
|
|
1042
|
+
--bg-bg4-report-xspector-light-mode: #ffffff;
|
|
1043
|
+
--bg-bg4-skyller: #ffffff;
|
|
1044
1044
|
--bg-bg5-xspector: #000000;
|
|
1045
|
-
--bg-bg5-report-xspector-light-mode: #
|
|
1046
|
-
--bg-bg5-skyller: #
|
|
1045
|
+
--bg-bg5-report-xspector-light-mode: #000000;
|
|
1046
|
+
--bg-bg5-skyller: #000000;
|
|
1047
1047
|
--bg-stroke3-xspector: #000000;
|
|
1048
|
-
--bg-stroke3-report-xspector-light-mode: #
|
|
1049
|
-
--bg-stroke3-skyller: #
|
|
1048
|
+
--bg-stroke3-report-xspector-light-mode: #000000;
|
|
1049
|
+
--bg-stroke3-skyller: #000000;
|
|
1050
1050
|
--bg-stroke4-xspector: #000000;
|
|
1051
|
-
--bg-stroke4-report-xspector-light-mode: #
|
|
1052
|
-
--bg-stroke4-skyller: #
|
|
1051
|
+
--bg-stroke4-report-xspector-light-mode: #000000;
|
|
1052
|
+
--bg-stroke4-skyller: #000000;
|
|
1053
1053
|
--bg-stroke5-xspector: #000000;
|
|
1054
|
-
--bg-stroke5-report-xspector-light-mode: #
|
|
1055
|
-
--bg-stroke5-skyller: #
|
|
1054
|
+
--bg-stroke5-report-xspector-light-mode: #000000;
|
|
1055
|
+
--bg-stroke5-skyller: #000000;
|
|
1056
1056
|
|
|
1057
1057
|
/* BUTTON RADIUS */
|
|
1058
1058
|
--button-l-round: 8px;
|