@2urgseui/core 0.1.1 → 0.1.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/dist/index.cjs +235 -6
- package/dist/index.d.cts +50 -2
- package/dist/index.d.ts +50 -2
- package/dist/index.js +230 -6
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -226,6 +226,11 @@ __export(index_exports, {
|
|
|
226
226
|
colors: () => colors,
|
|
227
227
|
containerVariants: () => containerVariants,
|
|
228
228
|
dismissToast: () => dismissToast,
|
|
229
|
+
formFieldCanonicalizeNumericInput: () => formFieldCanonicalizeNumericInput,
|
|
230
|
+
formFieldFormatNumericDisplay: () => formFieldFormatNumericDisplay,
|
|
231
|
+
formFieldMaskExtractCanonical: () => formFieldMaskExtractCanonical,
|
|
232
|
+
formFieldMaskFormatDisplay: () => formFieldMaskFormatDisplay,
|
|
233
|
+
formFieldMaskSlotCount: () => formFieldMaskSlotCount,
|
|
229
234
|
formValueToAsyncSelectOption: () => formValueToAsyncSelectOption,
|
|
230
235
|
getIcon: () => getIcon,
|
|
231
236
|
headingVariants: () => headingVariants,
|
|
@@ -2756,6 +2761,106 @@ Textarea.displayName = "Textarea";
|
|
|
2756
2761
|
|
|
2757
2762
|
// source/components/primitive/FormField/form-field.tsx
|
|
2758
2763
|
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
2764
|
+
function stripThousands(s) {
|
|
2765
|
+
return s.replace(/,/g, "");
|
|
2766
|
+
}
|
|
2767
|
+
function formFieldCanonicalizeNumericInput(raw, allowDecimal) {
|
|
2768
|
+
const withoutCommas = stripThousands(raw);
|
|
2769
|
+
const pattern = allowDecimal ? /[^\d.]/g : /\D/g;
|
|
2770
|
+
let cleaned = withoutCommas.replace(pattern, "");
|
|
2771
|
+
if (!allowDecimal) return cleaned;
|
|
2772
|
+
const parts = cleaned.split(".");
|
|
2773
|
+
if (parts.length <= 1) return cleaned;
|
|
2774
|
+
const intPart = parts[0] ?? "";
|
|
2775
|
+
const frac = parts.slice(1).join("");
|
|
2776
|
+
return intPart + "." + frac;
|
|
2777
|
+
}
|
|
2778
|
+
function formatThousands(intPart) {
|
|
2779
|
+
if (!intPart) return "";
|
|
2780
|
+
return intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
2781
|
+
}
|
|
2782
|
+
function formFieldFormatNumericDisplay(stored, allowDecimal, useGrouping) {
|
|
2783
|
+
if (stored == null) return "";
|
|
2784
|
+
const s = String(stored);
|
|
2785
|
+
if (s === "") return "";
|
|
2786
|
+
if (!useGrouping) return s;
|
|
2787
|
+
if (allowDecimal && s.includes(".")) {
|
|
2788
|
+
const [intPart, ...rest] = s.split(".");
|
|
2789
|
+
const frac = rest.join("");
|
|
2790
|
+
return formatThousands(intPart ?? "") + "." + frac;
|
|
2791
|
+
}
|
|
2792
|
+
return formatThousands(s);
|
|
2793
|
+
}
|
|
2794
|
+
var FORM_FIELD_MASK_SLOTS = /* @__PURE__ */ new Set(["#", "h", "H", "X", "x", "*"]);
|
|
2795
|
+
function isFormFieldMaskSlotChar(p) {
|
|
2796
|
+
return FORM_FIELD_MASK_SLOTS.has(p);
|
|
2797
|
+
}
|
|
2798
|
+
function matchesMaskSlot(slot, ch) {
|
|
2799
|
+
switch (slot) {
|
|
2800
|
+
case "#":
|
|
2801
|
+
return /\d/.test(ch);
|
|
2802
|
+
case "h":
|
|
2803
|
+
case "H":
|
|
2804
|
+
return /[0-9a-fA-F]/.test(ch);
|
|
2805
|
+
case "X":
|
|
2806
|
+
case "x":
|
|
2807
|
+
return /[A-Za-z]/.test(ch);
|
|
2808
|
+
case "*":
|
|
2809
|
+
return /[A-Za-z0-9]/.test(ch);
|
|
2810
|
+
default:
|
|
2811
|
+
return false;
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
function normalizeMaskSlotChar(slot, ch) {
|
|
2815
|
+
switch (slot) {
|
|
2816
|
+
case "#":
|
|
2817
|
+
return ch;
|
|
2818
|
+
case "h":
|
|
2819
|
+
return ch.toLowerCase();
|
|
2820
|
+
case "H":
|
|
2821
|
+
return /\d/.test(ch) ? ch : ch.toUpperCase();
|
|
2822
|
+
case "X":
|
|
2823
|
+
return ch.toUpperCase();
|
|
2824
|
+
case "x":
|
|
2825
|
+
return ch.toLowerCase();
|
|
2826
|
+
case "*":
|
|
2827
|
+
return ch;
|
|
2828
|
+
default:
|
|
2829
|
+
return ch;
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
function formFieldMaskSlotCount(pattern) {
|
|
2833
|
+
return [...pattern].filter(isFormFieldMaskSlotChar).length;
|
|
2834
|
+
}
|
|
2835
|
+
function formFieldMaskExtractCanonical(raw, pattern) {
|
|
2836
|
+
const slots = [...pattern].filter(isFormFieldMaskSlotChar);
|
|
2837
|
+
if (!slots.length) return "";
|
|
2838
|
+
const out = [];
|
|
2839
|
+
let si = 0;
|
|
2840
|
+
for (const ch of raw) {
|
|
2841
|
+
if (si >= slots.length) break;
|
|
2842
|
+
const slot = slots[si];
|
|
2843
|
+
if (matchesMaskSlot(slot, ch)) {
|
|
2844
|
+
out.push(normalizeMaskSlotChar(slot, ch));
|
|
2845
|
+
si++;
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
return out.join("");
|
|
2849
|
+
}
|
|
2850
|
+
function formFieldMaskFormatDisplay(pattern, canonical) {
|
|
2851
|
+
let ci = 0;
|
|
2852
|
+
let result = "";
|
|
2853
|
+
for (const p of pattern) {
|
|
2854
|
+
if (isFormFieldMaskSlotChar(p)) {
|
|
2855
|
+
if (ci >= canonical.length) break;
|
|
2856
|
+
result += canonical[ci];
|
|
2857
|
+
ci++;
|
|
2858
|
+
} else if (ci < canonical.length) {
|
|
2859
|
+
result += p;
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
return result;
|
|
2863
|
+
}
|
|
2759
2864
|
var VARIANTS_NEED_CONTROL = [
|
|
2760
2865
|
"checkbox",
|
|
2761
2866
|
"switch",
|
|
@@ -2794,6 +2899,8 @@ function FormField({
|
|
|
2794
2899
|
richTextProps,
|
|
2795
2900
|
dropzoneProps,
|
|
2796
2901
|
asyncSelectProps,
|
|
2902
|
+
numericInput,
|
|
2903
|
+
maskInput,
|
|
2797
2904
|
className,
|
|
2798
2905
|
renderInput
|
|
2799
2906
|
}) {
|
|
@@ -2801,6 +2908,39 @@ function FormField({
|
|
|
2801
2908
|
const inputId = `field-${generatedId}`;
|
|
2802
2909
|
const descriptionId = description ? `${inputId}-description` : void 0;
|
|
2803
2910
|
const externalError = error2 ? String(error2) : void 0;
|
|
2911
|
+
if (maskInput && numericInput) {
|
|
2912
|
+
throw new Error(
|
|
2913
|
+
"FormField does not support `maskInput` and `numericInput` together; use only one."
|
|
2914
|
+
);
|
|
2915
|
+
}
|
|
2916
|
+
if (maskInput) {
|
|
2917
|
+
if (!control) {
|
|
2918
|
+
throw new Error(
|
|
2919
|
+
"FormField `maskInput` requires the `control` prop (Controller). Use `control`, not `register`, for masked fields."
|
|
2920
|
+
);
|
|
2921
|
+
}
|
|
2922
|
+
if (variant !== "input") {
|
|
2923
|
+
throw new Error('FormField `maskInput` is only supported with the default `variant="input"`.');
|
|
2924
|
+
}
|
|
2925
|
+
if (!maskInput.pattern?.trim()) {
|
|
2926
|
+
throw new Error("FormField `maskInput.pattern` must be a non-empty string.");
|
|
2927
|
+
}
|
|
2928
|
+
if (formFieldMaskSlotCount(maskInput.pattern) === 0) {
|
|
2929
|
+
throw new Error(
|
|
2930
|
+
"FormField `maskInput.pattern` must include at least one slot token: `#`, `h`, `H`, `X`, `x`, or `*`."
|
|
2931
|
+
);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
if (numericInput) {
|
|
2935
|
+
if (!control) {
|
|
2936
|
+
throw new Error(
|
|
2937
|
+
"FormField `numericInput` requires the `control` prop (Controller). Use `control`, not `register`, for formatted numeric fields."
|
|
2938
|
+
);
|
|
2939
|
+
}
|
|
2940
|
+
if (variant !== "input") {
|
|
2941
|
+
throw new Error('FormField `numericInput` is only supported with the default `variant="input"`.');
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2804
2944
|
if (!control && VARIANTS_NEED_CONTROL.includes(variant)) {
|
|
2805
2945
|
throw new Error(
|
|
2806
2946
|
`FormField variant "${variant}" requires the control prop (React Hook Form Controller).`
|
|
@@ -2854,7 +2994,9 @@ function FormField({
|
|
|
2854
2994
|
otpProps,
|
|
2855
2995
|
richTextProps,
|
|
2856
2996
|
dropzoneProps,
|
|
2857
|
-
asyncSelectProps
|
|
2997
|
+
asyncSelectProps,
|
|
2998
|
+
numericInput: renderInput ? void 0 : numericInput,
|
|
2999
|
+
maskInput: renderInput ? void 0 : maskInput
|
|
2858
3000
|
}
|
|
2859
3001
|
);
|
|
2860
3002
|
const labelBlock = isCheckboxInline || !hasFieldLabel2 ? null : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
@@ -3003,7 +3145,9 @@ function FormFieldVariantControl({
|
|
|
3003
3145
|
otpProps,
|
|
3004
3146
|
richTextProps,
|
|
3005
3147
|
dropzoneProps,
|
|
3006
|
-
asyncSelectProps
|
|
3148
|
+
asyncSelectProps,
|
|
3149
|
+
numericInput,
|
|
3150
|
+
maskInput
|
|
3007
3151
|
}) {
|
|
3008
3152
|
switch (variant) {
|
|
3009
3153
|
case "textarea":
|
|
@@ -3241,22 +3385,102 @@ function FormFieldVariantControl({
|
|
|
3241
3385
|
`${String(field.name)}-${field.value === null ? "cleared" : typeof FileList !== "undefined" && field.value instanceof FileList ? field.value.length : "open"}`
|
|
3242
3386
|
) });
|
|
3243
3387
|
case "input":
|
|
3244
|
-
default:
|
|
3388
|
+
default: {
|
|
3389
|
+
const {
|
|
3390
|
+
onChange: inputOnChangeFromProps,
|
|
3391
|
+
value: _omitValueFromInputProps,
|
|
3392
|
+
type: inputTypeProp,
|
|
3393
|
+
inputMode: inputModeProp,
|
|
3394
|
+
disabled: inputDisabledFromProps,
|
|
3395
|
+
...restInputProps
|
|
3396
|
+
} = inputProps ?? {};
|
|
3397
|
+
const inputDisabled = Boolean(inputDisabledFromProps) || Boolean(field.disabled);
|
|
3398
|
+
if (maskInput) {
|
|
3399
|
+
const pattern = maskInput.pattern;
|
|
3400
|
+
const rawStored = field.value == null || field.value === "" ? "" : String(field.value);
|
|
3401
|
+
const displayValue = formFieldMaskFormatDisplay(pattern, rawStored);
|
|
3402
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "w-full min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3403
|
+
Input,
|
|
3404
|
+
{
|
|
3405
|
+
...restInputProps,
|
|
3406
|
+
id: inputId,
|
|
3407
|
+
name: field.name,
|
|
3408
|
+
"aria-describedby": describedBy,
|
|
3409
|
+
error: hasError,
|
|
3410
|
+
type: "text",
|
|
3411
|
+
inputMode: maskInput.inputMode ?? "text",
|
|
3412
|
+
maxLength: pattern.length,
|
|
3413
|
+
autoComplete: "off",
|
|
3414
|
+
spellCheck: false,
|
|
3415
|
+
value: displayValue,
|
|
3416
|
+
onChange: (e) => {
|
|
3417
|
+
const canonical = formFieldMaskExtractCanonical(
|
|
3418
|
+
e.target.value,
|
|
3419
|
+
pattern
|
|
3420
|
+
);
|
|
3421
|
+
field.onChange(canonical);
|
|
3422
|
+
},
|
|
3423
|
+
onBlur: field.onBlur,
|
|
3424
|
+
ref: field.ref,
|
|
3425
|
+
disabled: inputDisabled
|
|
3426
|
+
}
|
|
3427
|
+
) });
|
|
3428
|
+
}
|
|
3429
|
+
if (numericInput) {
|
|
3430
|
+
const allowDecimal = numericInput.allowDecimal ?? false;
|
|
3431
|
+
const useGrouping = numericInput.useGrouping ?? true;
|
|
3432
|
+
const rawStored = field.value == null || field.value === "" ? "" : String(field.value);
|
|
3433
|
+
const displayValue = formFieldFormatNumericDisplay(
|
|
3434
|
+
rawStored,
|
|
3435
|
+
allowDecimal,
|
|
3436
|
+
useGrouping
|
|
3437
|
+
);
|
|
3438
|
+
const defaultInputMode = allowDecimal ? "decimal" : "numeric";
|
|
3439
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "w-full min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3440
|
+
Input,
|
|
3441
|
+
{
|
|
3442
|
+
...restInputProps,
|
|
3443
|
+
id: inputId,
|
|
3444
|
+
name: field.name,
|
|
3445
|
+
"aria-describedby": describedBy,
|
|
3446
|
+
error: hasError,
|
|
3447
|
+
type: "text",
|
|
3448
|
+
inputMode: numericInput.inputMode ?? defaultInputMode,
|
|
3449
|
+
value: displayValue,
|
|
3450
|
+
onChange: (e) => {
|
|
3451
|
+
const canonical = formFieldCanonicalizeNumericInput(
|
|
3452
|
+
e.target.value,
|
|
3453
|
+
allowDecimal
|
|
3454
|
+
);
|
|
3455
|
+
field.onChange(canonical);
|
|
3456
|
+
},
|
|
3457
|
+
onBlur: field.onBlur,
|
|
3458
|
+
ref: field.ref,
|
|
3459
|
+
disabled: inputDisabled
|
|
3460
|
+
}
|
|
3461
|
+
) });
|
|
3462
|
+
}
|
|
3245
3463
|
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "w-full min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3246
3464
|
Input,
|
|
3247
3465
|
{
|
|
3248
|
-
...
|
|
3466
|
+
...restInputProps,
|
|
3249
3467
|
id: inputId,
|
|
3250
3468
|
name: field.name,
|
|
3251
3469
|
"aria-describedby": describedBy,
|
|
3252
3470
|
error: hasError,
|
|
3471
|
+
type: inputTypeProp,
|
|
3472
|
+
inputMode: inputModeProp,
|
|
3253
3473
|
value: field.value ?? "",
|
|
3254
|
-
onChange:
|
|
3474
|
+
onChange: (e) => {
|
|
3475
|
+
inputOnChangeFromProps?.(e);
|
|
3476
|
+
field.onChange(e);
|
|
3477
|
+
},
|
|
3255
3478
|
onBlur: field.onBlur,
|
|
3256
3479
|
ref: field.ref,
|
|
3257
|
-
disabled:
|
|
3480
|
+
disabled: inputDisabled
|
|
3258
3481
|
}
|
|
3259
3482
|
) });
|
|
3483
|
+
}
|
|
3260
3484
|
}
|
|
3261
3485
|
}
|
|
3262
3486
|
|
|
@@ -5483,6 +5707,11 @@ var typography = {
|
|
|
5483
5707
|
colors,
|
|
5484
5708
|
containerVariants,
|
|
5485
5709
|
dismissToast,
|
|
5710
|
+
formFieldCanonicalizeNumericInput,
|
|
5711
|
+
formFieldFormatNumericDisplay,
|
|
5712
|
+
formFieldMaskExtractCanonical,
|
|
5713
|
+
formFieldMaskFormatDisplay,
|
|
5714
|
+
formFieldMaskSlotCount,
|
|
5486
5715
|
formValueToAsyncSelectOption,
|
|
5487
5716
|
getIcon,
|
|
5488
5717
|
headingVariants,
|
package/dist/index.d.cts
CHANGED
|
@@ -488,6 +488,44 @@ type FormFieldDropzoneConfig = Omit<FileDropzoneProps, "id" | "name" | "onChange
|
|
|
488
488
|
* The controlled field value is `{ value, label } | null | undefined` (legacy plain `string` is still read as id-only).
|
|
489
489
|
*/
|
|
490
490
|
type FormFieldAsyncSelectConfig = Omit<AsyncSelectProps, "value" | "onValueChange" | "disabled" | "error" | "name" | "id">;
|
|
491
|
+
/**
|
|
492
|
+
* Form state holds a **canonical string** (digits only, or digits + a single `.` + fraction when `allowDecimal`).
|
|
493
|
+
* The input shows **grouped** thousands when `useGrouping` is true. Use with `control` and default `input` variant only.
|
|
494
|
+
*/
|
|
495
|
+
type FormFieldNumericInputConfig = {
|
|
496
|
+
/** Allow a single decimal separator in the stored value. @default false */
|
|
497
|
+
allowDecimal?: boolean;
|
|
498
|
+
/** Show `,` thousands separators in the input while the stored value has none. @default true */
|
|
499
|
+
useGrouping?: boolean;
|
|
500
|
+
/** Passed to `<input inputMode>`. Defaults to `"decimal"` if `allowDecimal`, otherwise `"numeric"`. */
|
|
501
|
+
inputMode?: React.HTMLAttributes<HTMLInputElement>["inputMode"];
|
|
502
|
+
};
|
|
503
|
+
/** Raw input → canonical string for form state (no commas, optional single dot). */
|
|
504
|
+
declare function formFieldCanonicalizeNumericInput(raw: string, allowDecimal: boolean): string;
|
|
505
|
+
/** Canonical stored string → display string with optional grouping. */
|
|
506
|
+
declare function formFieldFormatNumericDisplay(stored: string, allowDecimal: boolean, useGrouping: boolean): string;
|
|
507
|
+
/** Number of editable slots in `pattern` (each `#`, `h`, `H`, `X`, `x`, or `*`). */
|
|
508
|
+
declare function formFieldMaskSlotCount(pattern: string): number;
|
|
509
|
+
/**
|
|
510
|
+
* Reads `raw` left-to-right and fills slots in order (ignores typed/pasted literals such as `-`).
|
|
511
|
+
* Form state should hold this canonical string (no separators).
|
|
512
|
+
*/
|
|
513
|
+
declare function formFieldMaskExtractCanonical(raw: string, pattern: string): string;
|
|
514
|
+
/** Inserts pattern literals between filled slots; omits trailing literals when incomplete. */
|
|
515
|
+
declare function formFieldMaskFormatDisplay(pattern: string, canonical: string): string;
|
|
516
|
+
/**
|
|
517
|
+
* Fixed mask for `control` + default `input`. Form state = **slot characters only** (no `-` etc.).
|
|
518
|
+
*
|
|
519
|
+
* **Tokens:** `#` digit · `h` hex (stored lower) · `H` hex (stored upper for a–f) · `X` letter upper · `x` letter lower · `*` alphanumeric.
|
|
520
|
+
* Any other character in `pattern` is a **literal** shown in that position (e.g. `-`).
|
|
521
|
+
*
|
|
522
|
+
* Example: `XXX-xxx-xxx` (letters). Hex / UUID-style: `hhhhhhhh-hhhh-hhhh-hhhh-hhhh-hhhh-hhhh-hhhh` (use `h`, not `x`, for hex digits).
|
|
523
|
+
*/
|
|
524
|
+
type FormFieldMaskInputConfig = {
|
|
525
|
+
pattern: string;
|
|
526
|
+
/** Passed to `<input inputMode>`. @default "text" */
|
|
527
|
+
inputMode?: React.HTMLAttributes<HTMLInputElement>["inputMode"];
|
|
528
|
+
};
|
|
491
529
|
interface FormFieldProps<TFieldValues extends FieldValues> {
|
|
492
530
|
name: Path<TFieldValues>;
|
|
493
531
|
/** When omitted, no visible label is rendered; use `aria-label` on controls or descriptions for a11y. */
|
|
@@ -511,6 +549,16 @@ interface FormFieldProps<TFieldValues extends FieldValues> {
|
|
|
511
549
|
dropzoneProps?: FormFieldDropzoneConfig;
|
|
512
550
|
/** With `variant="async-select"`: URL fetch config; form state is `AsyncSelectOption | null` (see `FormFieldAsyncSelectConfig`). */
|
|
513
551
|
asyncSelectProps?: FormFieldAsyncSelectConfig;
|
|
552
|
+
/**
|
|
553
|
+
* When set with `control` and default `input` variant (and no `renderInput`), formats integers/decimals in the field.
|
|
554
|
+
* Ignored when using `renderInput` (build a custom input there instead).
|
|
555
|
+
*/
|
|
556
|
+
numericInput?: FormFieldNumericInputConfig;
|
|
557
|
+
/**
|
|
558
|
+
* Fixed character mask (`pattern` with slot tokens + literals). Mutually exclusive with `numericInput`.
|
|
559
|
+
* Ignored when using `renderInput`.
|
|
560
|
+
*/
|
|
561
|
+
maskInput?: FormFieldMaskInputConfig;
|
|
514
562
|
className?: string;
|
|
515
563
|
renderInput?: (props: Omit<FormFieldRenderProps, "value"> & {
|
|
516
564
|
id: string;
|
|
@@ -521,7 +569,7 @@ interface FormFieldProps<TFieldValues extends FieldValues> {
|
|
|
521
569
|
value?: InputProps["value"] | AsyncSelectOption | null;
|
|
522
570
|
}) => React.ReactNode;
|
|
523
571
|
}
|
|
524
|
-
declare function FormField<TFieldValues extends FieldValues>({ name, label, register, control, rules, description, required, error, variant, inputProps, textareaProps, checkboxProps, switchProps, selectProps, radioProps, otpProps, richTextProps, dropzoneProps, asyncSelectProps, className, renderInput, }: FormFieldProps<TFieldValues>): react_jsx_runtime.JSX.Element;
|
|
572
|
+
declare function FormField<TFieldValues extends FieldValues>({ name, label, register, control, rules, description, required, error, variant, inputProps, textareaProps, checkboxProps, switchProps, selectProps, radioProps, otpProps, richTextProps, dropzoneProps, asyncSelectProps, numericInput, maskInput, className, renderInput, }: FormFieldProps<TFieldValues>): react_jsx_runtime.JSX.Element;
|
|
525
573
|
/** Maps react-hook-form field values to `AsyncSelect`’s `value` prop (supports `{ value, label }`, legacy `string`, `null`/`undefined`). */
|
|
526
574
|
declare function formValueToAsyncSelectOption(v: unknown): AsyncSelectOption | undefined;
|
|
527
575
|
|
|
@@ -892,4 +940,4 @@ declare namespace Toaster {
|
|
|
892
940
|
var displayName: string;
|
|
893
941
|
}
|
|
894
942
|
|
|
895
|
-
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, AppShell, type AppShellProps, AspectRatio, type AspectRatioProps, AsyncSelect, type AsyncSelectFetcher, type AsyncSelectOption, type AsyncSelectPage, type AsyncSelectProps, Avatar, AvatarFallback, AvatarImage, type AvatarProps, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, CalendarDayButton, Caption, type CaptionProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, type CheckboxProps, Collapsible, CollapsibleContent, CollapsibleTrigger, Container, type ContainerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FileDropzone, type FileDropzoneProps, FormField, type FormFieldAsyncSelectConfig, type FormFieldDropzoneConfig, type FormFieldOtpConfig, type FormFieldProps, type FormFieldRadioConfig, type FormFieldRadioOption, type FormFieldSelectConfig, type FormFieldSelectItem, type FormFieldVariant, Heading, type HeadingProps, Icon, type IconComponent, type IconComponentProps, type IconProps, Input, InputGroup, InputGroupIcon, InputGroupInput, type InputGroupInputProps, type InputGroupProps, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, type InputProps, Label, type LabelProps, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, type PopoverContentProps, PopoverTrigger, Progress, type ProgressProps, RadioGroup, RadioGroupItem, RichHtml, type RichHtmlProps, RichTextEditor, type RichTextEditorProps, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeparatorProps, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, type SkeletonProps, Slider, type SliderProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, type TextProps, Textarea, type TextareaProps, Toast, ToastAction, ToastClose, ToastDescription, type ToastPayload, type ToastProps, ToastProvider, type ToastRecord, ToastTitle, ToastViewport, Toaster, type ToasterProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buttonVariants, captionVariants, checkboxVariants, containerVariants, dismissToast, formValueToAsyncSelectOption, getIcon, headingVariants, inputGroupSelectTriggerTextAlignClass, inputVariants, labelVariants, registerIcons, separatorVariants, sidebarMenuButtonVariants, skeletonVariants, switchRootVariants, switchThumbVariants, textVariants, toast, toastVariants, useSidebar, useToast };
|
|
943
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, AppShell, type AppShellProps, AspectRatio, type AspectRatioProps, AsyncSelect, type AsyncSelectFetcher, type AsyncSelectOption, type AsyncSelectPage, type AsyncSelectProps, Avatar, AvatarFallback, AvatarImage, type AvatarProps, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, CalendarDayButton, Caption, type CaptionProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, type CheckboxProps, Collapsible, CollapsibleContent, CollapsibleTrigger, Container, type ContainerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FileDropzone, type FileDropzoneProps, FormField, type FormFieldAsyncSelectConfig, type FormFieldDropzoneConfig, type FormFieldMaskInputConfig, type FormFieldNumericInputConfig, type FormFieldOtpConfig, type FormFieldProps, type FormFieldRadioConfig, type FormFieldRadioOption, type FormFieldSelectConfig, type FormFieldSelectItem, type FormFieldVariant, Heading, type HeadingProps, Icon, type IconComponent, type IconComponentProps, type IconProps, Input, InputGroup, InputGroupIcon, InputGroupInput, type InputGroupInputProps, type InputGroupProps, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, type InputProps, Label, type LabelProps, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, type PopoverContentProps, PopoverTrigger, Progress, type ProgressProps, RadioGroup, RadioGroupItem, RichHtml, type RichHtmlProps, RichTextEditor, type RichTextEditorProps, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeparatorProps, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, type SkeletonProps, Slider, type SliderProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, type TextProps, Textarea, type TextareaProps, Toast, ToastAction, ToastClose, ToastDescription, type ToastPayload, type ToastProps, ToastProvider, type ToastRecord, ToastTitle, ToastViewport, Toaster, type ToasterProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buttonVariants, captionVariants, checkboxVariants, containerVariants, dismissToast, formFieldCanonicalizeNumericInput, formFieldFormatNumericDisplay, formFieldMaskExtractCanonical, formFieldMaskFormatDisplay, formFieldMaskSlotCount, formValueToAsyncSelectOption, getIcon, headingVariants, inputGroupSelectTriggerTextAlignClass, inputVariants, labelVariants, registerIcons, separatorVariants, sidebarMenuButtonVariants, skeletonVariants, switchRootVariants, switchThumbVariants, textVariants, toast, toastVariants, useSidebar, useToast };
|
package/dist/index.d.ts
CHANGED
|
@@ -488,6 +488,44 @@ type FormFieldDropzoneConfig = Omit<FileDropzoneProps, "id" | "name" | "onChange
|
|
|
488
488
|
* The controlled field value is `{ value, label } | null | undefined` (legacy plain `string` is still read as id-only).
|
|
489
489
|
*/
|
|
490
490
|
type FormFieldAsyncSelectConfig = Omit<AsyncSelectProps, "value" | "onValueChange" | "disabled" | "error" | "name" | "id">;
|
|
491
|
+
/**
|
|
492
|
+
* Form state holds a **canonical string** (digits only, or digits + a single `.` + fraction when `allowDecimal`).
|
|
493
|
+
* The input shows **grouped** thousands when `useGrouping` is true. Use with `control` and default `input` variant only.
|
|
494
|
+
*/
|
|
495
|
+
type FormFieldNumericInputConfig = {
|
|
496
|
+
/** Allow a single decimal separator in the stored value. @default false */
|
|
497
|
+
allowDecimal?: boolean;
|
|
498
|
+
/** Show `,` thousands separators in the input while the stored value has none. @default true */
|
|
499
|
+
useGrouping?: boolean;
|
|
500
|
+
/** Passed to `<input inputMode>`. Defaults to `"decimal"` if `allowDecimal`, otherwise `"numeric"`. */
|
|
501
|
+
inputMode?: React.HTMLAttributes<HTMLInputElement>["inputMode"];
|
|
502
|
+
};
|
|
503
|
+
/** Raw input → canonical string for form state (no commas, optional single dot). */
|
|
504
|
+
declare function formFieldCanonicalizeNumericInput(raw: string, allowDecimal: boolean): string;
|
|
505
|
+
/** Canonical stored string → display string with optional grouping. */
|
|
506
|
+
declare function formFieldFormatNumericDisplay(stored: string, allowDecimal: boolean, useGrouping: boolean): string;
|
|
507
|
+
/** Number of editable slots in `pattern` (each `#`, `h`, `H`, `X`, `x`, or `*`). */
|
|
508
|
+
declare function formFieldMaskSlotCount(pattern: string): number;
|
|
509
|
+
/**
|
|
510
|
+
* Reads `raw` left-to-right and fills slots in order (ignores typed/pasted literals such as `-`).
|
|
511
|
+
* Form state should hold this canonical string (no separators).
|
|
512
|
+
*/
|
|
513
|
+
declare function formFieldMaskExtractCanonical(raw: string, pattern: string): string;
|
|
514
|
+
/** Inserts pattern literals between filled slots; omits trailing literals when incomplete. */
|
|
515
|
+
declare function formFieldMaskFormatDisplay(pattern: string, canonical: string): string;
|
|
516
|
+
/**
|
|
517
|
+
* Fixed mask for `control` + default `input`. Form state = **slot characters only** (no `-` etc.).
|
|
518
|
+
*
|
|
519
|
+
* **Tokens:** `#` digit · `h` hex (stored lower) · `H` hex (stored upper for a–f) · `X` letter upper · `x` letter lower · `*` alphanumeric.
|
|
520
|
+
* Any other character in `pattern` is a **literal** shown in that position (e.g. `-`).
|
|
521
|
+
*
|
|
522
|
+
* Example: `XXX-xxx-xxx` (letters). Hex / UUID-style: `hhhhhhhh-hhhh-hhhh-hhhh-hhhh-hhhh-hhhh-hhhh` (use `h`, not `x`, for hex digits).
|
|
523
|
+
*/
|
|
524
|
+
type FormFieldMaskInputConfig = {
|
|
525
|
+
pattern: string;
|
|
526
|
+
/** Passed to `<input inputMode>`. @default "text" */
|
|
527
|
+
inputMode?: React.HTMLAttributes<HTMLInputElement>["inputMode"];
|
|
528
|
+
};
|
|
491
529
|
interface FormFieldProps<TFieldValues extends FieldValues> {
|
|
492
530
|
name: Path<TFieldValues>;
|
|
493
531
|
/** When omitted, no visible label is rendered; use `aria-label` on controls or descriptions for a11y. */
|
|
@@ -511,6 +549,16 @@ interface FormFieldProps<TFieldValues extends FieldValues> {
|
|
|
511
549
|
dropzoneProps?: FormFieldDropzoneConfig;
|
|
512
550
|
/** With `variant="async-select"`: URL fetch config; form state is `AsyncSelectOption | null` (see `FormFieldAsyncSelectConfig`). */
|
|
513
551
|
asyncSelectProps?: FormFieldAsyncSelectConfig;
|
|
552
|
+
/**
|
|
553
|
+
* When set with `control` and default `input` variant (and no `renderInput`), formats integers/decimals in the field.
|
|
554
|
+
* Ignored when using `renderInput` (build a custom input there instead).
|
|
555
|
+
*/
|
|
556
|
+
numericInput?: FormFieldNumericInputConfig;
|
|
557
|
+
/**
|
|
558
|
+
* Fixed character mask (`pattern` with slot tokens + literals). Mutually exclusive with `numericInput`.
|
|
559
|
+
* Ignored when using `renderInput`.
|
|
560
|
+
*/
|
|
561
|
+
maskInput?: FormFieldMaskInputConfig;
|
|
514
562
|
className?: string;
|
|
515
563
|
renderInput?: (props: Omit<FormFieldRenderProps, "value"> & {
|
|
516
564
|
id: string;
|
|
@@ -521,7 +569,7 @@ interface FormFieldProps<TFieldValues extends FieldValues> {
|
|
|
521
569
|
value?: InputProps["value"] | AsyncSelectOption | null;
|
|
522
570
|
}) => React.ReactNode;
|
|
523
571
|
}
|
|
524
|
-
declare function FormField<TFieldValues extends FieldValues>({ name, label, register, control, rules, description, required, error, variant, inputProps, textareaProps, checkboxProps, switchProps, selectProps, radioProps, otpProps, richTextProps, dropzoneProps, asyncSelectProps, className, renderInput, }: FormFieldProps<TFieldValues>): react_jsx_runtime.JSX.Element;
|
|
572
|
+
declare function FormField<TFieldValues extends FieldValues>({ name, label, register, control, rules, description, required, error, variant, inputProps, textareaProps, checkboxProps, switchProps, selectProps, radioProps, otpProps, richTextProps, dropzoneProps, asyncSelectProps, numericInput, maskInput, className, renderInput, }: FormFieldProps<TFieldValues>): react_jsx_runtime.JSX.Element;
|
|
525
573
|
/** Maps react-hook-form field values to `AsyncSelect`’s `value` prop (supports `{ value, label }`, legacy `string`, `null`/`undefined`). */
|
|
526
574
|
declare function formValueToAsyncSelectOption(v: unknown): AsyncSelectOption | undefined;
|
|
527
575
|
|
|
@@ -892,4 +940,4 @@ declare namespace Toaster {
|
|
|
892
940
|
var displayName: string;
|
|
893
941
|
}
|
|
894
942
|
|
|
895
|
-
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, AppShell, type AppShellProps, AspectRatio, type AspectRatioProps, AsyncSelect, type AsyncSelectFetcher, type AsyncSelectOption, type AsyncSelectPage, type AsyncSelectProps, Avatar, AvatarFallback, AvatarImage, type AvatarProps, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, CalendarDayButton, Caption, type CaptionProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, type CheckboxProps, Collapsible, CollapsibleContent, CollapsibleTrigger, Container, type ContainerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FileDropzone, type FileDropzoneProps, FormField, type FormFieldAsyncSelectConfig, type FormFieldDropzoneConfig, type FormFieldOtpConfig, type FormFieldProps, type FormFieldRadioConfig, type FormFieldRadioOption, type FormFieldSelectConfig, type FormFieldSelectItem, type FormFieldVariant, Heading, type HeadingProps, Icon, type IconComponent, type IconComponentProps, type IconProps, Input, InputGroup, InputGroupIcon, InputGroupInput, type InputGroupInputProps, type InputGroupProps, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, type InputProps, Label, type LabelProps, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, type PopoverContentProps, PopoverTrigger, Progress, type ProgressProps, RadioGroup, RadioGroupItem, RichHtml, type RichHtmlProps, RichTextEditor, type RichTextEditorProps, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeparatorProps, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, type SkeletonProps, Slider, type SliderProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, type TextProps, Textarea, type TextareaProps, Toast, ToastAction, ToastClose, ToastDescription, type ToastPayload, type ToastProps, ToastProvider, type ToastRecord, ToastTitle, ToastViewport, Toaster, type ToasterProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buttonVariants, captionVariants, checkboxVariants, containerVariants, dismissToast, formValueToAsyncSelectOption, getIcon, headingVariants, inputGroupSelectTriggerTextAlignClass, inputVariants, labelVariants, registerIcons, separatorVariants, sidebarMenuButtonVariants, skeletonVariants, switchRootVariants, switchThumbVariants, textVariants, toast, toastVariants, useSidebar, useToast };
|
|
943
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, AppShell, type AppShellProps, AspectRatio, type AspectRatioProps, AsyncSelect, type AsyncSelectFetcher, type AsyncSelectOption, type AsyncSelectPage, type AsyncSelectProps, Avatar, AvatarFallback, AvatarImage, type AvatarProps, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, CalendarDayButton, Caption, type CaptionProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, type CheckboxProps, Collapsible, CollapsibleContent, CollapsibleTrigger, Container, type ContainerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FileDropzone, type FileDropzoneProps, FormField, type FormFieldAsyncSelectConfig, type FormFieldDropzoneConfig, type FormFieldMaskInputConfig, type FormFieldNumericInputConfig, type FormFieldOtpConfig, type FormFieldProps, type FormFieldRadioConfig, type FormFieldRadioOption, type FormFieldSelectConfig, type FormFieldSelectItem, type FormFieldVariant, Heading, type HeadingProps, Icon, type IconComponent, type IconComponentProps, type IconProps, Input, InputGroup, InputGroupIcon, InputGroupInput, type InputGroupInputProps, type InputGroupProps, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, type InputProps, Label, type LabelProps, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, type PopoverContentProps, PopoverTrigger, Progress, type ProgressProps, RadioGroup, RadioGroupItem, RichHtml, type RichHtmlProps, RichTextEditor, type RichTextEditorProps, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeparatorProps, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, type SkeletonProps, Slider, type SliderProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, type TextProps, Textarea, type TextareaProps, Toast, ToastAction, ToastClose, ToastDescription, type ToastPayload, type ToastProps, ToastProvider, type ToastRecord, ToastTitle, ToastViewport, Toaster, type ToasterProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buttonVariants, captionVariants, checkboxVariants, containerVariants, dismissToast, formFieldCanonicalizeNumericInput, formFieldFormatNumericDisplay, formFieldMaskExtractCanonical, formFieldMaskFormatDisplay, formFieldMaskSlotCount, formValueToAsyncSelectOption, getIcon, headingVariants, inputGroupSelectTriggerTextAlignClass, inputVariants, labelVariants, registerIcons, separatorVariants, sidebarMenuButtonVariants, skeletonVariants, switchRootVariants, switchThumbVariants, textVariants, toast, toastVariants, useSidebar, useToast };
|
package/dist/index.js
CHANGED
|
@@ -2521,6 +2521,106 @@ Textarea.displayName = "Textarea";
|
|
|
2521
2521
|
|
|
2522
2522
|
// source/components/primitive/FormField/form-field.tsx
|
|
2523
2523
|
import { jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2524
|
+
function stripThousands(s) {
|
|
2525
|
+
return s.replace(/,/g, "");
|
|
2526
|
+
}
|
|
2527
|
+
function formFieldCanonicalizeNumericInput(raw, allowDecimal) {
|
|
2528
|
+
const withoutCommas = stripThousands(raw);
|
|
2529
|
+
const pattern = allowDecimal ? /[^\d.]/g : /\D/g;
|
|
2530
|
+
let cleaned = withoutCommas.replace(pattern, "");
|
|
2531
|
+
if (!allowDecimal) return cleaned;
|
|
2532
|
+
const parts = cleaned.split(".");
|
|
2533
|
+
if (parts.length <= 1) return cleaned;
|
|
2534
|
+
const intPart = parts[0] ?? "";
|
|
2535
|
+
const frac = parts.slice(1).join("");
|
|
2536
|
+
return intPart + "." + frac;
|
|
2537
|
+
}
|
|
2538
|
+
function formatThousands(intPart) {
|
|
2539
|
+
if (!intPart) return "";
|
|
2540
|
+
return intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
2541
|
+
}
|
|
2542
|
+
function formFieldFormatNumericDisplay(stored, allowDecimal, useGrouping) {
|
|
2543
|
+
if (stored == null) return "";
|
|
2544
|
+
const s = String(stored);
|
|
2545
|
+
if (s === "") return "";
|
|
2546
|
+
if (!useGrouping) return s;
|
|
2547
|
+
if (allowDecimal && s.includes(".")) {
|
|
2548
|
+
const [intPart, ...rest] = s.split(".");
|
|
2549
|
+
const frac = rest.join("");
|
|
2550
|
+
return formatThousands(intPart ?? "") + "." + frac;
|
|
2551
|
+
}
|
|
2552
|
+
return formatThousands(s);
|
|
2553
|
+
}
|
|
2554
|
+
var FORM_FIELD_MASK_SLOTS = /* @__PURE__ */ new Set(["#", "h", "H", "X", "x", "*"]);
|
|
2555
|
+
function isFormFieldMaskSlotChar(p) {
|
|
2556
|
+
return FORM_FIELD_MASK_SLOTS.has(p);
|
|
2557
|
+
}
|
|
2558
|
+
function matchesMaskSlot(slot, ch) {
|
|
2559
|
+
switch (slot) {
|
|
2560
|
+
case "#":
|
|
2561
|
+
return /\d/.test(ch);
|
|
2562
|
+
case "h":
|
|
2563
|
+
case "H":
|
|
2564
|
+
return /[0-9a-fA-F]/.test(ch);
|
|
2565
|
+
case "X":
|
|
2566
|
+
case "x":
|
|
2567
|
+
return /[A-Za-z]/.test(ch);
|
|
2568
|
+
case "*":
|
|
2569
|
+
return /[A-Za-z0-9]/.test(ch);
|
|
2570
|
+
default:
|
|
2571
|
+
return false;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
function normalizeMaskSlotChar(slot, ch) {
|
|
2575
|
+
switch (slot) {
|
|
2576
|
+
case "#":
|
|
2577
|
+
return ch;
|
|
2578
|
+
case "h":
|
|
2579
|
+
return ch.toLowerCase();
|
|
2580
|
+
case "H":
|
|
2581
|
+
return /\d/.test(ch) ? ch : ch.toUpperCase();
|
|
2582
|
+
case "X":
|
|
2583
|
+
return ch.toUpperCase();
|
|
2584
|
+
case "x":
|
|
2585
|
+
return ch.toLowerCase();
|
|
2586
|
+
case "*":
|
|
2587
|
+
return ch;
|
|
2588
|
+
default:
|
|
2589
|
+
return ch;
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
function formFieldMaskSlotCount(pattern) {
|
|
2593
|
+
return [...pattern].filter(isFormFieldMaskSlotChar).length;
|
|
2594
|
+
}
|
|
2595
|
+
function formFieldMaskExtractCanonical(raw, pattern) {
|
|
2596
|
+
const slots = [...pattern].filter(isFormFieldMaskSlotChar);
|
|
2597
|
+
if (!slots.length) return "";
|
|
2598
|
+
const out = [];
|
|
2599
|
+
let si = 0;
|
|
2600
|
+
for (const ch of raw) {
|
|
2601
|
+
if (si >= slots.length) break;
|
|
2602
|
+
const slot = slots[si];
|
|
2603
|
+
if (matchesMaskSlot(slot, ch)) {
|
|
2604
|
+
out.push(normalizeMaskSlotChar(slot, ch));
|
|
2605
|
+
si++;
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
return out.join("");
|
|
2609
|
+
}
|
|
2610
|
+
function formFieldMaskFormatDisplay(pattern, canonical) {
|
|
2611
|
+
let ci = 0;
|
|
2612
|
+
let result = "";
|
|
2613
|
+
for (const p of pattern) {
|
|
2614
|
+
if (isFormFieldMaskSlotChar(p)) {
|
|
2615
|
+
if (ci >= canonical.length) break;
|
|
2616
|
+
result += canonical[ci];
|
|
2617
|
+
ci++;
|
|
2618
|
+
} else if (ci < canonical.length) {
|
|
2619
|
+
result += p;
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
return result;
|
|
2623
|
+
}
|
|
2524
2624
|
var VARIANTS_NEED_CONTROL = [
|
|
2525
2625
|
"checkbox",
|
|
2526
2626
|
"switch",
|
|
@@ -2559,6 +2659,8 @@ function FormField({
|
|
|
2559
2659
|
richTextProps,
|
|
2560
2660
|
dropzoneProps,
|
|
2561
2661
|
asyncSelectProps,
|
|
2662
|
+
numericInput,
|
|
2663
|
+
maskInput,
|
|
2562
2664
|
className,
|
|
2563
2665
|
renderInput
|
|
2564
2666
|
}) {
|
|
@@ -2566,6 +2668,39 @@ function FormField({
|
|
|
2566
2668
|
const inputId = `field-${generatedId}`;
|
|
2567
2669
|
const descriptionId = description ? `${inputId}-description` : void 0;
|
|
2568
2670
|
const externalError = error ? String(error) : void 0;
|
|
2671
|
+
if (maskInput && numericInput) {
|
|
2672
|
+
throw new Error(
|
|
2673
|
+
"FormField does not support `maskInput` and `numericInput` together; use only one."
|
|
2674
|
+
);
|
|
2675
|
+
}
|
|
2676
|
+
if (maskInput) {
|
|
2677
|
+
if (!control) {
|
|
2678
|
+
throw new Error(
|
|
2679
|
+
"FormField `maskInput` requires the `control` prop (Controller). Use `control`, not `register`, for masked fields."
|
|
2680
|
+
);
|
|
2681
|
+
}
|
|
2682
|
+
if (variant !== "input") {
|
|
2683
|
+
throw new Error('FormField `maskInput` is only supported with the default `variant="input"`.');
|
|
2684
|
+
}
|
|
2685
|
+
if (!maskInput.pattern?.trim()) {
|
|
2686
|
+
throw new Error("FormField `maskInput.pattern` must be a non-empty string.");
|
|
2687
|
+
}
|
|
2688
|
+
if (formFieldMaskSlotCount(maskInput.pattern) === 0) {
|
|
2689
|
+
throw new Error(
|
|
2690
|
+
"FormField `maskInput.pattern` must include at least one slot token: `#`, `h`, `H`, `X`, `x`, or `*`."
|
|
2691
|
+
);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
if (numericInput) {
|
|
2695
|
+
if (!control) {
|
|
2696
|
+
throw new Error(
|
|
2697
|
+
"FormField `numericInput` requires the `control` prop (Controller). Use `control`, not `register`, for formatted numeric fields."
|
|
2698
|
+
);
|
|
2699
|
+
}
|
|
2700
|
+
if (variant !== "input") {
|
|
2701
|
+
throw new Error('FormField `numericInput` is only supported with the default `variant="input"`.');
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2569
2704
|
if (!control && VARIANTS_NEED_CONTROL.includes(variant)) {
|
|
2570
2705
|
throw new Error(
|
|
2571
2706
|
`FormField variant "${variant}" requires the control prop (React Hook Form Controller).`
|
|
@@ -2619,7 +2754,9 @@ function FormField({
|
|
|
2619
2754
|
otpProps,
|
|
2620
2755
|
richTextProps,
|
|
2621
2756
|
dropzoneProps,
|
|
2622
|
-
asyncSelectProps
|
|
2757
|
+
asyncSelectProps,
|
|
2758
|
+
numericInput: renderInput ? void 0 : numericInput,
|
|
2759
|
+
maskInput: renderInput ? void 0 : maskInput
|
|
2623
2760
|
}
|
|
2624
2761
|
);
|
|
2625
2762
|
const labelBlock = isCheckboxInline || !hasFieldLabel2 ? null : /* @__PURE__ */ jsx32(
|
|
@@ -2768,7 +2905,9 @@ function FormFieldVariantControl({
|
|
|
2768
2905
|
otpProps,
|
|
2769
2906
|
richTextProps,
|
|
2770
2907
|
dropzoneProps,
|
|
2771
|
-
asyncSelectProps
|
|
2908
|
+
asyncSelectProps,
|
|
2909
|
+
numericInput,
|
|
2910
|
+
maskInput
|
|
2772
2911
|
}) {
|
|
2773
2912
|
switch (variant) {
|
|
2774
2913
|
case "textarea":
|
|
@@ -3006,22 +3145,102 @@ function FormFieldVariantControl({
|
|
|
3006
3145
|
`${String(field.name)}-${field.value === null ? "cleared" : typeof FileList !== "undefined" && field.value instanceof FileList ? field.value.length : "open"}`
|
|
3007
3146
|
) });
|
|
3008
3147
|
case "input":
|
|
3009
|
-
default:
|
|
3148
|
+
default: {
|
|
3149
|
+
const {
|
|
3150
|
+
onChange: inputOnChangeFromProps,
|
|
3151
|
+
value: _omitValueFromInputProps,
|
|
3152
|
+
type: inputTypeProp,
|
|
3153
|
+
inputMode: inputModeProp,
|
|
3154
|
+
disabled: inputDisabledFromProps,
|
|
3155
|
+
...restInputProps
|
|
3156
|
+
} = inputProps ?? {};
|
|
3157
|
+
const inputDisabled = Boolean(inputDisabledFromProps) || Boolean(field.disabled);
|
|
3158
|
+
if (maskInput) {
|
|
3159
|
+
const pattern = maskInput.pattern;
|
|
3160
|
+
const rawStored = field.value == null || field.value === "" ? "" : String(field.value);
|
|
3161
|
+
const displayValue = formFieldMaskFormatDisplay(pattern, rawStored);
|
|
3162
|
+
return /* @__PURE__ */ jsx32("div", { className: "w-full min-w-0", children: /* @__PURE__ */ jsx32(
|
|
3163
|
+
Input,
|
|
3164
|
+
{
|
|
3165
|
+
...restInputProps,
|
|
3166
|
+
id: inputId,
|
|
3167
|
+
name: field.name,
|
|
3168
|
+
"aria-describedby": describedBy,
|
|
3169
|
+
error: hasError,
|
|
3170
|
+
type: "text",
|
|
3171
|
+
inputMode: maskInput.inputMode ?? "text",
|
|
3172
|
+
maxLength: pattern.length,
|
|
3173
|
+
autoComplete: "off",
|
|
3174
|
+
spellCheck: false,
|
|
3175
|
+
value: displayValue,
|
|
3176
|
+
onChange: (e) => {
|
|
3177
|
+
const canonical = formFieldMaskExtractCanonical(
|
|
3178
|
+
e.target.value,
|
|
3179
|
+
pattern
|
|
3180
|
+
);
|
|
3181
|
+
field.onChange(canonical);
|
|
3182
|
+
},
|
|
3183
|
+
onBlur: field.onBlur,
|
|
3184
|
+
ref: field.ref,
|
|
3185
|
+
disabled: inputDisabled
|
|
3186
|
+
}
|
|
3187
|
+
) });
|
|
3188
|
+
}
|
|
3189
|
+
if (numericInput) {
|
|
3190
|
+
const allowDecimal = numericInput.allowDecimal ?? false;
|
|
3191
|
+
const useGrouping = numericInput.useGrouping ?? true;
|
|
3192
|
+
const rawStored = field.value == null || field.value === "" ? "" : String(field.value);
|
|
3193
|
+
const displayValue = formFieldFormatNumericDisplay(
|
|
3194
|
+
rawStored,
|
|
3195
|
+
allowDecimal,
|
|
3196
|
+
useGrouping
|
|
3197
|
+
);
|
|
3198
|
+
const defaultInputMode = allowDecimal ? "decimal" : "numeric";
|
|
3199
|
+
return /* @__PURE__ */ jsx32("div", { className: "w-full min-w-0", children: /* @__PURE__ */ jsx32(
|
|
3200
|
+
Input,
|
|
3201
|
+
{
|
|
3202
|
+
...restInputProps,
|
|
3203
|
+
id: inputId,
|
|
3204
|
+
name: field.name,
|
|
3205
|
+
"aria-describedby": describedBy,
|
|
3206
|
+
error: hasError,
|
|
3207
|
+
type: "text",
|
|
3208
|
+
inputMode: numericInput.inputMode ?? defaultInputMode,
|
|
3209
|
+
value: displayValue,
|
|
3210
|
+
onChange: (e) => {
|
|
3211
|
+
const canonical = formFieldCanonicalizeNumericInput(
|
|
3212
|
+
e.target.value,
|
|
3213
|
+
allowDecimal
|
|
3214
|
+
);
|
|
3215
|
+
field.onChange(canonical);
|
|
3216
|
+
},
|
|
3217
|
+
onBlur: field.onBlur,
|
|
3218
|
+
ref: field.ref,
|
|
3219
|
+
disabled: inputDisabled
|
|
3220
|
+
}
|
|
3221
|
+
) });
|
|
3222
|
+
}
|
|
3010
3223
|
return /* @__PURE__ */ jsx32("div", { className: "w-full min-w-0", children: /* @__PURE__ */ jsx32(
|
|
3011
3224
|
Input,
|
|
3012
3225
|
{
|
|
3013
|
-
...
|
|
3226
|
+
...restInputProps,
|
|
3014
3227
|
id: inputId,
|
|
3015
3228
|
name: field.name,
|
|
3016
3229
|
"aria-describedby": describedBy,
|
|
3017
3230
|
error: hasError,
|
|
3231
|
+
type: inputTypeProp,
|
|
3232
|
+
inputMode: inputModeProp,
|
|
3018
3233
|
value: field.value ?? "",
|
|
3019
|
-
onChange:
|
|
3234
|
+
onChange: (e) => {
|
|
3235
|
+
inputOnChangeFromProps?.(e);
|
|
3236
|
+
field.onChange(e);
|
|
3237
|
+
},
|
|
3020
3238
|
onBlur: field.onBlur,
|
|
3021
3239
|
ref: field.ref,
|
|
3022
|
-
disabled:
|
|
3240
|
+
disabled: inputDisabled
|
|
3023
3241
|
}
|
|
3024
3242
|
) });
|
|
3243
|
+
}
|
|
3025
3244
|
}
|
|
3026
3245
|
}
|
|
3027
3246
|
|
|
@@ -4940,6 +5159,11 @@ export {
|
|
|
4940
5159
|
colors,
|
|
4941
5160
|
containerVariants,
|
|
4942
5161
|
dismissToast,
|
|
5162
|
+
formFieldCanonicalizeNumericInput,
|
|
5163
|
+
formFieldFormatNumericDisplay,
|
|
5164
|
+
formFieldMaskExtractCanonical,
|
|
5165
|
+
formFieldMaskFormatDisplay,
|
|
5166
|
+
formFieldMaskSlotCount,
|
|
4943
5167
|
formValueToAsyncSelectOption,
|
|
4944
5168
|
getIcon,
|
|
4945
5169
|
headingVariants,
|