@dreamtree-org/twreact-ui 1.3.10 → 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 (230) hide show
  1. package/README.md +70 -6
  2. package/ai-skills/dreamtree-ui.md +58 -15
  3. package/codemods/v2.cjs +198 -0
  4. package/dist/ai-schema.json +745 -53
  5. package/dist/charts/index.js +195 -66
  6. package/dist/components/blocks/AppShell.d.ts.map +1 -1
  7. package/dist/components/blocks/BottomActionBar.d.ts +6 -0
  8. package/dist/components/blocks/BottomActionBar.d.ts.map +1 -0
  9. package/dist/components/blocks/ConfirmDialog.d.ts.map +1 -1
  10. package/dist/components/blocks/PageHeader.d.ts.map +1 -1
  11. package/dist/components/charts/CartesianChart.d.ts.map +1 -1
  12. package/dist/components/charts/ChartFrame.d.ts.map +1 -1
  13. package/dist/components/charts/useChartLayout.d.ts +25 -0
  14. package/dist/components/charts/useChartLayout.d.ts.map +1 -0
  15. package/dist/components/core/Button.d.ts.map +1 -1
  16. package/dist/components/core/Checkbox.d.ts.map +1 -1
  17. package/dist/components/core/ColorPicker.d.ts.map +1 -1
  18. package/dist/components/core/DatePicker.d.ts.map +1 -1
  19. package/dist/components/core/DateRangePicker.d.ts.map +1 -1
  20. package/dist/components/core/Form.d.ts.map +1 -1
  21. package/dist/components/core/Input.d.ts.map +1 -1
  22. package/dist/components/core/Loader.d.ts.map +1 -1
  23. package/dist/components/core/LocationPicker.d.ts.map +1 -1
  24. package/dist/components/core/Popover.d.ts.map +1 -1
  25. package/dist/components/core/ProgressBar.d.ts.map +1 -1
  26. package/dist/components/core/Radio.d.ts.map +1 -1
  27. package/dist/components/core/Rate.d.ts.map +1 -1
  28. package/dist/components/core/RoundedTag.d.ts.map +1 -1
  29. package/dist/components/core/SearchField.d.ts +9 -0
  30. package/dist/components/core/SearchField.d.ts.map +1 -0
  31. package/dist/components/core/SegmentedControl.d.ts +8 -0
  32. package/dist/components/core/SegmentedControl.d.ts.map +1 -0
  33. package/dist/components/core/Select.d.ts.map +1 -1
  34. package/dist/components/core/Skeleton.d.ts.map +1 -1
  35. package/dist/components/core/Slider.d.ts.map +1 -1
  36. package/dist/components/core/SpeechToText.d.ts.map +1 -1
  37. package/dist/components/core/SwipeableListItem.d.ts +4 -0
  38. package/dist/components/core/SwipeableListItem.d.ts.map +1 -0
  39. package/dist/components/core/SwipeableListItemGesture.d.ts +58 -0
  40. package/dist/components/core/SwipeableListItemGesture.d.ts.map +1 -0
  41. package/dist/components/core/Switch.d.ts.map +1 -1
  42. package/dist/components/core/Table/ActionMenu.d.ts +10 -3
  43. package/dist/components/core/Table/ActionMenu.d.ts.map +1 -1
  44. package/dist/components/core/Table/MobileCard.d.ts +10 -1
  45. package/dist/components/core/Table/MobileCard.d.ts.map +1 -1
  46. package/dist/components/core/Table/MobileFilters.d.ts +8 -2
  47. package/dist/components/core/Table/MobileFilters.d.ts.map +1 -1
  48. package/dist/components/core/Table/Table.d.ts.map +1 -1
  49. package/dist/components/core/Table/TableRow.d.ts +2 -1
  50. package/dist/components/core/Table/TableRow.d.ts.map +1 -1
  51. package/dist/components/core/Tabs.d.ts +5 -8
  52. package/dist/components/core/Tabs.d.ts.map +1 -1
  53. package/dist/components/core/Textarea.d.ts.map +1 -1
  54. package/dist/components/core/ThreeDotPopover.d.ts +10 -22
  55. package/dist/components/core/ThreeDotPopover.d.ts.map +1 -1
  56. package/dist/components/core/Tooltip.d.ts +2 -1
  57. package/dist/components/core/Tooltip.d.ts.map +1 -1
  58. package/dist/components/core/internal/Calendar.d.ts +77 -0
  59. package/dist/components/core/internal/Calendar.d.ts.map +1 -0
  60. package/dist/components/core/menu/ActionMenuSurface.d.ts +42 -0
  61. package/dist/components/core/menu/ActionMenuSurface.d.ts.map +1 -0
  62. package/dist/components/core/menu/ActionSheetMenu.d.ts +17 -0
  63. package/dist/components/core/menu/ActionSheetMenu.d.ts.map +1 -0
  64. package/dist/components/core/menu/MenuPopover.d.ts +21 -0
  65. package/dist/components/core/menu/MenuPopover.d.ts.map +1 -0
  66. package/dist/components/core/menu/menuItems.d.ts +21 -0
  67. package/dist/components/core/menu/menuItems.d.ts.map +1 -0
  68. package/dist/components/core/menu/menuKeyboard.d.ts +16 -0
  69. package/dist/components/core/menu/menuKeyboard.d.ts.map +1 -0
  70. package/dist/components/feedback/ActionSheet.d.ts +4 -0
  71. package/dist/components/feedback/ActionSheet.d.ts.map +1 -0
  72. package/dist/components/feedback/Alert.d.ts.map +1 -1
  73. package/dist/components/feedback/Dialog.d.ts.map +1 -1
  74. package/dist/components/feedback/Drawer.d.ts.map +1 -1
  75. package/dist/components/feedback/Sheet.d.ts +4 -0
  76. package/dist/components/feedback/Sheet.d.ts.map +1 -0
  77. package/dist/components/feedback/Toast.d.ts +4 -3
  78. package/dist/components/feedback/Toast.d.ts.map +1 -1
  79. package/dist/components/feedback/ToastItem.d.ts +47 -0
  80. package/dist/components/feedback/ToastItem.d.ts.map +1 -0
  81. package/dist/components/feedback/ToastStack.d.ts +20 -0
  82. package/dist/components/feedback/ToastStack.d.ts.map +1 -0
  83. package/dist/components/feedback/ToastSwipe.d.ts +22 -0
  84. package/dist/components/feedback/ToastSwipe.d.ts.map +1 -0
  85. package/dist/components/feedback/Tour.d.ts.map +1 -1
  86. package/dist/components/feedback/sheetCore.d.ts +30 -0
  87. package/dist/components/feedback/sheetCore.d.ts.map +1 -0
  88. package/dist/components/navigation/Breadcrumbs.d.ts.map +1 -1
  89. package/dist/components/navigation/FootNav.d.ts.map +1 -1
  90. package/dist/components/navigation/Navbar.d.ts.map +1 -1
  91. package/dist/components/navigation/Sidebar.d.ts +6 -0
  92. package/dist/components/navigation/Sidebar.d.ts.map +1 -1
  93. package/dist/components/rich-text/RichTextEditor.d.ts.map +1 -1
  94. package/dist/components/rich-text/RichTextToolbar.d.ts +6 -2
  95. package/dist/components/rich-text/RichTextToolbar.d.ts.map +1 -1
  96. package/dist/components/utility/Avatar.d.ts +8 -0
  97. package/dist/components/utility/Avatar.d.ts.map +1 -1
  98. package/dist/components/utility/AvatarGroup.d.ts +4 -0
  99. package/dist/components/utility/AvatarGroup.d.ts.map +1 -0
  100. package/dist/components/utility/Badge.d.ts.map +1 -1
  101. package/dist/components/utility/Card.d.ts.map +1 -1
  102. package/dist/components/utility/Carousel.d.ts.map +1 -1
  103. package/dist/components/utility/FileUpload.d.ts.map +1 -1
  104. package/dist/components/utility/Pagination.d.ts.map +1 -1
  105. package/dist/components/utility/PullToRefresh.d.ts +5 -0
  106. package/dist/components/utility/PullToRefresh.d.ts.map +1 -0
  107. package/dist/components/utility/Sortable.d.ts.map +1 -1
  108. package/dist/components/utility/Stepper.d.ts.map +1 -1
  109. package/dist/esm/components/blocks/AppShell.js +97 -90
  110. package/dist/esm/components/blocks/BottomActionBar.js +132 -0
  111. package/dist/esm/components/blocks/ConfirmDialog.js +40 -4
  112. package/dist/esm/components/blocks/EmptyState.js +1 -1
  113. package/dist/esm/components/blocks/ErrorState.js +1 -1
  114. package/dist/esm/components/blocks/LoadingState.js +4 -4
  115. package/dist/esm/components/blocks/PageHeader.js +16 -3
  116. package/dist/esm/components/blocks/StatCard.js +6 -6
  117. package/dist/esm/components/blocks/StatusPill.js +5 -8
  118. package/dist/esm/components/charts/CartesianChart.js +85 -62
  119. package/dist/esm/components/charts/ChartFrame.js +12 -5
  120. package/dist/esm/components/charts/useChartLayout.js +105 -0
  121. package/dist/esm/components/core/Button.js +49 -25
  122. package/dist/esm/components/core/Checkbox.js +72 -57
  123. package/dist/esm/components/core/ColorPicker.js +31 -42
  124. package/dist/esm/components/core/DatePicker.js +117 -304
  125. package/dist/esm/components/core/DateRangePicker.js +260 -308
  126. package/dist/esm/components/core/Form.js +11 -2
  127. package/dist/esm/components/core/Input.js +100 -38
  128. package/dist/esm/components/core/Loader.js +30 -44
  129. package/dist/esm/components/core/LocationPicker.js +82 -16
  130. package/dist/esm/components/core/Popover.js +59 -48
  131. package/dist/esm/components/core/ProgressBar.js +40 -72
  132. package/dist/esm/components/core/Radio.js +10 -12
  133. package/dist/esm/components/core/Rate.js +77 -14
  134. package/dist/esm/components/core/RoundedTag.js +5 -5
  135. package/dist/esm/components/core/SearchField.js +311 -0
  136. package/dist/esm/components/core/SegmentedControl.js +189 -0
  137. package/dist/esm/components/core/Select.js +185 -190
  138. package/dist/esm/components/core/Skeleton.js +13 -14
  139. package/dist/esm/components/core/Slider.js +24 -6
  140. package/dist/esm/components/core/SpeechToText.js +68 -16
  141. package/dist/esm/components/core/SwipeableListItem.js +286 -0
  142. package/dist/esm/components/core/SwipeableListItemGesture.js +219 -0
  143. package/dist/esm/components/core/Switch.js +98 -28
  144. package/dist/esm/components/core/Table/ActionMenu.js +45 -231
  145. package/dist/esm/components/core/Table/ColumnMenu.js +1 -1
  146. package/dist/esm/components/core/Table/MobileCard.js +66 -16
  147. package/dist/esm/components/core/Table/MobileFilters.js +181 -10
  148. package/dist/esm/components/core/Table/Table.js +149 -102
  149. package/dist/esm/components/core/Table/TableRow.js +10 -2
  150. package/dist/esm/components/core/Tabs.js +186 -39
  151. package/dist/esm/components/core/Textarea.js +10 -2
  152. package/dist/esm/components/core/ThreeDotPopover.js +93 -99
  153. package/dist/esm/components/core/Tooltip.js +138 -145
  154. package/dist/esm/components/core/internal/Calendar.js +381 -0
  155. package/dist/esm/components/core/menu/ActionMenuSurface.js +110 -0
  156. package/dist/esm/components/core/menu/ActionSheetMenu.js +140 -0
  157. package/dist/esm/components/core/menu/MenuPopover.js +177 -0
  158. package/dist/esm/components/core/menu/menuItems.js +61 -0
  159. package/dist/esm/components/core/menu/menuKeyboard.js +54 -0
  160. package/dist/esm/components/feedback/ActionSheet.js +55 -0
  161. package/dist/esm/components/feedback/Alert.js +63 -53
  162. package/dist/esm/components/feedback/Dialog.js +141 -53
  163. package/dist/esm/components/feedback/Drawer.js +130 -132
  164. package/dist/esm/components/feedback/Sheet.js +125 -0
  165. package/dist/esm/components/feedback/Toast.js +21 -291
  166. package/dist/esm/components/feedback/ToastItem.js +348 -0
  167. package/dist/esm/components/feedback/ToastStack.js +130 -0
  168. package/dist/esm/components/feedback/ToastSwipe.js +52 -0
  169. package/dist/esm/components/feedback/Tour.js +107 -30
  170. package/dist/esm/components/feedback/sheetCore.js +494 -0
  171. package/dist/esm/components/navigation/Breadcrumbs.js +97 -35
  172. package/dist/esm/components/navigation/Dropdown.js +1 -1
  173. package/dist/esm/components/navigation/FootNav.js +102 -13
  174. package/dist/esm/components/navigation/Navbar.js +172 -37
  175. package/dist/esm/components/navigation/Sidebar.js +263 -111
  176. package/dist/esm/components/rich-text/RichTextEditor.js +9 -3
  177. package/dist/esm/components/rich-text/RichTextToolbar.js +29 -9
  178. package/dist/esm/components/utility/Avatar.js +14 -12
  179. package/dist/esm/components/utility/AvatarGroup.js +56 -0
  180. package/dist/esm/components/utility/Badge.js +43 -23
  181. package/dist/esm/components/utility/Card.js +32 -8
  182. package/dist/esm/components/utility/Carousel.js +139 -29
  183. package/dist/esm/components/utility/FileUpload.js +106 -31
  184. package/dist/esm/components/utility/Pagination.js +31 -13
  185. package/dist/esm/components/utility/Portal.js +31 -0
  186. package/dist/esm/components/utility/PullToRefresh.js +229 -0
  187. package/dist/esm/components/utility/Sortable.js +6 -2
  188. package/dist/esm/components/utility/Stepper.js +110 -47
  189. package/dist/esm/hooks/useFloatingPosition.js +82 -0
  190. package/dist/esm/hooks/useHaptics.js +65 -0
  191. package/dist/esm/hooks/usePresence.js +58 -0
  192. package/dist/esm/hooks/usePresentation.js +21 -0
  193. package/dist/esm/hooks/useSheetDrag.js +220 -0
  194. package/dist/esm/hooks/useSliderValues.js +8 -2
  195. package/dist/esm/hooks/useSortable.js +107 -5
  196. package/dist/esm/index.js +14 -2
  197. package/dist/esm/utils/position.js +10 -63
  198. package/dist/esm/utils/touchTarget.js +4 -1
  199. package/dist/hooks/useFloatingPosition.d.ts +8 -4
  200. package/dist/hooks/useFloatingPosition.d.ts.map +1 -1
  201. package/dist/hooks/useHaptics.d.ts +19 -0
  202. package/dist/hooks/useHaptics.d.ts.map +1 -0
  203. package/dist/hooks/useSheetDrag.d.ts +31 -0
  204. package/dist/hooks/useSheetDrag.d.ts.map +1 -0
  205. package/dist/hooks/useSliderValues.d.ts +6 -2
  206. package/dist/hooks/useSliderValues.d.ts.map +1 -1
  207. package/dist/hooks/useSortable.d.ts +11 -1
  208. package/dist/hooks/useSortable.d.ts.map +1 -1
  209. package/dist/index.css +1 -1
  210. package/dist/index.d.ts +11 -2
  211. package/dist/index.esm.css +1 -1
  212. package/dist/index.esm.js +10645 -3842
  213. package/dist/index.js +10657 -3845
  214. package/dist/rich-text/index.js +41 -11
  215. package/dist/types/blocks.d.ts +32 -2
  216. package/dist/types/blocks.d.ts.map +1 -1
  217. package/dist/types/charts.d.ts +3 -1
  218. package/dist/types/charts.d.ts.map +1 -1
  219. package/dist/types/index.d.ts +409 -26
  220. package/dist/types/index.d.ts.map +1 -1
  221. package/dist/types/rich-text.d.ts +5 -0
  222. package/dist/types/rich-text.d.ts.map +1 -1
  223. package/dist/utils/position.d.ts +0 -15
  224. package/dist/utils/position.d.ts.map +1 -1
  225. package/doc/README.md +16 -0
  226. package/mcp/catalog.snapshot.json +1540 -407
  227. package/package.json +7 -2
  228. package/dist/esm/hooks/useSwipeToClose.js +0 -66
  229. package/dist/hooks/useSwipeToClose.d.ts +0 -24
  230. package/dist/hooks/useSwipeToClose.d.ts.map +0 -1
@@ -0,0 +1,219 @@
1
+ 'use client';
2
+ import { slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
+ import { useState, useRef, useCallback } from 'react';
4
+ import { useLatestRef } from '../../hooks/useLatestRef.js';
5
+ import { rubberBand, FLICK_VELOCITY } from '../../hooks/useSheetDrag.js';
6
+
7
+ // Internal (not exported). The horizontal swipe gesture behind
8
+ // SwipeableListItem. Offsets are in px; positive moves the content right
9
+ // (revealing the leading actions), negative moves it left (trailing).
10
+ //
11
+ // The gesture only starts once horizontal movement dominates, so a vertical
12
+ // pan scrolls the page as usual (pair with `touch-action: pan-y`). Past the
13
+ // revealed actions the content rubber-bands, unless a full swipe is possible,
14
+ // in which case it follows the finger up to the row width.
15
+ var SLOP = 6; // px before a press becomes a swipe (or a scroll)
16
+ var VELOCITY_WINDOW = 100; // ms of recent samples used for release velocity
17
+ var FULL_SWIPE_MARGIN = 24; // px a full swipe must travel past the revealed actions
18
+ var FALLBACK_WIDTH = 320; // px, when the row has no layout (jsdom, hidden)
19
+ var now = function now() {
20
+ return Date.now();
21
+ };
22
+ var sideOf = function sideOf(offset) {
23
+ return offset > 0 ? "leading" : offset < 0 ? "trailing" : null;
24
+ };
25
+ /**
26
+ * Where the content rests for an open side.
27
+ * @param {'leading'|'trailing'|null} side
28
+ * @param {{ leading: number, trailing: number }} widths - revealed widths in px.
29
+ */
30
+ function restingOffset(side, widths) {
31
+ if (side === "leading") return widths.leading;
32
+ if (side === "trailing") return -widths.trailing;
33
+ return 0;
34
+ }
35
+ /** Distance (px) past which releasing commits the side's first action. */
36
+ function fullSwipeDistance(reveal, width, threshold) {
37
+ return Math.max(reveal + FULL_SWIPE_MARGIN, width * threshold);
38
+ }
39
+ /**
40
+ * Maps a raw finger offset to the displayed offset for one side.
41
+ * @param {number} raw - px, signed.
42
+ * @param {{ reveal: number, fullSwipe: boolean, width: number }} opts
43
+ */
44
+ function constrainOffset(raw, _ref) {
45
+ var reveal = _ref.reveal,
46
+ fullSwipe = _ref.fullSwipe,
47
+ width = _ref.width;
48
+ var sign = Math.sign(raw);
49
+ var distance = Math.abs(raw);
50
+ var limit = reveal > 0 && fullSwipe ? width : reveal;
51
+ if (distance <= limit) return raw;
52
+ return sign * (limit + rubberBand(distance - limit, width));
53
+ }
54
+ /**
55
+ * @param {{
56
+ * enabled: boolean,
57
+ * widths: { leading: number, trailing: number },
58
+ * openSide: 'leading'|'trailing'|null,
59
+ * fullSwipe: { leading: boolean, trailing: boolean },
60
+ * fullSwipeThreshold: number,
61
+ * onSettle: (r: { side: 'leading'|'trailing'|null, commit: boolean }) => void,
62
+ * }} options
63
+ * @returns {{ offset: number|null, dragging: boolean, armed: 'leading'|'trailing'|null,
64
+ * handlers: object, consumeClick: () => boolean }}
65
+ */
66
+ function useSwipeGesture(_ref2) {
67
+ var enabled = _ref2.enabled,
68
+ widths = _ref2.widths,
69
+ openSide = _ref2.openSide,
70
+ fullSwipe = _ref2.fullSwipe,
71
+ fullSwipeThreshold = _ref2.fullSwipeThreshold,
72
+ onSettle = _ref2.onSettle;
73
+ // `{ offset, width }` while a swipe is in progress, else null.
74
+ var _useState = useState(null),
75
+ _useState2 = _slicedToArray(_useState, 2),
76
+ drag = _useState2[0],
77
+ setDrag = _useState2[1];
78
+ var gesture = useRef(null);
79
+ var swallowClick = useRef(false);
80
+ var latest = useLatestRef({
81
+ widths: widths,
82
+ openSide: openSide,
83
+ fullSwipe: fullSwipe,
84
+ fullSwipeThreshold: fullSwipeThreshold,
85
+ onSettle: onSettle
86
+ });
87
+ var reset = useCallback(function () {
88
+ gesture.current = null;
89
+ setDrag(null);
90
+ }, []);
91
+ var onPointerDown = useCallback(function (e) {
92
+ var _el$getBoundingClient;
93
+ if (!enabled || e.button !== undefined && e.button !== 0) return;
94
+ var el = e.currentTarget;
95
+ var width = ((_el$getBoundingClient = el.getBoundingClientRect) === null || _el$getBoundingClient === void 0 ? void 0 : _el$getBoundingClient.call(el).width) || el.offsetWidth || FALLBACK_WIDTH;
96
+ var start = restingOffset(latest.current.openSide, latest.current.widths);
97
+ gesture.current = {
98
+ x: e.clientX,
99
+ y: e.clientY,
100
+ start: start,
101
+ width: width,
102
+ el: el,
103
+ pointerId: e.pointerId,
104
+ active: false,
105
+ current: start,
106
+ samples: [{
107
+ o: start,
108
+ t: now()
109
+ }]
110
+ };
111
+ swallowClick.current = false;
112
+ }, [enabled, latest]);
113
+ var onPointerMove = useCallback(function (e) {
114
+ var g = gesture.current;
115
+ if (!g) return;
116
+ var dx = e.clientX - g.x;
117
+ var dy = e.clientY - g.y;
118
+ if (!g.active) {
119
+ if (Math.max(Math.abs(dx), Math.abs(dy)) < SLOP) return;
120
+ if (Math.abs(dy) >= Math.abs(dx)) {
121
+ gesture.current = null; // a vertical pan: let the page scroll
122
+ return;
123
+ }
124
+ g.active = true;
125
+ swallowClick.current = true;
126
+ try {
127
+ var _g$el, _g$el$setPointerCaptu;
128
+ (_g$el = g.el) === null || _g$el === void 0 || (_g$el$setPointerCaptu = _g$el.setPointerCapture) === null || _g$el$setPointerCaptu === void 0 || _g$el$setPointerCaptu.call(_g$el, g.pointerId);
129
+ } catch (_unused) {
130
+ // The pointer may already be gone (synthetic events, fast lifts).
131
+ }
132
+ }
133
+ var raw = g.start + dx;
134
+ var side = sideOf(raw);
135
+ var _latest$current = latest.current,
136
+ w = _latest$current.widths,
137
+ fs = _latest$current.fullSwipe;
138
+ var next = side ? constrainOffset(raw, {
139
+ reveal: w[side],
140
+ fullSwipe: fs[side],
141
+ width: g.width
142
+ }) : 0;
143
+ g.current = next;
144
+ var t = now();
145
+ g.samples.push({
146
+ o: next,
147
+ t: t
148
+ });
149
+ while (g.samples.length > 2 && t - g.samples[0].t > VELOCITY_WINDOW) g.samples.shift();
150
+ setDrag({
151
+ offset: next,
152
+ width: g.width
153
+ });
154
+ }, [latest]);
155
+ var onPointerUp = useCallback(function () {
156
+ var g = gesture.current;
157
+ if (!g) return;
158
+ gesture.current = null;
159
+ if (!g.active) return;
160
+ var _latest$current2 = latest.current,
161
+ w = _latest$current2.widths,
162
+ fs = _latest$current2.fullSwipe,
163
+ t = _latest$current2.fullSwipeThreshold,
164
+ settle = _latest$current2.onSettle;
165
+ var value = g.current;
166
+ var side = sideOf(value);
167
+ var first = g.samples[0];
168
+ var velocity = (value - first.o) / Math.max(1, now() - first.t);
169
+ setDrag(null);
170
+ if (!side || w[side] === 0) {
171
+ settle({
172
+ side: null,
173
+ commit: false
174
+ });
175
+ return;
176
+ }
177
+ if (fs[side] && Math.abs(value) >= fullSwipeDistance(w[side], g.width, t)) {
178
+ settle({
179
+ side: side,
180
+ commit: true
181
+ });
182
+ return;
183
+ }
184
+ // A flick decides by direction; otherwise past half the actions opens.
185
+ var towardOpen = side === "leading" ? velocity > 0 : velocity < 0;
186
+ var flick = Math.abs(velocity) >= FLICK_VELOCITY;
187
+ var open = flick ? towardOpen : Math.abs(value) >= w[side] / 2;
188
+ settle({
189
+ side: open ? side : null,
190
+ commit: false
191
+ });
192
+ }, [latest]);
193
+ var onPointerCancel = useCallback(function () {
194
+ swallowClick.current = false;
195
+ reset();
196
+ }, [reset]);
197
+ // The click that ends a swipe must not activate the row.
198
+ var consumeClick = useCallback(function () {
199
+ if (!swallowClick.current) return false;
200
+ swallowClick.current = false;
201
+ return true;
202
+ }, []);
203
+ var dragSide = drag ? sideOf(drag.offset) : null;
204
+ var armed = dragSide && fullSwipe[dragSide] && widths[dragSide] > 0 && Math.abs(drag.offset) >= fullSwipeDistance(widths[dragSide], drag.width, fullSwipeThreshold) ? dragSide : null;
205
+ return {
206
+ offset: drag ? drag.offset : null,
207
+ dragging: drag != null,
208
+ armed: armed,
209
+ consumeClick: consumeClick,
210
+ handlers: enabled ? {
211
+ onPointerDown: onPointerDown,
212
+ onPointerMove: onPointerMove,
213
+ onPointerUp: onPointerUp,
214
+ onPointerCancel: onPointerCancel
215
+ } : {}
216
+ };
217
+ }
218
+
219
+ export { FALLBACK_WIDTH, constrainOffset, fullSwipeDistance, restingOffset, useSwipeGesture };
@@ -1,10 +1,11 @@
1
1
  'use client';
2
2
  import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
- import { forwardRef, useState, useId, useEffect } from 'react';
4
+ import { forwardRef, useState, useId, useEffect, useRef } from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
+ import { useHaptics } from '../../hooks/useHaptics.js';
6
7
 
7
- var _excluded = ["id", "name", "checked", "defaultChecked", "onChange", "disabled", "size", "color", "trackClass", "thumbClass", "className", "topLabel", "bottomLabel", "rightLabel", "leftLabel", "ariaLabel"];
8
+ var _excluded = ["id", "name", "checked", "defaultChecked", "onChange", "disabled", "size", "color", "trackClass", "thumbClass", "className", "topLabel", "bottomLabel", "rightLabel", "leftLabel", "ariaLabel", "onPointerDown", "onPointerMove", "onPointerUp", "onPointerCancel", "haptics"];
8
9
  /**
9
10
  * Switch.jsx
10
11
  * - Plain JSX (no TypeScript)
@@ -17,23 +18,31 @@ var _excluded = ["id", "name", "checked", "defaultChecked", "onChange", "disable
17
18
  * New props:
18
19
  * - topLabel, bottomLabel, rightLabel, leftLabel (renders label in those positions)
19
20
  */
21
+ // `md` takes the iOS 51×31 size on coarse pointers (27px knob, 2px inset).
20
22
  var SIZE_MAP = {
21
23
  sm: {
22
24
  track: "w-9 h-5",
23
25
  knob: "w-4 h-4",
24
- knobTranslate: "translate-x-4"
26
+ knobOn: "translate-x-4",
27
+ knobOff: "translate-x-1"
25
28
  },
26
29
  md: {
27
- track: "w-11 h-6",
28
- knob: "w-5 h-5",
29
- knobTranslate: "translate-x-5"
30
+ track: "w-11 h-6 [@media(pointer:coarse)]:h-[31px] [@media(pointer:coarse)]:w-[51px]",
31
+ knob: "w-5 h-5 [@media(pointer:coarse)]:h-[27px] [@media(pointer:coarse)]:w-[27px]",
32
+ knobOn: "translate-x-5 [@media(pointer:coarse)]:translate-x-[22px]",
33
+ knobOff: "translate-x-1 [@media(pointer:coarse)]:translate-x-0.5"
30
34
  },
31
35
  lg: {
32
36
  track: "w-14 h-8",
33
37
  knob: "w-6 h-6",
34
- knobTranslate: "translate-x-6"
38
+ knobOn: "translate-x-6",
39
+ knobOff: "translate-x-1"
35
40
  }
36
41
  };
42
+ // Horizontal travel (px) before a press becomes a drag.
43
+ var DRAG_THRESHOLD = 4;
44
+ // Token presets only: the raw-palette presets (blue / indigo / purple) were
45
+ // removed in 2.0.0 (DTUI-65).
37
46
  var COLOR_PRESETS = {
38
47
  primary: {
39
48
  bg: "bg-primary-600",
@@ -66,18 +75,6 @@ var COLOR_PRESETS = {
66
75
  warning: {
67
76
  bg: "bg-warning-600",
68
77
  ring: "focus-visible:ring-warning-300"
69
- },
70
- blue: {
71
- bg: "bg-blue-600",
72
- ring: "focus-visible:ring-blue-300"
73
- },
74
- indigo: {
75
- bg: "bg-indigo-600",
76
- ring: "focus-visible:ring-indigo-300"
77
- },
78
- purple: {
79
- bg: "bg-purple-600",
80
- ring: "focus-visible:ring-purple-300"
81
78
  }
82
79
  };
83
80
  var Switch = /*#__PURE__*/forwardRef(function Switch(props, ref) {
@@ -102,6 +99,12 @@ var Switch = /*#__PURE__*/forwardRef(function Switch(props, ref) {
102
99
  rightLabel = props.rightLabel,
103
100
  leftLabel = props.leftLabel,
104
101
  ariaLabel = props.ariaLabel,
102
+ onPointerDown = props.onPointerDown,
103
+ onPointerMove = props.onPointerMove,
104
+ onPointerUp = props.onPointerUp,
105
+ onPointerCancel = props.onPointerCancel,
106
+ _props$haptics = props.haptics,
107
+ haptics = _props$haptics === void 0 ? false : _props$haptics,
105
108
  rest = _objectWithoutProperties(props, _excluded);
106
109
  var isControlled = typeof checked === "boolean";
107
110
  var _useState = useState(defaultChecked),
@@ -115,31 +118,94 @@ var Switch = /*#__PURE__*/forwardRef(function Switch(props, ref) {
115
118
  if (!isControlled) setInternalChecked(defaultChecked);
116
119
  // eslint-disable-next-line react-hooks/exhaustive-deps
117
120
  }, [defaultChecked]);
118
- var toggle = function toggle() {
119
- if (disabled) return;
120
- var next = !isOn;
121
+ // Drag-to-toggle: slide the knob (iOS style). A tap still toggles via click.
122
+ var drag = useRef(null);
123
+ var suppressClick = useRef(false);
124
+ var _useState3 = useState(null),
125
+ _useState4 = _slicedToArray(_useState3, 2),
126
+ dragPreview = _useState4[0],
127
+ setDragPreview = _useState4[1];
128
+ var _useHaptics = useHaptics({
129
+ enabled: haptics
130
+ }),
131
+ haptic = _useHaptics.trigger;
132
+ var setOn = function setOn(next) {
133
+ if (disabled || next === isOn) return;
121
134
  if (!isControlled) setInternalChecked(next);
135
+ haptic("selection");
122
136
  onChange && onChange(next);
123
137
  };
138
+ var toggle = function toggle() {
139
+ if (disabled) return;
140
+ if (suppressClick.current) {
141
+ // The click that follows a drag's pointerup; the drag already decided.
142
+ suppressClick.current = false;
143
+ return;
144
+ }
145
+ setOn(!isOn);
146
+ };
147
+ var dragTarget = function dragTarget(dx, start) {
148
+ return dx > DRAG_THRESHOLD ? true : dx < -DRAG_THRESHOLD ? false : start;
149
+ };
150
+ var handlePointerDown = function handlePointerDown(e) {
151
+ var _e$currentTarget$setP, _e$currentTarget;
152
+ onPointerDown && onPointerDown(e);
153
+ if (disabled || e.button > 0) return;
154
+ suppressClick.current = false;
155
+ drag.current = {
156
+ x: e.clientX,
157
+ start: isOn,
158
+ moved: false
159
+ };
160
+ (_e$currentTarget$setP = (_e$currentTarget = e.currentTarget).setPointerCapture) === null || _e$currentTarget$setP === void 0 || _e$currentTarget$setP.call(_e$currentTarget, e.pointerId);
161
+ };
162
+ var handlePointerMove = function handlePointerMove(e) {
163
+ onPointerMove && onPointerMove(e);
164
+ if (!drag.current) return;
165
+ var dx = e.clientX - drag.current.x;
166
+ if (Math.abs(dx) > DRAG_THRESHOLD) drag.current.moved = true;
167
+ if (drag.current.moved) setDragPreview(dragTarget(dx, drag.current.start));
168
+ };
169
+ var endDrag = function endDrag(e, commit) {
170
+ var d = drag.current;
171
+ drag.current = null;
172
+ setDragPreview(null);
173
+ if (!d || !d.moved) return;
174
+ suppressClick.current = true;
175
+ if (commit) setOn(dragTarget(e.clientX - d.x, d.start));
176
+ };
177
+ var handlePointerUp = function handlePointerUp(e) {
178
+ onPointerUp && onPointerUp(e);
179
+ endDrag(e, true);
180
+ };
181
+ var handlePointerCancel = function handlePointerCancel(e) {
182
+ onPointerCancel && onPointerCancel(e);
183
+ endDrag(e, false);
184
+ };
124
185
  var handleKeyDown = function handleKeyDown(e) {
125
186
  if (disabled) return;
126
187
  if (e.key === " " || e.key === "Spacebar" || e.key === "Enter") {
127
188
  e.preventDefault();
128
- toggle();
189
+ setOn(!isOn);
129
190
  }
130
191
  };
131
192
  var sizeStuff = SIZE_MAP[size] || SIZE_MAP.md;
132
193
  var isPreset = COLOR_PRESETS[color];
133
194
  // Classes for the track (we use inline style for arbitrary trackClass when ON)
134
- var trackClasses = cn("relative inline-flex items-center rounded-full transition-colors duration-200 ease-in-out", sizeStuff.track, isOn ? trackClass ? "" : isPreset ? isPreset.bg : "" : "bg-gray-100", disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer", trackClass // Accepting trackClass as a class name
195
+ // While dragging, the knob and track follow the drag's preview state.
196
+ var shownOn = dragPreview !== null && dragPreview !== void 0 ? dragPreview : isOn;
197
+ var trackClasses = cn("relative inline-flex shrink-0 items-center rounded-full touch-pan-y select-none", "transition-colors duration-base ease-standard motion-reduce:transition-none", sizeStuff.track,
198
+ // Off track: a visible grey on white (light) and on dark surfaces.
199
+ shownOn ? trackClass ? "" : isPreset ? isPreset.bg : "" : "bg-gray-300 dark:bg-gray-600", disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer", trackClass // Accepting trackClass as a class name
135
200
  );
136
201
  // Knob classes (thumb)
137
- var knobBase = cn("inline-block rounded-full bg-white shadow transform ring-0 transition-transform duration-200 ease-in-out", sizeStuff.knob, thumbClass // Accepting thumbClass as a class name
202
+ // The knob stays white in both themes (it sits on a coloured or grey track).
203
+ var knobBase = cn("pointer-events-none inline-block rounded-full bg-white shadow-sm transform ring-0", "transition-transform duration-base ease-spring-out motion-reduce:transition-none", sizeStuff.knob, thumbClass // Accepting thumbClass as a class name
138
204
  );
139
- var knobTransform = isOn ? sizeStuff.knobTranslate : "translate-x-1";
205
+ var knobTransform = shownOn ? sizeStuff.knobOn : sizeStuff.knobOff;
140
206
  var ringClass = isPreset ? isPreset.ring : "focus-visible:ring-primary-300";
141
- var focusClasses = disabled ? "" : "focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ".concat(ringClass);
142
- var trackStyle = isOn && !trackClass && !isPreset ? {
207
+ var focusClasses = disabled ? "" : "focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-900 ".concat(ringClass);
208
+ var trackStyle = shownOn && !trackClass && !isPreset ? {
143
209
  backgroundColor: color
144
210
  } : undefined; // Fallback to default trackClass if trackClass is not a class
145
211
  var accessibleLabel = ariaLabel || rightLabel || leftLabel || topLabel || bottomLabel || "Toggle";
@@ -171,6 +237,10 @@ var Switch = /*#__PURE__*/forwardRef(function Switch(props, ref) {
171
237
  className: cn(trackClasses, focusClasses),
172
238
  onClick: toggle,
173
239
  onKeyDown: handleKeyDown,
240
+ onPointerDown: handlePointerDown,
241
+ onPointerMove: handlePointerMove,
242
+ onPointerUp: handlePointerUp,
243
+ onPointerCancel: handlePointerCancel,
174
244
  style: trackStyle
175
245
  }, rest), {}, {
176
246
  children: [jsx("span", {