@dashforge/tw 1.5.2 → 1.7.0
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 +202 -0
- package/LICENSE +21 -0
- package/dist/index.esm.js +535 -140
- package/dist/src/components/Autocomplete/Autocomplete.d.ts +22 -0
- package/dist/src/components/Autocomplete/Autocomplete.d.ts.map +1 -1
- package/dist/src/components/Autocomplete/autocomplete.props.type-test.d.ts +2 -0
- package/dist/src/components/Autocomplete/autocomplete.props.type-test.d.ts.map +1 -0
- package/dist/src/components/Autocomplete/autocomplete.types.d.ts +103 -22
- package/dist/src/components/Autocomplete/autocomplete.types.d.ts.map +1 -1
- package/dist/src/components/Button/button.variants.d.ts.map +1 -1
- package/dist/src/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/src/components/Checkbox/checkbox.types.d.ts +16 -0
- package/dist/src/components/Checkbox/checkbox.types.d.ts.map +1 -1
- package/dist/src/components/Checkbox/checkbox.variants.d.ts +3 -0
- package/dist/src/components/Checkbox/checkbox.variants.d.ts.map +1 -1
- package/dist/src/components/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/src/components/DateRangePicker/DateRangePicker.d.ts.map +1 -1
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
- package/dist/src/components/Dialog/Dialog.d.ts.map +1 -1
- package/dist/src/components/Dialog/dialog.types.d.ts +28 -0
- package/dist/src/components/Dialog/dialog.types.d.ts.map +1 -1
- package/dist/src/components/NumberField/NumberField.d.ts.map +1 -1
- package/dist/src/components/OTPField/OTPField.d.ts.map +1 -1
- package/dist/src/components/RadioGroup/RadioGroup.d.ts.map +1 -1
- package/dist/src/components/RadioGroup/radioGroup.variants.d.ts.map +1 -1
- package/dist/src/components/Select/Select.d.ts.map +1 -1
- package/dist/src/components/Slider/Slider.d.ts.map +1 -1
- package/dist/src/components/Switch/Switch.d.ts.map +1 -1
- package/dist/src/components/Switch/switch.types.d.ts +15 -0
- package/dist/src/components/Switch/switch.types.d.ts.map +1 -1
- package/dist/src/components/Switch/switch.variants.d.ts +3 -0
- package/dist/src/components/Switch/switch.variants.d.ts.map +1 -1
- package/dist/src/components/Tabs/tabs.variants.d.ts.map +1 -1
- package/dist/src/components/TextField/TextField.d.ts.map +1 -1
- package/dist/src/components/Textarea/Textarea.d.ts.map +1 -1
- package/dist/src/components/TimePicker/TimePicker.d.ts.map +1 -1
- package/package.json +6 -4
package/dist/index.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { useMemo, forwardRef, useContext, useState, useEffect, useRef, useId, useCallback, Fragment as Fragment$1, createContext, Children, isValidElement, memo, cloneElement, useLayoutEffect } from 'react';
|
|
3
3
|
import { Slot } from '@radix-ui/react-slot';
|
|
4
|
-
import { DashFormContext, useEngineVisibility } from '@dashforge/ui-core';
|
|
4
|
+
import { DashFormContext, useEngineVisibility, useWarnIfControlledInFormMode } from '@dashforge/ui-core';
|
|
5
5
|
import { useComponentDefaults } from '@dashforge/tw-theme';
|
|
6
6
|
import clsx from 'clsx';
|
|
7
7
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -13,8 +13,8 @@ import * as RadixTooltip from '@radix-ui/react-tooltip';
|
|
|
13
13
|
import * as RadixCheckbox from '@radix-ui/react-checkbox';
|
|
14
14
|
import * as RadixSwitch from '@radix-ui/react-switch';
|
|
15
15
|
import * as RadixRadioGroup from '@radix-ui/react-radio-group';
|
|
16
|
-
import { useCalendar, resolveCalendarKey, formatLongDate, parseISODate, formatTime, generateTimeOptions, timeStringToMinutes, getTodayISODate, useDateRange } from '@dashforge/calendar-core';
|
|
17
16
|
import * as RadixPopover from '@radix-ui/react-popover';
|
|
17
|
+
import { useCalendar, resolveCalendarKey, formatLongDate, parseISODate, formatTime, generateTimeOptions, timeStringToMinutes, getTodayISODate, useDateRange } from '@dashforge/calendar-core';
|
|
18
18
|
import * as RadixDialog from '@radix-ui/react-dialog';
|
|
19
19
|
import * as RadixSlider from '@radix-ui/react-slider';
|
|
20
20
|
import * as RadixMenu from '@radix-ui/react-dropdown-menu';
|
|
@@ -350,6 +350,12 @@ Spinner.displayName = 'Spinner';
|
|
|
350
350
|
'inline-flex items-center justify-center gap-2',
|
|
351
351
|
'font-medium',
|
|
352
352
|
'rounded-md',
|
|
353
|
+
// Pointer affordance — the browser UA cursor for `<button>` is
|
|
354
|
+
// `default`, not `pointer`. Applies to every non-disabled state
|
|
355
|
+
// (disabled + loading override below); `variant='link'` inherits
|
|
356
|
+
// this cursor too, matching what browsers show for anchors. See
|
|
357
|
+
// README-BUG § BUG 13.
|
|
358
|
+
'cursor-pointer',
|
|
353
359
|
'select-none whitespace-nowrap',
|
|
354
360
|
'transition-colors',
|
|
355
361
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
|
|
@@ -1033,7 +1039,14 @@ const tooltipVariants = tv({
|
|
|
1033
1039
|
const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
|
|
1034
1040
|
// Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
|
|
1035
1041
|
const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
|
|
1036
|
-
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
|
|
1042
|
+
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, // Consume `tooltip` here so it does not leak into `...rest` and
|
|
1043
|
+
// end up as an unknown HTML attribute on the rendered <input>.
|
|
1044
|
+
// React 19 passes lowercase unknown attrs through silently, so
|
|
1045
|
+
// the value would ship as `tooltip="…"` on the DOM without any
|
|
1046
|
+
// console signal. Also strips the object form (`{ content, side, … }`)
|
|
1047
|
+
// whose default stringification is `[object Object]`.
|
|
1048
|
+
// See README-BUG § BUG 9.
|
|
1049
|
+
tooltip: _tooltip } = merged, rest = _object_without_properties_loose(merged, [
|
|
1037
1050
|
"name",
|
|
1038
1051
|
"rules",
|
|
1039
1052
|
"visibleWhen",
|
|
@@ -1053,7 +1066,8 @@ const tooltipVariants = tv({
|
|
|
1053
1066
|
"onChange",
|
|
1054
1067
|
"onBlur",
|
|
1055
1068
|
"value",
|
|
1056
|
-
"defaultValue"
|
|
1069
|
+
"defaultValue",
|
|
1070
|
+
"tooltip"
|
|
1057
1071
|
]);
|
|
1058
1072
|
// ───── Hooks (unconditional) ─────
|
|
1059
1073
|
const bridge = useContext(DashFormContext);
|
|
@@ -1081,6 +1095,11 @@ const tooltipVariants = tv({
|
|
|
1081
1095
|
return ()=>{
|
|
1082
1096
|
isMountedRef.current = false;
|
|
1083
1097
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
1098
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
1099
|
+
// is configured to forget unmounted fields. Default `false`
|
|
1100
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
1101
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
1102
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
1084
1103
|
queueMicrotask(()=>{
|
|
1085
1104
|
var _cap_unregister;
|
|
1086
1105
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -1230,6 +1249,11 @@ const tooltipVariants = tv({
|
|
|
1230
1249
|
'rounded border bg-neutral-50',
|
|
1231
1250
|
'border-neutral-300',
|
|
1232
1251
|
'transition-colors',
|
|
1252
|
+
// Radix.Checkbox.Root renders a `<button>`, whose UA cursor is
|
|
1253
|
+
// `default`. The label already reads `cursor-pointer`, but the
|
|
1254
|
+
// control itself needs the same affordance when the pointer
|
|
1255
|
+
// sits directly on the box. See README-BUG § BUG 13.
|
|
1256
|
+
'cursor-pointer',
|
|
1233
1257
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-primary-500',
|
|
1234
1258
|
// Radix data-state hooks
|
|
1235
1259
|
'data-[state=checked]:bg-primary-500 data-[state=checked]:border-primary-500',
|
|
@@ -1238,6 +1262,10 @@ const tooltipVariants = tv({
|
|
|
1238
1262
|
],
|
|
1239
1263
|
indicator: 'flex items-center justify-center text-white',
|
|
1240
1264
|
label: 'select-none cursor-pointer text-neutral-900',
|
|
1265
|
+
// BUG 21: `*` marker after the label, matching the `textField.variants.ts`
|
|
1266
|
+
// `requiredMark` slot so a consent box aligns visually with sibling
|
|
1267
|
+
// required fields in a form.
|
|
1268
|
+
requiredMark: 'text-danger-500 ml-0.5',
|
|
1241
1269
|
helperText: 'text-sm text-neutral-600 mt-1',
|
|
1242
1270
|
errorText: 'text-sm text-danger-600 mt-1'
|
|
1243
1271
|
},
|
|
@@ -1346,13 +1374,13 @@ const tooltipVariants = tv({
|
|
|
1346
1374
|
* rather than MUI slot components.
|
|
1347
1375
|
*/ function Checkbox(props) {
|
|
1348
1376
|
var _ref;
|
|
1349
|
-
var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_control, _slotProps_control, _themeSlotProps_indicator, _slotProps_indicator, _themeSlotProps_label, _slotProps_label, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
|
|
1377
|
+
var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_control, _slotProps_control, _themeSlotProps_indicator, _slotProps_indicator, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
|
|
1350
1378
|
const themeDefaults = useComponentDefaults('Checkbox');
|
|
1351
1379
|
const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
|
|
1352
1380
|
const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
|
|
1353
1381
|
// Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
|
|
1354
1382
|
const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
|
|
1355
|
-
const { name, rules, visibleWhen, label, helperText, error, access, size, disabled, checked, defaultChecked, onCheckedChange, sx, slotProps } = merged;
|
|
1383
|
+
const { name, rules, visibleWhen, label, helperText, error, access, size, disabled, required, checked, defaultChecked, onCheckedChange, sx, slotProps } = merged;
|
|
1356
1384
|
// ───── Hooks (always called, unconditional) ─────
|
|
1357
1385
|
const bridge = useContext(DashFormContext);
|
|
1358
1386
|
const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
|
|
@@ -1362,6 +1390,32 @@ const tooltipVariants = tv({
|
|
|
1362
1390
|
const fieldMeta = useDashFieldMeta(name);
|
|
1363
1391
|
const accessState = useAccessState(access);
|
|
1364
1392
|
const controlId = useId();
|
|
1393
|
+
// BUG 21 v2: local ref for the Radix Root DOM button. We use this to
|
|
1394
|
+
// set `aria-required` post-mount because setting the attribute via a
|
|
1395
|
+
// JSX prop on `RadixCheckbox.Root` does not reach the browser DOM in
|
|
1396
|
+
// production builds (verified empirically in urbango on Radix v1.3.3;
|
|
1397
|
+
// jsdom does not surface the same gap).
|
|
1398
|
+
const buttonRef = useRef(null);
|
|
1399
|
+
const setControlRef = (node)=>{
|
|
1400
|
+
buttonRef.current = node;
|
|
1401
|
+
const regRef = registration == null ? void 0 : registration.ref;
|
|
1402
|
+
if (typeof regRef === 'function') {
|
|
1403
|
+
regRef(node);
|
|
1404
|
+
} else if (regRef && typeof regRef === 'object') {
|
|
1405
|
+
regRef.current = node;
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
useEffect(()=>{
|
|
1409
|
+
const node = buttonRef.current;
|
|
1410
|
+
if (!node) return;
|
|
1411
|
+
if (required) {
|
|
1412
|
+
node.setAttribute('aria-required', 'true');
|
|
1413
|
+
} else {
|
|
1414
|
+
node.removeAttribute('aria-required');
|
|
1415
|
+
}
|
|
1416
|
+
}, [
|
|
1417
|
+
required
|
|
1418
|
+
]);
|
|
1365
1419
|
// StrictMode-safe unregister-on-unmount — mirrors MUI Checkbox.tsx
|
|
1366
1420
|
// (the bridge identity changes on every keystroke, so we MUST NOT
|
|
1367
1421
|
// re-run cleanup on deps changes — empty deps + ref pattern).
|
|
@@ -1379,6 +1433,11 @@ const tooltipVariants = tv({
|
|
|
1379
1433
|
return ()=>{
|
|
1380
1434
|
isMountedRef.current = false;
|
|
1381
1435
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
1436
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
1437
|
+
// is configured to forget unmounted fields. Default `false`
|
|
1438
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
1439
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
1440
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
1382
1441
|
queueMicrotask(()=>{
|
|
1383
1442
|
var _cap_unregister;
|
|
1384
1443
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -1467,9 +1526,19 @@ const tooltipVariants = tv({
|
|
|
1467
1526
|
name: name
|
|
1468
1527
|
}, radixStateProps, {
|
|
1469
1528
|
disabled: effectiveDisabled,
|
|
1529
|
+
required: required,
|
|
1470
1530
|
onCheckedChange: handleCheckedChange,
|
|
1471
1531
|
onBlur: handleBlur,
|
|
1472
|
-
|
|
1532
|
+
// BUG 21 v2: compose our own ref with `registration.ref` so we can
|
|
1533
|
+
// ALSO set `aria-required` post-mount via a `useEffect` below. The
|
|
1534
|
+
// JSX-level `aria-required` prop was verified empirically not to
|
|
1535
|
+
// reach the DOM in a real browser (Radix v1.3.3 reads its own
|
|
1536
|
+
// `required` prop into context and re-emits `aria-required` from
|
|
1537
|
+
// the internal trigger, but the attribute is stripped somewhere
|
|
1538
|
+
// in the production path — jsdom does not catch it). Post-mount
|
|
1539
|
+
// ref-based setter is Radix-version-agnostic and lands on the
|
|
1540
|
+
// element the browser actually shows.
|
|
1541
|
+
ref: setControlRef,
|
|
1473
1542
|
className: cn(v.control(), themeSlotProps == null ? void 0 : (_themeSlotProps_control = themeSlotProps.control) == null ? void 0 : _themeSlotProps_control.className, slotProps == null ? void 0 : (_slotProps_control = slotProps.control) == null ? void 0 : _slotProps_control.className),
|
|
1474
1543
|
children: jsxs(RadixCheckbox.Indicator, {
|
|
1475
1544
|
className: cn(v.indicator(), 'group', themeSlotProps == null ? void 0 : (_themeSlotProps_indicator = themeSlotProps.indicator) == null ? void 0 : _themeSlotProps_indicator.className, slotProps == null ? void 0 : (_slotProps_indicator = slotProps.indicator) == null ? void 0 : _slotProps_indicator.className),
|
|
@@ -1486,10 +1555,17 @@ const tooltipVariants = tv({
|
|
|
1486
1555
|
jsxs("div", {
|
|
1487
1556
|
className: "flex flex-col",
|
|
1488
1557
|
children: [
|
|
1489
|
-
label &&
|
|
1558
|
+
label && jsxs("label", {
|
|
1490
1559
|
htmlFor: controlId,
|
|
1491
1560
|
className: cn(v.label(), themeSlotProps == null ? void 0 : (_themeSlotProps_label = themeSlotProps.label) == null ? void 0 : _themeSlotProps_label.className, slotProps == null ? void 0 : (_slotProps_label = slotProps.label) == null ? void 0 : _slotProps_label.className),
|
|
1492
|
-
children:
|
|
1561
|
+
children: [
|
|
1562
|
+
renderLabelWithTooltip(label, tooltipConfig),
|
|
1563
|
+
required && jsx("span", {
|
|
1564
|
+
"aria-hidden": "true",
|
|
1565
|
+
className: cn(v.requiredMark(), themeSlotProps == null ? void 0 : (_themeSlotProps_requiredMark = themeSlotProps.requiredMark) == null ? void 0 : _themeSlotProps_requiredMark.className, slotProps == null ? void 0 : (_slotProps_requiredMark = slotProps.requiredMark) == null ? void 0 : _slotProps_requiredMark.className),
|
|
1566
|
+
children: "*"
|
|
1567
|
+
})
|
|
1568
|
+
]
|
|
1493
1569
|
}),
|
|
1494
1570
|
resolvedHelperText && jsx("p", {
|
|
1495
1571
|
className: cn(resolvedError ? v.errorText() : v.helperText(), resolvedError ? [
|
|
@@ -1540,6 +1616,9 @@ const tooltipVariants = tv({
|
|
|
1540
1616
|
'data-[state=unchecked]:translate-x-0'
|
|
1541
1617
|
],
|
|
1542
1618
|
label: 'select-none cursor-pointer text-neutral-900',
|
|
1619
|
+
// BUG 21: `*` marker after the label, same tokens as
|
|
1620
|
+
// `checkboxVariants.requiredMark` for visual parity.
|
|
1621
|
+
requiredMark: 'text-danger-500 ml-0.5',
|
|
1543
1622
|
helperText: 'text-sm text-neutral-600 mt-1',
|
|
1544
1623
|
errorText: 'text-sm text-danger-600 mt-1'
|
|
1545
1624
|
},
|
|
@@ -1586,19 +1665,45 @@ const tooltipVariants = tv({
|
|
|
1586
1665
|
* translate-x distance so the knob lands flush at each end.
|
|
1587
1666
|
*/ function Switch(props) {
|
|
1588
1667
|
var _ref;
|
|
1589
|
-
var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_control, _slotProps_control, _themeSlotProps_thumb, _slotProps_thumb, _themeSlotProps_label, _slotProps_label, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
|
|
1668
|
+
var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_control, _slotProps_control, _themeSlotProps_thumb, _slotProps_thumb, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
|
|
1590
1669
|
const themeDefaults = useComponentDefaults('Switch');
|
|
1591
1670
|
const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
|
|
1592
1671
|
const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
|
|
1593
1672
|
// Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
|
|
1594
1673
|
const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
|
|
1595
|
-
const { name, rules, visibleWhen, label, helperText, error, access, size, disabled, checked, defaultChecked, onCheckedChange, sx, slotProps } = merged;
|
|
1674
|
+
const { name, rules, visibleWhen, label, helperText, error, access, size, disabled, required, checked, defaultChecked, onCheckedChange, sx, slotProps } = merged;
|
|
1596
1675
|
const bridge = useContext(DashFormContext);
|
|
1597
1676
|
const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
|
|
1598
1677
|
// Reactive snapshot — see Checkbox.tsx for the same rationale.
|
|
1599
1678
|
const fieldMeta = useDashFieldMeta(name);
|
|
1600
1679
|
const accessState = useAccessState(access);
|
|
1601
1680
|
const controlId = useId();
|
|
1681
|
+
// BUG 21 v2: local ref + post-mount `aria-required` setter — same
|
|
1682
|
+
// pattern as Checkbox.tsx. Radix.Switch.Root reads `required` prop
|
|
1683
|
+
// into context but the JSX `aria-required` attribute does not reach
|
|
1684
|
+
// the browser DOM in production builds. Setting via ref is
|
|
1685
|
+
// Radix-version-agnostic. See README-BUG.md § BUG 21.
|
|
1686
|
+
const buttonRef = useRef(null);
|
|
1687
|
+
const setControlRef = (node)=>{
|
|
1688
|
+
buttonRef.current = node;
|
|
1689
|
+
const regRef = registration == null ? void 0 : registration.ref;
|
|
1690
|
+
if (typeof regRef === 'function') {
|
|
1691
|
+
regRef(node);
|
|
1692
|
+
} else if (regRef && typeof regRef === 'object') {
|
|
1693
|
+
regRef.current = node;
|
|
1694
|
+
}
|
|
1695
|
+
};
|
|
1696
|
+
useEffect(()=>{
|
|
1697
|
+
const node = buttonRef.current;
|
|
1698
|
+
if (!node) return;
|
|
1699
|
+
if (required) {
|
|
1700
|
+
node.setAttribute('aria-required', 'true');
|
|
1701
|
+
} else {
|
|
1702
|
+
node.removeAttribute('aria-required');
|
|
1703
|
+
}
|
|
1704
|
+
}, [
|
|
1705
|
+
required
|
|
1706
|
+
]);
|
|
1602
1707
|
// StrictMode-safe unregister-on-unmount
|
|
1603
1708
|
const unregisterRef = useRef({
|
|
1604
1709
|
bridge,
|
|
@@ -1614,6 +1719,11 @@ const tooltipVariants = tv({
|
|
|
1614
1719
|
return ()=>{
|
|
1615
1720
|
isMountedRef.current = false;
|
|
1616
1721
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
1722
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
1723
|
+
// is configured to forget unmounted fields. Default `false`
|
|
1724
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
1725
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
1726
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
1617
1727
|
queueMicrotask(()=>{
|
|
1618
1728
|
var _cap_unregister;
|
|
1619
1729
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -1689,9 +1799,13 @@ const tooltipVariants = tv({
|
|
|
1689
1799
|
name: name
|
|
1690
1800
|
}, radixStateProps, {
|
|
1691
1801
|
disabled: effectiveDisabled,
|
|
1802
|
+
required: required,
|
|
1692
1803
|
onCheckedChange: handleCheckedChange,
|
|
1693
1804
|
onBlur: handleBlur,
|
|
1694
|
-
|
|
1805
|
+
// BUG 21 v2: `aria-required` set via `setControlRef` + `useEffect`
|
|
1806
|
+
// above, not via JSX prop. See Checkbox.tsx for the same pattern
|
|
1807
|
+
// and the reasoning.
|
|
1808
|
+
ref: setControlRef,
|
|
1695
1809
|
className: cn(v.control(), themeSlotProps == null ? void 0 : (_themeSlotProps_control = themeSlotProps.control) == null ? void 0 : _themeSlotProps_control.className, slotProps == null ? void 0 : (_slotProps_control = slotProps.control) == null ? void 0 : _slotProps_control.className),
|
|
1696
1810
|
children: jsx(RadixSwitch.Thumb, {
|
|
1697
1811
|
className: cn(v.thumb(), themeSlotProps == null ? void 0 : (_themeSlotProps_thumb = themeSlotProps.thumb) == null ? void 0 : _themeSlotProps_thumb.className, slotProps == null ? void 0 : (_slotProps_thumb = slotProps.thumb) == null ? void 0 : _slotProps_thumb.className)
|
|
@@ -1700,10 +1814,17 @@ const tooltipVariants = tv({
|
|
|
1700
1814
|
jsxs("div", {
|
|
1701
1815
|
className: "flex flex-col",
|
|
1702
1816
|
children: [
|
|
1703
|
-
label &&
|
|
1817
|
+
label && jsxs("label", {
|
|
1704
1818
|
htmlFor: controlId,
|
|
1705
1819
|
className: cn(v.label(), themeSlotProps == null ? void 0 : (_themeSlotProps_label = themeSlotProps.label) == null ? void 0 : _themeSlotProps_label.className, slotProps == null ? void 0 : (_slotProps_label = slotProps.label) == null ? void 0 : _slotProps_label.className),
|
|
1706
|
-
children:
|
|
1820
|
+
children: [
|
|
1821
|
+
renderLabelWithTooltip(label, tooltipConfig),
|
|
1822
|
+
required && jsx("span", {
|
|
1823
|
+
"aria-hidden": "true",
|
|
1824
|
+
className: cn(v.requiredMark(), themeSlotProps == null ? void 0 : (_themeSlotProps_requiredMark = themeSlotProps.requiredMark) == null ? void 0 : _themeSlotProps_requiredMark.className, slotProps == null ? void 0 : (_slotProps_requiredMark = slotProps.requiredMark) == null ? void 0 : _slotProps_requiredMark.className),
|
|
1825
|
+
children: "*"
|
|
1826
|
+
})
|
|
1827
|
+
]
|
|
1707
1828
|
}),
|
|
1708
1829
|
resolvedHelperText && jsx("p", {
|
|
1709
1830
|
className: cn(resolvedError ? v.errorText() : v.helperText(), resolvedError ? [
|
|
@@ -1754,6 +1875,11 @@ const tooltipVariants = tv({
|
|
|
1754
1875
|
'rounded-full border bg-neutral-50',
|
|
1755
1876
|
'border-neutral-300',
|
|
1756
1877
|
'transition-colors',
|
|
1878
|
+
// Radix.RadioGroup.Item renders a `<button>`, whose UA cursor
|
|
1879
|
+
// is `default`. optionLabel already reads `cursor-pointer`, but
|
|
1880
|
+
// the control itself needs the same affordance when the pointer
|
|
1881
|
+
// sits directly on the circle. See README-BUG § BUG 13.
|
|
1882
|
+
'cursor-pointer',
|
|
1757
1883
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-primary-500',
|
|
1758
1884
|
'data-[state=checked]:border-primary-500',
|
|
1759
1885
|
'data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed'
|
|
@@ -1879,6 +2005,11 @@ const tooltipVariants = tv({
|
|
|
1879
2005
|
return ()=>{
|
|
1880
2006
|
isMountedRef.current = false;
|
|
1881
2007
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
2008
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
2009
|
+
// is configured to forget unmounted fields. Default `false`
|
|
2010
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
2011
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
2012
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
1882
2013
|
queueMicrotask(()=>{
|
|
1883
2014
|
var _cap_unregister;
|
|
1884
2015
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -1889,6 +2020,15 @@ const tooltipVariants = tv({
|
|
|
1889
2020
|
// must run before the visibility early-returns so hook ordering is
|
|
1890
2021
|
// consistent across renders. See #113 / useStandaloneFieldWarning.
|
|
1891
2022
|
useStandaloneFieldWarning('RadioGroup', name, Boolean(bridge == null ? void 0 : bridge.register), explicitValue, onValueChange);
|
|
2023
|
+
// Mirror of the above: warn if under a <DashFormProvider> AND the
|
|
2024
|
+
// consumer passed controlled-mode props (silently ignored in form
|
|
2025
|
+
// mode). See BUG 2 in libs/dashforge/README-BUG.md. Zero cost in
|
|
2026
|
+
// production.
|
|
2027
|
+
useWarnIfControlledInFormMode('RadioGroup', name, {
|
|
2028
|
+
value: explicitValue,
|
|
2029
|
+
defaultValue,
|
|
2030
|
+
onValueChange
|
|
2031
|
+
});
|
|
1892
2032
|
// ───── Early returns ─────
|
|
1893
2033
|
if (!isVisible) return null;
|
|
1894
2034
|
if (!groupAccessState.visible) return null;
|
|
@@ -2136,7 +2276,11 @@ const tooltipVariants = tv({
|
|
|
2136
2276
|
const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
|
|
2137
2277
|
// Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
|
|
2138
2278
|
const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
|
|
2139
|
-
const { name, rules, visibleWhen, layout = 'stacked', size, resize, label, helperText, required, error, disabled, fullWidth, access, sx, slotProps, placeholder, rows = 3, onChange: userOnChange, onBlur: userOnBlur, value: userValue, defaultValue
|
|
2279
|
+
const { name, rules, visibleWhen, layout = 'stacked', size, resize, label, helperText, required, error, disabled, fullWidth, access, sx, slotProps, placeholder, rows = 3, onChange: userOnChange, onBlur: userOnBlur, value: userValue, defaultValue, // Consume `tooltip` here so it does not leak into `...rest` and
|
|
2280
|
+
// end up as an unknown HTML attribute on the rendered <textarea>
|
|
2281
|
+
// (React 19 forwards lowercase unknown attrs silently). See
|
|
2282
|
+
// README-BUG § BUG 9.
|
|
2283
|
+
tooltip: _tooltip } = merged, rest = _object_without_properties_loose(merged, [
|
|
2140
2284
|
"name",
|
|
2141
2285
|
"rules",
|
|
2142
2286
|
"visibleWhen",
|
|
@@ -2157,7 +2301,8 @@ const tooltipVariants = tv({
|
|
|
2157
2301
|
"onChange",
|
|
2158
2302
|
"onBlur",
|
|
2159
2303
|
"value",
|
|
2160
|
-
"defaultValue"
|
|
2304
|
+
"defaultValue",
|
|
2305
|
+
"tooltip"
|
|
2161
2306
|
]);
|
|
2162
2307
|
// ───── Hooks (unconditional) ─────
|
|
2163
2308
|
const bridge = useContext(DashFormContext);
|
|
@@ -2181,6 +2326,11 @@ const tooltipVariants = tv({
|
|
|
2181
2326
|
return ()=>{
|
|
2182
2327
|
isMountedRef.current = false;
|
|
2183
2328
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
2329
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
2330
|
+
// is configured to forget unmounted fields. Default `false`
|
|
2331
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
2332
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
2333
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
2184
2334
|
queueMicrotask(()=>{
|
|
2185
2335
|
var _cap_unregister;
|
|
2186
2336
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -2436,7 +2586,11 @@ const tooltipVariants = tv({
|
|
|
2436
2586
|
const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
|
|
2437
2587
|
// Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
|
|
2438
2588
|
const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
|
|
2439
|
-
const { name, rules, visibleWhen, layout = 'stacked', size, label, helperText, required, error, disabled, fullWidth, access, sx, slotProps, placeholder, min, max, step = 1, showStepper = false, onChange: userOnChange, onBlur: userOnBlur, value: userValue, defaultValue
|
|
2589
|
+
const { name, rules, visibleWhen, layout = 'stacked', size, label, helperText, required, error, disabled, fullWidth, access, sx, slotProps, placeholder, min, max, step = 1, showStepper = false, onChange: userOnChange, onBlur: userOnBlur, value: userValue, defaultValue, // Consume `tooltip` here so it does not leak into `...rest` and
|
|
2590
|
+
// end up as an unknown HTML attribute on the rendered <input>
|
|
2591
|
+
// (React 19 forwards lowercase unknown attrs silently). See
|
|
2592
|
+
// README-BUG § BUG 9.
|
|
2593
|
+
tooltip: _tooltip } = merged, rest = _object_without_properties_loose(merged, [
|
|
2440
2594
|
"name",
|
|
2441
2595
|
"rules",
|
|
2442
2596
|
"visibleWhen",
|
|
@@ -2459,7 +2613,8 @@ const tooltipVariants = tv({
|
|
|
2459
2613
|
"onChange",
|
|
2460
2614
|
"onBlur",
|
|
2461
2615
|
"value",
|
|
2462
|
-
"defaultValue"
|
|
2616
|
+
"defaultValue",
|
|
2617
|
+
"tooltip"
|
|
2463
2618
|
]);
|
|
2464
2619
|
// ───── Hooks (unconditional) ─────
|
|
2465
2620
|
const bridge = useContext(DashFormContext);
|
|
@@ -2498,6 +2653,11 @@ const tooltipVariants = tv({
|
|
|
2498
2653
|
return ()=>{
|
|
2499
2654
|
isMountedRef.current = false;
|
|
2500
2655
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
2656
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
2657
|
+
// is configured to forget unmounted fields. Default `false`
|
|
2658
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
2659
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
2660
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
2501
2661
|
queueMicrotask(()=>{
|
|
2502
2662
|
var _cap_unregister;
|
|
2503
2663
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -2792,6 +2952,11 @@ const tooltipVariants = tv({
|
|
|
2792
2952
|
return ()=>{
|
|
2793
2953
|
isMountedRef.current = false;
|
|
2794
2954
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
2955
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
2956
|
+
// is configured to forget unmounted fields. Default `false`
|
|
2957
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
2958
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
2959
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
2795
2960
|
queueMicrotask(()=>{
|
|
2796
2961
|
var _cap_unregister;
|
|
2797
2962
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -3185,14 +3350,51 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3185
3350
|
* (case-insensitive). Multi-select, free-solo text, generic option
|
|
3186
3351
|
* shapes, and async runtime options (`optionsFromFieldData`) are
|
|
3187
3352
|
* deferred to F5-A-bis.
|
|
3188
|
-
*/
|
|
3189
|
-
|
|
3353
|
+
*/ /**
|
|
3354
|
+
* Is this event ours?
|
|
3355
|
+
*
|
|
3356
|
+
* "Ours" is the root PLUS the listbox, and the second half is the whole
|
|
3357
|
+
* point. Since the listbox was portaled to `document.body` it is no
|
|
3358
|
+
* longer inside the component's DOM subtree, so `root.contains(target)`
|
|
3359
|
+
* answers FALSE for the options themselves — and two separate pieces of
|
|
3360
|
+
* code used that question to mean "is this mine?":
|
|
3361
|
+
*
|
|
3362
|
+
* the click-outside handler, which closed the popover and reset the
|
|
3363
|
+
* input on a mousedown over an option, before the option's own
|
|
3364
|
+
* `onClick` could run (mouse selection broken, keyboard fine);
|
|
3365
|
+
*
|
|
3366
|
+
* Radix's `DismissableLayer`, which dismissed on the focus that the
|
|
3367
|
+
* combobox input itself receives.
|
|
3368
|
+
*
|
|
3369
|
+
* Both are BUG 7 and BUG 8 in `libs/dashforge/README-BUG.md`. The
|
|
3370
|
+
* predicate exists so the rule has ONE definition and a test can pin
|
|
3371
|
+
* it — `rootRef` does not span the whole widget any more, and anything
|
|
3372
|
+
* here that reasons about containment has to say so out loud.
|
|
3373
|
+
*/ function isWithinCombobox(target, root, listbox) {
|
|
3374
|
+
if (!target) return false;
|
|
3375
|
+
return Boolean(root == null ? void 0 : root.contains(target)) || Boolean(listbox == null ? void 0 : listbox.contains(target));
|
|
3376
|
+
}
|
|
3377
|
+
function Autocomplete(props) {
|
|
3378
|
+
var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_inputWrapper, _slotProps_inputWrapper, _themeSlotProps_chipsList, _slotProps_chipsList, _themeSlotProps_input, _slotProps_input, _themeSlotProps_clearButton, _slotProps_clearButton, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_input1, _slotProps_input1, _themeSlotProps_clearButton1, _slotProps_clearButton1, _themeSlotProps_trigger1, _slotProps_trigger1, _themeSlotProps_popover, _slotProps_popover, _themeSlotProps_listBox, _slotProps_listBox, _themeSlotProps_emptyState, _slotProps_emptyState, _themeSlotProps_emptyState1, _slotProps_emptyState1, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
|
|
3190
3379
|
const themeDefaults = useComponentDefaults('Autocomplete');
|
|
3191
3380
|
const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
|
|
3192
3381
|
const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
|
|
3193
3382
|
// Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
|
|
3194
3383
|
const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
|
|
3195
3384
|
const { name, rules, options, visibleWhen, layout = 'stacked', size, label, helperText, required, error, disabled, fullWidth, access, sx, slotProps, placeholder, value: explicitValue, defaultValue, onValueChange, emptyMessage = 'No matching options', getOptionValue = defaultGetOptionValue, getOptionLabel = defaultGetOptionLabel, getOptionDisabled = defaultGetOptionDisabled, getOptionKey, multiple, freeSolo, loadOptions, loadDebounceMs = 250, loadingMessage = 'Loading…' } = merged;
|
|
3385
|
+
// Dev-mode warning: if this Autocomplete is running under a
|
|
3386
|
+
// <DashFormProvider> AND the consumer has passed any of the
|
|
3387
|
+
// controlled / uncontrolled value props, warn once per
|
|
3388
|
+
// (component, name, prop) that those props are silently ignored
|
|
3389
|
+
// in form mode. See BUG 2 in libs/dashforge/README-BUG.md — the
|
|
3390
|
+
// type-side split can only catch the `rules + defaultValue` combo
|
|
3391
|
+
// at compile time; this runtime rete covers the common case where
|
|
3392
|
+
// consumers pass no `rules`. Zero cost in production.
|
|
3393
|
+
useWarnIfControlledInFormMode('Autocomplete', name, {
|
|
3394
|
+
value: explicitValue,
|
|
3395
|
+
defaultValue,
|
|
3396
|
+
onValueChange
|
|
3397
|
+
});
|
|
3196
3398
|
const isMulti = Boolean(multiple);
|
|
3197
3399
|
// Default the React-key accessor to `getOptionValue` (typical case:
|
|
3198
3400
|
// value is unique). Consumers override only when distinct option
|
|
@@ -3231,6 +3433,11 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3231
3433
|
return ()=>{
|
|
3232
3434
|
isMountedRef.current = false;
|
|
3233
3435
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
3436
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
3437
|
+
// is configured to forget unmounted fields. Default `false`
|
|
3438
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
3439
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
3440
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
3234
3441
|
queueMicrotask(()=>{
|
|
3235
3442
|
var _cap_unregister;
|
|
3236
3443
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -3510,7 +3717,9 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3510
3717
|
const handlePointerDown = (event)=>{
|
|
3511
3718
|
const root = rootRef.current;
|
|
3512
3719
|
if (!root) return;
|
|
3513
|
-
|
|
3720
|
+
// The listbox counts as ours even though it is portaled out of
|
|
3721
|
+
// the root — see `isWithinCombobox`.
|
|
3722
|
+
if (isWithinCombobox(event.target, root, listboxRef.current)) return;
|
|
3514
3723
|
closePopover();
|
|
3515
3724
|
// Multi mode: just clear the filter query (chips remain).
|
|
3516
3725
|
// Single + free-solo: commit the typed text as the value (the
|
|
@@ -3789,127 +3998,254 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
|
|
|
3789
3998
|
children: "*"
|
|
3790
3999
|
}))
|
|
3791
4000
|
}),
|
|
3792
|
-
jsxs(
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
4001
|
+
jsxs(RadixPopover.Root, {
|
|
4002
|
+
open: isOpen,
|
|
4003
|
+
onOpenChange: (next)=>{
|
|
4004
|
+
if (!next) setIsOpen(false);
|
|
4005
|
+
},
|
|
4006
|
+
modal: false,
|
|
3796
4007
|
children: [
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
children:
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
4008
|
+
jsx(RadixPopover.Anchor, {
|
|
4009
|
+
asChild: true,
|
|
4010
|
+
children: jsxs("div", {
|
|
4011
|
+
className: cn(v.inputWrapper(), // Multi mode: let chips wrap and replace the fixed `h-*`
|
|
4012
|
+
// with `min-h-*` so the wrapper grows with selections.
|
|
4013
|
+
isMulti && 'flex-wrap min-h-[var(--ac-min-h,2.5rem)] !h-auto py-1', themeSlotProps == null ? void 0 : (_themeSlotProps_inputWrapper = themeSlotProps.inputWrapper) == null ? void 0 : _themeSlotProps_inputWrapper.className, slotProps == null ? void 0 : (_slotProps_inputWrapper = slotProps.inputWrapper) == null ? void 0 : _slotProps_inputWrapper.className),
|
|
4014
|
+
children: [
|
|
4015
|
+
isMulti && selectedKeys.size > 0 && jsx("div", {
|
|
4016
|
+
className: cn(v.chipsList(), themeSlotProps == null ? void 0 : (_themeSlotProps_chipsList = themeSlotProps.chipsList) == null ? void 0 : _themeSlotProps_chipsList.className, slotProps == null ? void 0 : (_slotProps_chipsList = slotProps.chipsList) == null ? void 0 : _slotProps_chipsList.className),
|
|
4017
|
+
children: Array.from(selectedKeys).map((key)=>{
|
|
4018
|
+
var _labelAsString;
|
|
4019
|
+
var _themeSlotProps_chip, _slotProps_chip, _themeSlotProps_chipRemove, _slotProps_chipRemove;
|
|
4020
|
+
const opt = options.find((o)=>getOptionValue(o) === key);
|
|
4021
|
+
const labelNode = opt ? getOptionLabel(opt) : key;
|
|
4022
|
+
const labelText = opt ? (_labelAsString = labelAsString(opt)) != null ? _labelAsString : key : key;
|
|
4023
|
+
return jsxs("span", {
|
|
4024
|
+
className: cn(v.chip(), themeSlotProps == null ? void 0 : (_themeSlotProps_chip = themeSlotProps.chip) == null ? void 0 : _themeSlotProps_chip.className, slotProps == null ? void 0 : (_slotProps_chip = slotProps.chip) == null ? void 0 : _slotProps_chip.className),
|
|
4025
|
+
"data-chip-key": key,
|
|
4026
|
+
children: [
|
|
4027
|
+
labelNode,
|
|
4028
|
+
!effectiveDisabled && !effectiveReadOnly && jsx("button", {
|
|
4029
|
+
type: "button",
|
|
4030
|
+
onMouseDown: preventBlur,
|
|
4031
|
+
onClick: ()=>{
|
|
4032
|
+
var _inputRef_current;
|
|
4033
|
+
commitSelection(key); // toggle removes
|
|
4034
|
+
(_inputRef_current = inputRef.current) == null ? void 0 : _inputRef_current.focus();
|
|
4035
|
+
},
|
|
4036
|
+
"aria-label": `Remove ${labelText}`,
|
|
4037
|
+
tabIndex: -1,
|
|
4038
|
+
className: cn(v.chipRemove(), themeSlotProps == null ? void 0 : (_themeSlotProps_chipRemove = themeSlotProps.chipRemove) == null ? void 0 : _themeSlotProps_chipRemove.className, slotProps == null ? void 0 : (_slotProps_chipRemove = slotProps.chipRemove) == null ? void 0 : _slotProps_chipRemove.className),
|
|
4039
|
+
children: jsx(CloseIcon, {})
|
|
4040
|
+
})
|
|
4041
|
+
]
|
|
4042
|
+
}, key);
|
|
3822
4043
|
})
|
|
3823
|
-
|
|
3824
|
-
|
|
4044
|
+
}),
|
|
4045
|
+
isMulti ? jsxs("div", {
|
|
4046
|
+
className: cn(// `min-w-0` on the controls group (and on the input
|
|
4047
|
+
// below) so the input can shrink when chips have taken
|
|
4048
|
+
// most of the outer wrapper's width. Without it, the
|
|
4049
|
+
// group's intrinsic min-content-size (input floor +
|
|
4050
|
+
// shrink-0 buttons) pushed the chevron past the right
|
|
4051
|
+
// edge by ~6px, which turned into a horizontal scrollbar
|
|
4052
|
+
// inside a `<Dialog>` (whose body is `overflow-y-auto`
|
|
4053
|
+
// and so computes `overflow-x: auto`). See BUG 5 in
|
|
4054
|
+
// README-BUG.md — the regression from the BUG 3 fix.
|
|
4055
|
+
'flex items-center flex-nowrap flex-1 min-w-0', // The gap keeps the clear × visually separated from the
|
|
4056
|
+
// chevron when both are present.
|
|
4057
|
+
hasAnySelection && !effectiveDisabled && !effectiveReadOnly && 'gap-0.5'),
|
|
4058
|
+
children: [
|
|
4059
|
+
jsx("input", {
|
|
4060
|
+
ref: inputRef,
|
|
4061
|
+
id: `${baseId}-input`,
|
|
4062
|
+
name: name,
|
|
4063
|
+
type: "text",
|
|
4064
|
+
role: "combobox",
|
|
4065
|
+
"aria-autocomplete": "list",
|
|
4066
|
+
"aria-expanded": isOpen,
|
|
4067
|
+
"aria-controls": isOpen ? listboxId : undefined,
|
|
4068
|
+
"aria-activedescendant": activeOptionId,
|
|
4069
|
+
"aria-labelledby": label ? labelId : undefined,
|
|
4070
|
+
"aria-describedby": resolvedHelperText ? helperId : undefined,
|
|
4071
|
+
"aria-invalid": resolvedError ? true : undefined,
|
|
4072
|
+
"aria-required": required ? true : undefined,
|
|
4073
|
+
placeholder: // In multi mode hide the placeholder once the user has
|
|
4074
|
+
// at least one chip — chips already convey "something
|
|
4075
|
+
// is selected".
|
|
4076
|
+
selectedKeys.size > 0 ? undefined : placeholder,
|
|
4077
|
+
value: displayInputValue,
|
|
4078
|
+
onChange: handleInputChange,
|
|
4079
|
+
onKeyDown: handleKeyDown,
|
|
4080
|
+
onFocus: openPopover,
|
|
4081
|
+
onBlur: handleBlur,
|
|
4082
|
+
disabled: effectiveDisabled,
|
|
4083
|
+
readOnly: effectiveReadOnly,
|
|
4084
|
+
autoComplete: "off",
|
|
4085
|
+
// `flex-1` inside the non-wrap controls row: the input
|
|
4086
|
+
// expands to consume whatever the controls-row has left
|
|
4087
|
+
// after the two buttons. `basis-24` is the *preferred*
|
|
4088
|
+
// starting size; `min-w-0` lets the input shrink below
|
|
4089
|
+
// that when chips have consumed most of the outer
|
|
4090
|
+
// wrapper — same BUG 5 rationale as the group's
|
|
4091
|
+
// `min-w-0`. Buttons stay `shrink-0` and always
|
|
4092
|
+
// reachable; the input becomes narrow but still usable.
|
|
4093
|
+
className: cn(v.input(), 'min-w-0 flex-1 basis-24', themeSlotProps == null ? void 0 : (_themeSlotProps_input = themeSlotProps.input) == null ? void 0 : _themeSlotProps_input.className, slotProps == null ? void 0 : (_slotProps_input = slotProps.input) == null ? void 0 : _slotProps_input.className)
|
|
4094
|
+
}),
|
|
4095
|
+
hasAnySelection && !effectiveDisabled && !effectiveReadOnly && jsx("button", {
|
|
4096
|
+
type: "button",
|
|
4097
|
+
onMouseDown: preventBlur,
|
|
4098
|
+
onClick: handleClearClick,
|
|
4099
|
+
"aria-label": "Clear selection",
|
|
4100
|
+
tabIndex: -1,
|
|
4101
|
+
className: cn(v.clearButton(), themeSlotProps == null ? void 0 : (_themeSlotProps_clearButton = themeSlotProps.clearButton) == null ? void 0 : _themeSlotProps_clearButton.className, slotProps == null ? void 0 : (_slotProps_clearButton = slotProps.clearButton) == null ? void 0 : _slotProps_clearButton.className),
|
|
4102
|
+
children: jsx(CloseIcon, {})
|
|
4103
|
+
}),
|
|
4104
|
+
jsx("button", {
|
|
4105
|
+
type: "button",
|
|
4106
|
+
onMouseDown: preventBlur,
|
|
4107
|
+
onClick: handleTriggerClick,
|
|
4108
|
+
"aria-label": "Open",
|
|
4109
|
+
"aria-haspopup": "listbox",
|
|
4110
|
+
"aria-expanded": isOpen,
|
|
4111
|
+
tabIndex: -1,
|
|
4112
|
+
disabled: effectiveDisabled,
|
|
4113
|
+
className: cn(v.trigger(), themeSlotProps == null ? void 0 : (_themeSlotProps_trigger = themeSlotProps.trigger) == null ? void 0 : _themeSlotProps_trigger.className, slotProps == null ? void 0 : (_slotProps_trigger = slotProps.trigger) == null ? void 0 : _slotProps_trigger.className),
|
|
4114
|
+
children: jsx(ChevronDownIcon$1, {})
|
|
4115
|
+
})
|
|
4116
|
+
]
|
|
4117
|
+
}) : jsxs(Fragment, {
|
|
4118
|
+
children: [
|
|
4119
|
+
jsx("input", {
|
|
4120
|
+
ref: inputRef,
|
|
4121
|
+
id: `${baseId}-input`,
|
|
4122
|
+
name: name,
|
|
4123
|
+
type: "text",
|
|
4124
|
+
role: "combobox",
|
|
4125
|
+
"aria-autocomplete": "list",
|
|
4126
|
+
"aria-expanded": isOpen,
|
|
4127
|
+
"aria-controls": isOpen ? listboxId : undefined,
|
|
4128
|
+
"aria-activedescendant": activeOptionId,
|
|
4129
|
+
"aria-labelledby": label ? labelId : undefined,
|
|
4130
|
+
"aria-describedby": resolvedHelperText ? helperId : undefined,
|
|
4131
|
+
"aria-invalid": resolvedError ? true : undefined,
|
|
4132
|
+
"aria-required": required ? true : undefined,
|
|
4133
|
+
placeholder: placeholder,
|
|
4134
|
+
value: displayInputValue,
|
|
4135
|
+
onChange: handleInputChange,
|
|
4136
|
+
onKeyDown: handleKeyDown,
|
|
4137
|
+
onFocus: openPopover,
|
|
4138
|
+
onBlur: handleBlur,
|
|
4139
|
+
disabled: effectiveDisabled,
|
|
4140
|
+
readOnly: effectiveReadOnly,
|
|
4141
|
+
autoComplete: "off",
|
|
4142
|
+
className: cn(v.input(), themeSlotProps == null ? void 0 : (_themeSlotProps_input1 = themeSlotProps.input) == null ? void 0 : _themeSlotProps_input1.className, slotProps == null ? void 0 : (_slotProps_input1 = slotProps.input) == null ? void 0 : _slotProps_input1.className)
|
|
4143
|
+
}),
|
|
4144
|
+
hasAnySelection && !effectiveDisabled && !effectiveReadOnly && jsx("button", {
|
|
4145
|
+
type: "button",
|
|
4146
|
+
onMouseDown: preventBlur,
|
|
4147
|
+
onClick: handleClearClick,
|
|
4148
|
+
"aria-label": "Clear selection",
|
|
4149
|
+
tabIndex: -1,
|
|
4150
|
+
className: cn(v.clearButton(), themeSlotProps == null ? void 0 : (_themeSlotProps_clearButton1 = themeSlotProps.clearButton) == null ? void 0 : _themeSlotProps_clearButton1.className, slotProps == null ? void 0 : (_slotProps_clearButton1 = slotProps.clearButton) == null ? void 0 : _slotProps_clearButton1.className),
|
|
4151
|
+
children: jsx(CloseIcon, {})
|
|
4152
|
+
}),
|
|
4153
|
+
jsx("button", {
|
|
4154
|
+
type: "button",
|
|
4155
|
+
onMouseDown: preventBlur,
|
|
4156
|
+
onClick: handleTriggerClick,
|
|
4157
|
+
"aria-label": "Open",
|
|
4158
|
+
"aria-haspopup": "listbox",
|
|
4159
|
+
"aria-expanded": isOpen,
|
|
4160
|
+
tabIndex: -1,
|
|
4161
|
+
disabled: effectiveDisabled,
|
|
4162
|
+
className: cn(v.trigger(), themeSlotProps == null ? void 0 : (_themeSlotProps_trigger1 = themeSlotProps.trigger) == null ? void 0 : _themeSlotProps_trigger1.className, slotProps == null ? void 0 : (_slotProps_trigger1 = slotProps.trigger) == null ? void 0 : _slotProps_trigger1.className),
|
|
4163
|
+
children: jsx(ChevronDownIcon$1, {})
|
|
4164
|
+
})
|
|
4165
|
+
]
|
|
4166
|
+
})
|
|
4167
|
+
]
|
|
3825
4168
|
})
|
|
3826
4169
|
}),
|
|
3827
|
-
jsx(
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
"data-disabled": optDisabled || undefined,
|
|
3907
|
-
onMouseDown: preventBlur,
|
|
3908
|
-
onClick: ()=>handleOptionClick(opt),
|
|
3909
|
-
onMouseEnter: ()=>setHighlightedIndex(idx),
|
|
3910
|
-
className: cn(v.listItem(), themeSlotProps == null ? void 0 : (_themeSlotProps_listItem = themeSlotProps.listItem) == null ? void 0 : _themeSlotProps_listItem.className, slotProps == null ? void 0 : (_slotProps_listItem = slotProps.listItem) == null ? void 0 : _slotProps_listItem.className),
|
|
3911
|
-
children: getOptionLabel(opt)
|
|
3912
|
-
}, resolveOptionKey(opt));
|
|
4170
|
+
isOpen && jsx(RadixPopover.Portal, {
|
|
4171
|
+
children: jsx(RadixPopover.Content, {
|
|
4172
|
+
asChild: true,
|
|
4173
|
+
side: "bottom",
|
|
4174
|
+
align: "start",
|
|
4175
|
+
sideOffset: 4,
|
|
4176
|
+
collisionPadding: 8,
|
|
4177
|
+
// Anchor width → listbox width (Radix exposes this CSS var).
|
|
4178
|
+
style: {
|
|
4179
|
+
width: 'var(--radix-popover-trigger-width)'
|
|
4180
|
+
},
|
|
4181
|
+
// Focus stays on the combobox input; Radix must not steal it.
|
|
4182
|
+
onOpenAutoFocus: (e)=>e.preventDefault(),
|
|
4183
|
+
onCloseAutoFocus: (e)=>e.preventDefault(),
|
|
4184
|
+
/*
|
|
4185
|
+
THE COMBOBOX INPUT IS NOT "OUTSIDE".
|
|
4186
|
+
|
|
4187
|
+
In a combobox the focused element is the `<input>`, and the
|
|
4188
|
+
input is the ANCHOR — it lives outside `Popover.Content`.
|
|
4189
|
+
Radix's `DismissableLayer` does not know that: it sees a
|
|
4190
|
+
`focusin` on an element outside the layer and dismisses.
|
|
4191
|
+
|
|
4192
|
+
The symptom is that the listbox opens on focus and closes
|
|
4193
|
+
about 10ms later, so the FIRST keystroke after clicking the
|
|
4194
|
+
field never shows suggestions and the field looks broken.
|
|
4195
|
+
Later keystrokes work, because they raise no new focus
|
|
4196
|
+
event — which makes it look intermittent rather than
|
|
4197
|
+
systematic.
|
|
4198
|
+
|
|
4199
|
+
Both handlers guard on the component root, which contains
|
|
4200
|
+
the input, the chevron and the clear button. Anything
|
|
4201
|
+
genuinely outside still dismisses, so click-away is
|
|
4202
|
+
unaffected.
|
|
4203
|
+
*/ onFocusOutside: (event)=>{
|
|
4204
|
+
if (isWithinCombobox(event.target, rootRef.current, listboxRef.current)) {
|
|
4205
|
+
event.preventDefault();
|
|
4206
|
+
}
|
|
4207
|
+
},
|
|
4208
|
+
onInteractOutside: (event)=>{
|
|
4209
|
+
if (isWithinCombobox(event.target, rootRef.current, listboxRef.current)) {
|
|
4210
|
+
event.preventDefault();
|
|
4211
|
+
}
|
|
4212
|
+
},
|
|
4213
|
+
children: jsx("ul", {
|
|
4214
|
+
ref: listboxRef,
|
|
4215
|
+
id: listboxId,
|
|
4216
|
+
role: "listbox",
|
|
4217
|
+
"aria-labelledby": label ? labelId : undefined,
|
|
4218
|
+
className: cn(v.popover(), v.listBox(), themeSlotProps == null ? void 0 : (_themeSlotProps_popover = themeSlotProps.popover) == null ? void 0 : _themeSlotProps_popover.className, slotProps == null ? void 0 : (_slotProps_popover = slotProps.popover) == null ? void 0 : _slotProps_popover.className, themeSlotProps == null ? void 0 : (_themeSlotProps_listBox = themeSlotProps.listBox) == null ? void 0 : _themeSlotProps_listBox.className, slotProps == null ? void 0 : (_slotProps_listBox = slotProps.listBox) == null ? void 0 : _slotProps_listBox.className),
|
|
4219
|
+
children: isLoading ? jsx("li", {
|
|
4220
|
+
className: cn(v.emptyState(), themeSlotProps == null ? void 0 : (_themeSlotProps_emptyState = themeSlotProps.emptyState) == null ? void 0 : _themeSlotProps_emptyState.className, slotProps == null ? void 0 : (_slotProps_emptyState = slotProps.emptyState) == null ? void 0 : _slotProps_emptyState.className),
|
|
4221
|
+
"aria-live": "polite",
|
|
4222
|
+
"aria-busy": "true",
|
|
4223
|
+
children: loadingMessage
|
|
4224
|
+
}) : filteredOptions.length === 0 ? jsx("li", {
|
|
4225
|
+
className: cn(v.emptyState(), themeSlotProps == null ? void 0 : (_themeSlotProps_emptyState1 = themeSlotProps.emptyState) == null ? void 0 : _themeSlotProps_emptyState1.className, slotProps == null ? void 0 : (_slotProps_emptyState1 = slotProps.emptyState) == null ? void 0 : _slotProps_emptyState1.className),
|
|
4226
|
+
children: emptyMessage
|
|
4227
|
+
}) : filteredOptions.map((opt, idx)=>{
|
|
4228
|
+
var _themeSlotProps_listItem, _slotProps_listItem;
|
|
4229
|
+
const optValue = getOptionValue(opt);
|
|
4230
|
+
const optDisabled = getOptionDisabled(opt);
|
|
4231
|
+
const isHighlighted = idx === highlightedIndex;
|
|
4232
|
+
const isSelected = selectedKeys.has(optValue);
|
|
4233
|
+
return jsx("li", {
|
|
4234
|
+
id: `${optionIdPrefix}${optValue}`,
|
|
4235
|
+
role: "option",
|
|
4236
|
+
"aria-selected": isSelected,
|
|
4237
|
+
"aria-disabled": optDisabled || undefined,
|
|
4238
|
+
"data-focused": isHighlighted || undefined,
|
|
4239
|
+
"data-selected": isSelected || undefined,
|
|
4240
|
+
"data-disabled": optDisabled || undefined,
|
|
4241
|
+
onMouseDown: preventBlur,
|
|
4242
|
+
onClick: ()=>handleOptionClick(opt),
|
|
4243
|
+
onMouseEnter: ()=>setHighlightedIndex(idx),
|
|
4244
|
+
className: cn(v.listItem(), themeSlotProps == null ? void 0 : (_themeSlotProps_listItem = themeSlotProps.listItem) == null ? void 0 : _themeSlotProps_listItem.className, slotProps == null ? void 0 : (_slotProps_listItem = slotProps.listItem) == null ? void 0 : _slotProps_listItem.className),
|
|
4245
|
+
children: getOptionLabel(opt)
|
|
4246
|
+
}, resolveOptionKey(opt));
|
|
4247
|
+
})
|
|
4248
|
+
})
|
|
3913
4249
|
})
|
|
3914
4250
|
})
|
|
3915
4251
|
]
|
|
@@ -4409,12 +4745,26 @@ function CalendarIcon$2() {
|
|
|
4409
4745
|
return ()=>{
|
|
4410
4746
|
isMountedRef.current = false;
|
|
4411
4747
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
4748
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
4749
|
+
// is configured to forget unmounted fields. Default `false`
|
|
4750
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
4751
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
4752
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
4412
4753
|
queueMicrotask(()=>{
|
|
4413
4754
|
var _cap_unregister;
|
|
4414
4755
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
4415
4756
|
});
|
|
4416
4757
|
};
|
|
4417
4758
|
}, []);
|
|
4759
|
+
// Dev-mode warning: if under <DashFormProvider> AND the consumer
|
|
4760
|
+
// passed controlled-mode props (silently ignored in form mode),
|
|
4761
|
+
// fire once per (component, name, prop). Zero cost in prod. See
|
|
4762
|
+
// BUG 2 in libs/dashforge/README-BUG.md.
|
|
4763
|
+
useWarnIfControlledInFormMode('DatePicker', name, {
|
|
4764
|
+
value: explicitValue,
|
|
4765
|
+
defaultValue,
|
|
4766
|
+
onChange
|
|
4767
|
+
});
|
|
4418
4768
|
// ───── Derived ─────
|
|
4419
4769
|
const effectiveDisabled = Boolean(disabled) || accessState.disabled;
|
|
4420
4770
|
const effectiveReadOnly = accessState.readonly;
|
|
@@ -4683,6 +5033,11 @@ function ClockIcon() {
|
|
|
4683
5033
|
return ()=>{
|
|
4684
5034
|
isMountedRef.current = false;
|
|
4685
5035
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
5036
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
5037
|
+
// is configured to forget unmounted fields. Default `false`
|
|
5038
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
5039
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
5040
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
4686
5041
|
queueMicrotask(()=>{
|
|
4687
5042
|
var _cap_unregister;
|
|
4688
5043
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -5020,6 +5375,11 @@ const DATETIME_PATTERN = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2})/;
|
|
|
5020
5375
|
return ()=>{
|
|
5021
5376
|
isMountedRef.current = false;
|
|
5022
5377
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
5378
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
5379
|
+
// is configured to forget unmounted fields. Default `false`
|
|
5380
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
5381
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
5382
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
5023
5383
|
queueMicrotask(()=>{
|
|
5024
5384
|
var _cap_unregister;
|
|
5025
5385
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -5584,6 +5944,11 @@ function formatRange(range, locale) {
|
|
|
5584
5944
|
return ()=>{
|
|
5585
5945
|
isMountedRef.current = false;
|
|
5586
5946
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
5947
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
5948
|
+
// is configured to forget unmounted fields. Default `false`
|
|
5949
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
5950
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
5951
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
5587
5952
|
queueMicrotask(()=>{
|
|
5588
5953
|
var _cap_unregister;
|
|
5589
5954
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -9728,6 +10093,11 @@ const DEFAULT_STEP = 1;
|
|
|
9728
10093
|
return ()=>{
|
|
9729
10094
|
isMountedRef.current = false;
|
|
9730
10095
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
10096
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
10097
|
+
// is configured to forget unmounted fields. Default `false`
|
|
10098
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
10099
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
10100
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
9731
10101
|
queueMicrotask(()=>{
|
|
9732
10102
|
var _cap_unregister;
|
|
9733
10103
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -10249,6 +10619,17 @@ Slider.displayName = 'Slider';
|
|
|
10249
10619
|
const helperId = `${controlId}-help`;
|
|
10250
10620
|
const isFormMode = Boolean(bridge == null ? void 0 : bridge.register);
|
|
10251
10621
|
useStandaloneFieldWarning('Select', name, isFormMode, userValue, onChange);
|
|
10622
|
+
// Mirror of useStandaloneFieldWarning: warn when running under a
|
|
10623
|
+
// <DashFormProvider> AND the consumer passed controlled-mode props
|
|
10624
|
+
// that are silently ignored in form mode. See BUG 2 in
|
|
10625
|
+
// libs/dashforge/README-BUG.md — closes the case the type-side
|
|
10626
|
+
// discriminant cannot see (React context is invisible at compile
|
|
10627
|
+
// time). Zero cost in production.
|
|
10628
|
+
useWarnIfControlledInFormMode('Select', name, {
|
|
10629
|
+
value: userValue,
|
|
10630
|
+
defaultValue,
|
|
10631
|
+
onChange
|
|
10632
|
+
});
|
|
10252
10633
|
// StrictMode-safe unregister
|
|
10253
10634
|
const unregisterRef = useRef({
|
|
10254
10635
|
bridge,
|
|
@@ -10264,6 +10645,11 @@ Slider.displayName = 'Slider';
|
|
|
10264
10645
|
return ()=>{
|
|
10265
10646
|
isMountedRef.current = false;
|
|
10266
10647
|
const { bridge: cap, name: capName } = unregisterRef.current;
|
|
10648
|
+
// BUG 22: only release bridge state on unmount if the form
|
|
10649
|
+
// is configured to forget unmounted fields. Default `false`
|
|
10650
|
+
// keeps values in RHF so <Stepper> / tab-swap patterns can
|
|
10651
|
+
// read earlier answers back on later steps. See README-BUG.md § BUG 22.
|
|
10652
|
+
if (!(cap == null ? void 0 : cap.shouldUnregister)) return;
|
|
10267
10653
|
queueMicrotask(()=>{
|
|
10268
10654
|
var _cap_unregister;
|
|
10269
10655
|
if (!isMountedRef.current) cap == null ? void 0 : (_cap_unregister = cap.unregister) == null ? void 0 : _cap_unregister.call(cap, capName);
|
|
@@ -15208,10 +15594,10 @@ VisuallyHidden.displayName = 'VisuallyHidden';
|
|
|
15208
15594
|
* `useConfirm()` from `ConfirmDialog`). Use `<Dialog>` when you want
|
|
15209
15595
|
* the control flow living in your component tree.
|
|
15210
15596
|
*/ function Dialog(props) {
|
|
15211
|
-
var _themeSlotProps_overlay, _slotProps_overlay, _themeSlotProps_content, _slotProps_content, _themeSlotProps_title, _slotProps_title, _themeSlotProps_description, _slotProps_description, _themeSlotProps_body, _slotProps_body, _themeSlotProps_closeButton, _slotProps_closeButton;
|
|
15597
|
+
var _themeSlotProps_overlay, _slotProps_overlay, _themeSlotProps_content, _slotProps_content, _themeSlotProps_title, _slotProps_title, _themeSlotProps_description, _slotProps_description, _themeSlotProps_body, _slotProps_body, _themeSlotProps_actions, _slotProps_actions, _themeSlotProps_closeButton, _slotProps_closeButton;
|
|
15212
15598
|
const themeDefaults = useComponentDefaults('Dialog');
|
|
15213
15599
|
const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
|
|
15214
|
-
const { open, onOpenChange, children, title, description, showCloseButton = true, disableBackdropClose = false, disableEscapeClose = false, size = 'md', sx, slotProps } = merged;
|
|
15600
|
+
const { open, onOpenChange, children, title, description, actions, showCloseButton = true, disableBackdropClose = false, disableEscapeClose = false, size = 'md', sx, slotProps } = merged;
|
|
15215
15601
|
// Theme-level slotProps (Option C Track B). Theme classes go first,
|
|
15216
15602
|
// instance classes second, so `tailwind-merge` picks the instance
|
|
15217
15603
|
// where they collide.
|
|
@@ -15251,6 +15637,10 @@ VisuallyHidden.displayName = 'VisuallyHidden';
|
|
|
15251
15637
|
className: cn(v.body(), themeSlotProps == null ? void 0 : (_themeSlotProps_body = themeSlotProps.body) == null ? void 0 : _themeSlotProps_body.className, slotProps == null ? void 0 : (_slotProps_body = slotProps.body) == null ? void 0 : _slotProps_body.className),
|
|
15252
15638
|
children: children
|
|
15253
15639
|
}),
|
|
15640
|
+
actions != null && jsx("div", {
|
|
15641
|
+
className: cn(v.actions(), themeSlotProps == null ? void 0 : (_themeSlotProps_actions = themeSlotProps.actions) == null ? void 0 : _themeSlotProps_actions.className, slotProps == null ? void 0 : (_slotProps_actions = slotProps.actions) == null ? void 0 : _slotProps_actions.className),
|
|
15642
|
+
children: actions
|
|
15643
|
+
}),
|
|
15254
15644
|
showCloseButton && jsx(RadixDialog.Close, {
|
|
15255
15645
|
"aria-label": "Close",
|
|
15256
15646
|
className: cn(v.closeButton(), themeSlotProps == null ? void 0 : (_themeSlotProps_closeButton = themeSlotProps.closeButton) == null ? void 0 : _themeSlotProps_closeButton.className, slotProps == null ? void 0 : (_slotProps_closeButton = slotProps.closeButton) == null ? void 0 : _slotProps_closeButton.className),
|
|
@@ -15283,6 +15673,11 @@ const tabsVariants = tv({
|
|
|
15283
15673
|
trigger: [
|
|
15284
15674
|
'inline-flex items-center justify-center whitespace-nowrap',
|
|
15285
15675
|
'text-sm font-medium transition-colors',
|
|
15676
|
+
// Radix Tabs.Trigger renders a `<button>`, whose UA cursor is
|
|
15677
|
+
// `default`. `disabled:pointer-events-none` neutralises it on
|
|
15678
|
+
// the disabled state (no pointer events → no cursor swap). See
|
|
15679
|
+
// README-BUG § BUG 13.
|
|
15680
|
+
'cursor-pointer',
|
|
15286
15681
|
'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',
|
|
15287
15682
|
'disabled:opacity-50 disabled:pointer-events-none',
|
|
15288
15683
|
'motion-reduce:transition-none'
|