@dreamtree-org/twreact-ui 1.3.8 → 1.3.10

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 (109) hide show
  1. package/DESIGN.md +11 -3
  2. package/README.md +9 -1
  3. package/ai-skills/dreamtree-ui.md +2 -2
  4. package/dist/ai-schema.json +19 -8
  5. package/dist/charts/index.js +69 -24
  6. package/dist/components/blocks/AppShell.d.ts.map +1 -1
  7. package/dist/components/charts/chartTheme.d.ts +2 -5
  8. package/dist/components/charts/chartTheme.d.ts.map +1 -1
  9. package/dist/components/core/Accordion.d.ts.map +1 -1
  10. package/dist/components/core/ColorPicker.d.ts.map +1 -1
  11. package/dist/components/core/DatePicker.d.ts.map +1 -1
  12. package/dist/components/core/Form.d.ts.map +1 -1
  13. package/dist/components/core/Input.d.ts.map +1 -1
  14. package/dist/components/core/PriceRangePicker.d.ts +0 -29
  15. package/dist/components/core/PriceRangePicker.d.ts.map +1 -1
  16. package/dist/components/core/Radio.d.ts.map +1 -1
  17. package/dist/components/core/Rate.d.ts.map +1 -1
  18. package/dist/components/core/RoundedTag.d.ts.map +1 -1
  19. package/dist/components/core/Table/ColumnMenu.d.ts.map +1 -1
  20. package/dist/components/core/Table/Pagination.d.ts.map +1 -1
  21. package/dist/components/core/Table/Table.d.ts.map +1 -1
  22. package/dist/components/core/Table/TableRow.d.ts.map +1 -1
  23. package/dist/components/core/Table/helpers.d.ts +2 -0
  24. package/dist/components/core/Table/helpers.d.ts.map +1 -1
  25. package/dist/components/core/Table/useClickOutside.d.ts +5 -2
  26. package/dist/components/core/Table/useClickOutside.d.ts.map +1 -1
  27. package/dist/components/core/Tabs.d.ts.map +1 -1
  28. package/dist/components/core/TextToSpeech.d.ts +2 -7
  29. package/dist/components/core/TextToSpeech.d.ts.map +1 -1
  30. package/dist/components/core/Tooltip.d.ts.map +1 -1
  31. package/dist/components/feedback/Alert.d.ts.map +1 -1
  32. package/dist/components/feedback/Dialog.d.ts.map +1 -1
  33. package/dist/components/feedback/Toast.d.ts.map +1 -1
  34. package/dist/components/navigation/Sidebar.d.ts.map +1 -1
  35. package/dist/components/utility/Avatar.d.ts.map +1 -1
  36. package/dist/components/utility/Carousel.d.ts.map +1 -1
  37. package/dist/components/utility/Portal.d.ts +6 -0
  38. package/dist/components/utility/Portal.d.ts.map +1 -0
  39. package/dist/esm/components/blocks/AppShell.js +24 -18
  40. package/dist/esm/components/charts/CartesianChart.js +2 -1
  41. package/dist/esm/components/charts/DonutChart.js +2 -1
  42. package/dist/esm/components/charts/Sparkline.js +2 -1
  43. package/dist/esm/components/charts/chartTheme.js +1 -22
  44. package/dist/esm/components/core/Accordion.js +40 -9
  45. package/dist/esm/components/core/Button.js +8 -8
  46. package/dist/esm/components/core/ColorPicker.js +19 -7
  47. package/dist/esm/components/core/DatePicker.js +97 -58
  48. package/dist/esm/components/core/Form.js +35 -8
  49. package/dist/esm/components/core/Input.js +3 -1
  50. package/dist/esm/components/core/PriceRangePicker.js +205 -218
  51. package/dist/esm/components/core/Radio.js +36 -6
  52. package/dist/esm/components/core/Rate.js +41 -50
  53. package/dist/esm/components/core/RoundedTag.js +25 -8
  54. package/dist/esm/components/core/Table/ColumnMenu.js +2 -4
  55. package/dist/esm/components/core/Table/Pagination.js +15 -18
  56. package/dist/esm/components/core/Table/Table.js +11 -5
  57. package/dist/esm/components/core/Table/TableRow.js +2 -2
  58. package/dist/esm/components/core/Table/helpers.js +33 -1
  59. package/dist/esm/components/core/Tabs.js +6 -5
  60. package/dist/esm/components/core/TextToSpeech.js +121 -28
  61. package/dist/esm/components/core/Tooltip.js +100 -28
  62. package/dist/esm/components/feedback/Alert.js +21 -11
  63. package/dist/esm/components/feedback/Dialog.js +44 -100
  64. package/dist/esm/components/feedback/Toast.js +24 -5
  65. package/dist/esm/components/navigation/Sidebar.js +13 -61
  66. package/dist/esm/components/utility/Avatar.js +23 -10
  67. package/dist/esm/components/utility/Carousel.js +34 -17
  68. package/dist/esm/components/utility/Pagination.js +2 -2
  69. package/dist/esm/hooks/useClickOutside.js +51 -0
  70. package/dist/esm/hooks/useFocusTrap.js +10 -2
  71. package/dist/esm/hooks/useMediaQuery.js +61 -0
  72. package/dist/esm/hooks/useReducedMotion.js +17 -0
  73. package/dist/esm/hooks/useScrollLock.js +76 -9
  74. package/dist/esm/hooks/useTheme.js +6 -1
  75. package/dist/esm/utils/position.js +6 -0
  76. package/dist/hooks/useClickOutside.d.ts +14 -0
  77. package/dist/hooks/useClickOutside.d.ts.map +1 -0
  78. package/dist/hooks/useFloatingPosition.d.ts +37 -0
  79. package/dist/hooks/useFloatingPosition.d.ts.map +1 -0
  80. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  81. package/dist/hooks/useMediaQuery.d.ts +22 -0
  82. package/dist/hooks/useMediaQuery.d.ts.map +1 -0
  83. package/dist/hooks/usePresence.d.ts +16 -0
  84. package/dist/hooks/usePresence.d.ts.map +1 -0
  85. package/dist/hooks/usePresentation.d.ts +9 -0
  86. package/dist/hooks/usePresentation.d.ts.map +1 -0
  87. package/dist/hooks/useReducedMotion.d.ts +10 -0
  88. package/dist/hooks/useReducedMotion.d.ts.map +1 -0
  89. package/dist/hooks/useScrollLock.d.ts +11 -1
  90. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  91. package/dist/hooks/useTheme.d.ts.map +1 -1
  92. package/dist/index.css +1 -1
  93. package/dist/index.esm.css +1 -1
  94. package/dist/index.esm.js +1719 -1201
  95. package/dist/index.js +1718 -1200
  96. package/dist/tokens/tailwind3-preset.cjs +101 -8
  97. package/dist/tokens/theme.css +27 -0
  98. package/dist/tokens/tokens.json +175 -0
  99. package/dist/tokens/vars.css +2 -0
  100. package/dist/types/index.d.ts +8 -2
  101. package/dist/types/index.d.ts.map +1 -1
  102. package/dist/utils/position.d.ts.map +1 -1
  103. package/dist/utils/touchTarget.d.ts +1 -0
  104. package/dist/utils/touchTarget.d.ts.map +1 -1
  105. package/mcp/catalog.snapshot.json +83 -53
  106. package/package.json +2 -1
  107. package/tokens/lib.cjs +35 -0
  108. package/tokens/tokens.json +58 -2
  109. package/dist/esm/components/core/Table/useClickOutside.js +0 -26
@@ -1,12 +1,14 @@
1
1
  'use client';
2
- import { objectWithoutProperties as _objectWithoutProperties, typeof as _typeof, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
- import { forwardRef, useState, useEffect, useMemo } from 'react';
2
+ import { objectWithoutProperties as _objectWithoutProperties, typeof as _typeof, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2, defineProperty as _defineProperty } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import { forwardRef, useState, useEffect, useRef, useMemo } from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
+ import { toPercent, keyToValue } from '../../hooks/useSliderValues.js';
7
+ import { TOUCH_TARGET } from '../../utils/touchTarget.js';
6
8
 
7
9
  var _excluded = ["min", "max", "step", "minGap", "value", "defaultValue", "onChange", "onFinalChange", "showInputs", "showTooltips", "disabled", "presets", "formatValue", "className", "size", "colorClass", "ariaLabel"];
8
10
  /**
9
- * PriceRangeSlider - UI library component (Tailwind)
11
+ * PriceRangePicker - dual-thumb range picker (Tailwind)
10
12
  *
11
13
  * Props
12
14
  * - min: number (default 0)
@@ -16,7 +18,7 @@ var _excluded = ["min", "max", "step", "minGap", "value", "defaultValue", "onCha
16
18
  * - value: { min: number, max: number } | null (controlled)
17
19
  * - defaultValue: { min: number, max: number } (uncontrolled initial)
18
20
  * - onChange: (value) => void
19
- * - onFinalChange: (value) => void // called on mouseup/touchend or Enter in inputs
21
+ * - onFinalChange: (value) => void // called when a drag ends, on thumb keys, or Enter/blur in inputs
20
22
  * - showInputs: boolean (default true)
21
23
  * - showTooltips: boolean (default true)
22
24
  * - disabled: boolean
@@ -24,16 +26,29 @@ var _excluded = ["min", "max", "step", "minGap", "value", "defaultValue", "onCha
24
26
  * - formatValue: (n) => string (formatting callback, default: n => n)
25
27
  * - className: string (container custom classes)
26
28
  * - size: 'sm' | 'md' | 'lg' (affects thumb size & spacing)
27
- * - colorClass: Tailwind class for active track/thumb, default 'bg-pink-500'
29
+ * - colorClass: Tailwind bg class for the active track and thumbs, default 'bg-primary-600'
28
30
  * - ariaLabel: string
29
31
  *
30
- * Usage notes
31
- * - Controlled mode: pass `value` and handle updates via onChange.
32
- * - Uncontrolled mode: omit `value` and use `defaultValue`.
33
- *
34
32
  * Accessibility
35
- * - Inputs are labelled and thumbs are keyboard-focusable
33
+ * - Each thumb is a `role="slider"` with arrow / Page / Home / End keys.
34
+ * - Dragging uses pointer capture, so mouse, touch and pen all work and a
35
+ * release outside the track still ends the drag.
36
36
  */
37
+ var THUMB_SIZE = {
38
+ sm: "h-4 w-4",
39
+ md: "h-5 w-5",
40
+ lg: "h-7 w-7"
41
+ };
42
+ var TRACK_HEIGHT = {
43
+ sm: "h-1",
44
+ md: "h-1.5",
45
+ lg: "h-2"
46
+ };
47
+ var INPUT_WIDTH = {
48
+ sm: "w-16",
49
+ md: "w-20",
50
+ lg: "w-24"
51
+ };
37
52
  var PriceRangePicker = /*#__PURE__*/forwardRef(function PriceRangePicker(_ref, ref) {
38
53
  var _ref$min = _ref.min,
39
54
  min = _ref$min === void 0 ? 0 : _ref$min,
@@ -71,188 +86,169 @@ var PriceRangePicker = /*#__PURE__*/forwardRef(function PriceRangePicker(_ref, r
71
86
  _ref$size = _ref.size,
72
87
  size = _ref$size === void 0 ? "md" : _ref$size,
73
88
  _ref$colorClass = _ref.colorClass,
74
- colorClass = _ref$colorClass === void 0 ? "bg-pink-500" : _ref$colorClass,
89
+ colorClass = _ref$colorClass === void 0 ? "bg-primary-600" : _ref$colorClass,
75
90
  _ref$ariaLabel = _ref.ariaLabel,
76
91
  ariaLabel = _ref$ariaLabel === void 0 ? "Price range" : _ref$ariaLabel,
77
92
  rest = _objectWithoutProperties(_ref, _excluded);
78
93
  var isControlled = value && _typeof(value) === "object";
79
- var _useState = useState(isControlled ? value.min : defaultValue.min),
94
+ var _useState = useState(function () {
95
+ return isControlled ? {
96
+ min: value.min,
97
+ max: value.max
98
+ } : {
99
+ min: defaultValue.min,
100
+ max: defaultValue.max
101
+ };
102
+ }),
80
103
  _useState2 = _slicedToArray(_useState, 2),
81
- internalMin = _useState2[0],
82
- setInternalMin = _useState2[1];
83
- var _useState3 = useState(isControlled ? value.max : defaultValue.max),
84
- _useState4 = _slicedToArray(_useState3, 2),
85
- internalMax = _useState4[0],
86
- setInternalMax = _useState4[1];
104
+ internal = _useState2[0],
105
+ setInternal = _useState2[1];
106
+ var current = isControlled ? {
107
+ min: value.min,
108
+ max: value.max
109
+ } : internal;
87
110
  // Inputs mirror state so editing doesn't immediately break constraints
88
- var _useState5 = useState(internalMin),
111
+ var _useState3 = useState(current.min),
112
+ _useState4 = _slicedToArray(_useState3, 2),
113
+ minInput = _useState4[0],
114
+ setMinInput = _useState4[1];
115
+ var _useState5 = useState(current.max),
89
116
  _useState6 = _slicedToArray(_useState5, 2),
90
- minInput = _useState6[0],
91
- setMinInput = _useState6[1];
92
- var _useState7 = useState(internalMax),
93
- _useState8 = _slicedToArray(_useState7, 2),
94
- maxInput = _useState8[0],
95
- setMaxInput = _useState8[1];
96
- // dragging state
97
- var _useState9 = useState(false),
98
- _useState0 = _slicedToArray(_useState9, 2),
99
- isDragging = _useState0[0],
100
- setIsDragging = _useState0[1];
117
+ maxInput = _useState6[0],
118
+ setMaxInput = _useState6[1];
101
119
  useEffect(function () {
102
- if (isControlled) {
103
- setInternalMin(value.min);
104
- setInternalMax(value.max);
105
- setMinInput(value.min);
106
- setMaxInput(value.max);
107
- }
108
- }, [value, isControlled]);
109
- // Update inputs when internal changes (uncontrolled)
110
- useEffect(function () {
111
- if (!isControlled) {
112
- setMinInput(internalMin);
113
- setMaxInput(internalMax);
114
- }
115
- }, [internalMin, internalMax, isControlled]);
120
+ setMinInput(current.min);
121
+ setMaxInput(current.max);
122
+ }, [current.min, current.max]);
123
+ var trackRef = useRef(null);
124
+ var thumbRefs = [useRef(null), useRef(null)];
125
+ // Active drag thumb ("min" | "max" | null) plus the latest value, so the
126
+ // pointer-up handler reports what was actually rendered, not stale state.
127
+ var _useState7 = useState(null),
128
+ _useState8 = _slicedToArray(_useState7, 2),
129
+ dragging = _useState8[0],
130
+ setDragging = _useState8[1];
131
+ var latest = useRef(current);
132
+ latest.current = current;
116
133
  var clamp = function clamp(v) {
117
134
  return Math.min(max, Math.max(min, v));
118
135
  };
136
+ var snap = function snap(v) {
137
+ return clamp(Math.round((v - min) / step) * step + min);
138
+ };
119
139
  var setValues = function setValues(next) {
120
- // next: {min, max}
121
- // enforce gap and clamp
122
- var a = clamp(Math.round(next.min / step) * step);
123
- var b = clamp(Math.round(next.max / step) * step);
140
+ var a = snap(next.min);
141
+ var b = snap(next.max);
124
142
  if (b - a < minGap) {
125
- // push them apart (prefer keeping changed thumb)
126
- if (next._active === "min") {
127
- a = clamp(b - minGap);
128
- } else if (next._active === "max") {
129
- b = clamp(a + minGap);
130
- } else {
131
- // equal distribution
132
- if (a + minGap <= max) b = a + minGap;else a = b - minGap;
133
- }
134
- }
135
- if (isControlled) {
136
- onChange({
137
- min: a,
138
- max: b
139
- });
140
- } else {
141
- setInternalMin(a);
142
- setInternalMax(b);
143
- onChange({
144
- min: a,
145
- max: b
146
- });
143
+ // push them apart (prefer keeping the thumb that moved)
144
+ if (next._active === "min") a = clamp(b - minGap);else if (next._active === "max") b = clamp(a + minGap);else if (a + minGap <= max) b = a + minGap;else a = b - minGap;
147
145
  }
146
+ var out = {
147
+ min: a,
148
+ max: b
149
+ };
150
+ latest.current = out;
151
+ if (!isControlled) setInternal(out);
152
+ onChange(out);
153
+ return out;
148
154
  };
149
155
  var handleFinal = function handleFinal(next) {
150
- onFinalChange({
156
+ return onFinalChange({
151
157
  min: next.min,
152
158
  max: next.max
153
159
  });
154
160
  };
155
- // Handlers for range inputs
156
- var handleMinRange = function handleMinRange(e) {
157
- if (disabled) return;
158
- var v = parseInt(e.target.value, 10);
159
- setValues({
160
- min: v,
161
- max: isControlled ? value.max : internalMax,
162
- _active: "min"
163
- });
161
+ var bounds = {
162
+ min: min,
163
+ max: max,
164
+ step: step,
165
+ largeStep: Math.max(step, (max - min) / 10)
164
166
  };
165
- var handleMaxRange = function handleMaxRange(e) {
166
- if (disabled) return;
167
- var v = parseInt(e.target.value, 10);
168
- setValues({
169
- min: isControlled ? value.min : internalMin,
170
- max: v,
171
- _active: "max"
172
- });
167
+ // Thumb keyboard handling
168
+ var handleThumbKey = function handleThumbKey(which) {
169
+ return function (e) {
170
+ if (disabled) return;
171
+ var next = keyToValue(e.key, current[which], bounds);
172
+ if (next === null) return;
173
+ e.preventDefault();
174
+ handleFinal(setValues(_objectSpread2(_objectSpread2({}, current), {}, _defineProperty(_defineProperty({}, which, next), "_active", which))));
175
+ };
176
+ };
177
+ // Pointer drag on the track: grab the nearest thumb and follow the pointer.
178
+ var toValue = function toValue(clientX) {
179
+ var rect = trackRef.current.getBoundingClientRect();
180
+ var ratio = rect.width ? (clientX - rect.left) / rect.width : 0;
181
+ return min + Math.min(1, Math.max(0, ratio)) * (max - min);
182
+ };
183
+ var onPointerDown = function onPointerDown(e) {
184
+ var _e$currentTarget$setP, _e$currentTarget, _thumbRefs$current;
185
+ if (disabled || e.button > 0) return;
186
+ var raw = toValue(e.clientX);
187
+ var which = Math.abs(raw - current.min) < Math.abs(raw - current.max) || current.min === current.max && raw < current.min ? "min" : "max";
188
+ (_e$currentTarget$setP = (_e$currentTarget = e.currentTarget).setPointerCapture) === null || _e$currentTarget$setP === void 0 || _e$currentTarget$setP.call(_e$currentTarget, e.pointerId);
189
+ setDragging(which);
190
+ setValues(_objectSpread2(_objectSpread2({}, current), {}, _defineProperty(_defineProperty({}, which, raw), "_active", which)));
191
+ (_thumbRefs$current = thumbRefs[which === "min" ? 0 : 1].current) === null || _thumbRefs$current === void 0 || _thumbRefs$current.focus();
192
+ };
193
+ var onPointerMove = function onPointerMove(e) {
194
+ if (!dragging) return;
195
+ setValues(_objectSpread2(_objectSpread2({}, latest.current), {}, _defineProperty(_defineProperty({}, dragging, toValue(e.clientX)), "_active", dragging)));
173
196
  };
174
- // Inputs change handlers (number boxes)
175
- var handleMinInputChange = function handleMinInputChange(e) {
176
- var raw = e.target.value;
177
- var parsed = raw === "" ? "" : parseInt(raw, 10);
178
- setMinInput(parsed);
197
+ var onPointerUp = function onPointerUp() {
198
+ if (!dragging) return;
199
+ setDragging(null);
200
+ handleFinal(latest.current);
179
201
  };
180
- var handleMaxInputChange = function handleMaxInputChange(e) {
181
- var raw = e.target.value;
182
- var parsed = raw === "" ? "" : parseInt(raw, 10);
183
- setMaxInput(parsed);
202
+ // Number inputs
203
+ var parseInput = function parseInput(raw) {
204
+ return raw === "" ? "" : parseInt(raw, 10);
184
205
  };
185
206
  var commitMinInput = function commitMinInput() {
186
- var v = minInput === "" ? min : parseInt(minInput, 10);
187
- v = clamp(v);
188
- setValues({
207
+ var v = clamp(minInput === "" ? min : parseInt(minInput, 10));
208
+ handleFinal(setValues({
189
209
  min: v,
190
- max: isControlled ? value.max : internalMax,
210
+ max: current.max,
191
211
  _active: "min"
192
- });
193
- handleFinal({
194
- min: v,
195
- max: isControlled ? value.max : internalMax
196
- });
212
+ }));
197
213
  };
198
214
  var commitMaxInput = function commitMaxInput() {
199
- var v = maxInput === "" ? max : parseInt(maxInput, 10);
200
- v = clamp(v);
201
- setValues({
202
- min: isControlled ? value.min : internalMin,
215
+ var v = clamp(maxInput === "" ? max : parseInt(maxInput, 10));
216
+ handleFinal(setValues({
217
+ min: current.min,
203
218
  max: v,
204
219
  _active: "max"
205
- });
206
- handleFinal({
207
- min: isControlled ? value.min : internalMin,
208
- max: v
209
- });
210
- };
211
- // Keyboard handling for inputs
212
- var handleMinInputKey = function handleMinInputKey(e) {
213
- if (e.key === "Enter") commitMinInput();
220
+ }));
214
221
  };
215
- var handleMaxInputKey = function handleMaxInputKey(e) {
216
- if (e.key === "Enter") commitMaxInput();
217
- };
218
- // Dragging helpers
219
- var startDrag = function startDrag() {
220
- return setIsDragging(true);
221
- };
222
- var stopDrag = function stopDrag() {
223
- setIsDragging(false);
224
- handleFinal({
225
- min: isControlled ? value.min : internalMin,
226
- max: isControlled ? value.max : internalMax
227
- });
228
- };
229
- var minPercent = useMemo(function () {
230
- return ((isControlled ? value.min : internalMin) - min) / (max - min) * 100;
231
- }, [value, internalMin, min, max, isControlled]);
232
- var maxPercent = useMemo(function () {
233
- return ((isControlled ? value.max : internalMax) - min) / (max - min) * 100;
234
- }, [value, internalMax, min, max, isControlled]);
235
- var thumbSize = size === "sm" ? 16 : size === "lg" ? 28 : 22;
236
- var inputWidth = size === "sm" ? "w-12" : size === "lg" ? "w-20" : "w-14";
237
- var trackHeight = size === "sm" ? "h-1" : size === "lg" ? "h-2" : "h-1.5";
238
- // Preset helper
239
222
  var applyPreset = function applyPreset(p) {
240
223
  if (!p || !p.value) return;
241
- var _p$value = p.value,
242
- pmin = _p$value.min,
243
- pmax = _p$value.max;
244
- setValues({
245
- min: pmin,
246
- max: pmax
247
- });
248
- handleFinal({
249
- min: pmin,
250
- max: pmax
251
- });
224
+ handleFinal(setValues({
225
+ min: p.value.min,
226
+ max: p.value.max
227
+ }));
252
228
  };
229
+ var minPercent = useMemo(function () {
230
+ return toPercent(current.min, min, max);
231
+ }, [current.min, min, max]);
232
+ var maxPercent = useMemo(function () {
233
+ return toPercent(current.max, min, max);
234
+ }, [current.max, min, max]);
235
+ var inputClass = cn(INPUT_WIDTH[size] || INPUT_WIDTH.md, "rounded-control border border-line bg-surface py-2 text-center text-sm text-foreground", "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500", "disabled:opacity-50");
236
+ var thumbs = [{
237
+ which: "min",
238
+ percent: minPercent,
239
+ valueNow: current.min,
240
+ vmin: min,
241
+ vmax: current.max
242
+ }, {
243
+ which: "max",
244
+ percent: maxPercent,
245
+ valueNow: current.max,
246
+ vmin: current.min,
247
+ vmax: max
248
+ }];
253
249
  return jsxs("div", _objectSpread2(_objectSpread2({
254
250
  ref: ref,
255
- className: cn("bg-white rounded-lg p-4 w-full max-w-lg", className)
251
+ className: cn("bg-surface rounded-lg p-4 w-full max-w-lg", className)
256
252
  }, rest), {}, {
257
253
  children: [jsxs("div", {
258
254
  className: "flex items-center justify-between",
@@ -262,37 +258,48 @@ var PriceRangePicker = /*#__PURE__*/forwardRef(function PriceRangePicker(_ref, r
262
258
  className: "flex-1",
263
259
  children: jsx("input", {
264
260
  type: "number",
261
+ inputMode: "decimal",
265
262
  "aria-label": "Minimum",
266
263
  value: minInput,
267
- onChange: handleMinInputChange,
264
+ onChange: function onChange(e) {
265
+ return setMinInput(parseInput(e.target.value));
266
+ },
268
267
  onBlur: commitMinInput,
269
- onKeyDown: handleMinInputKey,
270
- className: "".concat(inputWidth, " py-2 border border-gray-300 rounded-md text-center text-sm disabled:opacity-50"),
268
+ onKeyDown: function onKeyDown(e) {
269
+ return e.key === "Enter" && commitMinInput();
270
+ },
271
+ className: inputClass,
271
272
  min: min,
272
273
  max: max,
273
274
  disabled: disabled
274
275
  })
275
276
  }), jsx("div", {
276
- className: "text-xs text-primary-500",
277
+ className: "text-xs text-muted",
278
+ "aria-hidden": "true",
277
279
  children: "\u2014"
278
280
  }), jsx("div", {
279
281
  className: "flex-1 flex justify-end",
280
282
  children: jsx("input", {
281
283
  type: "number",
284
+ inputMode: "decimal",
282
285
  "aria-label": "Maximum",
283
286
  value: maxInput,
284
- onChange: handleMaxInputChange,
287
+ onChange: function onChange(e) {
288
+ return setMaxInput(parseInput(e.target.value));
289
+ },
285
290
  onBlur: commitMaxInput,
286
- onKeyDown: handleMaxInputKey,
287
- className: "".concat(inputWidth, " py-2 border border-gray-300 rounded-md text-center text-sm disabled:opacity-50"),
291
+ onKeyDown: function onKeyDown(e) {
292
+ return e.key === "Enter" && commitMaxInput();
293
+ },
294
+ className: inputClass,
288
295
  min: min,
289
296
  max: max,
290
297
  disabled: disabled
291
298
  })
292
299
  })]
293
300
  }) : jsxs("div", {
294
- className: "text-sm",
295
- children: [formatValue(isControlled ? value.min : internalMin), " - ", formatValue(isControlled ? value.max : internalMax)]
301
+ className: "text-sm text-foreground",
302
+ children: [formatValue(current.min), " - ", formatValue(current.max)]
296
303
  }), presets && presets.length > 0 && jsx("div", {
297
304
  className: "ml-4 flex gap-2",
298
305
  children: presets.map(function (p) {
@@ -301,73 +308,53 @@ var PriceRangePicker = /*#__PURE__*/forwardRef(function PriceRangePicker(_ref, r
301
308
  onClick: function onClick() {
302
309
  return applyPreset(p);
303
310
  },
304
- className: "text-xs px-2 py-1 border rounded-md hover:bg-gray-50 disabled:opacity-50",
311
+ className: cn("text-xs px-2 py-1 border border-line rounded-md text-foreground hover:bg-background", "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:opacity-50", TOUCH_TARGET),
305
312
  disabled: disabled,
306
313
  children: p.label
307
314
  }, p.id);
308
315
  })
309
316
  })]
310
317
  }), jsxs("div", {
311
- className: "relative w-full mt-4 ".concat(trackHeight, " bg-gray-300 rounded-full"),
318
+ ref: trackRef,
319
+ "data-testid": "price-range-track",
320
+ className: cn("relative mt-6 flex h-6 w-full touch-none select-none items-center", disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"),
321
+ onPointerDown: onPointerDown,
322
+ onPointerMove: onPointerMove,
323
+ onPointerUp: onPointerUp,
324
+ onPointerCancel: onPointerUp,
312
325
  children: [jsx("div", {
313
- className: "absolute ".concat(trackHeight, " rounded-full ").concat(colorClass),
326
+ className: cn("w-full rounded-full bg-gray-200 dark:bg-gray-700", TRACK_HEIGHT[size] || TRACK_HEIGHT.md)
327
+ }), jsx("div", {
328
+ className: cn("absolute rounded-full", TRACK_HEIGHT[size] || TRACK_HEIGHT.md, colorClass),
314
329
  style: {
315
330
  left: "".concat(minPercent, "%"),
316
331
  right: "".concat(100 - maxPercent, "%")
317
332
  }
318
- }), jsx("input", {
319
- type: "range",
320
- min: min,
321
- max: max,
322
- step: step,
323
- value: isControlled ? value.min : internalMin,
324
- onChange: handleMinRange,
325
- onMouseDown: startDrag,
326
- onMouseUp: stopDrag,
327
- onTouchStart: startDrag,
328
- onTouchEnd: stopDrag,
329
- disabled: disabled,
330
- "aria-label": "".concat(ariaLabel, " - min"),
331
- className: "absolute inset-0 w-full appearance-none bg-transparent pointer-events-auto",
332
- style: {
333
- zIndex: 20
334
- }
335
- }), jsx("input", {
336
- type: "range",
337
- min: min,
338
- max: max,
339
- step: step,
340
- value: isControlled ? value.max : internalMax,
341
- onChange: handleMaxRange,
342
- onMouseDown: startDrag,
343
- onMouseUp: stopDrag,
344
- onTouchStart: startDrag,
345
- onTouchEnd: stopDrag,
346
- disabled: disabled,
347
- "aria-label": "".concat(ariaLabel, " - max"),
348
- className: "absolute inset-0 w-full appearance-none bg-transparent pointer-events-auto",
349
- style: {
350
- zIndex: 10
351
- }
352
- }), showTooltips && isDragging && jsxs(Fragment, {
353
- children: [jsx("div", {
354
- className: "absolute -top-9 text-xs bg-white px-2 py-1 rounded-md shadow-sm border",
355
- style: {
356
- left: "".concat(minPercent, "%"),
357
- transform: "translateX(-50%)"
358
- },
359
- children: formatValue(isControlled ? value.min : internalMin)
360
- }), jsx("div", {
361
- className: "absolute -top-9 text-xs bg-white px-2 py-1 rounded-md shadow-sm border",
333
+ }), thumbs.map(function (t, i) {
334
+ return jsx("div", {
335
+ ref: thumbRefs[i],
336
+ role: "slider",
337
+ tabIndex: disabled ? -1 : 0,
338
+ "aria-label": "".concat(ariaLabel, " - ").concat(t.which),
339
+ "aria-valuemin": t.vmin,
340
+ "aria-valuemax": t.vmax,
341
+ "aria-valuenow": t.valueNow,
342
+ "aria-valuetext": formatValue(t.valueNow),
343
+ "aria-orientation": "horizontal",
344
+ "aria-disabled": disabled || undefined,
345
+ onKeyDown: handleThumbKey(t.which),
362
346
  style: {
363
- left: "".concat(maxPercent, "%"),
364
- transform: "translateX(-50%)"
347
+ left: "".concat(t.percent, "%")
365
348
  },
366
- children: formatValue(isControlled ? value.max : internalMax)
367
- })]
349
+ className: cn("absolute -translate-x-1/2 rounded-full border-[3px] border-white shadow-md dark:border-gray-900", "transition-shadow motion-reduce:transition-none", "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2",
350
+ // Grow the hit area to 44px on touch without changing the visual size.
351
+ "before:absolute before:left-1/2 before:top-1/2 before:-translate-x-1/2 before:-translate-y-1/2", "[@media(pointer:coarse)]:before:h-11 [@media(pointer:coarse)]:before:w-11 before:content-['']", THUMB_SIZE[size] || THUMB_SIZE.md, colorClass),
352
+ children: showTooltips && dragging === t.which && jsx("span", {
353
+ className: "pointer-events-none absolute -top-9 left-1/2 -translate-x-1/2 whitespace-nowrap rounded-md border border-line bg-surface px-2 py-1 text-xs text-foreground shadow-sm",
354
+ children: formatValue(t.valueNow)
355
+ })
356
+ }, t.which);
368
357
  })]
369
- }), jsx("style", {
370
- children: "\n input[type=range] { -webkit-appearance: none; appearance: none; background: transparent; }\n input[type=range]::-webkit-slider-runnable-track { height: 6px; background: transparent; }\n input[type=range]::-moz-range-track { height: 6px; background: transparent; }\n\n input[type=range]::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n height: ".concat(thumbSize, "px;\n width: ").concat(thumbSize, "px;\n border-radius: 9999px;\n border: 3px solid #ffffff;\n background: #ff6b6b;\n box-shadow: 0 2px 8px rgba(0,0,0,0.12);\n cursor: pointer;\n margin-top: -").concat(Math.round(thumbSize / 2 - 6), "px; /* vertical centering tweak */\n position: relative;\n }\n\n input[type=range]::-moz-range-thumb {\n height: ").concat(thumbSize, "px;\n width: ").concat(thumbSize, "px;\n border-radius: 9999px;\n border: 3px solid #ffffff;\n background: #ff6b6b;\n box-shadow: 0 2px 8px rgba(0,0,0,0.12);\n cursor: pointer;\n position: relative;\n }\n\n /* Make the track invisible so our styled track shows through */\n input[type=range]::-webkit-slider-runnable-track { background: transparent; }\n input[type=range]::-moz-range-track { background: transparent; }\n\n ")
371
358
  })]
372
359
  }));
373
360
  });
@@ -1,7 +1,7 @@
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, useId, useState } from 'react';
4
+ import { forwardRef, useId, useState, useRef, useCallback, useEffect } from 'react';
5
5
  import PropTypes from '../../_vendor/prop-types/index.js';
6
6
 
7
7
  var _excluded = ["id", "name", "value", "checked", "defaultChecked", "onChange", "disabled", "required", "label", "size", "labelPosition", "radioColor", "className", "ariaLabel"];
@@ -74,6 +74,32 @@ var Radio = /*#__PURE__*/forwardRef(function Radio(_ref, ref) {
74
74
  if (checked === undefined) setInternalChecked(!!defaultChecked);
75
75
  }
76
76
  var isChecked = checked !== undefined ? checked : internalChecked;
77
+ // Merge the forwarded ref with our own so we can read the native state.
78
+ var inputRef = useRef(null);
79
+ var setRefs = useCallback(function (node) {
80
+ inputRef.current = node;
81
+ if (typeof ref === "function") ref(node);else if (ref) ref.current = node;
82
+ }, [ref]);
83
+ // DTUI-17: browsers never fire `change` on the radio being *unchecked*
84
+ // when a sibling in the same group is selected. In uncontrolled mode,
85
+ // listen for changes on any same-name radio and re-sync our visual state
86
+ // from the native input, so only one dot shows at a time.
87
+ useEffect(function () {
88
+ if (checked !== undefined || !name) return undefined;
89
+ var doc = inputRef.current && inputRef.current.ownerDocument;
90
+ if (!doc) return undefined;
91
+ var sync = function sync(e) {
92
+ var t = e.target;
93
+ var el = inputRef.current;
94
+ if (!el || t === el || !t || t.type !== "radio" || t.name !== name) return;
95
+ if (t.form !== el.form) return;
96
+ setInternalChecked(el.checked);
97
+ };
98
+ doc.addEventListener("change", sync, true);
99
+ return function () {
100
+ return doc.removeEventListener("change", sync, true);
101
+ };
102
+ }, [checked, name]);
77
103
  function handleChange(e) {
78
104
  if (checked === undefined) setInternalChecked(e.target.checked);
79
105
  if (typeof onChange === "function") onChange(e);
@@ -91,7 +117,7 @@ var Radio = /*#__PURE__*/forwardRef(function Radio(_ref, ref) {
91
117
  children: " *"
92
118
  }) : null]
93
119
  }), jsx("input", _objectSpread2({
94
- ref: ref,
120
+ ref: setRefs,
95
121
  id: inputId,
96
122
  type: "radio",
97
123
  name: name,
@@ -102,18 +128,22 @@ var Radio = /*#__PURE__*/forwardRef(function Radio(_ref, ref) {
102
128
  disabled: disabled,
103
129
  required: required,
104
130
  "aria-label": ariaLabel,
105
- className: "absolute inset-0 w-full h-full opacity-0 appearance-none m-0 z-10 cursor-pointer"
131
+ className: "peer absolute inset-0 w-full h-full opacity-0 appearance-none m-0 z-10 cursor-pointer disabled:cursor-not-allowed"
106
132
  }, props)), jsx("span", {
107
133
  "aria-hidden": true,
108
- className: cn("inline-flex items-center justify-center rounded-full transition-all"),
134
+ "data-state": isChecked ? "checked" : "unchecked",
135
+ className: cn("inline-flex items-center justify-center rounded-full transition-all motion-reduce:transition-none",
136
+ // Visible focus indicator: the native input is opacity-0, so the
137
+ // ring is drawn on the visual circle when the input has focus.
138
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-primary-500 peer-focus-visible:ring-offset-2 dark:peer-focus-visible:ring-offset-gray-900"),
109
139
  style: {
110
140
  "--radio-accent": accentColor
111
141
  },
112
142
  children: jsx("span", {
113
- className: cn("flex items-center justify-center rounded-full border transition-colors duration-150 ease-in-out border-slate-300 dark:border-gray-600", sizeClasses.outer, disabled ? "bg-slate-100 dark:bg-gray-800" : "bg-white dark:bg-gray-900", isChecked ? "shadow-inner" : ""),
143
+ className: cn("flex items-center justify-center rounded-full border transition-colors duration-150 ease-in-out motion-reduce:transition-none border-slate-300 dark:border-gray-600", sizeClasses.outer, disabled ? "bg-slate-100 dark:bg-gray-800" : "bg-white dark:bg-gray-900", isChecked ? "shadow-inner" : ""),
114
144
  children: jsx("span", {
115
145
  "aria-hidden": true,
116
- className: cn("rounded-full transform transition-all duration-200 ease-out", sizeClasses.dot),
146
+ className: cn("rounded-full transform transition-all duration-200 ease-out motion-reduce:transition-none", sizeClasses.dot),
117
147
  style: {
118
148
  backgroundColor: isChecked ? accentColor : "transparent",
119
149
  opacity: isChecked ? 1 : 0,