@dreamtree-org/twreact-ui 1.3.11 → 2.0.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 (164) hide show
  1. package/README.md +40 -6
  2. package/ai-skills/dreamtree-ui.md +47 -9
  3. package/codemods/v2.cjs +198 -0
  4. package/dist/ai-schema.json +609 -33
  5. package/dist/components/blocks/AppShell.d.ts.map +1 -1
  6. package/dist/components/blocks/BottomActionBar.d.ts +6 -0
  7. package/dist/components/blocks/BottomActionBar.d.ts.map +1 -0
  8. package/dist/components/blocks/ConfirmDialog.d.ts.map +1 -1
  9. package/dist/components/blocks/PageHeader.d.ts.map +1 -1
  10. package/dist/components/core/Checkbox.d.ts.map +1 -1
  11. package/dist/components/core/DatePicker.d.ts.map +1 -1
  12. package/dist/components/core/DateRangePicker.d.ts.map +1 -1
  13. package/dist/components/core/Form.d.ts.map +1 -1
  14. package/dist/components/core/Input.d.ts.map +1 -1
  15. package/dist/components/core/Popover.d.ts.map +1 -1
  16. package/dist/components/core/ProgressBar.d.ts.map +1 -1
  17. package/dist/components/core/Rate.d.ts.map +1 -1
  18. package/dist/components/core/SearchField.d.ts +9 -0
  19. package/dist/components/core/SearchField.d.ts.map +1 -0
  20. package/dist/components/core/SegmentedControl.d.ts +8 -0
  21. package/dist/components/core/SegmentedControl.d.ts.map +1 -0
  22. package/dist/components/core/Select.d.ts.map +1 -1
  23. package/dist/components/core/SwipeableListItem.d.ts +4 -0
  24. package/dist/components/core/SwipeableListItem.d.ts.map +1 -0
  25. package/dist/components/core/SwipeableListItemGesture.d.ts +58 -0
  26. package/dist/components/core/SwipeableListItemGesture.d.ts.map +1 -0
  27. package/dist/components/core/Switch.d.ts.map +1 -1
  28. package/dist/components/core/Table/ActionMenu.d.ts +10 -3
  29. package/dist/components/core/Table/ActionMenu.d.ts.map +1 -1
  30. package/dist/components/core/Table/MobileCard.d.ts +10 -1
  31. package/dist/components/core/Table/MobileCard.d.ts.map +1 -1
  32. package/dist/components/core/Table/MobileFilters.d.ts +8 -2
  33. package/dist/components/core/Table/MobileFilters.d.ts.map +1 -1
  34. package/dist/components/core/Table/Table.d.ts.map +1 -1
  35. package/dist/components/core/Table/TableRow.d.ts +2 -1
  36. package/dist/components/core/Table/TableRow.d.ts.map +1 -1
  37. package/dist/components/core/Tabs.d.ts +3 -3
  38. package/dist/components/core/Tabs.d.ts.map +1 -1
  39. package/dist/components/core/ThreeDotPopover.d.ts +10 -22
  40. package/dist/components/core/ThreeDotPopover.d.ts.map +1 -1
  41. package/dist/components/core/Tooltip.d.ts +2 -1
  42. package/dist/components/core/Tooltip.d.ts.map +1 -1
  43. package/dist/components/core/internal/Calendar.d.ts +77 -0
  44. package/dist/components/core/internal/Calendar.d.ts.map +1 -0
  45. package/dist/components/core/menu/ActionMenuSurface.d.ts +42 -0
  46. package/dist/components/core/menu/ActionMenuSurface.d.ts.map +1 -0
  47. package/dist/components/core/menu/ActionSheetMenu.d.ts +17 -0
  48. package/dist/components/core/menu/ActionSheetMenu.d.ts.map +1 -0
  49. package/dist/components/core/menu/MenuPopover.d.ts +21 -0
  50. package/dist/components/core/menu/MenuPopover.d.ts.map +1 -0
  51. package/dist/components/core/menu/menuItems.d.ts +21 -0
  52. package/dist/components/core/menu/menuItems.d.ts.map +1 -0
  53. package/dist/components/core/menu/menuKeyboard.d.ts +16 -0
  54. package/dist/components/core/menu/menuKeyboard.d.ts.map +1 -0
  55. package/dist/components/feedback/ActionSheet.d.ts +4 -0
  56. package/dist/components/feedback/ActionSheet.d.ts.map +1 -0
  57. package/dist/components/feedback/Alert.d.ts.map +1 -1
  58. package/dist/components/feedback/Dialog.d.ts.map +1 -1
  59. package/dist/components/feedback/Drawer.d.ts.map +1 -1
  60. package/dist/components/feedback/Sheet.d.ts +4 -0
  61. package/dist/components/feedback/Sheet.d.ts.map +1 -0
  62. package/dist/components/feedback/Toast.d.ts +4 -3
  63. package/dist/components/feedback/Toast.d.ts.map +1 -1
  64. package/dist/components/feedback/ToastItem.d.ts +47 -0
  65. package/dist/components/feedback/ToastItem.d.ts.map +1 -0
  66. package/dist/components/feedback/ToastStack.d.ts +20 -0
  67. package/dist/components/feedback/ToastStack.d.ts.map +1 -0
  68. package/dist/components/feedback/ToastSwipe.d.ts +22 -0
  69. package/dist/components/feedback/ToastSwipe.d.ts.map +1 -0
  70. package/dist/components/feedback/Tour.d.ts.map +1 -1
  71. package/dist/components/feedback/sheetCore.d.ts +30 -0
  72. package/dist/components/feedback/sheetCore.d.ts.map +1 -0
  73. package/dist/components/navigation/Navbar.d.ts.map +1 -1
  74. package/dist/components/navigation/Sidebar.d.ts +6 -0
  75. package/dist/components/navigation/Sidebar.d.ts.map +1 -1
  76. package/dist/components/rich-text/RichTextEditor.d.ts.map +1 -1
  77. package/dist/components/rich-text/RichTextToolbar.d.ts +6 -2
  78. package/dist/components/rich-text/RichTextToolbar.d.ts.map +1 -1
  79. package/dist/components/utility/PullToRefresh.d.ts +5 -0
  80. package/dist/components/utility/PullToRefresh.d.ts.map +1 -0
  81. package/dist/esm/components/blocks/AppShell.js +97 -90
  82. package/dist/esm/components/blocks/BottomActionBar.js +132 -0
  83. package/dist/esm/components/blocks/ConfirmDialog.js +40 -4
  84. package/dist/esm/components/blocks/PageHeader.js +16 -3
  85. package/dist/esm/components/core/Checkbox.js +13 -24
  86. package/dist/esm/components/core/DatePicker.js +117 -304
  87. package/dist/esm/components/core/DateRangePicker.js +260 -308
  88. package/dist/esm/components/core/Form.js +11 -2
  89. package/dist/esm/components/core/Input.js +13 -18
  90. package/dist/esm/components/core/Popover.js +59 -48
  91. package/dist/esm/components/core/ProgressBar.js +36 -64
  92. package/dist/esm/components/core/Rate.js +5 -10
  93. package/dist/esm/components/core/SearchField.js +311 -0
  94. package/dist/esm/components/core/SegmentedControl.js +189 -0
  95. package/dist/esm/components/core/Select.js +185 -190
  96. package/dist/esm/components/core/SwipeableListItem.js +286 -0
  97. package/dist/esm/components/core/SwipeableListItemGesture.js +219 -0
  98. package/dist/esm/components/core/Switch.js +11 -13
  99. package/dist/esm/components/core/Table/ActionMenu.js +45 -231
  100. package/dist/esm/components/core/Table/ColumnMenu.js +1 -1
  101. package/dist/esm/components/core/Table/MobileCard.js +66 -16
  102. package/dist/esm/components/core/Table/MobileFilters.js +181 -10
  103. package/dist/esm/components/core/Table/Table.js +149 -102
  104. package/dist/esm/components/core/Table/TableRow.js +10 -2
  105. package/dist/esm/components/core/Tabs.js +7 -7
  106. package/dist/esm/components/core/ThreeDotPopover.js +93 -99
  107. package/dist/esm/components/core/Tooltip.js +138 -145
  108. package/dist/esm/components/core/internal/Calendar.js +381 -0
  109. package/dist/esm/components/core/menu/ActionMenuSurface.js +110 -0
  110. package/dist/esm/components/core/menu/ActionSheetMenu.js +140 -0
  111. package/dist/esm/components/core/menu/MenuPopover.js +177 -0
  112. package/dist/esm/components/core/menu/menuItems.js +61 -0
  113. package/dist/esm/components/core/menu/menuKeyboard.js +54 -0
  114. package/dist/esm/components/feedback/ActionSheet.js +55 -0
  115. package/dist/esm/components/feedback/Alert.js +63 -53
  116. package/dist/esm/components/feedback/Dialog.js +141 -53
  117. package/dist/esm/components/feedback/Drawer.js +130 -132
  118. package/dist/esm/components/feedback/Sheet.js +125 -0
  119. package/dist/esm/components/feedback/Toast.js +21 -291
  120. package/dist/esm/components/feedback/ToastItem.js +348 -0
  121. package/dist/esm/components/feedback/ToastStack.js +130 -0
  122. package/dist/esm/components/feedback/ToastSwipe.js +52 -0
  123. package/dist/esm/components/feedback/Tour.js +107 -30
  124. package/dist/esm/components/feedback/sheetCore.js +494 -0
  125. package/dist/esm/components/navigation/Dropdown.js +1 -1
  126. package/dist/esm/components/navigation/Navbar.js +172 -37
  127. package/dist/esm/components/navigation/Sidebar.js +263 -111
  128. package/dist/esm/components/rich-text/RichTextEditor.js +9 -3
  129. package/dist/esm/components/rich-text/RichTextToolbar.js +29 -9
  130. package/dist/esm/components/utility/Portal.js +31 -0
  131. package/dist/esm/components/utility/PullToRefresh.js +229 -0
  132. package/dist/esm/hooks/useFloatingPosition.js +82 -0
  133. package/dist/esm/hooks/useHaptics.js +65 -0
  134. package/dist/esm/hooks/usePresence.js +58 -0
  135. package/dist/esm/hooks/usePresentation.js +21 -0
  136. package/dist/esm/hooks/useSheetDrag.js +220 -0
  137. package/dist/esm/index.js +13 -2
  138. package/dist/esm/utils/position.js +10 -63
  139. package/dist/hooks/useFloatingPosition.d.ts +8 -4
  140. package/dist/hooks/useFloatingPosition.d.ts.map +1 -1
  141. package/dist/hooks/useHaptics.d.ts +19 -0
  142. package/dist/hooks/useHaptics.d.ts.map +1 -0
  143. package/dist/hooks/useSheetDrag.d.ts +31 -0
  144. package/dist/hooks/useSheetDrag.d.ts.map +1 -0
  145. package/dist/index.css +1 -1
  146. package/dist/index.d.ts +9 -1
  147. package/dist/index.esm.css +1 -1
  148. package/dist/index.esm.js +8851 -3198
  149. package/dist/index.js +8856 -3195
  150. package/dist/rich-text/index.js +41 -11
  151. package/dist/types/blocks.d.ts +32 -2
  152. package/dist/types/blocks.d.ts.map +1 -1
  153. package/dist/types/index.d.ts +353 -19
  154. package/dist/types/index.d.ts.map +1 -1
  155. package/dist/types/rich-text.d.ts +5 -0
  156. package/dist/types/rich-text.d.ts.map +1 -1
  157. package/dist/utils/position.d.ts +0 -15
  158. package/dist/utils/position.d.ts.map +1 -1
  159. package/doc/README.md +15 -0
  160. package/mcp/catalog.snapshot.json +1398 -464
  161. package/package.json +7 -2
  162. package/dist/esm/hooks/useSwipeToClose.js +0 -66
  163. package/dist/hooks/useSwipeToClose.d.ts +0 -24
  164. package/dist/hooks/useSwipeToClose.d.ts.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2, toConsumableArray as _toConsumableArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import React, { useMemo, useState, useEffect, useId, useRef, useLayoutEffect } from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
6
  import { ChevronRight, ChevronLeft, ChevronDown } from 'lucide-react';
@@ -8,8 +8,109 @@ import Tooltip from '../core/Tooltip.js';
8
8
  import { useLatestRef } from '../../hooks/useLatestRef.js';
9
9
  import { useFocusTrap } from '../../hooks/useFocusTrap.js';
10
10
  import { useScrollLock } from '../../hooks/useScrollLock.js';
11
+ import { useMediaQuery } from '../../hooks/useMediaQuery.js';
12
+ import { useSheetDrag, FLICK_VELOCITY } from '../../hooks/useSheetDrag.js';
11
13
 
12
- var _excluded = ["items", "collapsed", "onToggle", "className", "containerClassName", "stickyOffset", "logo", "footer", "user", "onUserClick", "drawerPosition", "isMobileOpen", "setIsMobileOpen", "showCollapsedTooltips", "tooltipOptions", "linkAs", "expandedIds", "defaultExpandedIds", "onExpandedChange"];
14
+ var _excluded = ["items", "collapsed", "onToggle", "className", "containerClassName", "stickyOffset", "logo", "footer", "user", "onUserClick", "drawerPosition", "isMobileOpen", "setIsMobileOpen", "showCollapsedTooltips", "tooltipOptions", "linkAs", "expandedIds", "defaultExpandedIds", "onExpandedChange", "swipeable"];
15
+ // From `md` up the sidebar is an in-flow sticky column; below it, the same
16
+ // tree becomes an off-canvas drawer (modal dialog while open).
17
+ var SIDEBAR_DESKTOP_QUERY = "(min-width: 768px)";
18
+ // Swipe gestures (phones). The drawer closes past CLOSE_DISTANCE or on a
19
+ // flick; an edge swipe opens it past OPEN_DISTANCE.
20
+ var EDGE_WIDTH = 20; // px from the screen edge where an open swipe may start
21
+ var OPEN_DISTANCE = 56; // px
22
+ var CLOSE_DISTANCE = 80; // px
23
+ var FLICK_MIN_DISTANCE = 16; // px
24
+ var SWIPE_SLOP = 6; // px before the edge gesture picks an axis
25
+ /**
26
+ * Edge swipe to open (touch only). Window-level listeners, so nothing covers
27
+ * the page edge and taps there still land. Returns the live drag distance
28
+ * (px toward open) while a swipe is in progress, else 0.
29
+ */
30
+ function useEdgeSwipe(_ref) {
31
+ var enabled = _ref.enabled,
32
+ fromRight = _ref.fromRight,
33
+ onOpen = _ref.onOpen;
34
+ var _useState = useState(0),
35
+ _useState2 = _slicedToArray(_useState, 2),
36
+ distance = _useState2[0],
37
+ setDistance = _useState2[1];
38
+ var onOpenRef = useLatestRef(onOpen);
39
+ useEffect(function () {
40
+ if (!enabled || typeof window === "undefined") return undefined;
41
+ var g = null;
42
+ var point = function point(e) {
43
+ var _e$touches, _e$changedTouches;
44
+ return ((_e$touches = e.touches) === null || _e$touches === void 0 ? void 0 : _e$touches[0]) || ((_e$changedTouches = e.changedTouches) === null || _e$changedTouches === void 0 ? void 0 : _e$changedTouches[0]);
45
+ };
46
+ var towardOpen = function towardOpen(origin, p) {
47
+ return fromRight ? origin.x - p.clientX : p.clientX - origin.x;
48
+ };
49
+ var onStart = function onStart(e) {
50
+ var p = point(e);
51
+ if (!p || e.touches && e.touches.length > 1) return;
52
+ var width = window.innerWidth || 0;
53
+ var atEdge = fromRight ? p.clientX >= width - EDGE_WIDTH : p.clientX <= EDGE_WIDTH;
54
+ g = atEdge ? {
55
+ x: p.clientX,
56
+ y: p.clientY,
57
+ t: Date.now(),
58
+ active: false
59
+ } : null;
60
+ };
61
+ var onMove = function onMove(e) {
62
+ if (!g) return;
63
+ var p = point(e);
64
+ if (!p) return;
65
+ var d = towardOpen(g, p);
66
+ var cross = p.clientY - g.y;
67
+ if (!g.active) {
68
+ if (Math.max(Math.abs(d), Math.abs(cross)) < SWIPE_SLOP) return;
69
+ if (d <= 0 || Math.abs(d) < Math.abs(cross)) {
70
+ g = null; // a vertical scroll, or away from the drawer
71
+ return;
72
+ }
73
+ g.active = true;
74
+ }
75
+ if (e.cancelable) e.preventDefault();
76
+ setDistance(Math.max(0, d));
77
+ };
78
+ var onEnd = function onEnd(e) {
79
+ if (!g) return;
80
+ var gesture = g;
81
+ g = null;
82
+ if (!gesture.active) return;
83
+ setDistance(0);
84
+ var p = point(e);
85
+ var d = p ? towardOpen(gesture, p) : 0;
86
+ var velocity = d / Math.max(1, Date.now() - gesture.t);
87
+ if (d >= OPEN_DISTANCE || velocity >= FLICK_VELOCITY && d > FLICK_MIN_DISTANCE) {
88
+ var _onOpenRef$current;
89
+ (_onOpenRef$current = onOpenRef.current) === null || _onOpenRef$current === void 0 || _onOpenRef$current.call(onOpenRef);
90
+ }
91
+ };
92
+ var onCancel = function onCancel() {
93
+ var _g;
94
+ if ((_g = g) !== null && _g !== void 0 && _g.active) setDistance(0);
95
+ g = null;
96
+ };
97
+ window.addEventListener("touchstart", onStart, {
98
+ passive: true
99
+ });
100
+ window.addEventListener("touchmove", onMove, {
101
+ passive: false
102
+ });
103
+ window.addEventListener("touchend", onEnd);
104
+ window.addEventListener("touchcancel", onCancel);
105
+ return function () {
106
+ window.removeEventListener("touchstart", onStart);
107
+ window.removeEventListener("touchmove", onMove);
108
+ window.removeEventListener("touchend", onEnd);
109
+ window.removeEventListener("touchcancel", onCancel);
110
+ };
111
+ }, [enabled, fromRight, onOpenRef]);
112
+ return enabled ? distance : 0;
113
+ }
13
114
  // useLayoutEffect warns during SSR; fall back to useEffect on the server.
14
115
  var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
15
116
  // Ctrl/Cmd/Shift/Alt-click or a non-primary button: the browser opens the
@@ -82,51 +183,58 @@ var hasBadge = function hasBadge(item) {
82
183
  * - drawerPosition: 'left' | 'right'
83
184
  * - isMobileOpen, setIsMobileOpen
84
185
  * - showCollapsedTooltips: boolean (default true)
186
+ * - swipeable: boolean (default true) — phones: swipe in from the screen edge
187
+ * to open the drawer, swipe it back toward its edge to close
188
+ *
189
+ * The content renders once: a sticky column from `md` up, an off-canvas
190
+ * drawer (modal dialog while open) below.
85
191
  */
86
- var Sidebar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
87
- var _ref$items = _ref.items,
88
- items = _ref$items === void 0 ? [] : _ref$items,
89
- _ref$collapsed = _ref.collapsed,
90
- collapsed = _ref$collapsed === void 0 ? false : _ref$collapsed,
91
- onToggle = _ref.onToggle,
92
- className = _ref.className,
93
- containerClassName = _ref.containerClassName,
94
- stickyOffset = _ref.stickyOffset,
95
- logo = _ref.logo,
96
- footer = _ref.footer,
97
- user = _ref.user,
98
- onUserClick = _ref.onUserClick,
99
- _ref$drawerPosition = _ref.drawerPosition,
100
- drawerPosition = _ref$drawerPosition === void 0 ? "left" : _ref$drawerPosition,
101
- _ref$isMobileOpen = _ref.isMobileOpen,
102
- isMobileOpen = _ref$isMobileOpen === void 0 ? false : _ref$isMobileOpen,
103
- setIsMobileOpen = _ref.setIsMobileOpen,
104
- _ref$showCollapsedToo = _ref.showCollapsedTooltips,
105
- showCollapsedTooltips = _ref$showCollapsedToo === void 0 ? true : _ref$showCollapsedToo,
106
- tooltipOptions = _ref.tooltipOptions,
107
- _ref$linkAs = _ref.linkAs,
108
- Link = _ref$linkAs === void 0 ? "a" : _ref$linkAs,
109
- expandedIds = _ref.expandedIds,
110
- defaultExpandedIds = _ref.defaultExpandedIds,
111
- onExpandedChange = _ref.onExpandedChange,
112
- props = _objectWithoutProperties(_ref, _excluded);
192
+ var Sidebar = /*#__PURE__*/React.forwardRef(function (_ref2, ref) {
193
+ var _ref2$items = _ref2.items,
194
+ items = _ref2$items === void 0 ? [] : _ref2$items,
195
+ _ref2$collapsed = _ref2.collapsed,
196
+ collapsed = _ref2$collapsed === void 0 ? false : _ref2$collapsed,
197
+ onToggle = _ref2.onToggle,
198
+ className = _ref2.className,
199
+ containerClassName = _ref2.containerClassName,
200
+ stickyOffset = _ref2.stickyOffset,
201
+ logo = _ref2.logo,
202
+ footer = _ref2.footer,
203
+ user = _ref2.user,
204
+ onUserClick = _ref2.onUserClick,
205
+ _ref2$drawerPosition = _ref2.drawerPosition,
206
+ drawerPosition = _ref2$drawerPosition === void 0 ? "left" : _ref2$drawerPosition,
207
+ _ref2$isMobileOpen = _ref2.isMobileOpen,
208
+ isMobileOpen = _ref2$isMobileOpen === void 0 ? false : _ref2$isMobileOpen,
209
+ setIsMobileOpen = _ref2.setIsMobileOpen,
210
+ _ref2$showCollapsedTo = _ref2.showCollapsedTooltips,
211
+ showCollapsedTooltips = _ref2$showCollapsedTo === void 0 ? true : _ref2$showCollapsedTo,
212
+ tooltipOptions = _ref2.tooltipOptions,
213
+ _ref2$linkAs = _ref2.linkAs,
214
+ Link = _ref2$linkAs === void 0 ? "a" : _ref2$linkAs,
215
+ expandedIds = _ref2.expandedIds,
216
+ defaultExpandedIds = _ref2.defaultExpandedIds,
217
+ onExpandedChange = _ref2.onExpandedChange,
218
+ _ref2$swipeable = _ref2.swipeable,
219
+ swipeable = _ref2$swipeable === void 0 ? true : _ref2$swipeable,
220
+ props = _objectWithoutProperties(_ref2, _excluded);
113
221
  // -- expansion (controlled via `expandedIds`, else internal) ----------------
114
222
  var isExpansionControlled = expandedIds !== undefined;
115
223
  var activeAncestors = useMemo(function () {
116
224
  return findActiveAncestors(items);
117
225
  }, [items]);
118
226
  var activeKey = activeAncestors.join("\0");
119
- var _useState = useState(function () {
227
+ var _useState3 = useState(function () {
120
228
  return mergeIds(Array.isArray(defaultExpandedIds) ? defaultExpandedIds : [], activeAncestors);
121
229
  }),
122
- _useState2 = _slicedToArray(_useState, 2),
123
- internalExpanded = _useState2[0],
124
- setInternalExpanded = _useState2[1];
125
- // Open the groups holding a newly-active item (adjust-during-render).
126
- var _useState3 = useState(activeKey),
127
230
  _useState4 = _slicedToArray(_useState3, 2),
128
- prevActiveKey = _useState4[0],
129
- setPrevActiveKey = _useState4[1];
231
+ internalExpanded = _useState4[0],
232
+ setInternalExpanded = _useState4[1];
233
+ // Open the groups holding a newly-active item (adjust-during-render).
234
+ var _useState5 = useState(activeKey),
235
+ _useState6 = _slicedToArray(_useState5, 2),
236
+ prevActiveKey = _useState6[0],
237
+ setPrevActiveKey = _useState6[1];
130
238
  if (activeKey !== prevActiveKey) {
131
239
  setPrevActiveKey(activeKey);
132
240
  if (!isExpansionControlled) {
@@ -155,36 +263,66 @@ var Sidebar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
155
263
  if (!isExpansionControlled) setInternalExpanded(next);
156
264
  onExpandedChange === null || onExpandedChange === void 0 || onExpandedChange(next);
157
265
  };
158
- var _useState5 = useState(null),
159
- _useState6 = _slicedToArray(_useState5, 2),
160
- activeItemId = _useState6[0],
161
- setActiveItemId = _useState6[1];
162
- // Content is rendered twice (desktop pane + mobile drawer), so DOM ids are
163
- // scoped per instance and per pane.
266
+ var _useState7 = useState(null),
267
+ _useState8 = _slicedToArray(_useState7, 2),
268
+ activeItemId = _useState8[0],
269
+ setActiveItemId = _useState8[1];
270
+ // DOM ids are scoped per instance.
164
271
  var instanceId = useId();
165
272
  var drawerRef = useRef(null);
166
273
  var setIsMobileOpenRef = useLatestRef(setIsMobileOpen);
167
- var mobileOpen = isMobileOpen !== null && isMobileOpen !== void 0 ? isMobileOpen : false;
168
- var effectiveCollapsed = mobileOpen ? false : collapsed;
274
+ // One tree for every viewport: an in-flow column from `md` up, an
275
+ // off-canvas drawer below. Desktop on the server and while hydrating.
276
+ var isDesktop = useMediaQuery(SIDEBAR_DESKTOP_QUERY, {
277
+ defaultValue: true
278
+ });
279
+ var drawerMode = !isDesktop;
280
+ var mobileOpen = Boolean(isMobileOpen);
281
+ var drawerOpen = drawerMode && mobileOpen;
282
+ var effectiveCollapsed = drawerMode ? false : collapsed;
283
+ var isRight = drawerPosition === "right";
169
284
  // Ref-counted, so a Dialog/Drawer stacked on top never unlocks the page early.
170
- useScrollLock(mobileOpen);
285
+ useScrollLock(drawerOpen);
171
286
  // The drawer stays mounted (it slides in/out), so when it is shut it must
172
287
  // leave the tab order and the a11y tree. `inert` is set imperatively: React
173
288
  // 18 does not know the attribute and React 19 treats `inert=""` as false.
174
289
  useIsomorphicLayoutEffect(function () {
175
290
  var drawer = drawerRef.current;
176
291
  if (!drawer) return;
177
- if (mobileOpen) drawer.removeAttribute("inert");else drawer.setAttribute("inert", "");
178
- }, [mobileOpen]);
179
- // Open drawer = modal dialog: initial focus inside, Tab trapped (skipped
180
- // while md:hidden hides it), Escape closes, focus returns to the opener.
292
+ if (drawerMode && !mobileOpen) drawer.setAttribute("inert", "");else drawer.removeAttribute("inert");
293
+ }, [drawerMode, mobileOpen]);
294
+ // Open drawer = modal dialog: initial focus inside, Tab trapped, Escape
295
+ // closes, focus returns to the opener. Never on md+ (in-flow column).
181
296
  useFocusTrap(drawerRef, {
182
- active: mobileOpen,
297
+ active: drawerOpen,
183
298
  onEscape: function onEscape() {
184
299
  var _setIsMobileOpenRef$c;
185
300
  return (_setIsMobileOpenRef$c = setIsMobileOpenRef.current) === null || _setIsMobileOpenRef$c === void 0 ? void 0 : _setIsMobileOpenRef$c.call(setIsMobileOpenRef, false);
186
301
  }
187
302
  });
303
+ // Swipe toward the drawer's edge to close it (pointer drag, reusing the
304
+ // Sheet gesture core). `touch-pan-y` on the panel keeps vertical scrolling native.
305
+ var canSwipe = swipeable && drawerMode && typeof setIsMobileOpen === "function";
306
+ var closeDrag = useSheetDrag({
307
+ edge: isRight ? "right" : "left",
308
+ enabled: canSwipe && mobileOpen,
309
+ onRelease: function onRelease(_ref3) {
310
+ var _setIsMobileOpenRef$c2;
311
+ var delta = _ref3.delta,
312
+ velocity = _ref3.velocity;
313
+ var flick = velocity >= FLICK_VELOCITY && delta > FLICK_MIN_DISTANCE;
314
+ if (flick || delta >= CLOSE_DISTANCE) (_setIsMobileOpenRef$c2 = setIsMobileOpenRef.current) === null || _setIsMobileOpenRef$c2 === void 0 || _setIsMobileOpenRef$c2.call(setIsMobileOpenRef, false);
315
+ }
316
+ });
317
+ // Swipe in from the screen edge to open it.
318
+ var openDistance = useEdgeSwipe({
319
+ enabled: canSwipe && !mobileOpen,
320
+ fromRight: isRight,
321
+ onOpen: function onOpen() {
322
+ var _setIsMobileOpenRef$c3;
323
+ return (_setIsMobileOpenRef$c3 = setIsMobileOpenRef.current) === null || _setIsMobileOpenRef$c3 === void 0 ? void 0 : _setIsMobileOpenRef$c3.call(setIsMobileOpenRef, true);
324
+ }
325
+ });
188
326
  var closeMobile = function closeMobile() {
189
327
  if (mobileOpen && setIsMobileOpen) setIsMobileOpen(false);
190
328
  };
@@ -239,7 +377,7 @@ var Sidebar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
239
377
  onUserClick === null || onUserClick === void 0 || onUserClick(user);
240
378
  closeMobile();
241
379
  };
242
- var _renderMenuItem = function renderMenuItem(item, level, pane) {
380
+ var _renderMenuItem = function renderMenuItem(item, level) {
243
381
  var _item$badgeLabel, _item$rel;
244
382
  var hasChildren = hasChildItems(item);
245
383
  var isExpanded = isExpandedId(item.id);
@@ -248,7 +386,7 @@ var Sidebar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
248
386
  var labelHidden = effectiveCollapsed && level === 0;
249
387
  var hasHref = Boolean(item.href) && !item.disabled;
250
388
  var ItemTag = hasHref ? Link : "a";
251
- var groupId = hasChildren ? "".concat(instanceId, "-").concat(pane, "-group-").concat(item.id) : undefined;
389
+ var groupId = hasChildren ? "".concat(instanceId, "-group-").concat(item.id) : undefined;
252
390
  var showBadge = hasBadge(item);
253
391
  var badgeText = showBadge ? String((_item$badgeLabel = item.badgeLabel) !== null && _item$badgeLabel !== void 0 ? _item$badgeLabel : item.badge) : undefined;
254
392
  // Collapsed: the badge shrinks onto the icon.
@@ -341,23 +479,23 @@ var Sidebar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
341
479
  id: groupId,
342
480
  className: "mt-1 space-y-1 ml-2",
343
481
  children: item.children.map(function (child) {
344
- return _renderMenuItem(child, level + 1, pane);
482
+ return _renderMenuItem(child, level + 1);
345
483
  })
346
484
  })]
347
485
  }, item.id);
348
486
  };
349
487
  // Section headings (items with `group`). Collapsed: the heading becomes a
350
488
  // divider and stays available to screen readers.
351
- var renderNavContent = function renderNavContent(pane) {
489
+ var renderNavContent = function renderNavContent() {
352
490
  if (!items.some(function (item) {
353
491
  return item && item.group;
354
492
  })) {
355
493
  return items.map(function (item) {
356
- return _renderMenuItem(item, 0, pane);
494
+ return _renderMenuItem(item, 0);
357
495
  });
358
496
  }
359
497
  return groupItems(items).map(function (section, idx) {
360
- var headingId = "".concat(instanceId, "-").concat(pane, "-section-").concat(idx);
498
+ var headingId = "".concat(instanceId, "-section-").concat(idx);
361
499
  return jsxs("div", {
362
500
  role: section.key ? "group" : undefined,
363
501
  "aria-labelledby": section.key ? headingId : undefined,
@@ -377,14 +515,27 @@ var Sidebar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
377
515
  className: "px-3 pb-1 pt-2 text-micro font-semibold uppercase tracking-wide text-muted",
378
516
  children: section.key
379
517
  })), section.items.map(function (item) {
380
- return _renderMenuItem(item, 0, pane);
518
+ return _renderMenuItem(item, 0);
381
519
  })]
382
520
  }, section.key || "__ungrouped");
383
521
  });
384
522
  };
523
+ // In the drawer the button closes it; on md+ it collapses / expands.
385
524
  var handleToggleClick = function handleToggleClick() {
386
- if (mobileOpen && setIsMobileOpen) setIsMobileOpen(false);else onToggle === null || onToggle === void 0 || onToggle();
525
+ if (drawerMode && mobileOpen && setIsMobileOpen) setIsMobileOpen(false);else onToggle === null || onToggle === void 0 || onToggle();
387
526
  };
527
+ var toggleLabel = drawerMode ? "Close navigation" : effectiveCollapsed ? "Expand sidebar" : "Collapse sidebar";
528
+ var CloseChevron = isRight ? ChevronRight : ChevronLeft;
529
+ var toggleIcon = drawerMode ? jsx(CloseChevron, {
530
+ className: "h-5 w-5",
531
+ "aria-hidden": "true"
532
+ }) : effectiveCollapsed ? jsx(ChevronRight, {
533
+ className: "h-5 w-5",
534
+ "aria-hidden": "true"
535
+ }) : jsx(ChevronLeft, {
536
+ className: "h-5 w-5",
537
+ "aria-hidden": "true"
538
+ });
388
539
  var renderUserBlock = function renderUserBlock() {
389
540
  var content = jsxs(Fragment, {
390
541
  children: [user.avatar && jsx("img", {
@@ -418,11 +569,11 @@ var Sidebar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
418
569
  children: content
419
570
  });
420
571
  };
421
- var renderFooter = function renderFooter(pane) {
572
+ var renderFooter = function renderFooter() {
422
573
  if (footer === undefined || footer === null || footer === false) return null;
423
574
  var content = typeof footer === "function" ? footer({
424
575
  collapsed: effectiveCollapsed,
425
- isMobile: pane === "mobile",
576
+ isMobile: drawerMode,
426
577
  closeMobile: closeMobile
427
578
  }) : footer;
428
579
  if (content === undefined || content === null || content === false) return null;
@@ -432,32 +583,26 @@ var Sidebar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
432
583
  children: content
433
584
  });
434
585
  };
435
- var renderSidebarContent = function renderSidebarContent(pane) {
586
+ var renderSidebarContent = function renderSidebarContent() {
436
587
  return jsxs("div", {
437
- className: cn("flex h-full flex-col bg-surface text-foreground border-r border-line shadow-sm", "w-64 transition-all duration-200", {
438
- "w-16": effectiveCollapsed,
439
- "w-64": !effectiveCollapsed
440
- }),
588
+ className: cn("flex h-full flex-col bg-surface text-foreground border-line shadow-sm", isRight ? "border-l" : "border-r", "transition-[width] duration-200 motion-reduce:transition-none", effectiveCollapsed ? "w-16" : "w-64"),
441
589
  children: [jsxs("div", {
442
- className: "flex h-16 items-center justify-between border-b border-line px-4",
590
+ className: cn("flex h-16 shrink-0 items-center border-b border-line", effectiveCollapsed ? "justify-center px-2" : "justify-between px-4"),
443
591
  children: [!effectiveCollapsed && logo && jsx("div", {
444
- className: "flex items-center",
592
+ className: "flex min-w-0 items-center",
445
593
  children: logo
446
594
  }), jsx("button", {
447
595
  type: "button",
448
596
  onClick: handleToggleClick,
449
- className: "rounded-md p-1 hover:bg-gray-100 dark:hover:bg-gray-800",
450
- "aria-label": effectiveCollapsed ? "Expand sidebar" : "Collapse sidebar",
451
- children: effectiveCollapsed ? jsx(ChevronRight, {
452
- className: "h-5 w-5"
453
- }) : jsx(ChevronLeft, {
454
- className: "h-5 w-5"
455
- })
597
+ className: cn("inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-md", "hover:bg-gray-100 dark:hover:bg-gray-800", "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"),
598
+ "aria-label": toggleLabel,
599
+ "data-sidebar-toggle": "",
600
+ children: toggleIcon
456
601
  })]
457
602
  }), jsx("nav", {
458
- className: "flex-1 space-y-1 overflow-y-auto p-4 [&::-webkit-scrollbar]:w-2\n [&::-webkit-scrollbar-track]:bg-gray-100 dark:[&::-webkit-scrollbar-track]:bg-gray-800\n [&::-webkit-scrollbar-thumb]:bg-gray-300 dark:[&::-webkit-scrollbar-thumb]:bg-gray-700\n hover:[&::-webkit-scrollbar-thumb]:bg-gray-400",
459
- children: renderNavContent(pane)
460
- }), renderFooter(pane), user && jsx("div", {
603
+ className: "flex-1 space-y-1 overflow-y-auto overscroll-contain p-4 [&::-webkit-scrollbar]:w-2\n [&::-webkit-scrollbar-track]:bg-gray-100 dark:[&::-webkit-scrollbar-track]:bg-gray-800\n [&::-webkit-scrollbar-thumb]:bg-gray-300 dark:[&::-webkit-scrollbar-thumb]:bg-gray-700\n hover:[&::-webkit-scrollbar-thumb]:bg-gray-400",
604
+ children: renderNavContent()
605
+ }), renderFooter(), user && jsx("div", {
461
606
  className: "border-t border-line p-4",
462
607
  children: jsx("div", {
463
608
  className: "relative",
@@ -466,54 +611,61 @@ var Sidebar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
466
611
  })]
467
612
  });
468
613
  };
469
- var isRight = drawerPosition === "right";
470
- var mobileDrawerClasses = cn("fixed top-0 z-50 h-full transition-transform duration-300 ease-in-out motion-reduce:transition-none md:hidden", {
471
- "left-0": !isRight,
472
- "right-0": isRight
473
- }, {
474
- "translate-x-0": mobileOpen,
475
- "-translate-x-full": !mobileOpen && !isRight,
476
- "translate-x-full": !mobileOpen && isRight
477
- });
478
- var desktopPlacementClass = isRight ? "md:order-last" : "";
614
+ // Where the drawer sits while a swipe is in progress (phones only).
615
+ var drawerTransform;
616
+ if (drawerMode && closeDrag.dragging && closeDrag.delta > 0) {
617
+ drawerTransform = "translateX(".concat(isRight ? "" : "-").concat(closeDrag.delta, "px)");
618
+ } else if (drawerMode && openDistance > 0) {
619
+ drawerTransform = isRight ? "translateX(max(0px, calc(100% - ".concat(openDistance, "px)))") : "translateX(min(0px, calc(-100% + ".concat(openDistance, "px)))");
620
+ }
621
+ var tracking = drawerTransform !== undefined;
622
+ var drawerClasses = cn(
623
+ // Phones: an off-canvas drawer above the backdrop, padded by the safe area.
624
+ "fixed inset-y-0 z-modal h-dvh bg-surface pt-safe pb-safe focus:outline-none", isRight ? "right-0 pr-safe" : "left-0 pl-safe", "transition-transform duration-300 ease-in-out motion-reduce:transition-none", mobileOpen ? "translate-x-0" : isRight ? "translate-x-full" : "-translate-x-full", tracking && "transition-none", canSwipe && "touch-pan-y",
625
+ // md+: an in-flow column inside the sticky wrapper.
626
+ "md:static md:z-auto md:h-full md:translate-x-0 md:transition-none md:touch-auto");
479
627
  // A top bar above the sidebar: stick below it and shrink to fit.
480
628
  var offset = typeof stickyOffset === "number" ? "".concat(stickyOffset, "px") : stickyOffset;
481
629
  var containerStyle = offset ? {
482
630
  top: offset,
483
631
  height: "calc(100dvh - ".concat(offset, ")")
484
632
  } : undefined;
485
- return jsxs(Fragment, {
486
- children: [jsx("div", {
487
- className: cn("hidden md:flex sticky top-0 h-screen", containerClassName),
488
- style: containerStyle,
489
- "data-sidebar-container": "",
490
- children: jsx("div", _objectSpread2(_objectSpread2({
491
- ref: ref,
492
- className: cn(desktopPlacementClass, className)
493
- }, props), {}, {
494
- children: renderSidebarContent("desktop")
495
- }))
496
- }), mobileOpen && jsx("div", {
497
- className: "fixed inset-0 bg-black/30 z-40 md:hidden",
633
+ return jsxs("div", {
634
+ // `contents` below md: the wrapper adds no box, the drawer is fixed.
635
+ className: cn("contents md:flex sticky top-0 h-[100dvh]", isRight && "md:order-last", containerClassName),
636
+ style: containerStyle,
637
+ "data-sidebar-container": "",
638
+ children: [drawerOpen && jsx("div", {
639
+ className: "fixed inset-0 z-overlay bg-black/30 animate-fade-in motion-reduce:animate-none md:hidden",
498
640
  onClick: function onClick() {
499
641
  return setIsMobileOpen === null || setIsMobileOpen === void 0 ? void 0 : setIsMobileOpen(false);
500
642
  },
501
- "aria-hidden": "true"
502
- }), jsx("aside", {
643
+ "aria-hidden": "true",
644
+ "data-sidebar-backdrop": ""
645
+ }), jsx("aside", _objectSpread2(_objectSpread2(_objectSpread2({
503
646
  ref: drawerRef,
504
- className: mobileDrawerClasses,
647
+ className: drawerClasses,
648
+ style: drawerTransform ? {
649
+ transform: drawerTransform
650
+ } : undefined,
651
+ "data-presentation": drawerMode ? "drawer" : "column",
652
+ "data-dragging": tracking ? "" : undefined
653
+ }, drawerMode ? {
505
654
  role: "dialog",
506
655
  "aria-modal": "true",
507
656
  "aria-label": "Navigation",
508
657
  "aria-hidden": mobileOpen ? undefined : true,
509
- tabIndex: -1,
510
- style: {
511
- zIndex: 50
512
- },
513
- children: renderSidebarContent("mobile")
514
- })]
658
+ tabIndex: -1
659
+ } : {}), closeDrag.bodyDragProps), {}, {
660
+ children: jsx("div", _objectSpread2(_objectSpread2({
661
+ ref: ref,
662
+ className: cn("h-full", className)
663
+ }, props), {}, {
664
+ children: renderSidebarContent()
665
+ }))
666
+ }))]
515
667
  });
516
668
  });
517
669
  Sidebar.displayName = "Sidebar";
518
670
 
519
- export { Sidebar as default };
671
+ export { SIDEBAR_DESKTOP_QUERY, Sidebar as default };
@@ -9,7 +9,7 @@ import { cn } from '../../utils/cn.js';
9
9
  import { useLatestRef } from '../../hooks/useLatestRef.js';
10
10
  import { selectToolbarState, DEFAULT_TOOLBAR, RichTextToolbar } from './RichTextToolbar.js';
11
11
 
12
- var _excluded = ["id", "className", "editorClassName", "label", "error", "helperText", "required", "disabled", "readOnly", "placeholder", "value", "defaultValue", "onChange", "outputFormat", "onFocus", "onBlur", "toolbar", "toolbarLabel", "minHeight", "maxHeight", "autoFocus", "extensions", "aria-label", "aria-describedby"];
12
+ var _excluded = ["id", "className", "editorClassName", "label", "error", "helperText", "required", "disabled", "readOnly", "placeholder", "value", "defaultValue", "onChange", "outputFormat", "onFocus", "onBlur", "toolbar", "toolbarLabel", "stickyToolbar", "minHeight", "maxHeight", "autoFocus", "extensions", "aria-label", "aria-describedby"];
13
13
  // Tailwind's preflight strips list bullets, heading sizes, etc. These arbitrary
14
14
  // variants restore readable defaults inside the editable area only.
15
15
  var CONTENT_CLASSES = cn("px-3 py-2 text-sm text-gray-900 focus:outline-none dark:text-gray-100", "[&_p]:my-1 [&_h1]:my-2 [&_h1]:text-2xl [&_h1]:font-bold [&_h2]:my-2 [&_h2]:text-xl [&_h2]:font-semibold [&_h3]:my-1.5 [&_h3]:text-lg [&_h3]:font-semibold", "[&_ul]:list-disc [&_ul]:pl-6 [&_ol]:list-decimal [&_ol]:pl-6 [&_li]:my-0.5", "[&_blockquote]:my-2 [&_blockquote]:border-l-4 [&_blockquote]:border-gray-300 [&_blockquote]:pl-3 [&_blockquote]:text-gray-600 dark:[&_blockquote]:border-gray-600 dark:[&_blockquote]:text-gray-300", "[&_code]:rounded [&_code]:bg-gray-100 [&_code]:px-1 [&_code]:font-mono [&_code]:text-[0.85em] dark:[&_code]:bg-gray-800", "[&_pre]:my-2 [&_pre]:rounded-md [&_pre]:bg-gray-100 [&_pre]:p-3 dark:[&_pre]:bg-gray-800 [&_pre_code]:bg-transparent [&_pre_code]:p-0", "[&_a]:text-primary-600 [&_a]:underline dark:[&_a]:text-primary-400 [&_hr]:my-3 [&_hr]:border-gray-200 dark:[&_hr]:border-gray-700");
@@ -46,6 +46,8 @@ var RichTextEditor = /*#__PURE__*/forwardRef(function (_ref, ref) {
46
46
  toolbar = _ref$toolbar === void 0 ? DEFAULT_TOOLBAR : _ref$toolbar,
47
47
  _ref$toolbarLabel = _ref.toolbarLabel,
48
48
  toolbarLabel = _ref$toolbarLabel === void 0 ? "Formatting" : _ref$toolbarLabel,
49
+ _ref$stickyToolbar = _ref.stickyToolbar,
50
+ stickyToolbar = _ref$stickyToolbar === void 0 ? false : _ref$stickyToolbar,
49
51
  _ref$minHeight = _ref.minHeight,
50
52
  minHeight = _ref$minHeight === void 0 ? 120 : _ref$minHeight,
51
53
  maxHeight = _ref.maxHeight,
@@ -196,14 +198,18 @@ var RichTextEditor = /*#__PURE__*/forwardRef(function (_ref, ref) {
196
198
  children: "*"
197
199
  })]
198
200
  }), jsxs("div", {
199
- className: cn("overflow-hidden rounded-md border bg-white dark:bg-gray-900", error ? "border-error-500" : "border-gray-300 dark:border-gray-600", disabled && "cursor-not-allowed opacity-50"),
201
+ className: cn(
202
+ // overflow-clip (not hidden) keeps the rounded corners without
203
+ // creating a scroll container, so a sticky toolbar can stick to the page.
204
+ stickyToolbar ? "overflow-clip" : "overflow-hidden", "rounded-md border bg-white dark:bg-gray-900", error ? "border-error-500" : "border-gray-300 dark:border-gray-600", disabled && "cursor-not-allowed opacity-50"),
200
205
  children: [showToolbar && jsx(RichTextToolbar, {
201
206
  editor: editor,
202
207
  state: state,
203
208
  items: items,
204
209
  disabled: disabled,
205
210
  controlsId: editorId,
206
- label: toolbarLabel
211
+ label: toolbarLabel,
212
+ sticky: stickyToolbar
207
213
  }), jsxs("div", {
208
214
  className: cn("relative overflow-y-auto focus-within:ring-2 focus-within:ring-inset", error ? "focus-within:ring-error-500" : "focus-within:ring-primary-500"),
209
215
  style: {