@dashforge/tw 1.4.0 → 1.5.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/index.esm.js +238 -132
  3. package/dist/src/components/Autocomplete/Autocomplete.d.ts.map +1 -1
  4. package/dist/src/components/Autocomplete/autocomplete.types.d.ts +10 -1
  5. package/dist/src/components/Autocomplete/autocomplete.types.d.ts.map +1 -1
  6. package/dist/src/components/Checkbox/Checkbox.d.ts.map +1 -1
  7. package/dist/src/components/Checkbox/checkbox.types.d.ts +10 -1
  8. package/dist/src/components/Checkbox/checkbox.types.d.ts.map +1 -1
  9. package/dist/src/components/DatePicker/DatePicker.d.ts.map +1 -1
  10. package/dist/src/components/DatePicker/datePicker.types.d.ts +10 -1
  11. package/dist/src/components/DatePicker/datePicker.types.d.ts.map +1 -1
  12. package/dist/src/components/DateRangePicker/DateRangePicker.d.ts.map +1 -1
  13. package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts +10 -1
  14. package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts.map +1 -1
  15. package/dist/src/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
  16. package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts +10 -1
  17. package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts.map +1 -1
  18. package/dist/src/components/NumberField/NumberField.d.ts.map +1 -1
  19. package/dist/src/components/NumberField/numberField.types.d.ts +10 -1
  20. package/dist/src/components/NumberField/numberField.types.d.ts.map +1 -1
  21. package/dist/src/components/OTPField/OTPField.d.ts.map +1 -1
  22. package/dist/src/components/OTPField/otpField.types.d.ts +10 -1
  23. package/dist/src/components/OTPField/otpField.types.d.ts.map +1 -1
  24. package/dist/src/components/RadioGroup/RadioGroup.d.ts.map +1 -1
  25. package/dist/src/components/RadioGroup/radioGroup.types.d.ts +10 -1
  26. package/dist/src/components/RadioGroup/radioGroup.types.d.ts.map +1 -1
  27. package/dist/src/components/Select/Select.d.ts.map +1 -1
  28. package/dist/src/components/Select/select.types.d.ts +10 -1
  29. package/dist/src/components/Select/select.types.d.ts.map +1 -1
  30. package/dist/src/components/Switch/Switch.d.ts.map +1 -1
  31. package/dist/src/components/Switch/switch.types.d.ts +10 -1
  32. package/dist/src/components/Switch/switch.types.d.ts.map +1 -1
  33. package/dist/src/components/TextField/TextField.d.ts.map +1 -1
  34. package/dist/src/components/TextField/textField.types.d.ts +11 -1
  35. package/dist/src/components/TextField/textField.types.d.ts.map +1 -1
  36. package/dist/src/components/Textarea/Textarea.d.ts.map +1 -1
  37. package/dist/src/components/Textarea/textarea.types.d.ts +10 -1
  38. package/dist/src/components/Textarea/textarea.types.d.ts.map +1 -1
  39. package/dist/src/components/TimePicker/TimePicker.d.ts.map +1 -1
  40. package/dist/src/components/TimePicker/timePicker.types.d.ts +10 -1
  41. package/dist/src/components/TimePicker/timePicker.types.d.ts.map +1 -1
  42. package/dist/src/components/_shared/fieldTooltip.d.ts +43 -0
  43. package/dist/src/components/_shared/fieldTooltip.d.ts.map +1 -0
  44. package/dist/src/index.d.ts +1 -1
  45. package/package.json +5 -5
package/dist/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
- import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
- import { useMemo, forwardRef, useContext, useState, useEffect, useRef, useId, useCallback, Fragment, createContext, Children, isValidElement, memo, cloneElement, useLayoutEffect } from 'react';
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
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
4
  import { DashFormContext, useEngineVisibility } from '@dashforge/ui-core';
5
5
  import { useComponentDefaults } from '@dashforge/tw-theme';
@@ -9,6 +9,7 @@ import { useRbacOptional, resolveAccessState } from '@dashforge/rbac';
9
9
  import { tv } from 'tailwind-variants';
10
10
  export { tv } from 'tailwind-variants';
11
11
  import { useDashFieldMeta } from '@dashforge/forms';
12
+ import * as RadixTooltip from '@radix-ui/react-tooltip';
12
13
  import * as RadixCheckbox from '@radix-ui/react-checkbox';
13
14
  import * as RadixSwitch from '@radix-ui/react-switch';
14
15
  import * as RadixRadioGroup from '@radix-ui/react-radio-group';
@@ -17,7 +18,6 @@ import * as RadixPopover from '@radix-ui/react-popover';
17
18
  import * as RadixDialog from '@radix-ui/react-dialog';
18
19
  import * as RadixSlider from '@radix-ui/react-slider';
19
20
  import * as RadixMenu from '@radix-ui/react-dropdown-menu';
20
- import * as RadixTooltip from '@radix-ui/react-tooltip';
21
21
  import * as RadixAccordion from '@radix-ui/react-accordion';
22
22
 
23
23
  function _extends() {
@@ -630,6 +630,172 @@ Spinner.displayName = 'Spinner';
630
630
  }));
631
631
  });
632
632
 
633
+ const tooltipVariants = tv({
634
+ slots: {
635
+ content: [
636
+ 'z-50 max-w-xs rounded-md px-3 py-1.5 text-xs font-medium',
637
+ // Auto-invert via the dashforgePreset() CSS-var swap:
638
+ // - Light mode: `bg-neutral-900` = #171717 (dark surface) +
639
+ // `text-neutral-50` = #fafafa (light text) — DARK tooltip
640
+ // on the light page (high contrast).
641
+ // - Dark mode: `bg-neutral-900` flips to #f5f5f5 (light) +
642
+ // `text-neutral-50` flips to #0a0a0a (dark text) — LIGHT
643
+ // tooltip on the dark page (high contrast against page).
644
+ // Adding `dark:bg-neutral-50 dark:text-neutral-900` (the old
645
+ // pattern) would force the dark-mode tooltip back to a dark
646
+ // surface — invisible against the dark page.
647
+ 'bg-neutral-900 text-neutral-50',
648
+ 'shadow-md',
649
+ 'data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95',
650
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
651
+ 'motion-reduce:transition-none motion-reduce:duration-0'
652
+ ],
653
+ // Arrow tracks the content bg — auto-inverts the same way.
654
+ arrow: 'fill-neutral-900'
655
+ }
656
+ });
657
+
658
+ /**
659
+ * Dashforge TW `<Tooltip>` — hoverable / focusable tooltip.
660
+ *
661
+ * Built on `@radix-ui/react-tooltip`:
662
+ * - APG tooltip pattern: `role="tooltip"`, `aria-describedby` wired
663
+ * to the trigger automatically (Radix manages the id linkage).
664
+ * - Shows on hover OR keyboard focus.
665
+ * - Dismissed on Escape or pointer leave.
666
+ *
667
+ * Wrap any single React node — it becomes the trigger via Radix's
668
+ * `asChild` slot. The trigger inherits `aria-describedby` so screen
669
+ * readers announce the tooltip content along with the trigger.
670
+ *
671
+ * @example
672
+ * ```tsx
673
+ * <Tooltip content="Delete this item">
674
+ * <Button variant="ghost">
675
+ * <TrashIcon />
676
+ * </Button>
677
+ * </Tooltip>
678
+ * ```
679
+ *
680
+ * Each consumer can also wrap the entire app once in
681
+ * `<RadixTooltip.Provider>` to share delay/skip configuration; we
682
+ * include a per-component provider here for ease-of-use.
683
+ */ function Tooltip(props) {
684
+ var _themeSlotProps_content, _slotProps_content, _themeSlotProps_arrow, _slotProps_arrow;
685
+ const themeDefaults = useComponentDefaults('Tooltip');
686
+ const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
687
+ const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
688
+ const { children, content, side = 'top', align = 'center', hideArrow = false, delayDuration = 200, open, onOpenChange, sideOffset = 4, sx, slotProps } = merged;
689
+ const v = tooltipVariants();
690
+ return jsx(RadixTooltip.Provider, {
691
+ delayDuration: delayDuration,
692
+ children: jsxs(RadixTooltip.Root, {
693
+ open: open,
694
+ onOpenChange: onOpenChange,
695
+ children: [
696
+ jsx(RadixTooltip.Trigger, {
697
+ asChild: true,
698
+ children: children
699
+ }),
700
+ jsx(RadixTooltip.Portal, {
701
+ children: jsxs(RadixTooltip.Content, {
702
+ side: side,
703
+ align: align,
704
+ sideOffset: sideOffset,
705
+ className: cn(v.content(), sx, themeSlotProps == null ? void 0 : (_themeSlotProps_content = themeSlotProps.content) == null ? void 0 : _themeSlotProps_content.className, slotProps == null ? void 0 : (_slotProps_content = slotProps.content) == null ? void 0 : _slotProps_content.className),
706
+ children: [
707
+ content,
708
+ !hideArrow && jsx(RadixTooltip.Arrow, {
709
+ className: cn(v.arrow(), themeSlotProps == null ? void 0 : (_themeSlotProps_arrow = themeSlotProps.arrow) == null ? void 0 : _themeSlotProps_arrow.className, slotProps == null ? void 0 : (_slotProps_arrow = slotProps.arrow) == null ? void 0 : _slotProps_arrow.className)
710
+ })
711
+ ]
712
+ })
713
+ })
714
+ ]
715
+ })
716
+ });
717
+ }
718
+
719
+ /** Normalise `string | config` to a config (or null when absent). */ function normalizeFieldTooltip(tooltip) {
720
+ if (tooltip == null) return null;
721
+ return typeof tooltip === 'string' ? {
722
+ content: tooltip
723
+ } : tooltip;
724
+ }
725
+ /**
726
+ * Resolve the effective tooltip config (Option C): deep-merge the
727
+ * per-instance `tooltip` over the theme default. The instance wins per
728
+ * key, so the theme can set `icon` / `position` / `side` while the
729
+ * instance supplies just `content` (via the string shorthand).
730
+ */ function resolveFieldTooltip(instance, themeDefault) {
731
+ const inst = normalizeFieldTooltip(instance);
732
+ const base = normalizeFieldTooltip(themeDefault);
733
+ if (!inst && !base) return null;
734
+ if (!inst) return base;
735
+ if (!base) return inst;
736
+ return _extends({}, base, inst);
737
+ }
738
+ /** Built-in default trigger icon — inline SVG, no external dependency. */ function InfoCircleIcon() {
739
+ return jsxs("svg", {
740
+ width: "16",
741
+ height: "16",
742
+ viewBox: "0 0 24 24",
743
+ fill: "none",
744
+ stroke: "currentColor",
745
+ strokeWidth: "1.7",
746
+ strokeLinecap: "round",
747
+ strokeLinejoin: "round",
748
+ "aria-hidden": "true",
749
+ children: [
750
+ jsx("circle", {
751
+ cx: "12",
752
+ cy: "12",
753
+ r: "9"
754
+ }),
755
+ jsx("path", {
756
+ d: "M12 16v-4"
757
+ }),
758
+ jsx("path", {
759
+ d: "M12 8h.01"
760
+ })
761
+ ]
762
+ });
763
+ }
764
+ /** The focusable ⓘ trigger + the Radix-based `<Tooltip>` popup. */ function LabelTooltipTrigger({ config }) {
765
+ const { content, icon, position = 'after', side = 'top' } = config;
766
+ return jsx(Tooltip, {
767
+ content: content,
768
+ side: side,
769
+ children: jsx("button", {
770
+ type: "button",
771
+ "aria-label": "More information",
772
+ className: cn('inline-flex items-center justify-center align-middle leading-none text-neutral-500 hover:text-neutral-700 cursor-help', position === 'before' ? 'mr-1' : 'ml-1'),
773
+ children: icon != null ? icon : jsx(InfoCircleIcon, {})
774
+ })
775
+ });
776
+ }
777
+ /**
778
+ * Compose a label node with its help-tooltip trigger. Pass the resolved
779
+ * config (from {@link resolveFieldTooltip}) or `null`. Returns the plain
780
+ * label when there is no tooltip.
781
+ */ function renderLabelWithTooltip(label, config) {
782
+ if (!config) return label;
783
+ const trigger = jsx(LabelTooltipTrigger, {
784
+ config: config
785
+ });
786
+ return config.position === 'before' ? jsxs(Fragment, {
787
+ children: [
788
+ trigger,
789
+ label
790
+ ]
791
+ }) : jsxs(Fragment, {
792
+ children: [
793
+ label,
794
+ trigger
795
+ ]
796
+ });
797
+ }
798
+
633
799
  /**
634
800
  * `useStandaloneFieldWarning` — dev-only guard for `@dashforge/tw` form
635
801
  * widgets rendered outside a `DashFormProvider` and without a controlled
@@ -849,10 +1015,12 @@ Spinner.displayName = 'Spinner';
849
1015
  * - `aria-describedby` links the input to the helper/error text node.
850
1016
  * - Required fields get the native `required` attribute + a visual `*`.
851
1017
  */ function TextField(props) {
852
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_inputWrapper, _slotProps_inputWrapper, _slotProps_prefix, _themeSlotProps_input, _slotProps_input, _slotProps_suffix, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
1018
+ var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_inputWrapper, _slotProps_inputWrapper, _slotProps_prefix, _themeSlotProps_input, _slotProps_input, _slotProps_suffix, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
853
1019
  const themeDefaults = useComponentDefaults('TextField');
854
1020
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
855
1021
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
1022
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
1023
+ const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
856
1024
  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 } = merged, rest = _object_without_properties_loose(merged, [
857
1025
  "name",
858
1026
  "rules",
@@ -975,7 +1143,7 @@ Spinner.displayName = 'Spinner';
975
1143
  htmlFor: inputId,
976
1144
  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),
977
1145
  children: [
978
- label,
1146
+ renderLabelWithTooltip(label, tooltipConfig),
979
1147
  required && jsx("span", {
980
1148
  "aria-hidden": "true",
981
1149
  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),
@@ -1169,10 +1337,12 @@ Spinner.displayName = 'Spinner';
1169
1337
  * rather than MUI slot components.
1170
1338
  */ function Checkbox(props) {
1171
1339
  var _ref;
1172
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_control, _slotProps_control, _themeSlotProps_indicator, _slotProps_indicator, _themeSlotProps_label, _slotProps_label, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
1340
+ 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;
1173
1341
  const themeDefaults = useComponentDefaults('Checkbox');
1174
1342
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
1175
1343
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
1344
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
1345
+ const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
1176
1346
  const { name, rules, visibleWhen, label, helperText, error, access, size, disabled, checked, defaultChecked, onCheckedChange, sx, slotProps } = merged;
1177
1347
  // ───── Hooks (always called, unconditional) ─────
1178
1348
  const bridge = useContext(DashFormContext);
@@ -1310,7 +1480,7 @@ Spinner.displayName = 'Spinner';
1310
1480
  label && jsx("label", {
1311
1481
  htmlFor: controlId,
1312
1482
  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),
1313
- children: label
1483
+ children: renderLabelWithTooltip(label, tooltipConfig)
1314
1484
  }),
1315
1485
  resolvedHelperText && jsx("p", {
1316
1486
  className: cn(resolvedError ? v.errorText() : v.helperText(), resolvedError ? [
@@ -1407,10 +1577,12 @@ Spinner.displayName = 'Spinner';
1407
1577
  * translate-x distance so the knob lands flush at each end.
1408
1578
  */ function Switch(props) {
1409
1579
  var _ref;
1410
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_control, _slotProps_control, _themeSlotProps_thumb, _slotProps_thumb, _themeSlotProps_label, _slotProps_label, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
1580
+ 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;
1411
1581
  const themeDefaults = useComponentDefaults('Switch');
1412
1582
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
1413
1583
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
1584
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
1585
+ const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
1414
1586
  const { name, rules, visibleWhen, label, helperText, error, access, size, disabled, checked, defaultChecked, onCheckedChange, sx, slotProps } = merged;
1415
1587
  const bridge = useContext(DashFormContext);
1416
1588
  const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
@@ -1522,7 +1694,7 @@ Spinner.displayName = 'Spinner';
1522
1694
  label && jsx("label", {
1523
1695
  htmlFor: controlId,
1524
1696
  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),
1525
- children: label
1697
+ children: renderLabelWithTooltip(label, tooltipConfig)
1526
1698
  }),
1527
1699
  resolvedHelperText && jsx("p", {
1528
1700
  className: cn(resolvedError ? v.errorText() : v.helperText(), resolvedError ? [
@@ -1660,10 +1832,12 @@ Spinner.displayName = 'Spinner';
1660
1832
  * keyboard-accessible by default (arrow keys move selection).
1661
1833
  */ function RadioGroup(props) {
1662
1834
  var _ref;
1663
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_optionList, _slotProps_optionList, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
1835
+ var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_optionList, _slotProps_optionList, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
1664
1836
  const themeDefaults = useComponentDefaults('RadioGroup');
1665
1837
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
1666
1838
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
1839
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
1840
+ const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
1667
1841
  const { name, options, label, rules, helperText, required, error, disabled, size, layout, access, visibleWhen, sx, slotProps, value: explicitValue, defaultValue, onValueChange } = merged;
1668
1842
  // ───── Hooks (unconditional) ─────
1669
1843
  const bridge = useContext(DashFormContext);
@@ -1783,7 +1957,7 @@ Spinner.displayName = 'Spinner';
1783
1957
  id: labelId,
1784
1958
  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),
1785
1959
  children: [
1786
- label,
1960
+ renderLabelWithTooltip(label, tooltipConfig),
1787
1961
  required && jsx("span", {
1788
1962
  "aria-hidden": "true",
1789
1963
  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),
@@ -1950,10 +2124,12 @@ Spinner.displayName = 'Spinner';
1950
2124
  * `aria-describedby` links to the helper/error text node, required
1951
2125
  * fields get the native `required` attribute + a visual `*`.
1952
2126
  */ function Textarea(props) {
1953
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_inputWrapper, _slotProps_inputWrapper, _themeSlotProps_input, _slotProps_input, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
2127
+ var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_inputWrapper, _slotProps_inputWrapper, _themeSlotProps_input, _slotProps_input, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
1954
2128
  const themeDefaults = useComponentDefaults('Textarea');
1955
2129
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
1956
2130
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
2131
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
2132
+ const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
1957
2133
  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 } = merged, rest = _object_without_properties_loose(merged, [
1958
2134
  "name",
1959
2135
  "rules",
@@ -2065,7 +2241,7 @@ Spinner.displayName = 'Spinner';
2065
2241
  htmlFor: inputId,
2066
2242
  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),
2067
2243
  children: [
2068
- label,
2244
+ renderLabelWithTooltip(label, tooltipConfig),
2069
2245
  required && jsx("span", {
2070
2246
  "aria-hidden": "true",
2071
2247
  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),
@@ -2251,10 +2427,12 @@ Spinner.displayName = 'Spinner';
2251
2427
  * `aria-valuemin` / `aria-valuemax` automatically)
2252
2428
  */ function NumberField(props) {
2253
2429
  var _parseFromInput, _parseFromInput1;
2254
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_inputWrapper, _slotProps_inputWrapper, _themeSlotProps_input, _slotProps_input, _themeSlotProps_stepper, _slotProps_stepper, _themeSlotProps_stepperButton, _slotProps_stepperButton, _themeSlotProps_stepperButton1, _slotProps_stepperButton1, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
2430
+ var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_inputWrapper, _slotProps_inputWrapper, _themeSlotProps_input, _slotProps_input, _themeSlotProps_stepper, _slotProps_stepper, _themeSlotProps_stepperButton, _slotProps_stepperButton, _themeSlotProps_stepperButton1, _slotProps_stepperButton1, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
2255
2431
  const themeDefaults = useComponentDefaults('NumberField');
2256
2432
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
2257
2433
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
2434
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
2435
+ const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
2258
2436
  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 } = merged, rest = _object_without_properties_loose(merged, [
2259
2437
  "name",
2260
2438
  "rules",
@@ -2409,7 +2587,7 @@ Spinner.displayName = 'Spinner';
2409
2587
  htmlFor: inputId,
2410
2588
  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),
2411
2589
  children: [
2412
- label,
2590
+ renderLabelWithTooltip(label, tooltipConfig),
2413
2591
  required && jsx("span", {
2414
2592
  "aria-hidden": "true",
2415
2593
  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),
@@ -2582,10 +2760,12 @@ Spinner.displayName = 'Spinner';
2582
2760
  * `inputMode="numeric" | "text"` per mode + `autoComplete="one-time-code"`
2583
2761
  * so iOS Safari + Android Chrome trigger SMS autofill correctly.
2584
2762
  */ function OTPField(props) {
2585
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_slotsRow, _slotProps_slotsRow, _themeSlotProps_hiddenInput, _slotProps_hiddenInput, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
2763
+ var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_slotsRow, _slotProps_slotsRow, _themeSlotProps_hiddenInput, _slotProps_hiddenInput, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
2586
2764
  const themeDefaults = useComponentDefaults('OTPField');
2587
2765
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
2588
2766
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
2767
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
2768
+ const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
2589
2769
  const { name, rules, visibleWhen, size, label, helperText, required, error, disabled, access, sx, slotProps, length = 6, mode = 'numeric', value: userValue, defaultValue, onChange: userOnChange, onComplete } = merged;
2590
2770
  // ───── Hooks (unconditional) ─────
2591
2771
  const bridge = useContext(DashFormContext);
@@ -2693,7 +2873,7 @@ Spinner.displayName = 'Spinner';
2693
2873
  htmlFor: inputId,
2694
2874
  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),
2695
2875
  children: [
2696
- label,
2876
+ renderLabelWithTooltip(label, tooltipConfig),
2697
2877
  required && jsx("span", {
2698
2878
  "aria-hidden": "true",
2699
2879
  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),
@@ -3009,10 +3189,12 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
3009
3189
  * shapes, and async runtime options (`optionsFromFieldData`) are
3010
3190
  * deferred to F5-A-bis.
3011
3191
  */ function Autocomplete(props) {
3012
- var _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_popover, _slotProps_popover, _themeSlotProps_listBox, _slotProps_listBox, _themeSlotProps_emptyState, _slotProps_emptyState, _themeSlotProps_emptyState1, _slotProps_emptyState1, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
3192
+ 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_popover, _slotProps_popover, _themeSlotProps_listBox, _slotProps_listBox, _themeSlotProps_emptyState, _slotProps_emptyState, _themeSlotProps_emptyState1, _slotProps_emptyState1, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
3013
3193
  const themeDefaults = useComponentDefaults('Autocomplete');
3014
3194
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
3015
3195
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
3196
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
3197
+ const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
3016
3198
  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;
3017
3199
  const isMulti = Boolean(multiple);
3018
3200
  // Default the React-key accessor to `getOptionValue` (typical case:
@@ -3605,7 +3787,7 @@ const defaultGetOptionDisabled = (option)=>Boolean(option.disabled);
3605
3787
  htmlFor: `${baseId}-input`,
3606
3788
  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),
3607
3789
  children: [
3608
- label,
3790
+ renderLabelWithTooltip(label, tooltipConfig),
3609
3791
  required && jsx("span", {
3610
3792
  "aria-hidden": "true",
3611
3793
  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),
@@ -4202,10 +4384,12 @@ function CalendarIcon$2() {
4202
4384
  * Storage contract: an ISO `YYYY-MM-DD` string, or `null` — shared with the
4203
4385
  * MUI `@dashforge/ui` `DatePicker`.
4204
4386
  */ function DatePicker(_props) {
4205
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
4387
+ var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
4206
4388
  const themeDefaults = useComponentDefaults('DatePicker');
4207
4389
  const props = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, _props);
4208
4390
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
4391
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
4392
+ const tooltipConfig = resolveFieldTooltip(_props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
4209
4393
  const { name, rules, label, helperText, error, required, disabled, placeholder, layout = 'stacked', visibleWhen, access, value: explicitValue, defaultValue, onChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, fullWidth, sx, slotProps, testId } = props;
4210
4394
  // ───── Hooks (unconditional, before any early return) ─────
4211
4395
  const bridge = useContext(DashFormContext);
@@ -4335,7 +4519,7 @@ function CalendarIcon$2() {
4335
4519
  htmlFor: fieldId,
4336
4520
  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),
4337
4521
  children: [
4338
- label,
4522
+ renderLabelWithTooltip(label, tooltipConfig),
4339
4523
  required && jsx("span", {
4340
4524
  "aria-hidden": "true",
4341
4525
  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),
@@ -4479,10 +4663,12 @@ function ClockIcon() {
4479
4663
  * shared with the MUI `@dashforge/ui` `TimePicker`. `hour12` is display-only.
4480
4664
  */ function TimePicker(_props) {
4481
4665
  var _timeStringToMinutes, _timeStringToMinutes1;
4482
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
4666
+ var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
4483
4667
  const themeDefaults = useComponentDefaults('TimePicker');
4484
4668
  const props = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, _props);
4485
4669
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
4670
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
4671
+ const tooltipConfig = resolveFieldTooltip(_props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
4486
4672
  const { name, rules, label, helperText, error, required, disabled, placeholder, layout = 'stacked', visibleWhen, access, value: explicitValue, defaultValue, onChange, minTime, maxTime, stepMinutes, hour12 = false, fullWidth, sx, slotProps, testId } = props;
4487
4673
  const bridge = useContext(DashFormContext);
4488
4674
  const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
@@ -4620,7 +4806,7 @@ function ClockIcon() {
4620
4806
  htmlFor: fieldId,
4621
4807
  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),
4622
4808
  children: [
4623
- label,
4809
+ renderLabelWithTooltip(label, tooltipConfig),
4624
4810
  required && jsx("span", {
4625
4811
  "aria-hidden": "true",
4626
4812
  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),
@@ -4817,10 +5003,12 @@ const DATETIME_PATTERN = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2})/;
4817
5003
  * Storage contract: a naive ISO datetime `"YYYY-MM-DDTHH:mm"` — no seconds,
4818
5004
  * no timezone. Shared with the MUI `@dashforge/ui` `DateTimePicker`.
4819
5005
  */ function DateTimePicker(_props) {
4820
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
5006
+ var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
4821
5007
  const themeDefaults = useComponentDefaults('DateTimePicker');
4822
5008
  const props = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, _props);
4823
5009
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
5010
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
5011
+ const tooltipConfig = resolveFieldTooltip(_props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
4824
5012
  const { name, rules, label, helperText, error, required, disabled, placeholder, layout = 'stacked', visibleWhen, access, value: explicitValue, defaultValue, onChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, stepMinutes, hour12 = false, fullWidth, sx, slotProps, testId } = props;
4825
5013
  const bridge = useContext(DashFormContext);
4826
5014
  const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
@@ -4950,7 +5138,7 @@ const DATETIME_PATTERN = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2})/;
4950
5138
  htmlFor: fieldId,
4951
5139
  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),
4952
5140
  children: [
4953
- label,
5141
+ renderLabelWithTooltip(label, tooltipConfig),
4954
5142
  required && jsx("span", {
4955
5143
  "aria-hidden": "true",
4956
5144
  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),
@@ -5382,10 +5570,12 @@ function formatRange(range, locale) {
5382
5570
  * Storage contract: a `{ start, end }` pair of ISO `YYYY-MM-DD` dates —
5383
5571
  * shared with the MUI `@dashforge/ui` `DateRangePicker`.
5384
5572
  */ function DateRangePicker(_props) {
5385
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
5573
+ var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_helperText, _slotProps_helperText;
5386
5574
  const themeDefaults = useComponentDefaults('DateRangePicker');
5387
5575
  const props = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, _props);
5388
5576
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
5577
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
5578
+ const tooltipConfig = resolveFieldTooltip(_props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
5389
5579
  const { name, rules, label, helperText, error, required, disabled, placeholder, layout = 'stacked', visibleWhen, access, value: explicitValue, defaultValue, onChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, fullWidth, sx, slotProps, testId } = props;
5390
5580
  const bridge = useContext(DashFormContext);
5391
5581
  const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
@@ -5508,7 +5698,7 @@ function formatRange(range, locale) {
5508
5698
  htmlFor: fieldId,
5509
5699
  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),
5510
5700
  children: [
5511
- label,
5701
+ renderLabelWithTooltip(label, tooltipConfig),
5512
5702
  required && jsx("span", {
5513
5703
  "aria-hidden": "true",
5514
5704
  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),
@@ -5731,7 +5921,7 @@ function formatRange(range, locale) {
5731
5921
  href: item.href,
5732
5922
  onClick: item.onClick,
5733
5923
  className: cn(v.link(), themeSlotProps == null ? void 0 : (_themeSlotProps_link = themeSlotProps.link) == null ? void 0 : _themeSlotProps_link.className, slotProps == null ? void 0 : (_slotProps_link = slotProps.link) == null ? void 0 : _slotProps_link.className),
5734
- children: item.icon ? jsxs(Fragment, {
5924
+ children: item.icon ? jsxs(Fragment$1, {
5735
5925
  children: [
5736
5926
  jsx("span", {
5737
5927
  "aria-hidden": "true",
@@ -5750,7 +5940,7 @@ function formatRange(range, locale) {
5750
5940
  themeSlotProps == null ? void 0 : (_themeSlotProps_link1 = themeSlotProps.link) == null ? void 0 : _themeSlotProps_link1.className,
5751
5941
  slotProps == null ? void 0 : (_slotProps_link1 = slotProps.link) == null ? void 0 : _slotProps_link1.className
5752
5942
  ]),
5753
- children: item.icon ? jsxs(Fragment, {
5943
+ children: item.icon ? jsxs(Fragment$1, {
5754
5944
  children: [
5755
5945
  jsx("span", {
5756
5946
  "aria-hidden": "true",
@@ -5899,7 +6089,7 @@ function formatRange(range, locale) {
5899
6089
  const Link = linkComponent;
5900
6090
  const linkClasses = cn(v.itemLink(), isActive && v.itemActive(), themeSlotProps == null ? void 0 : (_themeSlotProps_itemLink = themeSlotProps.itemLink) == null ? void 0 : _themeSlotProps_itemLink.className, slotProps == null ? void 0 : (_slotProps_itemLink = slotProps.itemLink) == null ? void 0 : _slotProps_itemLink.className, isActive && (themeSlotProps == null ? void 0 : (_themeSlotProps_itemActive = themeSlotProps.itemActive) == null ? void 0 : _themeSlotProps_itemActive.className), slotProps == null ? void 0 : (_slotProps_itemActive = slotProps.itemActive) == null ? void 0 : _slotProps_itemActive.className);
5901
6091
  // Visual content — same in `<a>` / `<button>` branches below.
5902
- const content = jsxs(Fragment$1, {
6092
+ const content = jsxs(Fragment, {
5903
6093
  children: [
5904
6094
  item.icon && jsx("span", {
5905
6095
  "aria-hidden": "true",
@@ -6446,7 +6636,7 @@ function formatRange(range, locale) {
6446
6636
  jsxs("div", {
6447
6637
  className: v.body(),
6448
6638
  children: [
6449
- nav && jsxs(Fragment$1, {
6639
+ nav && jsxs(Fragment, {
6450
6640
  children: [
6451
6641
  jsx("aside", {
6452
6642
  className: cn(v.nav(), themeSlotProps == null ? void 0 : (_themeSlotProps_nav = themeSlotProps.nav) == null ? void 0 : _themeSlotProps_nav.className, slotProps == null ? void 0 : (_slotProps_nav = slotProps.nav) == null ? void 0 : _slotProps_nav.className),
@@ -6712,7 +6902,7 @@ const ConfirmContext = /*#__PURE__*/ createContext(null);
6712
6902
  "aria-labelledby": (opts == null ? void 0 : opts.title) != null ? titleId : undefined,
6713
6903
  onClick: handleDialogClick,
6714
6904
  className: cn(v.backdrop(), v.dialog(), (_mergedSlotProps_backdrop = mergedSlotProps.backdrop) == null ? void 0 : _mergedSlotProps_backdrop.className, (_mergedSlotProps_dialog = mergedSlotProps.dialog) == null ? void 0 : _mergedSlotProps_dialog.className),
6715
- children: opts != null && jsxs(Fragment$1, {
6905
+ children: opts != null && jsxs(Fragment, {
6716
6906
  children: [
6717
6907
  opts.title != null && jsx("h2", {
6718
6908
  id: titleId,
@@ -8746,7 +8936,7 @@ if (process.env.NODE_ENV !== 'production') {
8746
8936
  */ function flattenChildren(children) {
8747
8937
  const flat = [];
8748
8938
  Children.forEach(children, (child)=>{
8749
- if (/*#__PURE__*/ isValidElement(child) && child.type === Fragment) {
8939
+ if (/*#__PURE__*/ isValidElement(child) && child.type === Fragment$1) {
8750
8940
  const nested = child.props.children;
8751
8941
  flat.push(...flattenChildren(nested));
8752
8942
  return;
@@ -8799,7 +8989,7 @@ if (process.env.NODE_ENV !== 'production') {
8799
8989
  sawStrayDom = true;
8800
8990
  return;
8801
8991
  }
8802
- pass.push(jsx(Fragment, {
8992
+ pass.push(jsx(Fragment$1, {
8803
8993
  children: child
8804
8994
  }, `stepper-passthrough-${i}`));
8805
8995
  });
@@ -9089,7 +9279,7 @@ if (process.env.NODE_ENV !== 'production') {
9089
9279
  const labelClass = 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);
9090
9280
  const helperClass = cn(v.helperText(), themeSlotProps == null ? void 0 : (_themeSlotProps_helperText = themeSlotProps.helperText) == null ? void 0 : _themeSlotProps_helperText.className, slotProps == null ? void 0 : (_slotProps_helperText = slotProps.helperText) == null ? void 0 : _slotProps_helperText.className);
9091
9281
  const optionalClass = cn(v.optionalTag(), themeSlotProps == null ? void 0 : (_themeSlotProps_optionalTag = themeSlotProps.optionalTag) == null ? void 0 : _themeSlotProps_optionalTag.className, slotProps == null ? void 0 : (_slotProps_optionalTag = slotProps.optionalTag) == null ? void 0 : _slotProps_optionalTag.className);
9092
- return jsxs(Fragment, {
9282
+ return jsxs(Fragment$1, {
9093
9283
  children: [
9094
9284
  jsxs("div", {
9095
9285
  role: "listitem",
@@ -9205,10 +9395,10 @@ Stepper.displayName = 'Stepper';
9205
9395
  ]
9206
9396
  });
9207
9397
  }
9208
- if (icon) return jsx(Fragment$1, {
9398
+ if (icon) return jsx(Fragment, {
9209
9399
  children: icon
9210
9400
  });
9211
- return jsx(Fragment$1, {
9401
+ return jsx(Fragment, {
9212
9402
  children: index + 1
9213
9403
  });
9214
9404
  }
@@ -10060,10 +10250,12 @@ Slider.displayName = 'Slider';
10060
10250
  * array so `onChange` gets the precise literal type.
10061
10251
  */ function SelectInner(props, ref) {
10062
10252
  var _ref;
10063
- var _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_triggerText, _slotProps_triggerText, _themeSlotProps_triggerPlaceholder, _slotProps_triggerPlaceholder, _themeSlotProps_chevron, _slotProps_chevron, _themeSlotProps_popover, _slotProps_popover, _themeSlotProps_listBox, _slotProps_listBox, _themeSlotProps_emptyState, _slotProps_emptyState, _themeSlotProps_helperText, _slotProps_helperText, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_chipsList, _slotProps_chipsList, _themeSlotProps_chip, _slotProps_chip, _themeSlotProps_chipRemove, _slotProps_chipRemove, _triggerRef_current;
10253
+ var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_triggerText, _slotProps_triggerText, _themeSlotProps_triggerPlaceholder, _slotProps_triggerPlaceholder, _themeSlotProps_chevron, _slotProps_chevron, _themeSlotProps_popover, _slotProps_popover, _themeSlotProps_listBox, _slotProps_listBox, _themeSlotProps_emptyState, _slotProps_emptyState, _themeSlotProps_helperText, _slotProps_helperText, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_chipsList, _slotProps_chipsList, _themeSlotProps_chip, _slotProps_chip, _themeSlotProps_chipRemove, _slotProps_chipRemove, _triggerRef_current;
10064
10254
  const themeDefaults = useComponentDefaults('Select');
10065
10255
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
10066
10256
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
10257
+ // Option C deep-merge: instance `tooltip` over `theme…defaults.tooltip`.
10258
+ const tooltipConfig = resolveFieldTooltip(props.tooltip, themeDefaults == null ? void 0 : (_themeDefaults_defaults = themeDefaults.defaults) == null ? void 0 : _themeDefaults_defaults.tooltip);
10067
10259
  const { name, rules, label, helperText, error, required, disabled, fullWidth, layout, size, visibleWhen, access, options, placeholder = 'Select…', emptyState = 'No options', value: userValue, defaultValue, onChange, onBlur, multiple = false, sx, slotProps, testId } = merged;
10068
10260
  // ───── Hooks (unconditional, above the early returns) ─────
10069
10261
  const bridge = useContext(DashFormContext);
@@ -10424,7 +10616,7 @@ Slider.displayName = 'Slider';
10424
10616
  htmlFor: controlId,
10425
10617
  className: labelClasses,
10426
10618
  children: [
10427
- label,
10619
+ renderLabelWithTooltip(label, tooltipConfig),
10428
10620
  required && jsx("span", {
10429
10621
  className: requiredMarkClasses,
10430
10622
  children: "*"
@@ -13626,7 +13818,7 @@ CardActionArea.displayName = 'CardActionArea';
13626
13818
  // `inline-flex items-center` provides vertical alignment with
13627
13819
  // any leading / trailing slots.
13628
13820
  const leading = avatar != null ? avatar : icon;
13629
- const innerContent = jsxs(Fragment$1, {
13821
+ const innerContent = jsxs(Fragment, {
13630
13822
  children: [
13631
13823
  leading != null && jsx("span", {
13632
13824
  className: "-ml-0.5 inline-flex items-center shrink-0",
@@ -14039,7 +14231,7 @@ Typography.displayName = 'Typography';
14039
14231
  key
14040
14232
  }));
14041
14233
  } else {
14042
- result.push(jsx(Fragment, {
14234
+ result.push(jsx(Fragment$1, {
14043
14235
  children: divider
14044
14236
  }, key));
14045
14237
  }
@@ -15335,92 +15527,6 @@ function useTabs(options) {
15335
15527
  });
15336
15528
  }
15337
15529
 
15338
- const tooltipVariants = tv({
15339
- slots: {
15340
- content: [
15341
- 'z-50 max-w-xs rounded-md px-3 py-1.5 text-xs font-medium',
15342
- // Auto-invert via the dashforgePreset() CSS-var swap:
15343
- // - Light mode: `bg-neutral-900` = #171717 (dark surface) +
15344
- // `text-neutral-50` = #fafafa (light text) — DARK tooltip
15345
- // on the light page (high contrast).
15346
- // - Dark mode: `bg-neutral-900` flips to #f5f5f5 (light) +
15347
- // `text-neutral-50` flips to #0a0a0a (dark text) — LIGHT
15348
- // tooltip on the dark page (high contrast against page).
15349
- // Adding `dark:bg-neutral-50 dark:text-neutral-900` (the old
15350
- // pattern) would force the dark-mode tooltip back to a dark
15351
- // surface — invisible against the dark page.
15352
- 'bg-neutral-900 text-neutral-50',
15353
- 'shadow-md',
15354
- 'data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95',
15355
- 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
15356
- 'motion-reduce:transition-none motion-reduce:duration-0'
15357
- ],
15358
- // Arrow tracks the content bg — auto-inverts the same way.
15359
- arrow: 'fill-neutral-900'
15360
- }
15361
- });
15362
-
15363
- /**
15364
- * Dashforge TW `<Tooltip>` — hoverable / focusable tooltip.
15365
- *
15366
- * Built on `@radix-ui/react-tooltip`:
15367
- * - APG tooltip pattern: `role="tooltip"`, `aria-describedby` wired
15368
- * to the trigger automatically (Radix manages the id linkage).
15369
- * - Shows on hover OR keyboard focus.
15370
- * - Dismissed on Escape or pointer leave.
15371
- *
15372
- * Wrap any single React node — it becomes the trigger via Radix's
15373
- * `asChild` slot. The trigger inherits `aria-describedby` so screen
15374
- * readers announce the tooltip content along with the trigger.
15375
- *
15376
- * @example
15377
- * ```tsx
15378
- * <Tooltip content="Delete this item">
15379
- * <Button variant="ghost">
15380
- * <TrashIcon />
15381
- * </Button>
15382
- * </Tooltip>
15383
- * ```
15384
- *
15385
- * Each consumer can also wrap the entire app once in
15386
- * `<RadixTooltip.Provider>` to share delay/skip configuration; we
15387
- * include a per-component provider here for ease-of-use.
15388
- */ function Tooltip(props) {
15389
- var _themeSlotProps_content, _slotProps_content, _themeSlotProps_arrow, _slotProps_arrow;
15390
- const themeDefaults = useComponentDefaults('Tooltip');
15391
- const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
15392
- const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
15393
- const { children, content, side = 'top', align = 'center', hideArrow = false, delayDuration = 200, open, onOpenChange, sideOffset = 4, sx, slotProps } = merged;
15394
- const v = tooltipVariants();
15395
- return jsx(RadixTooltip.Provider, {
15396
- delayDuration: delayDuration,
15397
- children: jsxs(RadixTooltip.Root, {
15398
- open: open,
15399
- onOpenChange: onOpenChange,
15400
- children: [
15401
- jsx(RadixTooltip.Trigger, {
15402
- asChild: true,
15403
- children: children
15404
- }),
15405
- jsx(RadixTooltip.Portal, {
15406
- children: jsxs(RadixTooltip.Content, {
15407
- side: side,
15408
- align: align,
15409
- sideOffset: sideOffset,
15410
- className: cn(v.content(), sx, themeSlotProps == null ? void 0 : (_themeSlotProps_content = themeSlotProps.content) == null ? void 0 : _themeSlotProps_content.className, slotProps == null ? void 0 : (_slotProps_content = slotProps.content) == null ? void 0 : _slotProps_content.className),
15411
- children: [
15412
- content,
15413
- !hideArrow && jsx(RadixTooltip.Arrow, {
15414
- className: cn(v.arrow(), themeSlotProps == null ? void 0 : (_themeSlotProps_arrow = themeSlotProps.arrow) == null ? void 0 : _themeSlotProps_arrow.className, slotProps == null ? void 0 : (_slotProps_arrow = slotProps.arrow) == null ? void 0 : _slotProps_arrow.className)
15415
- })
15416
- ]
15417
- })
15418
- })
15419
- ]
15420
- })
15421
- });
15422
- }
15423
-
15424
15530
  const accordionVariants = tv({
15425
15531
  slots: {
15426
15532
  root: 'w-full',
@@ -17074,7 +17180,7 @@ function HeaderCell$1(props) {
17074
17180
  // ───── Data row ─────
17075
17181
  function Row(props) {
17076
17182
  const { row, rowId, rowIndex, isSelected, isExpanded, visibleCols, columnTypes, rowSelection, showSelectionColumn, showExpandColumn, showRowActionsColumn, expandable, rowActions, labels, toggleRow, toggleExpand, isInteractive, rowClass, cellClass, selectionCellClass, expandToggleCellClass, rowActionsCellClass, expandedRowClass, totalColumnCount } = props;
17077
- return jsxs(Fragment$1, {
17183
+ return jsxs(Fragment, {
17078
17184
  children: [
17079
17185
  jsxs("tr", {
17080
17186
  "data-selected": isSelected ? 'true' : 'false',
@@ -17249,7 +17355,7 @@ function SortIcon$1(props) {
17249
17355
  d: "M8 4l4 6H4z"
17250
17356
  }) : props.direction === 'desc' ? jsx("path", {
17251
17357
  d: "M8 12L4 6h8z"
17252
- }) : jsxs(Fragment$1, {
17358
+ }) : jsxs(Fragment, {
17253
17359
  children: [
17254
17360
  jsx("path", {
17255
17361
  d: "M8 3l3 4H5z",
@@ -19096,7 +19202,7 @@ function HeaderCell(props) {
19096
19202
  // ───── Virtualized body ─────
19097
19203
  function renderVirtualizedBody(args) {
19098
19204
  const { vWindow, renderedRows, visibleCols, columnTypes, columnWidths, selection, rowSelection, showSelectionColumn, showRowActionsColumn, rowActions, getRowId, isInteractive, labels, classes } = args;
19099
- return jsxs(Fragment$1, {
19205
+ return jsxs(Fragment, {
19100
19206
  children: [
19101
19207
  vWindow.paddingTop > 0 && jsx("tr", {
19102
19208
  "aria-hidden": "true",
@@ -19255,7 +19361,7 @@ function SortIcon(props) {
19255
19361
  d: "M8 4l4 6H4z"
19256
19362
  }) : props.direction === 'desc' ? jsx("path", {
19257
19363
  d: "M8 12L4 6h8z"
19258
- }) : jsxs(Fragment$1, {
19364
+ }) : jsxs(Fragment, {
19259
19365
  children: [
19260
19366
  jsx("path", {
19261
19367
  d: "M8 3l3 4H5z",
@@ -19321,6 +19427,6 @@ function SearchIcon(props) {
19321
19427
  */ // ───── Components ─────
19322
19428
  /**
19323
19429
  * Package version (synced with `package.json` at publish time).
19324
- */ const VERSION = '1.4.0';
19430
+ */ const VERSION = '1.5.0';
19325
19431
 
19326
19432
  export { AVATAR_SOFT_FALLBACK, AVATAR_TONE_FALLBACK, Accordion, Alert, AlertTitle, AppShell, AspectRatio, Autocomplete, Avatar, AvatarGroup, Badge, Box, Breadcrumbs, Button, Calendar, Card, CardActionArea, CardContent, Checkbox, Chip, ConfirmDialogProvider, Container, DRAWER_SIZE_PRESETS, DataGrid, DatePicker, DateRangePicker, DateTimePicker, Dialog, Divider, Drawer, Grid, ICON_BUTTON_BASE, ICON_BUTTON_SIZE_OVERRIDES, IconButton, Image, LeftNav, Link, Menu, MenuContent, MenuItem, MenuLabel, MenuSeparator, MenuSkeleton, MenuTrigger, NumberField, OTPField, Pagination, Popover, Progress, RadioGroup, RenderButton, RenderChip, RenderText, RenderTwoLine, RowActionsMenu, SPINNER_COLORS, SPINNER_STROKE_WIDTH, SPINNER_TRACK_OPACITY, Select, Skeleton, Slider, SnackbarProvider, Spinner, Stack, Step, Stepper, Switch, Table, Tabs, TextField, Textarea, TimePicker, Tooltip, TopBar, Typography, VERSION, Video, VisuallyHidden, accordionVariants, alertVariants, appShellVariants, autocompleteVariants, avatarVariants, badgeVariants, boxVariants, breadcrumbsVariants, buttonVariants, calendarDayVariants, calendarVariants, checkboxVariants, chipVariants, cn, confirmDialogVariants, containerVariants, dataGridVariants, datePickerVariants, dateRangeDayVariants, dateRangePickerVariants, dateTimePickerVariants, dialogVariants, dividerLineVariants, dividerVariants, drawerVariants, getNestedValue, gridVariants, imageVariants, leftNavVariants, linkVariants, menuVariants, numberFieldVariants, otpFieldVariants, paginationVariants, popoverVariants, progressVariants, radioGroupVariants, selectVariants, skeletonVariants, sliderVariants, snackbarVariants, spinnerVariants, stackVariants, stepperVariants, switchVariants, tableVariants, tabsVariants, textFieldVariants, textareaVariants, timePickerVariants, tooltipVariants, topBarVariants, typographyVariants, useAccessState, useConfirm, useSnackbar, useStep, videoVariants };