@2urgseui/core 0.1.1 → 0.1.2
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 +232 -5
- package/dist/index.d.cts +50 -2
- package/dist/index.d.ts +50 -2
- package/dist/index.js +227 -5
- 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,100 @@ 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
|
+
...restInputProps
|
|
3395
|
+
} = inputProps ?? {};
|
|
3396
|
+
if (maskInput) {
|
|
3397
|
+
const pattern = maskInput.pattern;
|
|
3398
|
+
const rawStored = field.value == null || field.value === "" ? "" : String(field.value);
|
|
3399
|
+
const displayValue = formFieldMaskFormatDisplay(pattern, rawStored);
|
|
3400
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "w-full min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3401
|
+
Input,
|
|
3402
|
+
{
|
|
3403
|
+
...restInputProps,
|
|
3404
|
+
id: inputId,
|
|
3405
|
+
name: field.name,
|
|
3406
|
+
"aria-describedby": describedBy,
|
|
3407
|
+
error: hasError,
|
|
3408
|
+
type: "text",
|
|
3409
|
+
inputMode: maskInput.inputMode ?? "text",
|
|
3410
|
+
maxLength: pattern.length,
|
|
3411
|
+
autoComplete: "off",
|
|
3412
|
+
spellCheck: false,
|
|
3413
|
+
value: displayValue,
|
|
3414
|
+
onChange: (e) => {
|
|
3415
|
+
const canonical = formFieldMaskExtractCanonical(
|
|
3416
|
+
e.target.value,
|
|
3417
|
+
pattern
|
|
3418
|
+
);
|
|
3419
|
+
field.onChange(canonical);
|
|
3420
|
+
},
|
|
3421
|
+
onBlur: field.onBlur,
|
|
3422
|
+
ref: field.ref,
|
|
3423
|
+
disabled: field.disabled
|
|
3424
|
+
}
|
|
3425
|
+
) });
|
|
3426
|
+
}
|
|
3427
|
+
if (numericInput) {
|
|
3428
|
+
const allowDecimal = numericInput.allowDecimal ?? false;
|
|
3429
|
+
const useGrouping = numericInput.useGrouping ?? true;
|
|
3430
|
+
const rawStored = field.value == null || field.value === "" ? "" : String(field.value);
|
|
3431
|
+
const displayValue = formFieldFormatNumericDisplay(
|
|
3432
|
+
rawStored,
|
|
3433
|
+
allowDecimal,
|
|
3434
|
+
useGrouping
|
|
3435
|
+
);
|
|
3436
|
+
const defaultInputMode = allowDecimal ? "decimal" : "numeric";
|
|
3437
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "w-full min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3438
|
+
Input,
|
|
3439
|
+
{
|
|
3440
|
+
...restInputProps,
|
|
3441
|
+
id: inputId,
|
|
3442
|
+
name: field.name,
|
|
3443
|
+
"aria-describedby": describedBy,
|
|
3444
|
+
error: hasError,
|
|
3445
|
+
type: "text",
|
|
3446
|
+
inputMode: numericInput.inputMode ?? defaultInputMode,
|
|
3447
|
+
value: displayValue,
|
|
3448
|
+
onChange: (e) => {
|
|
3449
|
+
const canonical = formFieldCanonicalizeNumericInput(
|
|
3450
|
+
e.target.value,
|
|
3451
|
+
allowDecimal
|
|
3452
|
+
);
|
|
3453
|
+
field.onChange(canonical);
|
|
3454
|
+
},
|
|
3455
|
+
onBlur: field.onBlur,
|
|
3456
|
+
ref: field.ref,
|
|
3457
|
+
disabled: field.disabled
|
|
3458
|
+
}
|
|
3459
|
+
) });
|
|
3460
|
+
}
|
|
3245
3461
|
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "w-full min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3246
3462
|
Input,
|
|
3247
3463
|
{
|
|
3248
|
-
...
|
|
3464
|
+
...restInputProps,
|
|
3249
3465
|
id: inputId,
|
|
3250
3466
|
name: field.name,
|
|
3251
3467
|
"aria-describedby": describedBy,
|
|
3252
3468
|
error: hasError,
|
|
3469
|
+
type: inputTypeProp,
|
|
3470
|
+
inputMode: inputModeProp,
|
|
3253
3471
|
value: field.value ?? "",
|
|
3254
|
-
onChange:
|
|
3472
|
+
onChange: (e) => {
|
|
3473
|
+
inputOnChangeFromProps?.(e);
|
|
3474
|
+
field.onChange(e);
|
|
3475
|
+
},
|
|
3255
3476
|
onBlur: field.onBlur,
|
|
3256
3477
|
ref: field.ref,
|
|
3257
3478
|
disabled: field.disabled
|
|
3258
3479
|
}
|
|
3259
3480
|
) });
|
|
3481
|
+
}
|
|
3260
3482
|
}
|
|
3261
3483
|
}
|
|
3262
3484
|
|
|
@@ -5483,6 +5705,11 @@ var typography = {
|
|
|
5483
5705
|
colors,
|
|
5484
5706
|
containerVariants,
|
|
5485
5707
|
dismissToast,
|
|
5708
|
+
formFieldCanonicalizeNumericInput,
|
|
5709
|
+
formFieldFormatNumericDisplay,
|
|
5710
|
+
formFieldMaskExtractCanonical,
|
|
5711
|
+
formFieldMaskFormatDisplay,
|
|
5712
|
+
formFieldMaskSlotCount,
|
|
5486
5713
|
formValueToAsyncSelectOption,
|
|
5487
5714
|
getIcon,
|
|
5488
5715
|
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,100 @@ 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
|
+
...restInputProps
|
|
3155
|
+
} = inputProps ?? {};
|
|
3156
|
+
if (maskInput) {
|
|
3157
|
+
const pattern = maskInput.pattern;
|
|
3158
|
+
const rawStored = field.value == null || field.value === "" ? "" : String(field.value);
|
|
3159
|
+
const displayValue = formFieldMaskFormatDisplay(pattern, rawStored);
|
|
3160
|
+
return /* @__PURE__ */ jsx32("div", { className: "w-full min-w-0", children: /* @__PURE__ */ jsx32(
|
|
3161
|
+
Input,
|
|
3162
|
+
{
|
|
3163
|
+
...restInputProps,
|
|
3164
|
+
id: inputId,
|
|
3165
|
+
name: field.name,
|
|
3166
|
+
"aria-describedby": describedBy,
|
|
3167
|
+
error: hasError,
|
|
3168
|
+
type: "text",
|
|
3169
|
+
inputMode: maskInput.inputMode ?? "text",
|
|
3170
|
+
maxLength: pattern.length,
|
|
3171
|
+
autoComplete: "off",
|
|
3172
|
+
spellCheck: false,
|
|
3173
|
+
value: displayValue,
|
|
3174
|
+
onChange: (e) => {
|
|
3175
|
+
const canonical = formFieldMaskExtractCanonical(
|
|
3176
|
+
e.target.value,
|
|
3177
|
+
pattern
|
|
3178
|
+
);
|
|
3179
|
+
field.onChange(canonical);
|
|
3180
|
+
},
|
|
3181
|
+
onBlur: field.onBlur,
|
|
3182
|
+
ref: field.ref,
|
|
3183
|
+
disabled: field.disabled
|
|
3184
|
+
}
|
|
3185
|
+
) });
|
|
3186
|
+
}
|
|
3187
|
+
if (numericInput) {
|
|
3188
|
+
const allowDecimal = numericInput.allowDecimal ?? false;
|
|
3189
|
+
const useGrouping = numericInput.useGrouping ?? true;
|
|
3190
|
+
const rawStored = field.value == null || field.value === "" ? "" : String(field.value);
|
|
3191
|
+
const displayValue = formFieldFormatNumericDisplay(
|
|
3192
|
+
rawStored,
|
|
3193
|
+
allowDecimal,
|
|
3194
|
+
useGrouping
|
|
3195
|
+
);
|
|
3196
|
+
const defaultInputMode = allowDecimal ? "decimal" : "numeric";
|
|
3197
|
+
return /* @__PURE__ */ jsx32("div", { className: "w-full min-w-0", children: /* @__PURE__ */ jsx32(
|
|
3198
|
+
Input,
|
|
3199
|
+
{
|
|
3200
|
+
...restInputProps,
|
|
3201
|
+
id: inputId,
|
|
3202
|
+
name: field.name,
|
|
3203
|
+
"aria-describedby": describedBy,
|
|
3204
|
+
error: hasError,
|
|
3205
|
+
type: "text",
|
|
3206
|
+
inputMode: numericInput.inputMode ?? defaultInputMode,
|
|
3207
|
+
value: displayValue,
|
|
3208
|
+
onChange: (e) => {
|
|
3209
|
+
const canonical = formFieldCanonicalizeNumericInput(
|
|
3210
|
+
e.target.value,
|
|
3211
|
+
allowDecimal
|
|
3212
|
+
);
|
|
3213
|
+
field.onChange(canonical);
|
|
3214
|
+
},
|
|
3215
|
+
onBlur: field.onBlur,
|
|
3216
|
+
ref: field.ref,
|
|
3217
|
+
disabled: field.disabled
|
|
3218
|
+
}
|
|
3219
|
+
) });
|
|
3220
|
+
}
|
|
3010
3221
|
return /* @__PURE__ */ jsx32("div", { className: "w-full min-w-0", children: /* @__PURE__ */ jsx32(
|
|
3011
3222
|
Input,
|
|
3012
3223
|
{
|
|
3013
|
-
...
|
|
3224
|
+
...restInputProps,
|
|
3014
3225
|
id: inputId,
|
|
3015
3226
|
name: field.name,
|
|
3016
3227
|
"aria-describedby": describedBy,
|
|
3017
3228
|
error: hasError,
|
|
3229
|
+
type: inputTypeProp,
|
|
3230
|
+
inputMode: inputModeProp,
|
|
3018
3231
|
value: field.value ?? "",
|
|
3019
|
-
onChange:
|
|
3232
|
+
onChange: (e) => {
|
|
3233
|
+
inputOnChangeFromProps?.(e);
|
|
3234
|
+
field.onChange(e);
|
|
3235
|
+
},
|
|
3020
3236
|
onBlur: field.onBlur,
|
|
3021
3237
|
ref: field.ref,
|
|
3022
3238
|
disabled: field.disabled
|
|
3023
3239
|
}
|
|
3024
3240
|
) });
|
|
3241
|
+
}
|
|
3025
3242
|
}
|
|
3026
3243
|
}
|
|
3027
3244
|
|
|
@@ -4940,6 +5157,11 @@ export {
|
|
|
4940
5157
|
colors,
|
|
4941
5158
|
containerVariants,
|
|
4942
5159
|
dismissToast,
|
|
5160
|
+
formFieldCanonicalizeNumericInput,
|
|
5161
|
+
formFieldFormatNumericDisplay,
|
|
5162
|
+
formFieldMaskExtractCanonical,
|
|
5163
|
+
formFieldMaskFormatDisplay,
|
|
5164
|
+
formFieldMaskSlotCount,
|
|
4943
5165
|
formValueToAsyncSelectOption,
|
|
4944
5166
|
getIcon,
|
|
4945
5167
|
headingVariants,
|