@dreamtree-org/twreact-ui 1.3.4 → 1.3.5

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 (36) hide show
  1. package/README.md +10 -5
  2. package/ai-skills/dreamtree-ui.md +5 -1
  3. package/dist/ai-schema.json +44 -2
  4. package/dist/components/core/Button.d.ts.map +1 -1
  5. package/dist/components/core/Radio.d.ts.map +1 -1
  6. package/dist/components/core/Select.d.ts.map +1 -1
  7. package/dist/components/feedback/Dialog.d.ts.map +1 -1
  8. package/dist/components/feedback/Drawer.d.ts.map +1 -1
  9. package/dist/components/navigation/FootNav.d.ts.map +1 -1
  10. package/dist/components/utility/Badge.d.ts.map +1 -1
  11. package/dist/components/utility/Stepper.d.ts.map +1 -1
  12. package/dist/esm/components/blocks/StatusPill.js +1 -1
  13. package/dist/esm/components/core/Button.js +23 -6
  14. package/dist/esm/components/core/Radio.js +3 -2
  15. package/dist/esm/components/core/Select.js +12 -2
  16. package/dist/esm/components/feedback/Dialog.js +4 -2
  17. package/dist/esm/components/feedback/Drawer.js +39 -10
  18. package/dist/esm/components/navigation/FootNav.js +41 -14
  19. package/dist/esm/components/utility/Badge.js +1 -0
  20. package/dist/esm/components/utility/Stepper.js +5 -2
  21. package/dist/esm/hooks/useSwipeToClose.js +66 -0
  22. package/dist/esm/utils/touchTarget.js +6 -0
  23. package/dist/hooks/useSwipeToClose.d.ts +24 -0
  24. package/dist/hooks/useSwipeToClose.d.ts.map +1 -0
  25. package/dist/index.css +1 -1
  26. package/dist/index.esm.css +1 -1
  27. package/dist/index.esm.js +191 -38
  28. package/dist/index.js +191 -38
  29. package/dist/types/index.d.ts +28 -2
  30. package/dist/types/index.d.ts.map +1 -1
  31. package/dist/utils/touchTarget.d.ts +2 -0
  32. package/dist/utils/touchTarget.d.ts.map +1 -0
  33. package/doc/README.md +1 -1
  34. package/mcp/README.md +1 -1
  35. package/mcp/catalog.snapshot.json +51 -21
  36. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -3449,7 +3449,7 @@ var Input = /*#__PURE__*/React.forwardRef(function (props, forwardedRef) {
3449
3449
  });
3450
3450
  Input.displayName = "Input";
3451
3451
 
3452
- var _excluded$N = ["className", "variant", "size", "disabled", "loading", "children", "leftIcon", "rightIcon", "fullWidth"];
3452
+ var _excluded$N = ["className", "variant", "size", "disabled", "loading", "children", "leftIcon", "rightIcon", "fullWidth", "as", "onClick"];
3453
3453
  function ownKeys$T(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3454
3454
  function _objectSpread$T(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$T(Object(t), true).forEach(function (r) { _defineProperty$4(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$T(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3455
3455
  var Button = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
@@ -3464,12 +3464,19 @@ var Button = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
3464
3464
  leftIcon = _ref.leftIcon,
3465
3465
  rightIcon = _ref.rightIcon,
3466
3466
  fullWidth = _ref.fullWidth,
3467
+ as = _ref.as,
3468
+ onClick = _ref.onClick,
3467
3469
  props = _objectWithoutProperties$1(_ref, _excluded$N);
3468
- var baseClasses = cn$1('inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none', {
3470
+ // `as` renders any element or component (an <a>, a router Link) with the
3471
+ // Button's look; a bare `href` implies an <a>.
3472
+ var Component = as || (props.href !== undefined ? 'a' : 'button');
3473
+ var isNativeButton = Component === 'button';
3474
+ var isDisabled = disabled || loading;
3475
+ var baseClasses = cn$1('inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:pointer-events-none', {
3469
3476
  // Variants
3470
3477
  'bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500': variant === 'primary',
3471
3478
  'bg-secondary-100 text-secondary-900 hover:bg-secondary-200 focus:ring-secondary-500': variant === 'secondary',
3472
- 'bg-transparent border border-gray-300 hover:bg-gray-50 focus:ring-primary-500': variant === 'outline',
3479
+ 'bg-transparent border border-gray-300 hover:bg-gray-50 focus:ring-primary-500 dark:border-gray-600 dark:text-gray-100 dark:hover:bg-gray-800 dark:focus:ring-offset-gray-900': variant === 'outline',
3473
3480
  'bg-transparent text-primary-600 hover:bg-primary-50 focus:ring-primary-500': variant === 'ghost',
3474
3481
  'bg-error-600 text-white hover:bg-error-700 focus:ring-error-500': variant === 'destructive',
3475
3482
  'bg-success-600 text-white hover:bg-success-700 focus:ring-success-500': variant === 'success',
@@ -3484,10 +3491,20 @@ var Button = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
3484
3491
  // Full width
3485
3492
  'w-full': fullWidth
3486
3493
  }, className);
3487
- return jsxRuntime.jsxs("button", _objectSpread$T(_objectSpread$T({
3494
+ return jsxRuntime.jsxs(Component, _objectSpread$T(_objectSpread$T(_objectSpread$T({
3488
3495
  ref: ref,
3489
- className: baseClasses,
3490
- disabled: disabled || loading
3496
+ className: baseClasses
3497
+ }, isNativeButton ? {
3498
+ disabled: isDisabled
3499
+ }
3500
+ // Links can't be `disabled`: announce it and swallow the click instead.
3501
+ : isDisabled && {
3502
+ 'aria-disabled': true,
3503
+ tabIndex: -1
3504
+ }), {}, {
3505
+ onClick: !isNativeButton && isDisabled ? function (e) {
3506
+ return e.preventDefault();
3507
+ } : onClick
3491
3508
  }, props), {}, {
3492
3509
  children: [loading && jsxRuntime.jsx(Loader2, {
3493
3510
  className: "mr-2 h-4 w-4 animate-spin"
@@ -3534,7 +3551,7 @@ function useLatestRef(value) {
3534
3551
  return ref;
3535
3552
  }
3536
3553
 
3537
- var _excluded$M = ["options", "value", "onChange", "placeholder", "label", "error", "helperText", "disabled", "required", "multiSelect", "searchable", "grouped", "onMenuItemRender", "className", "allowClear", "creatable", "onCreateOption", "onSearch", "loading", "selectAllOption", "closeOnSelect", "maxTagCount", "renderGroupLabel", "name"];
3554
+ var _excluded$M = ["options", "value", "onChange", "placeholder", "label", "error", "helperText", "disabled", "required", "multiSelect", "searchable", "grouped", "onMenuItemRender", "className", "allowClear", "creatable", "onCreateOption", "onSearch", "loading", "selectAllOption", "closeOnSelect", "maxTagCount", "renderGroupLabel", "name", "size"];
3538
3555
  function ownKeys$S(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3539
3556
  function _objectSpread$S(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$S(Object(t), true).forEach(function (r) { _defineProperty$4(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$S(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3540
3557
  /**
@@ -3635,6 +3652,14 @@ var highlight = function highlight() {
3635
3652
  }), label.slice(idx + term.length)]
3636
3653
  });
3637
3654
  };
3655
+ // Trigger sizes. min-h (not h) so multi-select tags can wrap. `md` keeps its
3656
+ // desktop look but grows to the 44px touch target on coarse pointers; `lg` is
3657
+ // 44px everywhere.
3658
+ var SIZE_CLASSES$1 = {
3659
+ sm: "min-h-8 px-2.5 py-1 text-xs",
3660
+ md: "min-h-[38px] px-3 py-2 text-sm [@media(pointer:coarse)]:min-h-11",
3661
+ lg: "min-h-11 px-4 py-2.5 text-base"
3662
+ };
3638
3663
  var Select = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
3639
3664
  var _ref$options = _ref.options,
3640
3665
  options = _ref$options === void 0 ? [] : _ref$options,
@@ -3671,6 +3696,8 @@ var Select = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
3671
3696
  maxTagCount = _ref$maxTagCount === void 0 ? 3 : _ref$maxTagCount,
3672
3697
  renderGroupLabel = _ref.renderGroupLabel,
3673
3698
  name = _ref.name,
3699
+ _ref$size = _ref.size,
3700
+ size = _ref$size === void 0 ? "md" : _ref$size,
3674
3701
  props = _objectWithoutProperties$1(_ref, _excluded$M);
3675
3702
  var _useState = React.useState(false),
3676
3703
  _useState2 = _slicedToArray(_useState, 2),
@@ -4241,7 +4268,7 @@ var Select = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
4241
4268
  "aria-labelledby": label ? "".concat(labelId, " ").concat(valueId) : valueId,
4242
4269
  "aria-describedby": describedBy,
4243
4270
  "aria-invalid": error ? true : undefined,
4244
- className: cn$1("flex w-full items-center justify-between rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent disabled:cursor-not-allowed disabled:opacity-50", {
4271
+ className: cn$1("flex w-full items-center justify-between rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent disabled:cursor-not-allowed disabled:opacity-50", SIZE_CLASSES$1[size] || SIZE_CLASSES$1.md, {
4245
4272
  "border-error-500 focus:ring-error-500": error,
4246
4273
  "text-primary-500": !selectedOptions.length,
4247
4274
  "pr-14": showClearButton
@@ -17238,7 +17265,8 @@ var Radio = /*#__PURE__*/React.forwardRef(function Radio(_ref, ref) {
17238
17265
  props = _objectWithoutProperties$1(_ref, _excluded$A);
17239
17266
  var autoId = React.useId();
17240
17267
  var inputId = id || "".concat(autoId, "-radio");
17241
- var accentColor = radioColor || "blue";
17268
+ // Default to the brand's primary token so the radio follows theming.
17269
+ var accentColor = radioColor || "rgb(var(--dt-primary-600))";
17242
17270
  var _useState = React.useState(!!defaultChecked),
17243
17271
  _useState2 = _slicedToArray(_useState, 2),
17244
17272
  internalChecked = _useState2[0],
@@ -17296,7 +17324,7 @@ var Radio = /*#__PURE__*/React.forwardRef(function Radio(_ref, ref) {
17296
17324
  "--radio-accent": accentColor
17297
17325
  },
17298
17326
  children: jsxRuntime.jsx("span", {
17299
- className: cn("flex items-center justify-center rounded-full border transition-colors duration-150 ease-in-out border-slate-300", sizeClasses.outer, disabled ? "bg-slate-100" : "bg-white", isChecked ? "shadow-inner" : ""),
17327
+ className: cn("flex items-center justify-center rounded-full border transition-colors duration-150 ease-in-out border-slate-300 dark:border-gray-600", sizeClasses.outer, disabled ? "bg-slate-100 dark:bg-gray-800" : "bg-white dark:bg-gray-900", isChecked ? "shadow-inner" : ""),
17300
17328
  children: jsxRuntime.jsx("span", {
17301
17329
  "aria-hidden": true,
17302
17330
  className: cn("rounded-full transform transition-all duration-200 ease-out", sizeClasses.dot),
@@ -20867,11 +20895,28 @@ function _objectSpread$o(e) { for (var r = 1; r < arguments.length; r++) { var t
20867
20895
  var hasBadge = function hasBadge(badge) {
20868
20896
  return badge === 0 || Boolean(badge);
20869
20897
  };
20870
- var FootNav = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
20871
- var _ref$items = _ref.items,
20872
- items = _ref$items === void 0 ? [] : _ref$items,
20873
- className = _ref.className,
20874
- props = _objectWithoutProperties$1(_ref, _excluded$k);
20898
+ // Only aria-* and data-* item keys reach the DOM (e.g. aria-haspopup="dialog"
20899
+ // on an item that opens a sheet); the rest of the item is data, not attributes.
20900
+ var passthrough = function passthrough(item) {
20901
+ return Object.fromEntries(Object.entries(item).filter(function (_ref) {
20902
+ var _ref2 = _slicedToArray(_ref, 1),
20903
+ key = _ref2[0];
20904
+ return /^(aria|data)-/.test(key);
20905
+ }));
20906
+ };
20907
+ var Badge$1 = function Badge(_ref3) {
20908
+ var badge = _ref3.badge;
20909
+ return jsxRuntime.jsx("span", {
20910
+ className: "absolute -top-1.5 left-full -ml-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-error-600 px-1 text-micro font-semibold text-white",
20911
+ "data-slot": "badge",
20912
+ children: badge
20913
+ });
20914
+ };
20915
+ var FootNav = /*#__PURE__*/React.forwardRef(function (_ref4, ref) {
20916
+ var _ref4$items = _ref4.items,
20917
+ items = _ref4$items === void 0 ? [] : _ref4$items,
20918
+ className = _ref4.className,
20919
+ props = _objectWithoutProperties$1(_ref4, _excluded$k);
20875
20920
  return jsxRuntime.jsx("nav", _objectSpread$o(_objectSpread$o({
20876
20921
  ref: ref,
20877
20922
  className: cn$1('fixed bottom-0 left-0 right-0 bg-surface border-t border-line px-2 py-2 md:hidden', className)
@@ -20880,27 +20925,37 @@ var FootNav = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
20880
20925
  className: "flex items-stretch justify-around gap-1",
20881
20926
  children: items.map(function (item, index) {
20882
20927
  var _item$id;
20883
- return jsxRuntime.jsxs("button", {
20928
+ var isAction = item.variant === 'action';
20929
+ return jsxRuntime.jsxs("button", _objectSpread$o(_objectSpread$o({
20884
20930
  type: "button",
20885
20931
  "aria-current": item.active ? 'page' : undefined,
20886
- className: cn$1('relative flex min-w-0 flex-1 flex-col items-center gap-1 rounded-md px-1 py-2 text-xs transition-colors', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-1 motion-reduce:transition-none', item.active ? 'bg-primary-50 font-semibold text-primary-700 dark:bg-primary-900/40 dark:text-primary-300' : 'font-medium text-muted hover:bg-gray-100 hover:text-foreground dark:hover:bg-gray-800'),
20932
+ "data-variant": isAction ? 'action' : undefined
20933
+ }, passthrough(item)), {}, {
20934
+ className: cn$1('relative flex min-w-0 flex-1 flex-col items-center gap-1 rounded-md px-1 py-2 text-xs transition-colors', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-1 motion-reduce:transition-none', isAction ? 'group justify-start py-0 font-medium text-muted' : item.active ? 'bg-primary-50 font-semibold text-primary-700 dark:bg-primary-900/40 dark:text-primary-300' : 'font-medium text-muted hover:bg-gray-100 hover:text-foreground dark:hover:bg-gray-800'),
20887
20935
  onClick: function onClick() {
20888
20936
  var _item$onClick;
20889
20937
  return (_item$onClick = item.onClick) === null || _item$onClick === void 0 ? void 0 : _item$onClick.call(item, item);
20890
20938
  },
20891
- children: [jsxRuntime.jsxs("span", {
20939
+ children: [isAction ?
20940
+ // Raised primary circle (FAB): 56px, lifted above the bar and
20941
+ // ringed in the surface colour so it reads as cut out of the bar.
20942
+ jsxRuntime.jsxs("span", {
20943
+ className: "relative -mt-7 inline-flex h-14 w-14 items-center justify-center rounded-full bg-primary-600 text-2xl text-white shadow-lg ring-4 ring-surface transition-colors group-hover:bg-primary-700 motion-reduce:transition-none",
20944
+ "data-slot": "icon",
20945
+ children: [item.icon, hasBadge(item.badge) && jsxRuntime.jsx(Badge$1, {
20946
+ badge: item.badge
20947
+ })]
20948
+ }) : jsxRuntime.jsxs("span", {
20892
20949
  className: "relative inline-flex text-lg",
20893
20950
  "data-slot": "icon",
20894
- children: [item.icon, hasBadge(item.badge) && jsxRuntime.jsx("span", {
20895
- className: "absolute -top-1.5 left-full -ml-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-error-600 px-1 text-micro font-semibold text-white",
20896
- "data-slot": "badge",
20897
- children: item.badge
20951
+ children: [item.icon, hasBadge(item.badge) && jsxRuntime.jsx(Badge$1, {
20952
+ badge: item.badge
20898
20953
  })]
20899
20954
  }), jsxRuntime.jsx("span", {
20900
20955
  className: "block max-w-full truncate",
20901
20956
  children: item.label
20902
20957
  })]
20903
- }, (_item$id = item.id) !== null && _item$id !== void 0 ? _item$id : index);
20958
+ }), (_item$id = item.id) !== null && _item$id !== void 0 ? _item$id : index);
20904
20959
  })
20905
20960
  })
20906
20961
  }));
@@ -20976,6 +21031,11 @@ var Breadcrumbs = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
20976
21031
  });
20977
21032
  Breadcrumbs.displayName = 'Breadcrumbs';
20978
21033
 
21034
+ // Internal (not exported). Grows an icon-only control to the 44×44 px minimum
21035
+ // touch target (WCAG 2.5.5, Apple HIG) on coarse pointers only, so desktop
21036
+ // layouts keep their compact size. Pair with inline-flex centering.
21037
+ var TOUCH_TARGET = "[@media(pointer:coarse)]:min-h-11 [@media(pointer:coarse)]:min-w-11";
21038
+
20979
21039
  var _excluded$i = ["isOpen", "onClose", "title", "children", "size", "dismissible", "showCloseButton", "footer", "className", "onOpen"],
20980
21040
  _excluded2$2 = ["children", "className"],
20981
21041
  _excluded3$2 = ["children", "className"],
@@ -21112,11 +21172,12 @@ var Dialog = /*#__PURE__*/React.forwardRef(function (_ref, _ref3) {
21112
21172
  children: title
21113
21173
  }), showCloseButton && jsxRuntime.jsx("button", {
21114
21174
  type: "button",
21115
- className: "rounded-md p-1 text-primary-400 hover:text-primary-600 hover:bg-gray-100",
21175
+ className: cn$1('inline-flex items-center justify-center rounded-md p-1 text-primary-400 hover:text-primary-600 hover:bg-gray-100', TOUCH_TARGET),
21116
21176
  onClick: onClose,
21117
21177
  "aria-label": "Close dialog",
21118
21178
  children: jsxRuntime.jsx(X, {
21119
- className: "h-5 w-5"
21179
+ className: "h-5 w-5",
21180
+ "aria-hidden": "true"
21120
21181
  })
21121
21182
  })]
21122
21183
  }), jsxRuntime.jsx("div", {
@@ -21635,7 +21696,68 @@ function useScrollLock(active) {
21635
21696
  }, [active]);
21636
21697
  }
21637
21698
 
21638
- var _excluded$f = ["isOpen", "onClose", "title", "description", "children", "placement", "size", "dismissible", "showCloseButton", "footer", "initialFocusRef", "className"],
21699
+ // Internal (not exported). Drag-down-to-dismiss for a bottom sheet. Spread
21700
+ // `dragProps` on the grab zone (handle + header), not the scrolling body, so a
21701
+ // swipe never fights the content's own scroll. Past `threshold` px (or a quick
21702
+ // flick) the drag calls onClose; otherwise the sheet snaps back.
21703
+ var FLICK_VELOCITY = 0.5; // px per ms
21704
+ function useSwipeToClose(_ref) {
21705
+ var enabled = _ref.enabled,
21706
+ onClose = _ref.onClose,
21707
+ _ref$threshold = _ref.threshold,
21708
+ threshold = _ref$threshold === void 0 ? 80 : _ref$threshold;
21709
+ var _useState = React.useState(0),
21710
+ _useState2 = _slicedToArray(_useState, 2),
21711
+ offset = _useState2[0],
21712
+ setOffset = _useState2[1];
21713
+ var _useState3 = React.useState(false),
21714
+ _useState4 = _slicedToArray(_useState3, 2),
21715
+ dragging = _useState4[0],
21716
+ setDragging = _useState4[1];
21717
+ var start = React.useRef(null);
21718
+ var end = React.useCallback(function (e) {
21719
+ if (!start.current) return;
21720
+ var dy = Math.max(0, e.clientY - start.current.y);
21721
+ var elapsed = Math.max(1, Date.now() - start.current.t);
21722
+ start.current = null;
21723
+ setDragging(false);
21724
+ setOffset(0);
21725
+ if (dy >= threshold || dy > 16 && dy / elapsed >= FLICK_VELOCITY) onClose === null || onClose === void 0 || onClose();
21726
+ }, [onClose, threshold]);
21727
+ if (!enabled) return {
21728
+ dragProps: {},
21729
+ offset: 0,
21730
+ dragging: false
21731
+ };
21732
+ return {
21733
+ offset: offset,
21734
+ dragging: dragging,
21735
+ dragProps: {
21736
+ onPointerDown: function onPointerDown(e) {
21737
+ var _e$currentTarget$setP, _e$currentTarget;
21738
+ if (e.button !== undefined && e.button !== 0) return;
21739
+ start.current = {
21740
+ y: e.clientY,
21741
+ t: Date.now()
21742
+ };
21743
+ setDragging(true);
21744
+ (_e$currentTarget$setP = (_e$currentTarget = e.currentTarget).setPointerCapture) === null || _e$currentTarget$setP === void 0 || _e$currentTarget$setP.call(_e$currentTarget, e.pointerId);
21745
+ },
21746
+ onPointerMove: function onPointerMove(e) {
21747
+ if (!start.current) return;
21748
+ setOffset(Math.max(0, e.clientY - start.current.y));
21749
+ },
21750
+ onPointerUp: end,
21751
+ onPointerCancel: function onPointerCancel() {
21752
+ start.current = null;
21753
+ setDragging(false);
21754
+ setOffset(0);
21755
+ }
21756
+ }
21757
+ };
21758
+ }
21759
+
21760
+ var _excluded$f = ["isOpen", "onClose", "title", "description", "children", "placement", "size", "dismissible", "showCloseButton", "footer", "initialFocusRef", "handle", "swipeToClose", "className", "style"],
21639
21761
  _excluded2$1 = ["className"],
21640
21762
  _excluded3$1 = ["className"],
21641
21763
  _excluded4$1 = ["className"];
@@ -21645,7 +21767,8 @@ var PLACEMENT_CLASSES = {
21645
21767
  right: "inset-y-0 right-0 h-full w-full border-l",
21646
21768
  left: "inset-y-0 left-0 h-full w-full border-r",
21647
21769
  top: "inset-x-0 top-0 w-full border-b",
21648
- bottom: "inset-x-0 bottom-0 w-full border-t"
21770
+ // Bottom sheet: rounded top and room for the home indicator (safe area).
21771
+ bottom: "inset-x-0 bottom-0 w-full overflow-hidden rounded-t-2xl border-t pb-[env(safe-area-inset-bottom)]"
21649
21772
  };
21650
21773
  var SIDE_SIZES = {
21651
21774
  sm: "max-w-xs",
@@ -21668,7 +21791,7 @@ var CloseButton = function CloseButton(_ref) {
21668
21791
  type: "button",
21669
21792
  onClick: onClose,
21670
21793
  "aria-label": "Close drawer",
21671
- className: cn$1("rounded-md p-1 text-gray-500 hover:bg-gray-100 hover:text-gray-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2", "dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200 dark:focus:ring-offset-gray-900", className),
21794
+ className: cn$1("inline-flex items-center justify-center rounded-md p-1 text-gray-500 hover:bg-gray-100 hover:text-gray-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2", "dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200 dark:focus:ring-offset-gray-900", TOUCH_TARGET, className),
21672
21795
  children: jsxRuntime.jsx(X, {
21673
21796
  className: "h-5 w-5",
21674
21797
  "aria-hidden": "true"
@@ -21691,7 +21814,11 @@ var Drawer = /*#__PURE__*/React.forwardRef(function (_ref2, _ref3) {
21691
21814
  showCloseButton = _ref2$showCloseButton === void 0 ? true : _ref2$showCloseButton,
21692
21815
  footer = _ref2.footer,
21693
21816
  initialFocusRef = _ref2.initialFocusRef,
21817
+ handle = _ref2.handle,
21818
+ _ref2$swipeToClose = _ref2.swipeToClose,
21819
+ swipeToClose = _ref2$swipeToClose === void 0 ? false : _ref2$swipeToClose,
21694
21820
  className = _ref2.className,
21821
+ style = _ref2.style,
21695
21822
  props = _objectWithoutProperties$1(_ref2, _excluded$f);
21696
21823
  var panelRef = React.useRef(null);
21697
21824
  var titleId = "drawer-title-".concat(React.useId());
@@ -21701,6 +21828,17 @@ var Drawer = /*#__PURE__*/React.forwardRef(function (_ref2, _ref3) {
21701
21828
  // Without title/description/footer the children are the panel content, so
21702
21829
  // Drawer.Header / Drawer.Body / Drawer.Footer compose without double padding.
21703
21830
  var composed = !title && !description && !footer;
21831
+ var showHandle = handle !== null && handle !== void 0 ? handle : side === "bottom";
21832
+ var swipe = useSwipeToClose({
21833
+ enabled: swipeToClose && side === "bottom" && dismissible,
21834
+ onClose: onClose
21835
+ });
21836
+ // The grab zone (handle + header) starts a swipe; the scrolling body never does.
21837
+ var grabProps = swipeToClose && side === "bottom" && dismissible ? _objectSpread$j(_objectSpread$j({}, swipe.dragProps), {}, {
21838
+ className: "touch-none cursor-grab"
21839
+ }) : {
21840
+ className: ""
21841
+ };
21704
21842
  useScrollLock(isOpen);
21705
21843
  useFocusTrap(panelRef, {
21706
21844
  active: isOpen,
@@ -21719,7 +21857,7 @@ var Drawer = /*#__PURE__*/React.forwardRef(function (_ref2, _ref3) {
21719
21857
  onClick: function onClick() {
21720
21858
  return dismissible && (onClose === null || onClose === void 0 ? void 0 : onClose());
21721
21859
  }
21722
- }), jsxRuntime.jsx("div", _objectSpread$j(_objectSpread$j({
21860
+ }), jsxRuntime.jsxs("div", _objectSpread$j(_objectSpread$j({
21723
21861
  ref: function ref(node) {
21724
21862
  return setRefs$1(node, panelRef, _ref3);
21725
21863
  },
@@ -21729,16 +21867,27 @@ var Drawer = /*#__PURE__*/React.forwardRef(function (_ref2, _ref3) {
21729
21867
  "aria-describedby": description ? descriptionId : undefined,
21730
21868
  tabIndex: -1,
21731
21869
  "data-placement": side,
21732
- className: cn$1("absolute flex flex-col border-gray-200 bg-white text-gray-900 shadow-xl focus:outline-none", "dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100", "animate-fade-in motion-reduce:animate-none", PLACEMENT_CLASSES[side], (isEdge ? EDGE_SIZES : SIDE_SIZES)[size] || (isEdge ? EDGE_SIZES.md : SIDE_SIZES.md), className)
21870
+ className: cn$1("absolute flex flex-col border-gray-200 bg-white text-gray-900 shadow-xl focus:outline-none", "dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100", "animate-fade-in motion-reduce:animate-none", PLACEMENT_CLASSES[side], (isEdge ? EDGE_SIZES : SIDE_SIZES)[size] || (isEdge ? EDGE_SIZES.md : SIDE_SIZES.md), !swipe.dragging && "transition-transform duration-200 ease-out motion-reduce:transition-none", className),
21871
+ style: swipe.offset ? _objectSpread$j(_objectSpread$j({}, style), {}, {
21872
+ transform: "translateY(".concat(swipe.offset, "px)")
21873
+ }) : style
21733
21874
  }, props), {}, {
21734
- children: composed ? jsxRuntime.jsxs(jsxRuntime.Fragment, {
21875
+ children: [showHandle && jsxRuntime.jsx("div", _objectSpread$j(_objectSpread$j({
21876
+ "aria-hidden": "true",
21877
+ "data-testid": "drawer-handle"
21878
+ }, grabProps), {}, {
21879
+ className: cn$1("flex shrink-0 justify-center pb-1 pt-3", grabProps.className),
21880
+ children: jsxRuntime.jsx("span", {
21881
+ className: "h-1.5 w-10 rounded-full bg-gray-300 dark:bg-gray-600"
21882
+ })
21883
+ })), composed ? jsxRuntime.jsxs(jsxRuntime.Fragment, {
21735
21884
  children: [showCloseButton && jsxRuntime.jsx(CloseButton, {
21736
21885
  onClose: onClose,
21737
21886
  className: "absolute right-3 top-3"
21738
21887
  }), children]
21739
21888
  }) : jsxRuntime.jsxs(jsxRuntime.Fragment, {
21740
- children: [jsxRuntime.jsxs("div", {
21741
- className: "flex items-start justify-between gap-4 border-b border-gray-200 px-6 py-4 dark:border-gray-700",
21889
+ children: [jsxRuntime.jsxs("div", _objectSpread$j(_objectSpread$j({}, grabProps), {}, {
21890
+ className: cn$1("flex items-start justify-between gap-4 border-b border-gray-200 px-6 py-4 dark:border-gray-700", showHandle && "pt-2", grabProps.className),
21742
21891
  children: [jsxRuntime.jsxs("div", {
21743
21892
  className: "min-w-0",
21744
21893
  children: [title && jsxRuntime.jsx("h2", {
@@ -21753,14 +21902,14 @@ var Drawer = /*#__PURE__*/React.forwardRef(function (_ref2, _ref3) {
21753
21902
  }), showCloseButton && jsxRuntime.jsx(CloseButton, {
21754
21903
  onClose: onClose
21755
21904
  })]
21756
- }), jsxRuntime.jsx("div", {
21905
+ })), jsxRuntime.jsx("div", {
21757
21906
  className: "min-h-0 flex-1 overflow-y-auto px-6 py-4",
21758
21907
  children: children
21759
21908
  }), footer && jsxRuntime.jsx("div", {
21760
21909
  className: "flex items-center justify-end gap-3 border-t border-gray-200 px-6 py-4 dark:border-gray-700",
21761
21910
  children: footer
21762
21911
  })]
21763
- })
21912
+ })]
21764
21913
  }))]
21765
21914
  }), document.body);
21766
21915
  });
@@ -22168,6 +22317,7 @@ var Badge = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
22168
22317
  success: 'bg-success-100 text-success-800',
22169
22318
  warning: 'bg-warning-100 text-warning-800',
22170
22319
  error: 'bg-error-100 text-error-800',
22320
+ info: 'bg-info-100 text-info-800',
22171
22321
  outline: 'border border-gray-300'
22172
22322
  };
22173
22323
  var sizes = {
@@ -22420,9 +22570,12 @@ var Stepper = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
22420
22570
  "aria-label": "Progress"
22421
22571
  }, props), {}, {
22422
22572
  children: steps.map(function (step, stepIndex) {
22573
+ var _step$title;
22423
22574
  var status = getStepStatus(stepIndex);
22424
22575
  var isLast = stepIndex === steps.length - 1;
22425
22576
  var colors = statusColors[status];
22577
+ // `label` is accepted as an alias for `title` (the docs used `label`).
22578
+ var stepTitle = (_step$title = step.title) !== null && _step$title !== void 0 ? _step$title : step.label;
22426
22579
  return jsxRuntime.jsxs("div", {
22427
22580
  className: cn$1('flex items-center', {
22428
22581
  'flex-col': orientation === 'horizontal',
@@ -22442,7 +22595,7 @@ var Stepper = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
22442
22595
  },
22443
22596
  disabled: !onStepClick || stepIndex > currentStep,
22444
22597
  "aria-current": status === 'current' ? 'step' : undefined,
22445
- "aria-label": "Step ".concat(stepIndex + 1).concat(step.title ? ": ".concat(step.title) : '').concat(status === 'completed' ? ' (completed)' : ''),
22598
+ "aria-label": "Step ".concat(stepIndex + 1).concat(stepTitle ? ": ".concat(stepTitle) : '').concat(status === 'completed' ? ' (completed)' : ''),
22446
22599
  children: status === 'completed' ? jsxRuntime.jsx(Check, {
22447
22600
  className: "h-4 w-4",
22448
22601
  "aria-hidden": "true"
@@ -22460,7 +22613,7 @@ var Stepper = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
22460
22613
  }),
22461
22614
  children: [jsxRuntime.jsx("span", {
22462
22615
  className: cn$1('text-sm font-medium', colors.text),
22463
- children: step.title
22616
+ children: stepTitle
22464
22617
  }), step.description && jsxRuntime.jsx("span", {
22465
22618
  className: "text-xs text-muted",
22466
22619
  children: step.description
@@ -23560,7 +23713,7 @@ function _objectSpread$7(e) { for (var r = 1; r < arguments.length; r++) { var t
23560
23713
  // One map from domain state to tone + human label; unknown states are humanized, never shown raw.
23561
23714
  var TONE_TO_VARIANT = {
23562
23715
  neutral: 'secondary',
23563
- info: 'primary',
23716
+ info: 'info',
23564
23717
  success: 'success',
23565
23718
  warning: 'warning',
23566
23719
  danger: 'error',
@@ -23,6 +23,14 @@ export interface ButtonProps extends BaseComponentProps, Omit<ButtonHTMLAttribut
23
23
  children?: ReactNode;
24
24
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
25
25
  type?: 'button' | 'submit' | 'reset';
26
+ /** Render as another element or component (e.g. `'a'` or a router `Link`) with the Button's look. */
27
+ as?: ElementType;
28
+ /** Link target; a bare `href` renders an `<a>`. */
29
+ href?: string;
30
+ target?: string;
31
+ rel?: string;
32
+ /** Router destination, forwarded when `as` is a router Link. */
33
+ to?: string | Record<string, unknown>;
26
34
  }
27
35
  export interface InputProps extends BaseComponentProps, Omit<InputHTMLAttributes<HTMLInputElement>, 'className'> {
28
36
  type?: 'text' | 'password' | 'email' | 'search' | 'number' | 'tel' | 'url';
@@ -78,6 +86,8 @@ export interface SelectProps extends BaseComponentProps {
78
86
  name?: string;
79
87
  onMenuItemRender?: (option: SelectOption) => ReactNode;
80
88
  renderGroupLabel?: (label: string) => ReactNode;
89
+ /** Trigger size. `md` grows to the 44px touch target on coarse pointers; `lg` is 44px everywhere. */
90
+ size?: 'sm' | 'md' | 'lg';
81
91
  }
82
92
  export interface CheckboxProps extends BaseComponentProps {
83
93
  checked?: boolean;
@@ -393,6 +403,10 @@ export interface SidebarItem extends NavItem {
393
403
  }
394
404
  export interface FootNavItem extends NavItem {
395
405
  badge?: string | number;
406
+ /** `'action'` renders a raised, circular primary button (FAB), e.g. a centre "+" that opens a sheet. */
407
+ variant?: 'default' | 'action';
408
+ /** aria-* and data-* keys are passed through to the item's button. */
409
+ [attr: `aria-${string}` | `data-${string}`]: string | number | boolean | undefined;
396
410
  }
397
411
  export interface MenuItem {
398
412
  id: string;
@@ -547,7 +561,7 @@ export interface AvatarProps extends BaseComponentProps {
547
561
  }
548
562
  export interface BadgeProps extends BaseComponentProps {
549
563
  children?: ReactNode;
550
- variant?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'outline';
564
+ variant?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' | 'outline';
551
565
  size?: Size;
552
566
  rounded?: boolean;
553
567
  }
@@ -603,13 +617,21 @@ export interface AccordionProps extends BaseComponentProps {
603
617
  transitionDuration?: number;
604
618
  }
605
619
  export interface StepperStep {
620
+ id?: string | number;
606
621
  title: string;
622
+ /** Alias for `title`; `title` wins when both are set. */
623
+ label?: string;
607
624
  description?: string;
608
625
  icon?: ReactNode;
609
626
  status?: 'completed' | 'current' | 'upcoming';
610
627
  }
628
+ /** A step named by `label` alone (the alias for `title`). */
629
+ export type StepperLabelStep = Omit<StepperStep, 'title' | 'label'> & {
630
+ label: string;
631
+ title?: string;
632
+ };
611
633
  export interface StepperProps extends BaseComponentProps {
612
- steps: StepperStep[];
634
+ steps: Array<StepperStep | StepperLabelStep>;
613
635
  currentStep: number;
614
636
  onStepClick?: (step: StepperStep, index: number) => void;
615
637
  orientation?: Orientation;
@@ -878,6 +900,10 @@ export interface DrawerProps extends BaseComponentProps, Omit<HTMLAttributes<HTM
878
900
  footer?: ReactNode;
879
901
  /** Element to focus when the drawer opens (defaults to the first focusable element). */
880
902
  initialFocusRef?: RefObject<HTMLElement>;
903
+ /** Decorative grab handle. Defaults to `true` for `placement="bottom"`. */
904
+ handle?: boolean;
905
+ /** Bottom sheet only: dragging the handle/header down past a threshold calls `onClose`. */
906
+ swipeToClose?: boolean;
881
907
  }
882
908
  export interface DrawerSectionProps extends BaseComponentProps, Omit<HTMLAttributes<HTMLDivElement>, 'className'> {
883
909
  children?: ReactNode;