@dashforge/tw 0.2.1-beta → 0.3.0-beta
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/CHANGELOG.md +134 -0
- package/CONSUMER-VALIDATION.md +130 -0
- package/dist/index.esm.js +309 -41
- package/dist/src/components/AppShell/AppShell.d.ts +14 -0
- package/dist/src/components/AppShell/AppShell.d.ts.map +1 -1
- package/dist/src/components/AppShell/appShell.variants.d.ts.map +1 -1
- package/dist/src/components/Autocomplete/Autocomplete.d.ts.map +1 -1
- package/dist/src/components/Autocomplete/autocomplete.variants.d.ts.map +1 -1
- package/dist/src/components/Breadcrumbs/breadcrumbs.variants.d.ts.map +1 -1
- package/dist/src/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/src/components/LeftNav/leftNav.variants.d.ts.map +1 -1
- package/dist/src/components/Snackbar/snackbar.variants.d.ts.map +1 -1
- package/dist/src/components/Switch/switch.variants.d.ts.map +1 -1
- package/dist/src/components/TextField/TextField.d.ts.map +1 -1
- package/dist/src/components/TextField/textField.types.d.ts +29 -3
- package/dist/src/components/TextField/textField.types.d.ts.map +1 -1
- package/dist/src/components/TextField/textField.variants.d.ts +6 -0
- package/dist/src/components/TextField/textField.variants.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/package.json +3 -3
- package/src/components/AppShell/AppShell.tsx +126 -1
- package/src/components/AppShell/appShell.variants.ts +8 -2
- package/src/components/Autocomplete/Autocomplete.tsx +77 -4
- package/src/components/Autocomplete/autocomplete.variants.ts +6 -0
- package/src/components/Breadcrumbs/breadcrumbs.variants.ts +5 -0
- package/src/components/Checkbox/Checkbox.tsx +43 -9
- package/src/components/LeftNav/leftNav.variants.ts +12 -1
- package/src/components/Snackbar/snackbar.variants.ts +4 -2
- package/src/components/Switch/switch.variants.ts +6 -1
- package/src/components/TextField/TextField.tsx +29 -0
- package/src/components/TextField/textField.types.ts +23 -3
- package/src/components/TextField/textField.variants.ts +18 -0
- package/src/index.ts +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -510,7 +510,24 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
510
510
|
'read-only:cursor-default'
|
|
511
511
|
],
|
|
512
512
|
helperText: 'mt-1 text-sm text-neutral-600',
|
|
513
|
-
errorText: 'mt-1 text-sm text-danger-600'
|
|
513
|
+
errorText: 'mt-1 text-sm text-danger-600',
|
|
514
|
+
/*
|
|
515
|
+
* `prefix` / `suffix` slot — inline adornment rendered before /
|
|
516
|
+
* after the input INSIDE the inputWrapper. `shrink-0` keeps the
|
|
517
|
+
* adornment width fixed so it doesn't compete for space with the
|
|
518
|
+
* input; `select-none` + `pointer-events-none` avoids
|
|
519
|
+
* accidentally stealing focus from the input on click (the
|
|
520
|
+
* inputWrapper handles focus via `focus-within`).
|
|
521
|
+
*/ prefix: [
|
|
522
|
+
'shrink-0 inline-flex items-center select-none pointer-events-none',
|
|
523
|
+
'text-neutral-500',
|
|
524
|
+
'mr-2'
|
|
525
|
+
],
|
|
526
|
+
suffix: [
|
|
527
|
+
'shrink-0 inline-flex items-center select-none pointer-events-none',
|
|
528
|
+
'text-neutral-500',
|
|
529
|
+
'ml-2'
|
|
530
|
+
]
|
|
514
531
|
},
|
|
515
532
|
variants: {
|
|
516
533
|
size: {
|
|
@@ -587,7 +604,7 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
587
604
|
* - `aria-describedby` links the input to the helper/error text node.
|
|
588
605
|
* - Required fields get the native `required` attribute + a visual `*`.
|
|
589
606
|
*/ function TextField(props) {
|
|
590
|
-
var _slotProps_root, _slotProps_label, _slotProps_requiredMark, _slotProps_inputWrapper, _slotProps_input, _slotProps_errorText, _slotProps_helperText;
|
|
607
|
+
var _slotProps_root, _slotProps_label, _slotProps_requiredMark, _slotProps_inputWrapper, _slotProps_prefix, _slotProps_input, _slotProps_suffix, _slotProps_errorText, _slotProps_helperText;
|
|
591
608
|
const { name, rules, visibleWhen, layout = 'stacked', size, label, helperText, required, error, disabled, fullWidth, access, sx, slotProps, placeholder, type, onChange: userOnChange, onBlur: userOnBlur, value: userValue, defaultValue } = props, rest = _object_without_properties_loose(props, [
|
|
592
609
|
"name",
|
|
593
610
|
"rules",
|
|
@@ -717,25 +734,37 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
717
734
|
})
|
|
718
735
|
]
|
|
719
736
|
}),
|
|
720
|
-
|
|
737
|
+
jsxs("div", {
|
|
721
738
|
className: cn(v.inputWrapper(), slotProps == null ? void 0 : (_slotProps_inputWrapper = slotProps.inputWrapper) == null ? void 0 : _slotProps_inputWrapper.className),
|
|
722
|
-
children:
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
+
children: [
|
|
740
|
+
(slotProps == null ? void 0 : (_slotProps_prefix = slotProps.prefix) == null ? void 0 : _slotProps_prefix.children) !== undefined && jsx("span", {
|
|
741
|
+
"aria-hidden": "true",
|
|
742
|
+
className: cn(v.prefix(), slotProps.prefix.className),
|
|
743
|
+
children: slotProps.prefix.children
|
|
744
|
+
}),
|
|
745
|
+
jsx("input", _extends({}, rest, {
|
|
746
|
+
id: inputId,
|
|
747
|
+
name: name,
|
|
748
|
+
type: type != null ? type : 'text',
|
|
749
|
+
placeholder: placeholder,
|
|
750
|
+
value: isFormMode ? resolvedValue != null ? resolvedValue : '' : userValue,
|
|
751
|
+
defaultValue: !isFormMode && userValue === undefined ? defaultValue : undefined,
|
|
752
|
+
disabled: effectiveDisabled,
|
|
753
|
+
readOnly: effectiveReadOnly,
|
|
754
|
+
required: required,
|
|
755
|
+
"aria-invalid": resolvedError || undefined,
|
|
756
|
+
"aria-describedby": resolvedHelperText ? helperId : undefined,
|
|
757
|
+
onChange: handleChange,
|
|
758
|
+
onBlur: handleBlur,
|
|
759
|
+
ref: inputRef,
|
|
760
|
+
className: cn(v.input(), slotProps == null ? void 0 : (_slotProps_input = slotProps.input) == null ? void 0 : _slotProps_input.className)
|
|
761
|
+
})),
|
|
762
|
+
(slotProps == null ? void 0 : (_slotProps_suffix = slotProps.suffix) == null ? void 0 : _slotProps_suffix.children) !== undefined && jsx("span", {
|
|
763
|
+
"aria-hidden": "true",
|
|
764
|
+
className: cn(v.suffix(), slotProps.suffix.className),
|
|
765
|
+
children: slotProps.suffix.children
|
|
766
|
+
})
|
|
767
|
+
]
|
|
739
768
|
}),
|
|
740
769
|
resolvedHelperText && jsx("p", {
|
|
741
770
|
id: helperId,
|
|
@@ -811,8 +840,8 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
811
840
|
});
|
|
812
841
|
|
|
813
842
|
/**
|
|
814
|
-
* Inline checkmark SVG used by `Radix.Indicator
|
|
815
|
-
* `currentColor` so the parent's `text-white` propagates
|
|
843
|
+
* Inline checkmark SVG used by `Radix.Indicator` when fully checked.
|
|
844
|
+
* Stroke uses `currentColor` so the parent's `text-white` propagates.
|
|
816
845
|
*
|
|
817
846
|
* @internal
|
|
818
847
|
*/ function CheckIcon({ className }) {
|
|
@@ -830,6 +859,32 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
830
859
|
})
|
|
831
860
|
});
|
|
832
861
|
}
|
|
862
|
+
/**
|
|
863
|
+
* Inline dash SVG used by `Radix.Indicator` when the checkbox is in
|
|
864
|
+
* the `indeterminate` tri-state ("some, but not all, children
|
|
865
|
+
* selected" — the canonical "select all" partial state).
|
|
866
|
+
*
|
|
867
|
+
* Pre-0.2.2-beta the indicator rendered the CheckIcon for BOTH
|
|
868
|
+
* checked and indeterminate (Radix mounts the Indicator for either
|
|
869
|
+
* state). We now discriminate via the `data-state` attribute Radix
|
|
870
|
+
* sets on the Indicator element itself — see the parent `Indicator`
|
|
871
|
+
* rendering below for the CSS toggle.
|
|
872
|
+
*
|
|
873
|
+
* @internal
|
|
874
|
+
*/ function DashIcon({ className }) {
|
|
875
|
+
return jsx("svg", {
|
|
876
|
+
"aria-hidden": "true",
|
|
877
|
+
viewBox: "0 0 16 16",
|
|
878
|
+
fill: "none",
|
|
879
|
+
className: className,
|
|
880
|
+
children: jsx("path", {
|
|
881
|
+
d: "M3 8h10",
|
|
882
|
+
stroke: "currentColor",
|
|
883
|
+
strokeWidth: "2.5",
|
|
884
|
+
strokeLinecap: "round"
|
|
885
|
+
})
|
|
886
|
+
});
|
|
887
|
+
}
|
|
833
888
|
/**
|
|
834
889
|
* Dashforge TW Checkbox — bridge-integrated form control.
|
|
835
890
|
*
|
|
@@ -975,11 +1030,16 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
975
1030
|
onBlur: handleBlur,
|
|
976
1031
|
ref: registration == null ? void 0 : registration.ref,
|
|
977
1032
|
className: cn(v.control(), slotProps == null ? void 0 : (_slotProps_control = slotProps.control) == null ? void 0 : _slotProps_control.className),
|
|
978
|
-
children:
|
|
979
|
-
className: cn(v.indicator(), slotProps == null ? void 0 : (_slotProps_indicator = slotProps.indicator) == null ? void 0 : _slotProps_indicator.className),
|
|
980
|
-
children:
|
|
981
|
-
|
|
982
|
-
|
|
1033
|
+
children: jsxs(RadixCheckbox.Indicator, {
|
|
1034
|
+
className: cn(v.indicator(), 'group', slotProps == null ? void 0 : (_slotProps_indicator = slotProps.indicator) == null ? void 0 : _slotProps_indicator.className),
|
|
1035
|
+
children: [
|
|
1036
|
+
jsx(CheckIcon, {
|
|
1037
|
+
className: "h-full w-full group-data-[state=indeterminate]:hidden"
|
|
1038
|
+
}),
|
|
1039
|
+
jsx(DashIcon, {
|
|
1040
|
+
className: "h-full w-full group-data-[state=checked]:hidden"
|
|
1041
|
+
})
|
|
1042
|
+
]
|
|
983
1043
|
})
|
|
984
1044
|
})),
|
|
985
1045
|
jsxs("div", {
|
|
@@ -1024,7 +1084,12 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
1024
1084
|
],
|
|
1025
1085
|
thumb: [
|
|
1026
1086
|
'pointer-events-none inline-block rounded-full bg-white shadow ring-0',
|
|
1027
|
-
|
|
1087
|
+
// Slide animation gated on `prefers-reduced-motion: no-preference`
|
|
1088
|
+
// (WCAG 2.3.3). The thumb still moves between positions instantly
|
|
1089
|
+
// for users who request reduced motion — the data-state change
|
|
1090
|
+
// applies the translate-x rule unconditionally, only the smooth
|
|
1091
|
+
// transition between the two is suppressed.
|
|
1092
|
+
'transition-transform motion-reduce:transition-none',
|
|
1028
1093
|
'data-[state=unchecked]:translate-x-0'
|
|
1029
1094
|
],
|
|
1030
1095
|
label: 'select-none cursor-pointer text-neutral-900',
|
|
@@ -2414,7 +2479,13 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
2414
2479
|
'flex items-center justify-center shrink-0 px-2',
|
|
2415
2480
|
'text-neutral-600 hover:text-neutral-900',
|
|
2416
2481
|
'disabled:cursor-not-allowed disabled:opacity-40',
|
|
2417
|
-
'transition-colors'
|
|
2482
|
+
'transition-colors',
|
|
2483
|
+
// Chevron flip on open — targets the SVG child via the aria-
|
|
2484
|
+
// expanded state on the button itself (set by React). Smooth
|
|
2485
|
+
// rotate gated on prefers-reduced-motion (WCAG 2.3.3); the
|
|
2486
|
+
// 180° state still applies, just without the animated tween.
|
|
2487
|
+
'[&[aria-expanded=true]>svg]:rotate-180',
|
|
2488
|
+
'[&>svg]:transition-transform [&>svg]:duration-150 motion-reduce:[&>svg]:transition-none'
|
|
2418
2489
|
],
|
|
2419
2490
|
clearButton: [
|
|
2420
2491
|
'flex items-center justify-center shrink-0 px-2',
|
|
@@ -2507,6 +2578,52 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
2507
2578
|
}
|
|
2508
2579
|
});
|
|
2509
2580
|
|
|
2581
|
+
/**
|
|
2582
|
+
* Inline SVG icons used by the Autocomplete chrome (chip remove, clear,
|
|
2583
|
+
* dropdown caret). Stroke uses `currentColor` so the parent's `text-*`
|
|
2584
|
+
* propagates — same pattern as Checkbox's CheckIcon.
|
|
2585
|
+
*
|
|
2586
|
+
* Why inline SVG (not lucide / heroicons / unicode glyphs):
|
|
2587
|
+
* - Zero icon-library dependency: keeps `@dashforge/tw` self-contained.
|
|
2588
|
+
* - Crisp at every size (the unicode `×` / `▾` glyphs we shipped pre-
|
|
2589
|
+
* 0.2.2 rendered as chunky font characters that looked unpolished
|
|
2590
|
+
* next to the rest of the design system).
|
|
2591
|
+
* - SVG scales with parent font-size cleanly via `width="1em" height="1em"`.
|
|
2592
|
+
*
|
|
2593
|
+
* @internal
|
|
2594
|
+
*/ function CloseIcon({ className }) {
|
|
2595
|
+
return jsx("svg", {
|
|
2596
|
+
"aria-hidden": "true",
|
|
2597
|
+
viewBox: "0 0 16 16",
|
|
2598
|
+
width: "1em",
|
|
2599
|
+
height: "1em",
|
|
2600
|
+
fill: "none",
|
|
2601
|
+
className: className,
|
|
2602
|
+
children: jsx("path", {
|
|
2603
|
+
d: "M4 4l8 8M12 4l-8 8",
|
|
2604
|
+
stroke: "currentColor",
|
|
2605
|
+
strokeWidth: "1.75",
|
|
2606
|
+
strokeLinecap: "round"
|
|
2607
|
+
})
|
|
2608
|
+
});
|
|
2609
|
+
}
|
|
2610
|
+
function ChevronDownIcon({ className }) {
|
|
2611
|
+
return jsx("svg", {
|
|
2612
|
+
"aria-hidden": "true",
|
|
2613
|
+
viewBox: "0 0 16 16",
|
|
2614
|
+
width: "1em",
|
|
2615
|
+
height: "1em",
|
|
2616
|
+
fill: "none",
|
|
2617
|
+
className: className,
|
|
2618
|
+
children: jsx("path", {
|
|
2619
|
+
d: "M4 6l4 4 4-4",
|
|
2620
|
+
stroke: "currentColor",
|
|
2621
|
+
strokeWidth: "1.75",
|
|
2622
|
+
strokeLinecap: "round",
|
|
2623
|
+
strokeLinejoin: "round"
|
|
2624
|
+
})
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2510
2627
|
/**
|
|
2511
2628
|
* Coerce a raw value (from bridge / props) into a canonical shape:
|
|
2512
2629
|
*
|
|
@@ -2813,7 +2930,15 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
2813
2930
|
if (next == null) {
|
|
2814
2931
|
setInputValue('');
|
|
2815
2932
|
} else {
|
|
2816
|
-
|
|
2933
|
+
// Look up the label in the EFFECTIVE option pool (static
|
|
2934
|
+
// `options` + async `asyncOptions` when `loadOptions` is
|
|
2935
|
+
// configured). Looking only at the static `options` here
|
|
2936
|
+
// means async-loaded picks never update the input — the
|
|
2937
|
+
// user sees their search query stick instead of the
|
|
2938
|
+
// selected label after click. Same lookup logic mirrored in
|
|
2939
|
+
// `displayInputValue` so the two paths can't drift.
|
|
2940
|
+
const pool = loadOptions && asyncOptions !== null ? asyncOptions : options;
|
|
2941
|
+
const found = pool.find((opt)=>getOptionValue(opt) === next);
|
|
2817
2942
|
const found_label = found ? labelAsString(found) : undefined;
|
|
2818
2943
|
if (found_label !== undefined) {
|
|
2819
2944
|
setInputValue(found_label);
|
|
@@ -2843,6 +2968,8 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
2843
2968
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2844
2969
|
[
|
|
2845
2970
|
options,
|
|
2971
|
+
asyncOptions,
|
|
2972
|
+
loadOptions,
|
|
2846
2973
|
isFormMode,
|
|
2847
2974
|
bridge,
|
|
2848
2975
|
name,
|
|
@@ -3189,7 +3316,7 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3189
3316
|
"aria-label": `Remove ${labelText}`,
|
|
3190
3317
|
tabIndex: -1,
|
|
3191
3318
|
className: cn(v.chipRemove(), slotProps == null ? void 0 : (_slotProps_chipRemove = slotProps.chipRemove) == null ? void 0 : _slotProps_chipRemove.className),
|
|
3192
|
-
children:
|
|
3319
|
+
children: jsx(CloseIcon, {})
|
|
3193
3320
|
})
|
|
3194
3321
|
]
|
|
3195
3322
|
}, key);
|
|
@@ -3233,7 +3360,7 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3233
3360
|
"aria-label": "Clear selection",
|
|
3234
3361
|
tabIndex: -1,
|
|
3235
3362
|
className: cn(v.clearButton(), slotProps == null ? void 0 : (_slotProps_clearButton = slotProps.clearButton) == null ? void 0 : _slotProps_clearButton.className),
|
|
3236
|
-
children:
|
|
3363
|
+
children: jsx(CloseIcon, {})
|
|
3237
3364
|
}),
|
|
3238
3365
|
jsx("button", {
|
|
3239
3366
|
type: "button",
|
|
@@ -3245,7 +3372,7 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3245
3372
|
tabIndex: -1,
|
|
3246
3373
|
disabled: effectiveDisabled,
|
|
3247
3374
|
className: cn(v.trigger(), slotProps == null ? void 0 : (_slotProps_trigger = slotProps.trigger) == null ? void 0 : _slotProps_trigger.className),
|
|
3248
|
-
children:
|
|
3375
|
+
children: jsx(ChevronDownIcon, {})
|
|
3249
3376
|
}),
|
|
3250
3377
|
isOpen && jsx("ul", {
|
|
3251
3378
|
ref: listboxRef,
|
|
@@ -3643,7 +3770,12 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3643
3770
|
'text-neutral-600 hover:text-primary-700',
|
|
3644
3771
|
'rounded-sm outline-none',
|
|
3645
3772
|
'focus-visible:ring-2 focus-visible:ring-primary-500/50',
|
|
3646
|
-
'transition-colors'
|
|
3773
|
+
'transition-colors',
|
|
3774
|
+
// Defensive `no-underline` — see the matching comment in
|
|
3775
|
+
// LeftNav itemLink slot. The Breadcrumbs root cause covers
|
|
3776
|
+
// TopBar too because Breadcrumbs is typically rendered in
|
|
3777
|
+
// TopBar's center slot.
|
|
3778
|
+
'no-underline hover:no-underline'
|
|
3647
3779
|
],
|
|
3648
3780
|
current: [
|
|
3649
3781
|
'inline-flex items-center gap-1 truncate',
|
|
@@ -3837,7 +3969,10 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3837
3969
|
root: [
|
|
3838
3970
|
'flex flex-col h-full',
|
|
3839
3971
|
'bg-neutral-50 border-r border-neutral-200',
|
|
3840
|
-
|
|
3972
|
+
// Width transition (rail-mode toggle) — gated on
|
|
3973
|
+
// prefers-reduced-motion (WCAG 2.3.3). The new width still
|
|
3974
|
+
// applies, just without the animated tween.
|
|
3975
|
+
'transition-[width] duration-200 motion-reduce:transition-none motion-reduce:duration-0'
|
|
3841
3976
|
],
|
|
3842
3977
|
brand: [
|
|
3843
3978
|
'flex items-center gap-2 px-3 h-14 shrink-0',
|
|
@@ -3851,7 +3986,15 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3851
3986
|
'outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50',
|
|
3852
3987
|
'transition-colors w-full',
|
|
3853
3988
|
'aria-disabled:opacity-50 aria-disabled:cursor-not-allowed',
|
|
3854
|
-
'aria-disabled:hover:bg-transparent'
|
|
3989
|
+
'aria-disabled:hover:bg-transparent',
|
|
3990
|
+
// Defensive `no-underline` — Tailwind's preflight removes the
|
|
3991
|
+
// default browser anchor underline globally, but environments
|
|
3992
|
+
// that DISABLE preflight (e.g. our docs-lab, where the tw
|
|
3993
|
+
// section coexists with MUI's chrome) get raw browser defaults
|
|
3994
|
+
// back. Without this, `<a>` items in the nav render underlined
|
|
3995
|
+
// in those contexts. Explicit `no-underline` + `hover:no-underline`
|
|
3996
|
+
// keeps the appearance consistent regardless of preflight state.
|
|
3997
|
+
'no-underline hover:no-underline'
|
|
3855
3998
|
],
|
|
3856
3999
|
itemActive: 'bg-primary-100 text-primary-900 font-medium',
|
|
3857
4000
|
itemIcon: 'shrink-0 w-5 h-5 flex items-center justify-center',
|
|
@@ -4259,14 +4402,20 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
4259
4402
|
nav: 'hidden md:flex shrink-0',
|
|
4260
4403
|
navMobile: [
|
|
4261
4404
|
'flex md:hidden fixed inset-y-0 left-0 z-40',
|
|
4262
|
-
|
|
4405
|
+
// Drawer slide-in is the most prominent motion in AppShell and
|
|
4406
|
+
// a clear WCAG 2.3.3 candidate. Gate the transition on
|
|
4407
|
+
// `prefers-reduced-motion: no-preference`; the data-driven
|
|
4408
|
+
// `-translate-x-full` / `translate-x-0` still applies, just
|
|
4409
|
+
// without the smooth slide.
|
|
4410
|
+
'transition-transform duration-200 ease-out motion-reduce:transition-none motion-reduce:duration-0',
|
|
4263
4411
|
'-translate-x-full'
|
|
4264
4412
|
],
|
|
4265
4413
|
main: 'flex-1 min-w-0 overflow-y-auto',
|
|
4266
4414
|
footer: 'shrink-0',
|
|
4267
4415
|
backdrop: [
|
|
4268
4416
|
'md:hidden fixed inset-0 z-30 bg-black/40',
|
|
4269
|
-
|
|
4417
|
+
// Backdrop opacity fade — micro motion, gated for consistency.
|
|
4418
|
+
'transition-opacity duration-200 motion-reduce:transition-none motion-reduce:duration-0',
|
|
4270
4419
|
'opacity-0 pointer-events-none'
|
|
4271
4420
|
]
|
|
4272
4421
|
},
|
|
@@ -4283,6 +4432,28 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
4283
4432
|
}
|
|
4284
4433
|
});
|
|
4285
4434
|
|
|
4435
|
+
/**
|
|
4436
|
+
* Query selector for natively-focusable elements + anything with an
|
|
4437
|
+
* explicit positive (or implicit `0`) tabindex. Used by the focus
|
|
4438
|
+
* trap to enumerate the drawer's interactive content.
|
|
4439
|
+
*
|
|
4440
|
+
* Notes:
|
|
4441
|
+
* - Excludes `[tabindex="-1"]` (intentionally non-tabbable).
|
|
4442
|
+
* - Excludes `:disabled` — the browser already skips them in tab
|
|
4443
|
+
* order; including them here would route focus to dead elements.
|
|
4444
|
+
* - `details > summary` is the standard way to get a focusable
|
|
4445
|
+
* `<details>` toggle.
|
|
4446
|
+
*
|
|
4447
|
+
* @internal
|
|
4448
|
+
*/ const FOCUSABLE_SELECTOR = [
|
|
4449
|
+
'a[href]',
|
|
4450
|
+
'button:not(:disabled)',
|
|
4451
|
+
'input:not(:disabled):not([type="hidden"])',
|
|
4452
|
+
'select:not(:disabled)',
|
|
4453
|
+
'textarea:not(:disabled)',
|
|
4454
|
+
'[tabindex]:not([tabindex="-1"]):not(:disabled)',
|
|
4455
|
+
'details > summary:first-of-type'
|
|
4456
|
+
].join(',');
|
|
4286
4457
|
/**
|
|
4287
4458
|
* Dashforge TW AppShell — top-level layout orchestrator.
|
|
4288
4459
|
*
|
|
@@ -4311,6 +4482,20 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
4311
4482
|
* - The mobile drawer + backdrop participate in the standard
|
|
4312
4483
|
* "click outside to close" pattern — `Escape` closes the drawer
|
|
4313
4484
|
* too (added via global keydown).
|
|
4485
|
+
* - **Focus trap** (WCAG 2.4.3) — when the mobile drawer opens:
|
|
4486
|
+
* 1. The previously-focused element is captured.
|
|
4487
|
+
* 2. Focus moves to the first focusable element inside the drawer.
|
|
4488
|
+
* 3. Tab / Shift+Tab wrap inside the drawer (cycle from
|
|
4489
|
+
* last → first and first → last) — Tab can never escape
|
|
4490
|
+
* to the page underneath while the drawer is open.
|
|
4491
|
+
* 4. On close, focus returns to the captured element (typically
|
|
4492
|
+
* the hamburger toggle the user pressed to open the drawer).
|
|
4493
|
+
* This pattern matches WAI-ARIA APG's modal dialog guidance even
|
|
4494
|
+
* though the drawer isn't strictly a dialog — same focus
|
|
4495
|
+
* management makes keyboard users' experience predictable.
|
|
4496
|
+
* - **`role="dialog"` + `aria-modal="true"`** are applied to the
|
|
4497
|
+
* drawer when open so screen readers announce it as a modal
|
|
4498
|
+
* overlay rather than just an aside.
|
|
4314
4499
|
*/ function AppShell(props) {
|
|
4315
4500
|
var _slotProps_root, _slotProps_header, _slotProps_nav, _slotProps_navMobile, _slotProps_backdrop, _slotProps_main, _slotProps_footer;
|
|
4316
4501
|
const { header, nav, footer, children, navOpen = false, onNavOpenChange, sx, slotProps } = props;
|
|
@@ -4340,6 +4525,80 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
4340
4525
|
navOpen,
|
|
4341
4526
|
onNavOpenChange
|
|
4342
4527
|
]);
|
|
4528
|
+
/*
|
|
4529
|
+
* Focus trap for the mobile drawer.
|
|
4530
|
+
*
|
|
4531
|
+
* Implementation:
|
|
4532
|
+
* - On open: capture the currently-focused element (`document.activeElement`)
|
|
4533
|
+
* so we can restore it on close. Then move focus to the first
|
|
4534
|
+
* focusable element inside the drawer.
|
|
4535
|
+
* - While open: a Tab/Shift+Tab keydown listener wraps focus inside
|
|
4536
|
+
* the drawer subtree (last → first on Tab from end, first → last
|
|
4537
|
+
* on Shift+Tab from start).
|
|
4538
|
+
* - On close: restore focus to the captured element.
|
|
4539
|
+
*
|
|
4540
|
+
* Hand-rolled (no `focus-trap-react` dep) — the logic is ~20 LOC and
|
|
4541
|
+
* scoped to a single drawer; adding a runtime dep felt like overkill.
|
|
4542
|
+
* If we ever need more sophisticated trap semantics (nested traps,
|
|
4543
|
+
* sentinel nodes, etc.) the dep is the right call.
|
|
4544
|
+
*/ const drawerRef = useRef(null);
|
|
4545
|
+
const restoreFocusRef = useRef(null);
|
|
4546
|
+
useEffect(()=>{
|
|
4547
|
+
if (!navOpen) return;
|
|
4548
|
+
// 1) Capture the element that had focus before the drawer opened.
|
|
4549
|
+
restoreFocusRef.current = document.activeElement;
|
|
4550
|
+
// 2) Move focus to the first focusable element inside the drawer.
|
|
4551
|
+
// Defer via rAF so the drawer DOM (slide-in animation start) has
|
|
4552
|
+
// settled and the elements are actually visible/focusable.
|
|
4553
|
+
const raf = requestAnimationFrame(()=>{
|
|
4554
|
+
const drawer = drawerRef.current;
|
|
4555
|
+
if (!drawer) return;
|
|
4556
|
+
const first = drawer.querySelector(FOCUSABLE_SELECTOR);
|
|
4557
|
+
first == null ? void 0 : first.focus();
|
|
4558
|
+
});
|
|
4559
|
+
// 3) Trap Tab / Shift+Tab inside the drawer.
|
|
4560
|
+
const onKeyDown = (event)=>{
|
|
4561
|
+
if (event.key !== 'Tab') return;
|
|
4562
|
+
const drawer = drawerRef.current;
|
|
4563
|
+
if (!drawer) return;
|
|
4564
|
+
const focusables = Array.from(drawer.querySelectorAll(FOCUSABLE_SELECTOR));
|
|
4565
|
+
if (focusables.length === 0) {
|
|
4566
|
+
event.preventDefault();
|
|
4567
|
+
return;
|
|
4568
|
+
}
|
|
4569
|
+
const first = focusables[0];
|
|
4570
|
+
const last = focusables[focusables.length - 1];
|
|
4571
|
+
const active = document.activeElement;
|
|
4572
|
+
if (event.shiftKey) {
|
|
4573
|
+
// Shift+Tab on first → wrap to last.
|
|
4574
|
+
if (active === first || !drawer.contains(active)) {
|
|
4575
|
+
event.preventDefault();
|
|
4576
|
+
last.focus();
|
|
4577
|
+
}
|
|
4578
|
+
} else {
|
|
4579
|
+
// Tab on last → wrap to first.
|
|
4580
|
+
if (active === last || !drawer.contains(active)) {
|
|
4581
|
+
event.preventDefault();
|
|
4582
|
+
first.focus();
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
};
|
|
4586
|
+
document.addEventListener('keydown', onKeyDown);
|
|
4587
|
+
return ()=>{
|
|
4588
|
+
cancelAnimationFrame(raf);
|
|
4589
|
+
document.removeEventListener('keydown', onKeyDown);
|
|
4590
|
+
// 4) Restore focus to the previously-focused element on close.
|
|
4591
|
+
// Guard against the element being removed from the DOM (rare
|
|
4592
|
+
// but possible if the consumer re-renders the page while the
|
|
4593
|
+
// drawer is open).
|
|
4594
|
+
const restore = restoreFocusRef.current;
|
|
4595
|
+
if (restore && document.body.contains(restore)) {
|
|
4596
|
+
restore.focus();
|
|
4597
|
+
}
|
|
4598
|
+
};
|
|
4599
|
+
}, [
|
|
4600
|
+
navOpen
|
|
4601
|
+
]);
|
|
4343
4602
|
return jsxs("div", {
|
|
4344
4603
|
className: cn(v.root(), sx, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className),
|
|
4345
4604
|
children: [
|
|
@@ -4357,8 +4616,15 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
4357
4616
|
children: nav
|
|
4358
4617
|
}),
|
|
4359
4618
|
jsx("aside", {
|
|
4619
|
+
ref: drawerRef,
|
|
4360
4620
|
className: cn(v.navMobile(), slotProps == null ? void 0 : (_slotProps_navMobile = slotProps.navMobile) == null ? void 0 : _slotProps_navMobile.className),
|
|
4361
4621
|
"aria-hidden": !navOpen,
|
|
4622
|
+
// When open, the drawer is a modal overlay — `dialog` +
|
|
4623
|
+
// `aria-modal="true"` so screen readers announce it as
|
|
4624
|
+
// such. When closed, drop both attributes (the aria-
|
|
4625
|
+
// hidden=true above already removes it from the AT tree).
|
|
4626
|
+
role: navOpen ? 'dialog' : undefined,
|
|
4627
|
+
"aria-modal": navOpen ? true : undefined,
|
|
4362
4628
|
children: nav
|
|
4363
4629
|
}),
|
|
4364
4630
|
jsx("div", {
|
|
@@ -4663,8 +4929,10 @@ const ConfirmContext = /*#__PURE__*/ createContext(null);
|
|
|
4663
4929
|
'rounded-lg border shadow-lg',
|
|
4664
4930
|
'text-sm bg-neutral-50 text-neutral-900',
|
|
4665
4931
|
// Subtle enter transition — opacity + translate, kept short so a
|
|
4666
|
-
// burst of snackbars feels snappy.
|
|
4667
|
-
|
|
4932
|
+
// burst of snackbars feels snappy. Gated on motion-reduce
|
|
4933
|
+
// (WCAG 2.3.3) — users who request reduced motion see snackbars
|
|
4934
|
+
// pop in instantly without the slide animation.
|
|
4935
|
+
'transition-all duration-200 motion-reduce:transition-none motion-reduce:duration-0',
|
|
4668
4936
|
'data-[state=entered]:opacity-100 data-[state=exited]:opacity-0'
|
|
4669
4937
|
],
|
|
4670
4938
|
icon: 'shrink-0 mt-0.5 w-5 h-5 inline-flex items-center justify-center',
|
|
@@ -6550,6 +6818,6 @@ VisuallyHidden.displayName = 'VisuallyHidden';
|
|
|
6550
6818
|
*/ // ───── Components ─────
|
|
6551
6819
|
/**
|
|
6552
6820
|
* Package version (synced with `package.json` at publish time).
|
|
6553
|
-
*/ const VERSION = '0.
|
|
6821
|
+
*/ const VERSION = '0.3.0-beta';
|
|
6554
6822
|
|
|
6555
6823
|
export { AppShell, AspectRatio, Autocomplete, Box, Breadcrumbs, Button, Checkbox, ConfirmDialogProvider, Container, DateTimePicker, Divider, Grid, LeftNav, NumberField, OTPField, RadioGroup, SnackbarProvider, Stack, Switch, TextField, Textarea, TopBar, Typography, VERSION, VisuallyHidden, appShellVariants, autocompleteVariants, boxVariants, breadcrumbsVariants, buttonVariants, checkboxVariants, cn, confirmDialogVariants, containerVariants, dateTimePickerVariants, dividerLineVariants, dividerVariants, gridVariants, isoToInputValue, leftNavVariants, numberFieldVariants, otpFieldVariants, radioGroupVariants, snackbarVariants, stackVariants, switchVariants, textFieldVariants, textareaVariants, topBarVariants, typographyVariants, useAccessState, useConfirm, useSnackbar };
|
|
@@ -27,6 +27,20 @@ import type { AppShellProps } from './appShell.types.js';
|
|
|
27
27
|
* - The mobile drawer + backdrop participate in the standard
|
|
28
28
|
* "click outside to close" pattern — `Escape` closes the drawer
|
|
29
29
|
* too (added via global keydown).
|
|
30
|
+
* - **Focus trap** (WCAG 2.4.3) — when the mobile drawer opens:
|
|
31
|
+
* 1. The previously-focused element is captured.
|
|
32
|
+
* 2. Focus moves to the first focusable element inside the drawer.
|
|
33
|
+
* 3. Tab / Shift+Tab wrap inside the drawer (cycle from
|
|
34
|
+
* last → first and first → last) — Tab can never escape
|
|
35
|
+
* to the page underneath while the drawer is open.
|
|
36
|
+
* 4. On close, focus returns to the captured element (typically
|
|
37
|
+
* the hamburger toggle the user pressed to open the drawer).
|
|
38
|
+
* This pattern matches WAI-ARIA APG's modal dialog guidance even
|
|
39
|
+
* though the drawer isn't strictly a dialog — same focus
|
|
40
|
+
* management makes keyboard users' experience predictable.
|
|
41
|
+
* - **`role="dialog"` + `aria-modal="true"`** are applied to the
|
|
42
|
+
* drawer when open so screen readers announce it as a modal
|
|
43
|
+
* overlay rather than just an aside.
|
|
30
44
|
*/
|
|
31
45
|
export declare function AppShell(props: AppShellProps): import("react/jsx-runtime").JSX.Element;
|
|
32
46
|
//# sourceMappingURL=AppShell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../../../src/components/AppShell/AppShell.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../../../src/components/AppShell/AppShell.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA0BzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,2CAsK5C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appShell.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/AppShell/appShell.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"appShell.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/AppShell/appShell.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CAoC3B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Autocomplete.d.ts","sourceRoot":"","sources":["../../../../src/components/Autocomplete/Autocomplete.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"Autocomplete.d.ts","sourceRoot":"","sources":["../../../../src/components/Autocomplete/Autocomplete.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,yBAAyB,CAAC;AA8FjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAG,kBAAkB,EACvD,KAAK,EAAE,iBAAiB,CAAC,OAAO,CAAC,kDAy5BlC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/Autocomplete/autocomplete.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"autocomplete.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/Autocomplete/autocomplete.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CAmH/B,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,YAAY,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumbs.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/Breadcrumbs/breadcrumbs.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"breadcrumbs.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/Breadcrumbs/breadcrumbs.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CAqC9B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../../src/components/Checkbox/Checkbox.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../../src/components/Checkbox/Checkbox.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA0DzD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,kDA8L5C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"leftNav.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/LeftNav/leftNav.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"leftNav.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/LeftNav/leftNav.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CAmF1B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,YAAY,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snackbar.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/Snackbar/snackbar.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"snackbar.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/Snackbar/snackbar.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CA0D3B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switch.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/Switch/switch.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"switch.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/Switch/switch.variants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CAsDzB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC,OAAO,cAAc,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../../src/components/TextField/TextField.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../../src/components/TextField/TextField.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,kDAoN9C"}
|
|
@@ -5,9 +5,27 @@ import type { TextFieldVariants } from './textField.variants.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* Per-slot overrides for `<TextField>`.
|
|
7
7
|
*
|
|
8
|
-
* Each slot accepts `{ className: string }`
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Each slot accepts `{ className: string }` (and, for `prefix` /
|
|
9
|
+
* `suffix`, a `children` node) so the override path is extensible —
|
|
10
|
+
* extra fields like `style`, `aria-*`, `data-*` can be added without
|
|
11
|
+
* a breaking change. Mirrors the MUI-side `slotProps` shape.
|
|
12
|
+
*
|
|
13
|
+
* **`prefix` / `suffix`** are inline adornments rendered INSIDE the
|
|
14
|
+
* `inputWrapper`, before / after the `<input>` element. Typical use:
|
|
15
|
+
* currency symbols, units, status icons. Passing only `className`
|
|
16
|
+
* (no `children`) is allowed but renders an empty slot — usually
|
|
17
|
+
* you'll always pair the two.
|
|
18
|
+
*
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <TextField
|
|
21
|
+
* name="price"
|
|
22
|
+
* type="number"
|
|
23
|
+
* slotProps={{
|
|
24
|
+
* prefix: { children: '$' },
|
|
25
|
+
* suffix: { children: 'USD' },
|
|
26
|
+
* }}
|
|
27
|
+
* />
|
|
28
|
+
* ```
|
|
11
29
|
*/
|
|
12
30
|
export interface TextFieldSlotProps {
|
|
13
31
|
root?: {
|
|
@@ -31,6 +49,14 @@ export interface TextFieldSlotProps {
|
|
|
31
49
|
errorText?: {
|
|
32
50
|
className?: string;
|
|
33
51
|
};
|
|
52
|
+
prefix?: {
|
|
53
|
+
children?: ReactNode;
|
|
54
|
+
className?: string;
|
|
55
|
+
};
|
|
56
|
+
suffix?: {
|
|
57
|
+
children?: ReactNode;
|
|
58
|
+
className?: string;
|
|
59
|
+
};
|
|
34
60
|
}
|
|
35
61
|
/**
|
|
36
62
|
* Props for the Dashforge TW `<TextField>`.
|