@dashforge/tw 0.2.0-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/A11Y.md +130 -0
- package/CHANGELOG.md +339 -82
- package/CONSUMER-VALIDATION.md +130 -0
- package/dist/index.esm.js +351 -47
- 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/Button/Button.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/NumberField/NumberField.d.ts.map +1 -1
- package/dist/src/components/RadioGroup/RadioGroup.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/Button/Button.tsx +11 -0
- package/src/components/Checkbox/Checkbox.tsx +63 -5
- package/src/components/LeftNav/leftNav.variants.ts +12 -1
- package/src/components/NumberField/NumberField.tsx +30 -2
- package/src/components/RadioGroup/RadioGroup.tsx +23 -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/LICENSE +0 -21
package/dist/index.esm.js
CHANGED
|
@@ -403,6 +403,13 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
403
403
|
fullWidth,
|
|
404
404
|
loading
|
|
405
405
|
}), sx);
|
|
406
|
+
/*
|
|
407
|
+
* `aria-busy` announces the loading state to assistive tech.
|
|
408
|
+
* `disabled` alone hides the reason (perm-denied vs loading vs
|
|
409
|
+
* intrinsic), so SR users only hear "dimmed/inactive" without
|
|
410
|
+
* knowing why. Adding `aria-busy={true}` while loading distinguishes
|
|
411
|
+
* "wait for the action to finish" from "you can't do this".
|
|
412
|
+
*/ const ariaBusy = loading ? true : undefined;
|
|
406
413
|
// `asChild` renders through Radix Slot: the immediate child element
|
|
407
414
|
// gets the resolved className, no extra Button DOM is emitted.
|
|
408
415
|
if (asChild) {
|
|
@@ -411,6 +418,7 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
411
418
|
className: classes,
|
|
412
419
|
"data-disabled": effectiveDisabled || undefined,
|
|
413
420
|
"aria-disabled": effectiveDisabled || undefined,
|
|
421
|
+
"aria-busy": ariaBusy,
|
|
414
422
|
children: children
|
|
415
423
|
});
|
|
416
424
|
}
|
|
@@ -418,6 +426,7 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
418
426
|
ref: ref,
|
|
419
427
|
type: (_rest_type = rest.type) != null ? _rest_type : 'button',
|
|
420
428
|
disabled: effectiveDisabled,
|
|
429
|
+
"aria-busy": ariaBusy,
|
|
421
430
|
className: classes
|
|
422
431
|
}, rest, {
|
|
423
432
|
children: [
|
|
@@ -501,7 +510,24 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
501
510
|
'read-only:cursor-default'
|
|
502
511
|
],
|
|
503
512
|
helperText: 'mt-1 text-sm text-neutral-600',
|
|
504
|
-
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
|
+
]
|
|
505
531
|
},
|
|
506
532
|
variants: {
|
|
507
533
|
size: {
|
|
@@ -578,7 +604,7 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
578
604
|
* - `aria-describedby` links the input to the helper/error text node.
|
|
579
605
|
* - Required fields get the native `required` attribute + a visual `*`.
|
|
580
606
|
*/ function TextField(props) {
|
|
581
|
-
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;
|
|
582
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, [
|
|
583
609
|
"name",
|
|
584
610
|
"rules",
|
|
@@ -708,25 +734,37 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
708
734
|
})
|
|
709
735
|
]
|
|
710
736
|
}),
|
|
711
|
-
|
|
737
|
+
jsxs("div", {
|
|
712
738
|
className: cn(v.inputWrapper(), slotProps == null ? void 0 : (_slotProps_inputWrapper = slotProps.inputWrapper) == null ? void 0 : _slotProps_inputWrapper.className),
|
|
713
|
-
children:
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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
|
+
]
|
|
730
768
|
}),
|
|
731
769
|
resolvedHelperText && jsx("p", {
|
|
732
770
|
id: helperId,
|
|
@@ -802,8 +840,8 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
802
840
|
});
|
|
803
841
|
|
|
804
842
|
/**
|
|
805
|
-
* Inline checkmark SVG used by `Radix.Indicator
|
|
806
|
-
* `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.
|
|
807
845
|
*
|
|
808
846
|
* @internal
|
|
809
847
|
*/ function CheckIcon({ className }) {
|
|
@@ -821,6 +859,32 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
821
859
|
})
|
|
822
860
|
});
|
|
823
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
|
+
}
|
|
824
888
|
/**
|
|
825
889
|
* Dashforge TW Checkbox — bridge-integrated form control.
|
|
826
890
|
*
|
|
@@ -966,12 +1030,16 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
966
1030
|
onBlur: handleBlur,
|
|
967
1031
|
ref: registration == null ? void 0 : registration.ref,
|
|
968
1032
|
className: cn(v.control(), slotProps == null ? void 0 : (_slotProps_control = slotProps.control) == null ? void 0 : _slotProps_control.className),
|
|
969
|
-
children:
|
|
970
|
-
className: cn(v.indicator(), slotProps == null ? void 0 : (_slotProps_indicator = slotProps.indicator) == null ? void 0 : _slotProps_indicator.className),
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
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
|
+
]
|
|
975
1043
|
})
|
|
976
1044
|
})),
|
|
977
1045
|
jsxs("div", {
|
|
@@ -1016,7 +1084,12 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
1016
1084
|
],
|
|
1017
1085
|
thumb: [
|
|
1018
1086
|
'pointer-events-none inline-block rounded-full bg-white shadow ring-0',
|
|
1019
|
-
|
|
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',
|
|
1020
1093
|
'data-[state=unchecked]:translate-x-0'
|
|
1021
1094
|
],
|
|
1022
1095
|
label: 'select-none cursor-pointer text-neutral-900',
|
|
@@ -1430,9 +1503,13 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
1430
1503
|
})
|
|
1431
1504
|
]
|
|
1432
1505
|
}),
|
|
1433
|
-
jsx(RadixRadioGroup.Root, {
|
|
1434
|
-
name: name
|
|
1435
|
-
|
|
1506
|
+
jsx(RadixRadioGroup.Root, _extends({
|
|
1507
|
+
name: name
|
|
1508
|
+
}, isFormMode || explicitValue !== undefined ? {
|
|
1509
|
+
value: resolvedValue
|
|
1510
|
+
} : {
|
|
1511
|
+
defaultValue: defaultValue != null ? defaultValue : undefined
|
|
1512
|
+
}, {
|
|
1436
1513
|
onValueChange: handleValueChange,
|
|
1437
1514
|
onBlur: handleBlur,
|
|
1438
1515
|
disabled: groupEffectiveDisabled,
|
|
@@ -1463,7 +1540,7 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
1463
1540
|
]
|
|
1464
1541
|
}, option.value);
|
|
1465
1542
|
})
|
|
1466
|
-
}),
|
|
1543
|
+
})),
|
|
1467
1544
|
resolvedHelperText && jsx("p", {
|
|
1468
1545
|
id: helperId,
|
|
1469
1546
|
className: cn(resolvedError ? v.errorText() : v.helperText(), resolvedError ? slotProps == null ? void 0 : (_slotProps_errorText = slotProps.errorText) == null ? void 0 : _slotProps_errorText.className : slotProps == null ? void 0 : (_slotProps_helperText = slotProps.helperText) == null ? void 0 : _slotProps_helperText.className),
|
|
@@ -1905,6 +1982,18 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
1905
1982
|
useDashFieldMeta(name);
|
|
1906
1983
|
const accessState = useAccessState(access);
|
|
1907
1984
|
const inputId = useId();
|
|
1985
|
+
/*
|
|
1986
|
+
* Local state for the STANDALONE UNCONTROLLED case (no bridge, no
|
|
1987
|
+
* `value` prop, only `defaultValue`). Mirrors OTPField. Without this,
|
|
1988
|
+
* `resolvedDisplayValue` would be a snapshot computed ONCE from
|
|
1989
|
+
* `defaultValue` and the controlled `<input value={...}>` would
|
|
1990
|
+
* snap user input back on every keystroke / stepper click — same
|
|
1991
|
+
* trap that hit Checkbox + RadioGroup in this package.
|
|
1992
|
+
*
|
|
1993
|
+
* In form mode the bridge owns state. In standalone CONTROLLED mode
|
|
1994
|
+
* (consumer passes `value`) the consumer owns state. Only this
|
|
1995
|
+
* branch needs the local hook.
|
|
1996
|
+
*/ const [uncontrolledValue, setUncontrolledValue] = useState(()=>formatForDisplay(defaultValue));
|
|
1908
1997
|
const helperId = `${inputId}-help`;
|
|
1909
1998
|
// StrictMode-safe unregister-on-unmount
|
|
1910
1999
|
const unregisterRef = useRef({
|
|
@@ -1942,7 +2031,7 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
1942
2031
|
resolvedHelperText = validation.helperText;
|
|
1943
2032
|
resolvedDisplayValue = userValue !== undefined ? formatForDisplay(userValue) : formatForDisplay(bridge.getValue(name));
|
|
1944
2033
|
} else {
|
|
1945
|
-
resolvedDisplayValue = userValue !== undefined ? formatForDisplay(userValue) :
|
|
2034
|
+
resolvedDisplayValue = userValue !== undefined ? formatForDisplay(userValue) : uncontrolledValue;
|
|
1946
2035
|
}
|
|
1947
2036
|
const writeToBridge = (parsed)=>{
|
|
1948
2037
|
if (!isFormMode || !bridge) return;
|
|
@@ -1957,6 +2046,13 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
1957
2046
|
// internal logic still sees the raw string change.
|
|
1958
2047
|
void registration.onChange(e);
|
|
1959
2048
|
}
|
|
2049
|
+
// Standalone uncontrolled mode: mirror the raw input string so the
|
|
2050
|
+
// controlled `<input value={...}>` reflects what the user typed.
|
|
2051
|
+
// Partial states (e.g. "-", "1.") are kept verbatim — `parseFromInput`
|
|
2052
|
+
// returns `undefined` for them so `writeToBridge` is a no-op above.
|
|
2053
|
+
if (!isFormMode && userValue === undefined) {
|
|
2054
|
+
setUncontrolledValue(e.target.value);
|
|
2055
|
+
}
|
|
1960
2056
|
userOnChange == null ? void 0 : userOnChange(e);
|
|
1961
2057
|
};
|
|
1962
2058
|
const handleBlur = (e)=>{
|
|
@@ -1972,6 +2068,11 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
1972
2068
|
if (typeof min === 'number') next = Math.max(min, next);
|
|
1973
2069
|
if (typeof max === 'number') next = Math.min(max, next);
|
|
1974
2070
|
writeToBridge(next);
|
|
2071
|
+
// Standalone uncontrolled: also persist the new value to local
|
|
2072
|
+
// state so the visible display tracks the stepper click.
|
|
2073
|
+
if (!isFormMode && userValue === undefined) {
|
|
2074
|
+
setUncontrolledValue(formatForDisplay(next));
|
|
2075
|
+
}
|
|
1975
2076
|
};
|
|
1976
2077
|
const canIncrement = !effectiveDisabled && (typeof max !== 'number' || ((_parseFromInput = parseFromInput(resolvedDisplayValue)) != null ? _parseFromInput : -Infinity) < max);
|
|
1977
2078
|
const canDecrement = !effectiveDisabled && (typeof min !== 'number' || ((_parseFromInput1 = parseFromInput(resolvedDisplayValue)) != null ? _parseFromInput1 : Infinity) > min);
|
|
@@ -2378,7 +2479,13 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
2378
2479
|
'flex items-center justify-center shrink-0 px-2',
|
|
2379
2480
|
'text-neutral-600 hover:text-neutral-900',
|
|
2380
2481
|
'disabled:cursor-not-allowed disabled:opacity-40',
|
|
2381
|
-
'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'
|
|
2382
2489
|
],
|
|
2383
2490
|
clearButton: [
|
|
2384
2491
|
'flex items-center justify-center shrink-0 px-2',
|
|
@@ -2471,6 +2578,52 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
2471
2578
|
}
|
|
2472
2579
|
});
|
|
2473
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
|
+
}
|
|
2474
2627
|
/**
|
|
2475
2628
|
* Coerce a raw value (from bridge / props) into a canonical shape:
|
|
2476
2629
|
*
|
|
@@ -2777,7 +2930,15 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
2777
2930
|
if (next == null) {
|
|
2778
2931
|
setInputValue('');
|
|
2779
2932
|
} else {
|
|
2780
|
-
|
|
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);
|
|
2781
2942
|
const found_label = found ? labelAsString(found) : undefined;
|
|
2782
2943
|
if (found_label !== undefined) {
|
|
2783
2944
|
setInputValue(found_label);
|
|
@@ -2807,6 +2968,8 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
2807
2968
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2808
2969
|
[
|
|
2809
2970
|
options,
|
|
2971
|
+
asyncOptions,
|
|
2972
|
+
loadOptions,
|
|
2810
2973
|
isFormMode,
|
|
2811
2974
|
bridge,
|
|
2812
2975
|
name,
|
|
@@ -3153,7 +3316,7 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3153
3316
|
"aria-label": `Remove ${labelText}`,
|
|
3154
3317
|
tabIndex: -1,
|
|
3155
3318
|
className: cn(v.chipRemove(), slotProps == null ? void 0 : (_slotProps_chipRemove = slotProps.chipRemove) == null ? void 0 : _slotProps_chipRemove.className),
|
|
3156
|
-
children:
|
|
3319
|
+
children: jsx(CloseIcon, {})
|
|
3157
3320
|
})
|
|
3158
3321
|
]
|
|
3159
3322
|
}, key);
|
|
@@ -3197,7 +3360,7 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3197
3360
|
"aria-label": "Clear selection",
|
|
3198
3361
|
tabIndex: -1,
|
|
3199
3362
|
className: cn(v.clearButton(), slotProps == null ? void 0 : (_slotProps_clearButton = slotProps.clearButton) == null ? void 0 : _slotProps_clearButton.className),
|
|
3200
|
-
children:
|
|
3363
|
+
children: jsx(CloseIcon, {})
|
|
3201
3364
|
}),
|
|
3202
3365
|
jsx("button", {
|
|
3203
3366
|
type: "button",
|
|
@@ -3209,7 +3372,7 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3209
3372
|
tabIndex: -1,
|
|
3210
3373
|
disabled: effectiveDisabled,
|
|
3211
3374
|
className: cn(v.trigger(), slotProps == null ? void 0 : (_slotProps_trigger = slotProps.trigger) == null ? void 0 : _slotProps_trigger.className),
|
|
3212
|
-
children:
|
|
3375
|
+
children: jsx(ChevronDownIcon, {})
|
|
3213
3376
|
}),
|
|
3214
3377
|
isOpen && jsx("ul", {
|
|
3215
3378
|
ref: listboxRef,
|
|
@@ -3607,7 +3770,12 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3607
3770
|
'text-neutral-600 hover:text-primary-700',
|
|
3608
3771
|
'rounded-sm outline-none',
|
|
3609
3772
|
'focus-visible:ring-2 focus-visible:ring-primary-500/50',
|
|
3610
|
-
'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'
|
|
3611
3779
|
],
|
|
3612
3780
|
current: [
|
|
3613
3781
|
'inline-flex items-center gap-1 truncate',
|
|
@@ -3801,7 +3969,10 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3801
3969
|
root: [
|
|
3802
3970
|
'flex flex-col h-full',
|
|
3803
3971
|
'bg-neutral-50 border-r border-neutral-200',
|
|
3804
|
-
|
|
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'
|
|
3805
3976
|
],
|
|
3806
3977
|
brand: [
|
|
3807
3978
|
'flex items-center gap-2 px-3 h-14 shrink-0',
|
|
@@ -3815,7 +3986,15 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3815
3986
|
'outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50',
|
|
3816
3987
|
'transition-colors w-full',
|
|
3817
3988
|
'aria-disabled:opacity-50 aria-disabled:cursor-not-allowed',
|
|
3818
|
-
'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'
|
|
3819
3998
|
],
|
|
3820
3999
|
itemActive: 'bg-primary-100 text-primary-900 font-medium',
|
|
3821
4000
|
itemIcon: 'shrink-0 w-5 h-5 flex items-center justify-center',
|
|
@@ -4223,14 +4402,20 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
4223
4402
|
nav: 'hidden md:flex shrink-0',
|
|
4224
4403
|
navMobile: [
|
|
4225
4404
|
'flex md:hidden fixed inset-y-0 left-0 z-40',
|
|
4226
|
-
|
|
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',
|
|
4227
4411
|
'-translate-x-full'
|
|
4228
4412
|
],
|
|
4229
4413
|
main: 'flex-1 min-w-0 overflow-y-auto',
|
|
4230
4414
|
footer: 'shrink-0',
|
|
4231
4415
|
backdrop: [
|
|
4232
4416
|
'md:hidden fixed inset-0 z-30 bg-black/40',
|
|
4233
|
-
|
|
4417
|
+
// Backdrop opacity fade — micro motion, gated for consistency.
|
|
4418
|
+
'transition-opacity duration-200 motion-reduce:transition-none motion-reduce:duration-0',
|
|
4234
4419
|
'opacity-0 pointer-events-none'
|
|
4235
4420
|
]
|
|
4236
4421
|
},
|
|
@@ -4247,6 +4432,28 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
4247
4432
|
}
|
|
4248
4433
|
});
|
|
4249
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(',');
|
|
4250
4457
|
/**
|
|
4251
4458
|
* Dashforge TW AppShell — top-level layout orchestrator.
|
|
4252
4459
|
*
|
|
@@ -4275,6 +4482,20 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
4275
4482
|
* - The mobile drawer + backdrop participate in the standard
|
|
4276
4483
|
* "click outside to close" pattern — `Escape` closes the drawer
|
|
4277
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.
|
|
4278
4499
|
*/ function AppShell(props) {
|
|
4279
4500
|
var _slotProps_root, _slotProps_header, _slotProps_nav, _slotProps_navMobile, _slotProps_backdrop, _slotProps_main, _slotProps_footer;
|
|
4280
4501
|
const { header, nav, footer, children, navOpen = false, onNavOpenChange, sx, slotProps } = props;
|
|
@@ -4304,6 +4525,80 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
4304
4525
|
navOpen,
|
|
4305
4526
|
onNavOpenChange
|
|
4306
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
|
+
]);
|
|
4307
4602
|
return jsxs("div", {
|
|
4308
4603
|
className: cn(v.root(), sx, slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className),
|
|
4309
4604
|
children: [
|
|
@@ -4321,8 +4616,15 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
4321
4616
|
children: nav
|
|
4322
4617
|
}),
|
|
4323
4618
|
jsx("aside", {
|
|
4619
|
+
ref: drawerRef,
|
|
4324
4620
|
className: cn(v.navMobile(), slotProps == null ? void 0 : (_slotProps_navMobile = slotProps.navMobile) == null ? void 0 : _slotProps_navMobile.className),
|
|
4325
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,
|
|
4326
4628
|
children: nav
|
|
4327
4629
|
}),
|
|
4328
4630
|
jsx("div", {
|
|
@@ -4627,8 +4929,10 @@ const ConfirmContext = /*#__PURE__*/ createContext(null);
|
|
|
4627
4929
|
'rounded-lg border shadow-lg',
|
|
4628
4930
|
'text-sm bg-neutral-50 text-neutral-900',
|
|
4629
4931
|
// Subtle enter transition — opacity + translate, kept short so a
|
|
4630
|
-
// burst of snackbars feels snappy.
|
|
4631
|
-
|
|
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',
|
|
4632
4936
|
'data-[state=entered]:opacity-100 data-[state=exited]:opacity-0'
|
|
4633
4937
|
],
|
|
4634
4938
|
icon: 'shrink-0 mt-0.5 w-5 h-5 inline-flex items-center justify-center',
|
|
@@ -6514,6 +6818,6 @@ VisuallyHidden.displayName = 'VisuallyHidden';
|
|
|
6514
6818
|
*/ // ───── Components ─────
|
|
6515
6819
|
/**
|
|
6516
6820
|
* Package version (synced with `package.json` at publish time).
|
|
6517
|
-
*/ const VERSION = '0.
|
|
6821
|
+
*/ const VERSION = '0.3.0-beta';
|
|
6518
6822
|
|
|
6519
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"}
|