@dreamtree-org/twreact-ui 1.3.9 → 1.3.11

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 (128) hide show
  1. package/README.md +32 -0
  2. package/ai-skills/dreamtree-ui.md +12 -7
  3. package/dist/ai-schema.json +156 -29
  4. package/dist/charts/index.js +195 -66
  5. package/dist/components/blocks/AppShell.d.ts.map +1 -1
  6. package/dist/components/charts/CartesianChart.d.ts.map +1 -1
  7. package/dist/components/charts/ChartFrame.d.ts.map +1 -1
  8. package/dist/components/charts/useChartLayout.d.ts +25 -0
  9. package/dist/components/charts/useChartLayout.d.ts.map +1 -0
  10. package/dist/components/core/Accordion.d.ts.map +1 -1
  11. package/dist/components/core/Button.d.ts.map +1 -1
  12. package/dist/components/core/Checkbox.d.ts.map +1 -1
  13. package/dist/components/core/ColorPicker.d.ts.map +1 -1
  14. package/dist/components/core/DatePicker.d.ts.map +1 -1
  15. package/dist/components/core/Form.d.ts.map +1 -1
  16. package/dist/components/core/Input.d.ts.map +1 -1
  17. package/dist/components/core/Loader.d.ts.map +1 -1
  18. package/dist/components/core/LocationPicker.d.ts.map +1 -1
  19. package/dist/components/core/PriceRangePicker.d.ts +0 -29
  20. package/dist/components/core/PriceRangePicker.d.ts.map +1 -1
  21. package/dist/components/core/ProgressBar.d.ts.map +1 -1
  22. package/dist/components/core/Radio.d.ts.map +1 -1
  23. package/dist/components/core/Rate.d.ts.map +1 -1
  24. package/dist/components/core/RoundedTag.d.ts.map +1 -1
  25. package/dist/components/core/Skeleton.d.ts.map +1 -1
  26. package/dist/components/core/Slider.d.ts.map +1 -1
  27. package/dist/components/core/SpeechToText.d.ts.map +1 -1
  28. package/dist/components/core/Switch.d.ts.map +1 -1
  29. package/dist/components/core/Table/ColumnMenu.d.ts.map +1 -1
  30. package/dist/components/core/Table/Pagination.d.ts.map +1 -1
  31. package/dist/components/core/Table/TableRow.d.ts.map +1 -1
  32. package/dist/components/core/Table/helpers.d.ts +2 -0
  33. package/dist/components/core/Table/helpers.d.ts.map +1 -1
  34. package/dist/components/core/Tabs.d.ts +2 -5
  35. package/dist/components/core/Tabs.d.ts.map +1 -1
  36. package/dist/components/core/TextToSpeech.d.ts +2 -7
  37. package/dist/components/core/TextToSpeech.d.ts.map +1 -1
  38. package/dist/components/core/Textarea.d.ts.map +1 -1
  39. package/dist/components/core/Tooltip.d.ts.map +1 -1
  40. package/dist/components/feedback/Alert.d.ts.map +1 -1
  41. package/dist/components/feedback/Dialog.d.ts.map +1 -1
  42. package/dist/components/feedback/Toast.d.ts.map +1 -1
  43. package/dist/components/navigation/Breadcrumbs.d.ts.map +1 -1
  44. package/dist/components/navigation/FootNav.d.ts.map +1 -1
  45. package/dist/components/navigation/Sidebar.d.ts.map +1 -1
  46. package/dist/components/utility/Avatar.d.ts +8 -0
  47. package/dist/components/utility/Avatar.d.ts.map +1 -1
  48. package/dist/components/utility/AvatarGroup.d.ts +4 -0
  49. package/dist/components/utility/AvatarGroup.d.ts.map +1 -0
  50. package/dist/components/utility/Badge.d.ts.map +1 -1
  51. package/dist/components/utility/Card.d.ts.map +1 -1
  52. package/dist/components/utility/Carousel.d.ts.map +1 -1
  53. package/dist/components/utility/FileUpload.d.ts.map +1 -1
  54. package/dist/components/utility/Pagination.d.ts.map +1 -1
  55. package/dist/components/utility/Sortable.d.ts.map +1 -1
  56. package/dist/components/utility/Stepper.d.ts.map +1 -1
  57. package/dist/esm/components/blocks/AppShell.js +24 -18
  58. package/dist/esm/components/blocks/EmptyState.js +1 -1
  59. package/dist/esm/components/blocks/ErrorState.js +1 -1
  60. package/dist/esm/components/blocks/LoadingState.js +4 -4
  61. package/dist/esm/components/blocks/StatCard.js +6 -6
  62. package/dist/esm/components/blocks/StatusPill.js +5 -8
  63. package/dist/esm/components/charts/CartesianChart.js +85 -62
  64. package/dist/esm/components/charts/ChartFrame.js +12 -5
  65. package/dist/esm/components/charts/useChartLayout.js +105 -0
  66. package/dist/esm/components/core/Accordion.js +40 -9
  67. package/dist/esm/components/core/Button.js +49 -25
  68. package/dist/esm/components/core/Checkbox.js +71 -45
  69. package/dist/esm/components/core/ColorPicker.js +49 -48
  70. package/dist/esm/components/core/DatePicker.js +97 -58
  71. package/dist/esm/components/core/Form.js +35 -8
  72. package/dist/esm/components/core/Input.js +89 -20
  73. package/dist/esm/components/core/Loader.js +30 -44
  74. package/dist/esm/components/core/LocationPicker.js +82 -16
  75. package/dist/esm/components/core/PriceRangePicker.js +205 -218
  76. package/dist/esm/components/core/ProgressBar.js +8 -12
  77. package/dist/esm/components/core/Radio.js +43 -15
  78. package/dist/esm/components/core/Rate.js +109 -50
  79. package/dist/esm/components/core/RoundedTag.js +27 -10
  80. package/dist/esm/components/core/Skeleton.js +13 -14
  81. package/dist/esm/components/core/Slider.js +24 -6
  82. package/dist/esm/components/core/SpeechToText.js +68 -16
  83. package/dist/esm/components/core/Switch.js +88 -16
  84. package/dist/esm/components/core/Table/ColumnMenu.js +2 -4
  85. package/dist/esm/components/core/Table/Pagination.js +15 -18
  86. package/dist/esm/components/core/Table/TableRow.js +2 -2
  87. package/dist/esm/components/core/Table/helpers.js +33 -1
  88. package/dist/esm/components/core/Tabs.js +185 -37
  89. package/dist/esm/components/core/TextToSpeech.js +121 -28
  90. package/dist/esm/components/core/Textarea.js +10 -2
  91. package/dist/esm/components/core/Tooltip.js +100 -28
  92. package/dist/esm/components/feedback/Alert.js +21 -11
  93. package/dist/esm/components/feedback/Dialog.js +44 -100
  94. package/dist/esm/components/feedback/Toast.js +24 -5
  95. package/dist/esm/components/navigation/Breadcrumbs.js +97 -35
  96. package/dist/esm/components/navigation/FootNav.js +102 -13
  97. package/dist/esm/components/navigation/Sidebar.js +13 -61
  98. package/dist/esm/components/utility/Avatar.js +37 -22
  99. package/dist/esm/components/utility/AvatarGroup.js +56 -0
  100. package/dist/esm/components/utility/Badge.js +43 -23
  101. package/dist/esm/components/utility/Card.js +32 -8
  102. package/dist/esm/components/utility/Carousel.js +165 -38
  103. package/dist/esm/components/utility/FileUpload.js +106 -31
  104. package/dist/esm/components/utility/Pagination.js +33 -15
  105. package/dist/esm/components/utility/Sortable.js +6 -2
  106. package/dist/esm/components/utility/Stepper.js +110 -47
  107. package/dist/esm/hooks/useFocusTrap.js +10 -2
  108. package/dist/esm/hooks/useSliderValues.js +8 -2
  109. package/dist/esm/hooks/useSortable.js +107 -5
  110. package/dist/esm/index.js +1 -0
  111. package/dist/esm/utils/touchTarget.js +4 -1
  112. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  113. package/dist/hooks/useSliderValues.d.ts +6 -2
  114. package/dist/hooks/useSliderValues.d.ts.map +1 -1
  115. package/dist/hooks/useSortable.d.ts +11 -1
  116. package/dist/hooks/useSortable.d.ts.map +1 -1
  117. package/dist/index.css +1 -1
  118. package/dist/index.d.ts +3 -2
  119. package/dist/index.esm.css +1 -1
  120. package/dist/index.esm.js +3734 -2174
  121. package/dist/index.js +3732 -2171
  122. package/dist/types/charts.d.ts +3 -1
  123. package/dist/types/charts.d.ts.map +1 -1
  124. package/dist/types/index.d.ts +64 -9
  125. package/dist/types/index.d.ts.map +1 -1
  126. package/doc/README.md +1 -0
  127. package/mcp/catalog.snapshot.json +350 -121
  128. package/package.json +1 -1
@@ -4,8 +4,21 @@ import { jsxs, jsx } from 'react/jsx-runtime';
4
4
  import { forwardRef, useRef, useState, useEffect, useCallback, useImperativeHandle } from 'react';
5
5
  import { MicOff, Mic } from 'lucide-react';
6
6
  import { cn } from '../../utils/cn.js';
7
+ import { PRESS_FEEDBACK } from '../../utils/touchTarget.js';
7
8
 
8
9
  var _excluded = ["lang", "continuous", "interimResults", "onSpeechComplete", "onSpeaking", "onError", "onStart", "onStop", "renderButton", "autoStart", "disabled", "resetOnStart", "className"];
10
+ // Level bars (DTUI-42). There is no audio-level API without getUserMedia, so the
11
+ // bars show *whether* speech is being heard (speechstart / results), not volume.
12
+ var LEVEL_BARS = [{
13
+ height: "h-2",
14
+ delay: "[animation-delay:0ms]"
15
+ }, {
16
+ height: "h-3.5",
17
+ delay: "[animation-delay:150ms]"
18
+ }, {
19
+ height: "h-2.5",
20
+ delay: "[animation-delay:300ms]"
21
+ }];
9
22
  var isProbablyMobile = function isProbablyMobile() {
10
23
  if (typeof navigator === "undefined") return false;
11
24
  return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
@@ -48,14 +61,19 @@ var SpeechToText = /*#__PURE__*/forwardRef(function (_ref, ref) {
48
61
  _useState2 = _slicedToArray(_useState, 2),
49
62
  _isListening = _useState2[0],
50
63
  setIsListening = _useState2[1];
51
- var _useState3 = useState(true),
64
+ // True while the engine reports speech (speechstart → speechend, or results).
65
+ var _useState3 = useState(false),
52
66
  _useState4 = _slicedToArray(_useState3, 2),
53
- _isSupported = _useState4[0],
54
- setIsSupported = _useState4[1];
55
- var _useState5 = useState(null),
67
+ isHearing = _useState4[0],
68
+ setIsHearing = _useState4[1];
69
+ var _useState5 = useState(true),
56
70
  _useState6 = _slicedToArray(_useState5, 2),
57
- error = _useState6[0],
58
- setError = _useState6[1];
71
+ _isSupported = _useState6[0],
72
+ setIsSupported = _useState6[1];
73
+ var _useState7 = useState(null),
74
+ _useState8 = _slicedToArray(_useState7, 2),
75
+ error = _useState8[0],
76
+ setError = _useState8[1];
59
77
  useEffect(function () {
60
78
  var hasSpeechRecognition = typeof window !== "undefined" && ("SpeechRecognition" in window || "webkitSpeechRecognition" in window);
61
79
  setIsSupported(hasSpeechRecognition);
@@ -76,6 +94,14 @@ var SpeechToText = /*#__PURE__*/forwardRef(function (_ref, ref) {
76
94
  recognition.continuous = isProbablyMobile() ? false : continuous;
77
95
  recognition.interimResults = interimResults;
78
96
  recognition.lang = lang;
97
+ recognition.onspeechstart = function () {
98
+ if (!isMountedRef.current) return;
99
+ setIsHearing(true);
100
+ };
101
+ recognition.onspeechend = function () {
102
+ if (!isMountedRef.current) return;
103
+ setIsHearing(false);
104
+ };
79
105
  recognition.onstart = function () {
80
106
  if (!isMountedRef.current) return;
81
107
  setIsListening(true);
@@ -97,6 +123,7 @@ var SpeechToText = /*#__PURE__*/forwardRef(function (_ref, ref) {
97
123
  interim += text;
98
124
  }
99
125
  }
126
+ if (interim || finalChunk) setIsHearing(true);
100
127
  if (interim) onSpeaking(interim.trim());
101
128
  if (finalChunk) onSpeechComplete(finalChunk.trim());
102
129
  };
@@ -107,11 +134,13 @@ var SpeechToText = /*#__PURE__*/forwardRef(function (_ref, ref) {
107
134
  var err = new Error((event === null || event === void 0 ? void 0 : event.error) || "Speech recognition error");
108
135
  setError(err);
109
136
  setIsListening(false);
137
+ setIsHearing(false);
110
138
  onError(err);
111
139
  };
112
140
  recognition.onend = function () {
113
141
  if (!isMountedRef.current) return;
114
142
  setIsListening(false);
143
+ setIsHearing(false);
115
144
  onStop();
116
145
  };
117
146
  recognitionRef.current = recognition;
@@ -192,6 +221,7 @@ var SpeechToText = /*#__PURE__*/forwardRef(function (_ref, ref) {
192
221
  // eslint-disable-next-line react-hooks/refs
193
222
  return renderButton({
194
223
  isListening: _isListening,
224
+ isHearing: isHearing,
195
225
  isSupported: _isSupported,
196
226
  error: error,
197
227
  start: startListening,
@@ -207,18 +237,40 @@ var SpeechToText = /*#__PURE__*/forwardRef(function (_ref, ref) {
207
237
  "aria-label": _isListening ? "Stop listening" : "Start listening",
208
238
  onClick: toggleListening,
209
239
  disabled: isDisabled,
210
- className: cn("inline-flex items-center gap-2 rounded-md px-4 h-10 text-sm font-medium", "border border-gray-300 bg-transparent transition-colors", "hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500", "disabled:opacity-50 disabled:pointer-events-none", {
211
- "text-error-600 border-error-600": _isListening,
212
- "text-gray-900": !_isListening
240
+ "data-listening": _isListening || undefined,
241
+ "data-hearing": _isListening && isHearing || undefined,
242
+ className: cn("inline-flex items-center gap-2 rounded-md px-4 h-10 text-sm font-medium", "[@media(pointer:coarse)]:min-h-11", "border border-gray-300 bg-transparent transition-colors duration-fast motion-reduce:transition-none", "hover:bg-gray-50 dark:border-gray-600 dark:hover:bg-gray-800", "focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-primary-500 dark:focus-visible:ring-offset-gray-900", "disabled:opacity-50 disabled:pointer-events-none", PRESS_FEEDBACK,
243
+ // PRESS_FEEDBACK sets transition-transform; keep the colour transition too.
244
+ "transition-[color,background-color,border-color,transform]", {
245
+ "text-error-600 border-error-600 dark:text-error-400 dark:border-error-400": _isListening,
246
+ "text-gray-900 dark:text-gray-100": !_isListening
213
247
  }, className)
214
248
  }, rest), {}, {
215
- children: [_isListening ? jsx(MicOff, {
216
- className: "h-4 w-4",
217
- "aria-hidden": "true"
218
- }) : jsx(Mic, {
219
- className: "h-4 w-4",
220
- "aria-hidden": "true"
221
- }), _isListening ? "Stop" : "Start"]
249
+ children: [jsxs("span", {
250
+ className: "relative inline-flex h-4 w-4 items-center justify-center",
251
+ "aria-hidden": "true",
252
+ children: [_isListening &&
253
+ // Listening pulse; a static ring replaces it under reduced motion.
254
+ jsx("span", {
255
+ "data-testid": "stt-pulse",
256
+ className: "absolute -inset-1.5 rounded-full bg-error-500/30 animate-ping motion-reduce:animate-none motion-reduce:bg-transparent motion-reduce:ring-2 motion-reduce:ring-error-500/60"
257
+ }), _isListening ? jsx(MicOff, {
258
+ className: "relative h-4 w-4",
259
+ "aria-hidden": "true"
260
+ }) : jsx(Mic, {
261
+ className: "relative h-4 w-4",
262
+ "aria-hidden": "true"
263
+ })]
264
+ }), _isListening ? "Stop" : "Start", _isListening && jsx("span", {
265
+ className: "inline-flex h-3.5 items-end gap-0.5",
266
+ "aria-hidden": "true",
267
+ "data-testid": "stt-level",
268
+ children: LEVEL_BARS.map(function (bar, i) {
269
+ return jsx("span", {
270
+ className: cn("w-0.5 rounded-full transition-colors duration-base motion-reduce:transition-none", isHearing ? cn("bg-current animate-pulse motion-reduce:animate-none", bar.height, bar.delay) : "h-1 bg-current opacity-40")
271
+ }, i);
272
+ })
273
+ })]
222
274
  }));
223
275
  });
224
276
  SpeechToText.displayName = "SpeechToText";
@@ -1,10 +1,10 @@
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
6
 
7
- var _excluded = ["id", "name", "checked", "defaultChecked", "onChange", "disabled", "size", "color", "trackClass", "thumbClass", "className", "topLabel", "bottomLabel", "rightLabel", "leftLabel", "ariaLabel"];
7
+ var _excluded = ["id", "name", "checked", "defaultChecked", "onChange", "disabled", "size", "color", "trackClass", "thumbClass", "className", "topLabel", "bottomLabel", "rightLabel", "leftLabel", "ariaLabel", "onPointerDown", "onPointerMove", "onPointerUp", "onPointerCancel"];
8
8
  /**
9
9
  * Switch.jsx
10
10
  * - Plain JSX (no TypeScript)
@@ -17,23 +17,29 @@ var _excluded = ["id", "name", "checked", "defaultChecked", "onChange", "disable
17
17
  * New props:
18
18
  * - topLabel, bottomLabel, rightLabel, leftLabel (renders label in those positions)
19
19
  */
20
+ // `md` takes the iOS 51×31 size on coarse pointers (27px knob, 2px inset).
20
21
  var SIZE_MAP = {
21
22
  sm: {
22
23
  track: "w-9 h-5",
23
24
  knob: "w-4 h-4",
24
- knobTranslate: "translate-x-4"
25
+ knobOn: "translate-x-4",
26
+ knobOff: "translate-x-1"
25
27
  },
26
28
  md: {
27
- track: "w-11 h-6",
28
- knob: "w-5 h-5",
29
- knobTranslate: "translate-x-5"
29
+ track: "w-11 h-6 [@media(pointer:coarse)]:h-[31px] [@media(pointer:coarse)]:w-[51px]",
30
+ knob: "w-5 h-5 [@media(pointer:coarse)]:h-[27px] [@media(pointer:coarse)]:w-[27px]",
31
+ knobOn: "translate-x-5 [@media(pointer:coarse)]:translate-x-[22px]",
32
+ knobOff: "translate-x-1 [@media(pointer:coarse)]:translate-x-0.5"
30
33
  },
31
34
  lg: {
32
35
  track: "w-14 h-8",
33
36
  knob: "w-6 h-6",
34
- knobTranslate: "translate-x-6"
37
+ knobOn: "translate-x-6",
38
+ knobOff: "translate-x-1"
35
39
  }
36
40
  };
41
+ // Horizontal travel (px) before a press becomes a drag.
42
+ var DRAG_THRESHOLD = 4;
37
43
  var COLOR_PRESETS = {
38
44
  primary: {
39
45
  bg: "bg-primary-600",
@@ -102,6 +108,10 @@ var Switch = /*#__PURE__*/forwardRef(function Switch(props, ref) {
102
108
  rightLabel = props.rightLabel,
103
109
  leftLabel = props.leftLabel,
104
110
  ariaLabel = props.ariaLabel,
111
+ onPointerDown = props.onPointerDown,
112
+ onPointerMove = props.onPointerMove,
113
+ onPointerUp = props.onPointerUp,
114
+ onPointerCancel = props.onPointerCancel,
105
115
  rest = _objectWithoutProperties(props, _excluded);
106
116
  var isControlled = typeof checked === "boolean";
107
117
  var _useState = useState(defaultChecked),
@@ -115,31 +125,89 @@ var Switch = /*#__PURE__*/forwardRef(function Switch(props, ref) {
115
125
  if (!isControlled) setInternalChecked(defaultChecked);
116
126
  // eslint-disable-next-line react-hooks/exhaustive-deps
117
127
  }, [defaultChecked]);
118
- var toggle = function toggle() {
119
- if (disabled) return;
120
- var next = !isOn;
128
+ // Drag-to-toggle: slide the knob (iOS style). A tap still toggles via click.
129
+ var drag = useRef(null);
130
+ var suppressClick = useRef(false);
131
+ var _useState3 = useState(null),
132
+ _useState4 = _slicedToArray(_useState3, 2),
133
+ dragPreview = _useState4[0],
134
+ setDragPreview = _useState4[1];
135
+ var setOn = function setOn(next) {
136
+ if (disabled || next === isOn) return;
121
137
  if (!isControlled) setInternalChecked(next);
122
138
  onChange && onChange(next);
123
139
  };
140
+ var toggle = function toggle() {
141
+ if (disabled) return;
142
+ if (suppressClick.current) {
143
+ // The click that follows a drag's pointerup; the drag already decided.
144
+ suppressClick.current = false;
145
+ return;
146
+ }
147
+ setOn(!isOn);
148
+ };
149
+ var dragTarget = function dragTarget(dx, start) {
150
+ return dx > DRAG_THRESHOLD ? true : dx < -DRAG_THRESHOLD ? false : start;
151
+ };
152
+ var handlePointerDown = function handlePointerDown(e) {
153
+ var _e$currentTarget$setP, _e$currentTarget;
154
+ onPointerDown && onPointerDown(e);
155
+ if (disabled || e.button > 0) return;
156
+ suppressClick.current = false;
157
+ drag.current = {
158
+ x: e.clientX,
159
+ start: isOn,
160
+ moved: false
161
+ };
162
+ (_e$currentTarget$setP = (_e$currentTarget = e.currentTarget).setPointerCapture) === null || _e$currentTarget$setP === void 0 || _e$currentTarget$setP.call(_e$currentTarget, e.pointerId);
163
+ };
164
+ var handlePointerMove = function handlePointerMove(e) {
165
+ onPointerMove && onPointerMove(e);
166
+ if (!drag.current) return;
167
+ var dx = e.clientX - drag.current.x;
168
+ if (Math.abs(dx) > DRAG_THRESHOLD) drag.current.moved = true;
169
+ if (drag.current.moved) setDragPreview(dragTarget(dx, drag.current.start));
170
+ };
171
+ var endDrag = function endDrag(e, commit) {
172
+ var d = drag.current;
173
+ drag.current = null;
174
+ setDragPreview(null);
175
+ if (!d || !d.moved) return;
176
+ suppressClick.current = true;
177
+ if (commit) setOn(dragTarget(e.clientX - d.x, d.start));
178
+ };
179
+ var handlePointerUp = function handlePointerUp(e) {
180
+ onPointerUp && onPointerUp(e);
181
+ endDrag(e, true);
182
+ };
183
+ var handlePointerCancel = function handlePointerCancel(e) {
184
+ onPointerCancel && onPointerCancel(e);
185
+ endDrag(e, false);
186
+ };
124
187
  var handleKeyDown = function handleKeyDown(e) {
125
188
  if (disabled) return;
126
189
  if (e.key === " " || e.key === "Spacebar" || e.key === "Enter") {
127
190
  e.preventDefault();
128
- toggle();
191
+ setOn(!isOn);
129
192
  }
130
193
  };
131
194
  var sizeStuff = SIZE_MAP[size] || SIZE_MAP.md;
132
195
  var isPreset = COLOR_PRESETS[color];
133
196
  // 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
197
+ // While dragging, the knob and track follow the drag's preview state.
198
+ var shownOn = dragPreview !== null && dragPreview !== void 0 ? dragPreview : isOn;
199
+ 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,
200
+ // Off track: a visible grey on white (light) and on dark surfaces.
201
+ 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
202
  );
136
203
  // 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
204
+ // The knob stays white in both themes (it sits on a coloured or grey track).
205
+ 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
206
  );
139
- var knobTransform = isOn ? sizeStuff.knobTranslate : "translate-x-1";
207
+ var knobTransform = shownOn ? sizeStuff.knobOn : sizeStuff.knobOff;
140
208
  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 ? {
209
+ var focusClasses = disabled ? "" : "focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-900 ".concat(ringClass);
210
+ var trackStyle = shownOn && !trackClass && !isPreset ? {
143
211
  backgroundColor: color
144
212
  } : undefined; // Fallback to default trackClass if trackClass is not a class
145
213
  var accessibleLabel = ariaLabel || rightLabel || leftLabel || topLabel || bottomLabel || "Toggle";
@@ -171,6 +239,10 @@ var Switch = /*#__PURE__*/forwardRef(function Switch(props, ref) {
171
239
  className: cn(trackClasses, focusClasses),
172
240
  onClick: toggle,
173
241
  onKeyDown: handleKeyDown,
242
+ onPointerDown: handlePointerDown,
243
+ onPointerMove: handlePointerMove,
244
+ onPointerUp: handlePointerUp,
245
+ onPointerCancel: handlePointerCancel,
174
246
  style: trackStyle
175
247
  }, rest), {}, {
176
248
  children: [jsx("span", {
@@ -1,8 +1,8 @@
1
1
  'use client';
2
- import { defineProperty as _defineProperty } from '../../../_virtual/_rollupPluginBabelHelpers.js';
3
2
  import { jsxs, jsx } from 'react/jsx-runtime';
4
3
  import 'react';
5
4
  import { cn } from '../../../utils/cn.js';
5
+ import { getHoverClass } from './helpers.js';
6
6
 
7
7
  var DEFAULT_ROW_HOVER = "bg-background";
8
8
  /**
@@ -32,9 +32,7 @@ function ColumnMenu(_ref) {
32
32
  className: "max-h-56 overflow-auto",
33
33
  children: columns.map(function (col) {
34
34
  return jsxs("label", {
35
- className: cn(_defineProperty({
36
- "flex cursor-pointer items-center gap-2 px-2 py-1.5 rounded": true
37
- }, "hover:" + rowHover, true)),
35
+ className: cn("flex cursor-pointer items-center gap-2 px-2 py-1.5 rounded", getHoverClass(rowHover)),
38
36
  children: [jsx("input", {
39
37
  type: "checkbox",
40
38
  checked: col.isVisible !== false,
@@ -1,27 +1,27 @@
1
1
  'use client';
2
- import { defineProperty as _defineProperty } from '../../../_virtual/_rollupPluginBabelHelpers.js';
3
2
  import { jsxs, jsx } from 'react/jsx-runtime';
4
3
  import 'react';
5
4
  import { cn } from '../../../utils/cn.js';
5
+ import { TOUCH_TARGET } from '../../../utils/touchTarget.js';
6
+ import { getHoverClass } from './helpers.js';
6
7
  import { ChevronsLeft, ChevronLeft, ChevronRight, ChevronsRight } from 'lucide-react';
7
8
 
8
9
  /**
9
10
  * Pagination controls component for the table
10
11
  * Supports both header (icon-only) and footer (with page numbers) variants
11
12
  */
12
- var DEFAULT_ROW_HOVER = "bg-background";
13
+ var NAV_BUTTON_BASE = "inline-flex items-center justify-center rounded-lg md:rounded-md border border-gray-300 disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500";
13
14
  // Header pagination (compact, icon-based)
14
15
  function PaginationHeader(_ref) {
15
- var _theme$rowHover;
16
16
  var currentPage = _ref.currentPage,
17
17
  totalPages = _ref.totalPages,
18
18
  onPageChange = _ref.onPageChange,
19
19
  theme = _ref.theme;
20
- var rowHover = (_theme$rowHover = theme === null || theme === void 0 ? void 0 : theme.rowHover) !== null && _theme$rowHover !== void 0 ? _theme$rowHover : DEFAULT_ROW_HOVER;
20
+ var hoverClass = getHoverClass(theme === null || theme === void 0 ? void 0 : theme.rowHover);
21
21
  return jsxs("div", {
22
22
  className: "flex items-center gap-1",
23
23
  children: [totalPages >= 4 && jsx("button", {
24
- className: cn("rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50", ["hover:" + rowHover]),
24
+ className: cn(NAV_BUTTON_BASE, "p-2", TOUCH_TARGET, hoverClass),
25
25
  disabled: currentPage === 1,
26
26
  onClick: function onClick() {
27
27
  return onPageChange(1);
@@ -31,7 +31,7 @@ function PaginationHeader(_ref) {
31
31
  className: "size-4 md:size-5 text-primary-800"
32
32
  })
33
33
  }), jsx("button", {
34
- className: cn("rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50", ["hover:" + rowHover]),
34
+ className: cn(NAV_BUTTON_BASE, "p-2", TOUCH_TARGET, hoverClass),
35
35
  disabled: currentPage === 1,
36
36
  onClick: function onClick() {
37
37
  return onPageChange(Math.max(1, currentPage - 1));
@@ -41,7 +41,7 @@ function PaginationHeader(_ref) {
41
41
  className: "size-4 md:size-5 text-primary-800"
42
42
  })
43
43
  }), jsx("button", {
44
- className: cn("rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50", ["hover:" + rowHover]),
44
+ className: cn(NAV_BUTTON_BASE, "p-2", TOUCH_TARGET, hoverClass),
45
45
  disabled: currentPage === totalPages,
46
46
  onClick: function onClick() {
47
47
  return onPageChange(Math.min(totalPages, currentPage + 1));
@@ -51,7 +51,7 @@ function PaginationHeader(_ref) {
51
51
  className: "size-4 md:size-5 text-primary-800"
52
52
  })
53
53
  }), totalPages >= 4 && jsx("button", {
54
- className: cn("rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50", ["hover:" + rowHover]),
54
+ className: cn(NAV_BUTTON_BASE, "p-2", TOUCH_TARGET, hoverClass),
55
55
  disabled: currentPage === totalPages,
56
56
  onClick: function onClick() {
57
57
  return onPageChange(totalPages);
@@ -65,13 +65,12 @@ function PaginationHeader(_ref) {
65
65
  }
66
66
  // Footer pagination (with page numbers)
67
67
  function PaginationFooter(_ref2) {
68
- var _theme$rowHover2;
69
68
  var currentPage = _ref2.currentPage,
70
69
  totalPages = _ref2.totalPages,
71
70
  onPageChange = _ref2.onPageChange,
72
71
  isMobileView = _ref2.isMobileView,
73
72
  theme = _ref2.theme;
74
- var rowHover = (_theme$rowHover2 = theme === null || theme === void 0 ? void 0 : theme.rowHover) !== null && _theme$rowHover2 !== void 0 ? _theme$rowHover2 : DEFAULT_ROW_HOVER;
73
+ var hoverClass = getHoverClass(theme === null || theme === void 0 ? void 0 : theme.rowHover);
75
74
  var visiblePageCount = isMobileView ? 5 : 3;
76
75
  return jsx("div", {
77
76
  className: "p-4 border-t",
@@ -83,7 +82,7 @@ function PaginationFooter(_ref2) {
83
82
  }), jsxs("div", {
84
83
  className: "flex items-center gap-1 sm:gap-2",
85
84
  children: [totalPages >= 4 && jsx("button", {
86
- className: cn("rounded-lg md:rounded-md border border-gray-300 disabled:opacity-50", isMobileView ? "p-2" : "px-4 py-2 text-sm", ["hover:" + rowHover]),
85
+ className: cn(NAV_BUTTON_BASE, isMobileView ? "p-2" : "px-4 py-2 text-sm", TOUCH_TARGET, hoverClass),
87
86
  disabled: currentPage === 1,
88
87
  onClick: function onClick() {
89
88
  return onPageChange(1);
@@ -93,7 +92,7 @@ function PaginationFooter(_ref2) {
93
92
  className: "size-4 text-primary-800"
94
93
  }) : "First"
95
94
  }), jsx("button", {
96
- className: cn("rounded-lg md:rounded-md border border-gray-300 disabled:opacity-50", isMobileView ? "p-2" : "px-4 py-2 text-sm", ["hover:" + rowHover]),
95
+ className: cn(NAV_BUTTON_BASE, isMobileView ? "p-2" : "px-4 py-2 text-sm", TOUCH_TARGET, hoverClass),
97
96
  disabled: currentPage === 1,
98
97
  onClick: function onClick() {
99
98
  return onPageChange(Math.max(1, currentPage - 1));
@@ -118,10 +117,8 @@ function PaginationFooter(_ref2) {
118
117
  pageNum = currentPage - 2 + i;
119
118
  }
120
119
  return jsx("button", {
121
- className: cn("h-8 w-8 rounded-lg md:rounded-md text-sm", _defineProperty({
122
- "bg-primary-600 text-white": pageNum === currentPage,
123
- "border border-gray-300": pageNum !== currentPage
124
- }, "hover:" + rowHover, pageNum !== currentPage)),
120
+ className: cn("inline-flex items-center justify-center h-8 w-8 rounded-lg md:rounded-md text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500", TOUCH_TARGET, pageNum === currentPage ? "bg-primary-600 text-white" : ["border border-gray-300", hoverClass]),
121
+ "aria-current": pageNum === currentPage ? "page" : undefined,
125
122
  onClick: function onClick() {
126
123
  return onPageChange(pageNum);
127
124
  },
@@ -129,7 +126,7 @@ function PaginationFooter(_ref2) {
129
126
  }, pageNum);
130
127
  })
131
128
  }), jsx("button", {
132
- className: cn("rounded-lg md:rounded-md border border-gray-300 disabled:opacity-50", isMobileView ? "p-2" : "px-4 py-2 text-sm", ["hover:" + rowHover]),
129
+ className: cn(NAV_BUTTON_BASE, isMobileView ? "p-2" : "px-4 py-2 text-sm", TOUCH_TARGET, hoverClass),
133
130
  disabled: currentPage === totalPages,
134
131
  onClick: function onClick() {
135
132
  return onPageChange(Math.min(totalPages, currentPage + 1));
@@ -139,7 +136,7 @@ function PaginationFooter(_ref2) {
139
136
  className: "size-4 text-primary-800"
140
137
  }) : "Next"
141
138
  }), totalPages >= 4 && jsx("button", {
142
- className: cn("rounded-lg md:rounded-md border border-gray-300 disabled:opacity-50", isMobileView ? "p-2" : "px-4 py-2 text-sm", ["hover:" + rowHover]),
139
+ className: cn(NAV_BUTTON_BASE, isMobileView ? "p-2" : "px-4 py-2 text-sm", TOUCH_TARGET, hoverClass),
143
140
  disabled: currentPage === totalPages,
144
141
  onClick: function onClick() {
145
142
  return onPageChange(totalPages);
@@ -5,7 +5,7 @@ import React from 'react';
5
5
  import { cn } from '../../../utils/cn.js';
6
6
  import { Trash2, MoreVertical } from 'lucide-react';
7
7
  import ActionMenu from './ActionMenu.js';
8
- import { getRowKey, resolveActions, rowHasDetails, getActionLabelText, getStripeProps } from './helpers.js';
8
+ import { getRowKey, resolveActions, rowHasDetails, getActionLabelText, getHoverClass, getStripeProps } from './helpers.js';
9
9
 
10
10
  /**
11
11
  * Desktop table row component
@@ -96,7 +96,7 @@ function TableRow(_ref) {
96
96
  return jsxs(React.Fragment, {
97
97
  children: [jsxs("tr", {
98
98
  style: stripeProps.style,
99
- className: cn(stripeProps.className, theme !== null && theme !== void 0 && theme.rowHover ? ["hover:" + theme.rowHover] : [], {
99
+ className: cn(stripeProps.className, theme !== null && theme !== void 0 && theme.rowHover ? getHoverClass(theme.rowHover) : [], {
100
100
  "cursor-pointer": !!onRowClick,
101
101
  "bg-primary-50": isSelected,
102
102
  "bg-primary-50/20": isRowActive && !isSelected,
@@ -135,5 +135,37 @@ var resolveActions = function resolveActions(actions, row, index) {
135
135
  menuActions: menuActions
136
136
  };
137
137
  };
138
+ /**
139
+ * Tailwind only generates classes it can see as complete literals in source, so a
140
+ * runtime-built `"hover:" + token` is purged from the compiled CSS. Bare `rowHover`
141
+ * tokens (the shipped defaults, past and present) map to static literals here.
142
+ */
143
+ var HOVER_BG_CLASSES = {
144
+ "bg-background": "hover:bg-background",
145
+ "bg-surface": "hover:bg-surface",
146
+ "bg-gray-50": "hover:bg-gray-50",
147
+ "bg-gray-100": "hover:bg-gray-100",
148
+ "bg-primary-50": "hover:bg-primary-50",
149
+ "bg-primary-100": "hover:bg-primary-100"
150
+ };
151
+ var DEFAULT_HOVER_CLASS = HOVER_BG_CLASSES["bg-background"];
152
+ /**
153
+ * Resolve `theme.rowHover` to a full hover class string.
154
+ * - A value that already contains a `hover:` variant is used as-is (preferred: pass
155
+ * the full literal, e.g. `"hover:bg-gray-100"`, so your Tailwind build sees it).
156
+ * - A bare token from the static map resolves to its literal.
157
+ * - Any other bare token falls back to a `hover:` prefix for backward compatibility;
158
+ * it only works if the consumer's own Tailwind build generates that class.
159
+ */
160
+ var getHoverClass = function getHoverClass(rowHover) {
161
+ if (!rowHover || typeof rowHover !== "string") return DEFAULT_HOVER_CLASS;
162
+ var value = rowHover.trim();
163
+ if (!value) return DEFAULT_HOVER_CLASS;
164
+ if (/(^|\s)hover:/.test(value)) return value;
165
+ if (HOVER_BG_CLASSES[value]) return HOVER_BG_CLASSES[value];
166
+ return value.split(/\s+/).map(function (cls) {
167
+ return "hover:".concat(cls);
168
+ }).join(" ");
169
+ };
138
170
 
139
- export { getActionLabelText, getRowKey, getStripeProps, mergeDefaultActions, resolveActions, rowHasDetails };
171
+ export { DEFAULT_HOVER_CLASS, getActionLabelText, getHoverClass, getRowKey, getStripeProps, mergeDefaultActions, resolveActions, rowHasDetails };