@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.
- package/README.md +32 -0
- package/ai-skills/dreamtree-ui.md +12 -7
- package/dist/ai-schema.json +156 -29
- package/dist/charts/index.js +195 -66
- package/dist/components/blocks/AppShell.d.ts.map +1 -1
- package/dist/components/charts/CartesianChart.d.ts.map +1 -1
- package/dist/components/charts/ChartFrame.d.ts.map +1 -1
- package/dist/components/charts/useChartLayout.d.ts +25 -0
- package/dist/components/charts/useChartLayout.d.ts.map +1 -0
- package/dist/components/core/Accordion.d.ts.map +1 -1
- package/dist/components/core/Button.d.ts.map +1 -1
- package/dist/components/core/Checkbox.d.ts.map +1 -1
- package/dist/components/core/ColorPicker.d.ts.map +1 -1
- package/dist/components/core/DatePicker.d.ts.map +1 -1
- package/dist/components/core/Form.d.ts.map +1 -1
- package/dist/components/core/Input.d.ts.map +1 -1
- package/dist/components/core/Loader.d.ts.map +1 -1
- package/dist/components/core/LocationPicker.d.ts.map +1 -1
- package/dist/components/core/PriceRangePicker.d.ts +0 -29
- package/dist/components/core/PriceRangePicker.d.ts.map +1 -1
- package/dist/components/core/ProgressBar.d.ts.map +1 -1
- package/dist/components/core/Radio.d.ts.map +1 -1
- package/dist/components/core/Rate.d.ts.map +1 -1
- package/dist/components/core/RoundedTag.d.ts.map +1 -1
- package/dist/components/core/Skeleton.d.ts.map +1 -1
- package/dist/components/core/Slider.d.ts.map +1 -1
- package/dist/components/core/SpeechToText.d.ts.map +1 -1
- package/dist/components/core/Switch.d.ts.map +1 -1
- package/dist/components/core/Table/ColumnMenu.d.ts.map +1 -1
- package/dist/components/core/Table/Pagination.d.ts.map +1 -1
- package/dist/components/core/Table/TableRow.d.ts.map +1 -1
- package/dist/components/core/Table/helpers.d.ts +2 -0
- package/dist/components/core/Table/helpers.d.ts.map +1 -1
- package/dist/components/core/Tabs.d.ts +2 -5
- package/dist/components/core/Tabs.d.ts.map +1 -1
- package/dist/components/core/TextToSpeech.d.ts +2 -7
- package/dist/components/core/TextToSpeech.d.ts.map +1 -1
- package/dist/components/core/Textarea.d.ts.map +1 -1
- package/dist/components/core/Tooltip.d.ts.map +1 -1
- package/dist/components/feedback/Alert.d.ts.map +1 -1
- package/dist/components/feedback/Dialog.d.ts.map +1 -1
- package/dist/components/feedback/Toast.d.ts.map +1 -1
- package/dist/components/navigation/Breadcrumbs.d.ts.map +1 -1
- package/dist/components/navigation/FootNav.d.ts.map +1 -1
- package/dist/components/navigation/Sidebar.d.ts.map +1 -1
- package/dist/components/utility/Avatar.d.ts +8 -0
- package/dist/components/utility/Avatar.d.ts.map +1 -1
- package/dist/components/utility/AvatarGroup.d.ts +4 -0
- package/dist/components/utility/AvatarGroup.d.ts.map +1 -0
- package/dist/components/utility/Badge.d.ts.map +1 -1
- package/dist/components/utility/Card.d.ts.map +1 -1
- package/dist/components/utility/Carousel.d.ts.map +1 -1
- package/dist/components/utility/FileUpload.d.ts.map +1 -1
- package/dist/components/utility/Pagination.d.ts.map +1 -1
- package/dist/components/utility/Sortable.d.ts.map +1 -1
- package/dist/components/utility/Stepper.d.ts.map +1 -1
- package/dist/esm/components/blocks/AppShell.js +24 -18
- package/dist/esm/components/blocks/EmptyState.js +1 -1
- package/dist/esm/components/blocks/ErrorState.js +1 -1
- package/dist/esm/components/blocks/LoadingState.js +4 -4
- package/dist/esm/components/blocks/StatCard.js +6 -6
- package/dist/esm/components/blocks/StatusPill.js +5 -8
- package/dist/esm/components/charts/CartesianChart.js +85 -62
- package/dist/esm/components/charts/ChartFrame.js +12 -5
- package/dist/esm/components/charts/useChartLayout.js +105 -0
- package/dist/esm/components/core/Accordion.js +40 -9
- package/dist/esm/components/core/Button.js +49 -25
- package/dist/esm/components/core/Checkbox.js +71 -45
- package/dist/esm/components/core/ColorPicker.js +49 -48
- package/dist/esm/components/core/DatePicker.js +97 -58
- package/dist/esm/components/core/Form.js +35 -8
- package/dist/esm/components/core/Input.js +89 -20
- package/dist/esm/components/core/Loader.js +30 -44
- package/dist/esm/components/core/LocationPicker.js +82 -16
- package/dist/esm/components/core/PriceRangePicker.js +205 -218
- package/dist/esm/components/core/ProgressBar.js +8 -12
- package/dist/esm/components/core/Radio.js +43 -15
- package/dist/esm/components/core/Rate.js +109 -50
- package/dist/esm/components/core/RoundedTag.js +27 -10
- package/dist/esm/components/core/Skeleton.js +13 -14
- package/dist/esm/components/core/Slider.js +24 -6
- package/dist/esm/components/core/SpeechToText.js +68 -16
- package/dist/esm/components/core/Switch.js +88 -16
- package/dist/esm/components/core/Table/ColumnMenu.js +2 -4
- package/dist/esm/components/core/Table/Pagination.js +15 -18
- package/dist/esm/components/core/Table/TableRow.js +2 -2
- package/dist/esm/components/core/Table/helpers.js +33 -1
- package/dist/esm/components/core/Tabs.js +185 -37
- package/dist/esm/components/core/TextToSpeech.js +121 -28
- package/dist/esm/components/core/Textarea.js +10 -2
- package/dist/esm/components/core/Tooltip.js +100 -28
- package/dist/esm/components/feedback/Alert.js +21 -11
- package/dist/esm/components/feedback/Dialog.js +44 -100
- package/dist/esm/components/feedback/Toast.js +24 -5
- package/dist/esm/components/navigation/Breadcrumbs.js +97 -35
- package/dist/esm/components/navigation/FootNav.js +102 -13
- package/dist/esm/components/navigation/Sidebar.js +13 -61
- package/dist/esm/components/utility/Avatar.js +37 -22
- package/dist/esm/components/utility/AvatarGroup.js +56 -0
- package/dist/esm/components/utility/Badge.js +43 -23
- package/dist/esm/components/utility/Card.js +32 -8
- package/dist/esm/components/utility/Carousel.js +165 -38
- package/dist/esm/components/utility/FileUpload.js +106 -31
- package/dist/esm/components/utility/Pagination.js +33 -15
- package/dist/esm/components/utility/Sortable.js +6 -2
- package/dist/esm/components/utility/Stepper.js +110 -47
- package/dist/esm/hooks/useFocusTrap.js +10 -2
- package/dist/esm/hooks/useSliderValues.js +8 -2
- package/dist/esm/hooks/useSortable.js +107 -5
- package/dist/esm/index.js +1 -0
- package/dist/esm/utils/touchTarget.js +4 -1
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useSliderValues.d.ts +6 -2
- package/dist/hooks/useSliderValues.d.ts.map +1 -1
- package/dist/hooks/useSortable.d.ts +11 -1
- package/dist/hooks/useSortable.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +3734 -2174
- package/dist/index.js +3732 -2171
- package/dist/types/charts.d.ts +3 -1
- package/dist/types/charts.d.ts.map +1 -1
- package/dist/types/index.d.ts +64 -9
- package/dist/types/index.d.ts.map +1 -1
- package/doc/README.md +1 -0
- package/mcp/catalog.snapshot.json +350 -121
- package/package.json +1 -1
|
@@ -3,9 +3,10 @@ import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _
|
|
|
3
3
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
4
|
import React, { useId, useMemo, useState, useRef, useImperativeHandle, useEffect, useLayoutEffect } from 'react';
|
|
5
5
|
import ReactDOM from 'react-dom';
|
|
6
|
-
import { parseISO, startOfMonth, startOfWeek, endOfMonth, endOfWeek, eachDayOfInterval, startOfDay, subMonths, format, addMonths,
|
|
6
|
+
import { parseISO, startOfMonth, startOfWeek, endOfMonth, endOfWeek, eachDayOfInterval, startOfDay, isSameDay, subMonths, format, addMonths, isSameMonth, isToday, isBefore, isAfter, addDays } from 'date-fns';
|
|
7
7
|
import { cn } from '../../utils/cn.js';
|
|
8
|
-
import {
|
|
8
|
+
import { TOUCH_TARGET } from '../../utils/touchTarget.js';
|
|
9
|
+
import { ChevronLeft, ChevronRight, Calendar, X } from 'lucide-react';
|
|
9
10
|
|
|
10
11
|
var _excluded = ["value", "onChange", "placeholder", "label", "error", "helperText", "disabled", "required", "minDate", "maxDate", "className", "weekStartsOn", "portal", "displayFormat", "locale", "showClear", "closeOnSelect"];
|
|
11
12
|
var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
@@ -64,19 +65,27 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
64
65
|
setCurrentMonth = _useState4[1];
|
|
65
66
|
var datePickerRef = useRef(null);
|
|
66
67
|
var popupRef = useRef(null);
|
|
68
|
+
var triggerRef = useRef(null);
|
|
67
69
|
var gridButtonRefs = useRef([]);
|
|
68
|
-
|
|
70
|
+
// Roving tabindex: the day the user moved to with the keyboard (or focused).
|
|
71
|
+
var _useState5 = useState(null),
|
|
69
72
|
_useState6 = _slicedToArray(_useState5, 2),
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
focusedDate = _useState6[0],
|
|
74
|
+
setFocusedDate = _useState6[1];
|
|
75
|
+
// Set when a keyboard action / open should move DOM focus to the active day.
|
|
76
|
+
var pendingFocusRef = useRef(false);
|
|
77
|
+
var _useState7 = useState({}),
|
|
78
|
+
_useState8 = _slicedToArray(_useState7, 2),
|
|
79
|
+
popupStyle = _useState8[0],
|
|
80
|
+
setPopupStyle = _useState8[1];
|
|
72
81
|
useImperativeHandle(forwardedRef, function () {
|
|
73
82
|
return datePickerRef.current;
|
|
74
83
|
}, []);
|
|
75
84
|
// Keep currentMonth in sync with external value changes
|
|
76
|
-
var
|
|
77
|
-
|
|
78
|
-
prevSelected =
|
|
79
|
-
setPrevSelected =
|
|
85
|
+
var _useState9 = useState(selected),
|
|
86
|
+
_useState0 = _slicedToArray(_useState9, 2),
|
|
87
|
+
prevSelected = _useState0[0],
|
|
88
|
+
setPrevSelected = _useState0[1];
|
|
80
89
|
if (selected !== prevSelected) {
|
|
81
90
|
setPrevSelected(selected);
|
|
82
91
|
if (selected) setCurrentMonth(selected);
|
|
@@ -98,7 +107,10 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
98
107
|
var handleKey = function handleKey(e) {
|
|
99
108
|
if (!isOpen) return;
|
|
100
109
|
if (e.key === "Escape") {
|
|
110
|
+
var _triggerRef$current;
|
|
111
|
+
var hadFocusInside = popupRef.current && popupRef.current.contains(document.activeElement);
|
|
101
112
|
setIsOpen(false);
|
|
113
|
+
if (hadFocusInside) (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 || _triggerRef$current.focus();
|
|
102
114
|
}
|
|
103
115
|
};
|
|
104
116
|
document.addEventListener("keydown", handleKey);
|
|
@@ -139,62 +151,81 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
139
151
|
var handleDateSelect = function handleDateSelect(date) {
|
|
140
152
|
if (isDateDisabled(date)) return;
|
|
141
153
|
onChange === null || onChange === void 0 || onChange(date);
|
|
142
|
-
if (closeOnSelect)
|
|
154
|
+
if (closeOnSelect) {
|
|
155
|
+
var _triggerRef$current2;
|
|
156
|
+
setIsOpen(false);
|
|
157
|
+
// The focused day unmounts with the popup; keep focus on the field.
|
|
158
|
+
(_triggerRef$current2 = triggerRef.current) === null || _triggerRef$current2 === void 0 || _triggerRef$current2.focus();
|
|
159
|
+
}
|
|
143
160
|
};
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
161
|
+
// Roving tabindex target: exactly one enabled day in the grid is tabbable.
|
|
162
|
+
// Priority: keyboard-focused day ?? selected ?? today (if shown & enabled)
|
|
163
|
+
// ?? first enabled day of the visible month ?? first enabled grid day.
|
|
164
|
+
var isUsable = function isUsable(d) {
|
|
165
|
+
return d && isSameMonth(d, currentMonth) && !isDateDisabled(d);
|
|
166
|
+
};
|
|
167
|
+
var activeDate = null;
|
|
168
|
+
if (isUsable(focusedDate)) activeDate = focusedDate;else if (isUsable(selected)) activeDate = selected;else if (isUsable(new Date())) activeDate = new Date();else activeDate = days.find(function (d) {
|
|
169
|
+
return isUsable(d);
|
|
170
|
+
}) || days.find(function (d) {
|
|
171
|
+
return !isDateDisabled(d);
|
|
172
|
+
}) || null;
|
|
173
|
+
var activeIndex = activeDate ? days.findIndex(function (d) {
|
|
174
|
+
return isSameDay(d, activeDate);
|
|
175
|
+
}) : -1;
|
|
176
|
+
// Move DOM focus to the active day after a keyboard action / open.
|
|
177
|
+
useEffect(function () {
|
|
178
|
+
if (!isOpen || !pendingFocusRef.current) return;
|
|
179
|
+
pendingFocusRef.current = false;
|
|
180
|
+
var btn = gridButtonRefs.current[activeIndex];
|
|
147
181
|
if (btn && typeof btn.focus === "function") btn.focus();
|
|
182
|
+
});
|
|
183
|
+
var moveFocusTo = function moveFocusTo(date) {
|
|
184
|
+
if (!date || isDateDisabled(date)) return;
|
|
185
|
+
if (!isSameMonth(date, currentMonth)) setCurrentMonth(date);
|
|
186
|
+
setFocusedDate(date);
|
|
187
|
+
pendingFocusRef.current = true;
|
|
148
188
|
};
|
|
149
189
|
var handleGridKeyDown = function handleGridKeyDown(e, idx) {
|
|
190
|
+
var day = days[idx];
|
|
150
191
|
if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", "Enter"].includes(e.key)) {
|
|
151
192
|
e.preventDefault();
|
|
152
193
|
}
|
|
153
194
|
switch (e.key) {
|
|
154
195
|
case "ArrowLeft":
|
|
155
|
-
|
|
196
|
+
moveFocusTo(addDays(day, -1));
|
|
156
197
|
break;
|
|
157
198
|
case "ArrowRight":
|
|
158
|
-
|
|
199
|
+
moveFocusTo(addDays(day, 1));
|
|
159
200
|
break;
|
|
160
201
|
case "ArrowUp":
|
|
161
|
-
|
|
202
|
+
moveFocusTo(addDays(day, -7));
|
|
162
203
|
break;
|
|
163
204
|
case "ArrowDown":
|
|
164
|
-
|
|
205
|
+
moveFocusTo(addDays(day, 7));
|
|
165
206
|
break;
|
|
166
207
|
case "PageUp":
|
|
167
|
-
|
|
168
|
-
return subMonths(m, 1);
|
|
169
|
-
});
|
|
208
|
+
moveFocusTo(subMonths(day, 1));
|
|
170
209
|
break;
|
|
171
210
|
case "PageDown":
|
|
172
|
-
|
|
173
|
-
return addMonths(m, 1);
|
|
174
|
-
});
|
|
211
|
+
moveFocusTo(addMonths(day, 1));
|
|
175
212
|
break;
|
|
176
213
|
case "Home":
|
|
177
|
-
|
|
214
|
+
moveFocusTo(days[Math.floor(idx / 7) * 7]);
|
|
178
215
|
break;
|
|
179
216
|
case "End":
|
|
180
|
-
|
|
217
|
+
moveFocusTo(days[Math.min(days.length - 1, Math.floor(idx / 7) * 7 + 6)]);
|
|
181
218
|
break;
|
|
182
219
|
case "Enter":
|
|
183
|
-
if (!isDateDisabled(
|
|
220
|
+
if (!isDateDisabled(day)) handleDateSelect(day);
|
|
184
221
|
break;
|
|
185
222
|
}
|
|
186
223
|
};
|
|
187
224
|
var openAndFocusSelected = function openAndFocusSelected() {
|
|
225
|
+
setFocusedDate(null);
|
|
188
226
|
setIsOpen(true);
|
|
189
|
-
// focus
|
|
190
|
-
|
|
191
|
-
var idx = selected ? days.findIndex(function (d) {
|
|
192
|
-
return isSameDay(d, selected);
|
|
193
|
-
}) : days.findIndex(function (d) {
|
|
194
|
-
return isToday(d);
|
|
195
|
-
});
|
|
196
|
-
if (idx >= 0) focusDayAtIndex(idx);
|
|
197
|
-
}, 0);
|
|
227
|
+
// the active-day effect moves focus once the grid has rendered
|
|
228
|
+
pendingFocusRef.current = true;
|
|
198
229
|
};
|
|
199
230
|
// compute popup position so it's aware of viewport edges (top/bottom/left/right)
|
|
200
231
|
var computePopupPosition = function computePopupPosition() {
|
|
@@ -266,7 +297,7 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
266
297
|
className: "mb-4 flex items-center justify-between",
|
|
267
298
|
children: [jsx("button", {
|
|
268
299
|
type: "button",
|
|
269
|
-
className: "rounded-md p-1 hover:bg-gray-100",
|
|
300
|
+
className: cn("inline-flex items-center justify-center rounded-md p-1 hover:bg-gray-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500", TOUCH_TARGET),
|
|
270
301
|
onClick: function onClick() {
|
|
271
302
|
return setCurrentMonth(subMonths(currentMonth, 1));
|
|
272
303
|
},
|
|
@@ -279,7 +310,7 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
279
310
|
children: format(currentMonth, "MMMM yyyy")
|
|
280
311
|
}), jsx("button", {
|
|
281
312
|
type: "button",
|
|
282
|
-
className: "rounded-md p-1 hover:bg-gray-100",
|
|
313
|
+
className: cn("inline-flex items-center justify-center rounded-md p-1 hover:bg-gray-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500", TOUCH_TARGET),
|
|
283
314
|
onClick: function onClick() {
|
|
284
315
|
return setCurrentMonth(addMonths(currentMonth, 1));
|
|
285
316
|
},
|
|
@@ -321,12 +352,15 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
321
352
|
return gridButtonRefs.current[idx] = el;
|
|
322
353
|
},
|
|
323
354
|
role: "gridcell",
|
|
324
|
-
tabIndex:
|
|
325
|
-
"aria-selected": isSelected,
|
|
355
|
+
tabIndex: idx === activeIndex ? 0 : -1,
|
|
356
|
+
"aria-selected": Boolean(isSelected),
|
|
326
357
|
onKeyDown: function onKeyDown(e) {
|
|
327
358
|
return handleGridKeyDown(e, idx);
|
|
328
359
|
},
|
|
329
|
-
|
|
360
|
+
onFocus: function onFocus() {
|
|
361
|
+
if (!focusedDate || !isSameDay(focusedDate, day)) setFocusedDate(day);
|
|
362
|
+
},
|
|
363
|
+
className: cn("h-8 w-8 rounded-md text-sm transition-colors motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-1", {
|
|
330
364
|
"text-primary-400": !isCurrentMonth,
|
|
331
365
|
"bg-primary-600 text-white": isSelected,
|
|
332
366
|
"bg-primary-100 text-primary-600": isTodayDate && !isSelected,
|
|
@@ -382,6 +416,7 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
382
416
|
ref: datePickerRef,
|
|
383
417
|
className: "relative",
|
|
384
418
|
children: [jsxs("button", _objectSpread2(_objectSpread2({
|
|
419
|
+
ref: triggerRef,
|
|
385
420
|
type: "button",
|
|
386
421
|
id: fieldId,
|
|
387
422
|
"aria-labelledby": label ? "".concat(labelId, " ").concat(valueId) : valueId,
|
|
@@ -389,7 +424,9 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
389
424
|
"aria-invalid": error ? true : undefined,
|
|
390
425
|
className: cn("flex w-full items-center justify-between rounded-md border border-gray-300 bg-white px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent disabled:cursor-not-allowed disabled:opacity-50", {
|
|
391
426
|
"border-error-500 focus:ring-error-500": error,
|
|
392
|
-
"text-primary-500": !selected
|
|
427
|
+
"text-primary-500": !selected,
|
|
428
|
+
// leave room for the sibling clear button overlaid on the field
|
|
429
|
+
"pr-16": showClear && selected
|
|
393
430
|
}, className),
|
|
394
431
|
onClick: function onClick() {
|
|
395
432
|
return isOpen ? setIsOpen(false) : openAndFocusSelected();
|
|
@@ -402,24 +439,26 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
402
439
|
className: "truncate",
|
|
403
440
|
id: valueId,
|
|
404
441
|
children: formatDisplayValue()
|
|
405
|
-
}),
|
|
406
|
-
className: "
|
|
407
|
-
|
|
408
|
-
type: "button",
|
|
409
|
-
onClick: function onClick(e) {
|
|
410
|
-
e.stopPropagation();
|
|
411
|
-
onChange === null || onChange === void 0 || onChange(null);
|
|
412
|
-
},
|
|
413
|
-
"aria-label": "Clear date",
|
|
414
|
-
className: "p-1",
|
|
415
|
-
children: jsx(X, {
|
|
416
|
-
className: "h-4 w-4 text-primary-400"
|
|
417
|
-
})
|
|
418
|
-
}), jsx(Calendar, {
|
|
419
|
-
className: "h-4 w-4 text-primary-400"
|
|
420
|
-
})]
|
|
442
|
+
}), jsx(Calendar, {
|
|
443
|
+
className: "h-4 w-4 shrink-0 text-primary-400",
|
|
444
|
+
"aria-hidden": "true"
|
|
421
445
|
})]
|
|
422
|
-
})),
|
|
446
|
+
})), showClear && selected && jsx("button", {
|
|
447
|
+
type: "button",
|
|
448
|
+
"aria-label": "Clear date",
|
|
449
|
+
disabled: disabled,
|
|
450
|
+
onClick: function onClick() {
|
|
451
|
+
var _triggerRef$current3;
|
|
452
|
+
onChange === null || onChange === void 0 || onChange(null);
|
|
453
|
+
// this button unmounts once the value clears
|
|
454
|
+
(_triggerRef$current3 = triggerRef.current) === null || _triggerRef$current3 === void 0 || _triggerRef$current3.focus();
|
|
455
|
+
},
|
|
456
|
+
className: "absolute right-9 top-1/2 inline-flex -translate-y-1/2 items-center justify-center rounded-md p-1 text-primary-400 hover:bg-gray-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:cursor-not-allowed disabled:opacity-50",
|
|
457
|
+
children: jsx(X, {
|
|
458
|
+
className: "h-4 w-4",
|
|
459
|
+
"aria-hidden": "true"
|
|
460
|
+
})
|
|
461
|
+
}), isOpen && (portal ? /*#__PURE__*/ReactDOM.createPortal(calendarPopup, document.body) : calendarPopup)]
|
|
423
462
|
}), error ? jsx("p", {
|
|
424
463
|
id: errorId,
|
|
425
464
|
className: "mt-1 text-sm text-error-500",
|
|
@@ -40,6 +40,19 @@ var FieldMessage = function FieldMessage(_ref) {
|
|
|
40
40
|
}
|
|
41
41
|
return null;
|
|
42
42
|
};
|
|
43
|
+
// Static class map for field groups (DTUI-19). A dynamic `grid-cols-${n}`
|
|
44
|
+
// string is invisible to Tailwind's content scan and gets purged, so every
|
|
45
|
+
// layout is spelled out here. All layouts collapse to one column below `sm`.
|
|
46
|
+
var GRID_COLS = {
|
|
47
|
+
1: "grid-cols-1",
|
|
48
|
+
2: "grid-cols-1 sm:grid-cols-2",
|
|
49
|
+
3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
50
|
+
4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
|
51
|
+
};
|
|
52
|
+
var gridColsClass = function gridColsClass(n) {
|
|
53
|
+
var cols = Math.min(4, Math.max(1, Math.trunc(Number(n)) || 1));
|
|
54
|
+
return GRID_COLS[cols];
|
|
55
|
+
};
|
|
43
56
|
var describedByFor = function describedByFor(fieldId, error, helperText) {
|
|
44
57
|
return error ? "".concat(fieldId, "-error") : helperText ? "".concat(fieldId, "-helper") : undefined;
|
|
45
58
|
};
|
|
@@ -72,8 +85,12 @@ var Form = /*#__PURE__*/forwardRef(function Form(_ref2, ref) {
|
|
|
72
85
|
_methods$formState = methods.formState,
|
|
73
86
|
errors = _methods$formState.errors,
|
|
74
87
|
isSubmitting = _methods$formState.isSubmitting;
|
|
75
|
-
var handleFormSubmit = useCallback(
|
|
76
|
-
|
|
88
|
+
var handleFormSubmit = useCallback(
|
|
89
|
+
// Return the handler's result so react-hook-form awaits an async
|
|
90
|
+
// onSubmit and `isSubmitting` (the submit button's loading state)
|
|
91
|
+
// reflects the real work (DTUI-19).
|
|
92
|
+
function (data) {
|
|
93
|
+
return onSubmit === null || onSubmit === void 0 ? void 0 : onSubmit(data);
|
|
77
94
|
}, [onSubmit]);
|
|
78
95
|
var handleReset = useCallback(function () {
|
|
79
96
|
reset(defaultValues);
|
|
@@ -259,7 +276,8 @@ var Form = /*#__PURE__*/forwardRef(function Form(_ref2, ref) {
|
|
|
259
276
|
ref = _ref8$field.ref;
|
|
260
277
|
return jsxs("div", {
|
|
261
278
|
className: "w-full",
|
|
262
|
-
children: [label && jsxs("
|
|
279
|
+
children: [label && jsxs("span", {
|
|
280
|
+
id: "".concat(fieldId, "-label"),
|
|
263
281
|
className: "block text-sm font-medium mb-2",
|
|
264
282
|
children: [label, required && jsx("span", {
|
|
265
283
|
className: "text-error-500 ml-1",
|
|
@@ -268,20 +286,29 @@ var Form = /*#__PURE__*/forwardRef(function Form(_ref2, ref) {
|
|
|
268
286
|
}), jsx("div", {
|
|
269
287
|
className: "space-y-2",
|
|
270
288
|
role: "radiogroup",
|
|
271
|
-
"aria-
|
|
289
|
+
"aria-labelledby": label ? "".concat(fieldId, "-label") : undefined,
|
|
290
|
+
"aria-required": required ? "true" : undefined,
|
|
272
291
|
"aria-describedby": describedBy,
|
|
273
|
-
children: options === null || options === void 0 ? void 0 : options.map(function (option) {
|
|
292
|
+
children: options === null || options === void 0 ? void 0 : options.map(function (option, index) {
|
|
293
|
+
// Shared `name` makes the options one native group
|
|
294
|
+
// (arrow keys, single tab stop); id/htmlFor makes each
|
|
295
|
+
// option label clickable and its accessible name.
|
|
296
|
+
var optionId = "".concat(fieldId, "-option-").concat(index);
|
|
274
297
|
return jsxs("div", {
|
|
275
298
|
className: "flex items-center",
|
|
276
299
|
children: [jsx("input", {
|
|
277
|
-
ref: ref,
|
|
300
|
+
ref: index === 0 ? ref : undefined,
|
|
301
|
+
id: optionId,
|
|
278
302
|
type: "radio",
|
|
303
|
+
name: name,
|
|
304
|
+
value: String(option.value),
|
|
279
305
|
checked: value === option.value,
|
|
280
306
|
onChange: function onChange() {
|
|
281
307
|
return _onChange2(option.value);
|
|
282
308
|
},
|
|
283
|
-
className: "text-primary-600 focus:ring-primary-500"
|
|
309
|
+
className: "text-primary-600 focus:ring-primary-500 focus-visible:ring-2"
|
|
284
310
|
}), jsx("label", {
|
|
311
|
+
htmlFor: optionId,
|
|
285
312
|
className: "ml-2 text-sm",
|
|
286
313
|
children: option.label
|
|
287
314
|
})]
|
|
@@ -484,7 +511,7 @@ var Form = /*#__PURE__*/forwardRef(function Form(_ref2, ref) {
|
|
|
484
511
|
className: "text-sm text-primary-600",
|
|
485
512
|
children: group.description
|
|
486
513
|
}), jsx("div", {
|
|
487
|
-
className: cn("grid gap-4",
|
|
514
|
+
className: cn("grid gap-4", gridColsClass(group.gridCols)),
|
|
488
515
|
children: group.fields.map(renderField)
|
|
489
516
|
})]
|
|
490
517
|
}, group.name);
|
|
@@ -1,11 +1,52 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
3
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
4
4
|
import { forwardRef, useId, useRef, useCallback, useState } from 'react';
|
|
5
5
|
import { Search, EyeOff, Eye, X } from 'lucide-react';
|
|
6
6
|
import { cn } from '../../utils/cn.js';
|
|
7
|
+
import { TOUCH_TARGET } from '../../utils/touchTarget.js';
|
|
7
8
|
|
|
8
|
-
var _excluded = ["id", "className", "type", "label", "placeholder", "error", "success", "helperText", "disabled", "required", "clearable", "icon", "iconPosition", "onClear", "showCount", "maxLength", "readOnly", "onChange", "value", "defaultValue"];
|
|
9
|
+
var _excluded = ["id", "className", "type", "label", "placeholder", "error", "success", "helperText", "disabled", "required", "clearable", "icon", "iconPosition", "onClear", "showCount", "maxLength", "readOnly", "onChange", "value", "defaultValue", "size", "floatingLabel", "otp"];
|
|
10
|
+
// Field heights. `md` keeps its 40px desktop look and grows to the 44px touch
|
|
11
|
+
// target on coarse pointers (same rule as Select); `lg` is 44px everywhere.
|
|
12
|
+
var SIZE_CLASSES = {
|
|
13
|
+
sm: "h-8 text-xs",
|
|
14
|
+
md: "h-10 text-sm leading-10 [@media(pointer:coarse)]:h-11",
|
|
15
|
+
lg: "h-11 text-base"
|
|
16
|
+
};
|
|
17
|
+
// Taller fields for `floatingLabel`, with top padding so the value sits below
|
|
18
|
+
// the floated label.
|
|
19
|
+
var FLOATING_SIZE_CLASSES = {
|
|
20
|
+
sm: "h-10 pt-3 text-sm",
|
|
21
|
+
md: "h-12 pt-4 text-sm",
|
|
22
|
+
lg: "h-14 pt-5 text-base"
|
|
23
|
+
};
|
|
24
|
+
// Mobile keyboard / autofill hints for native types. Applied only when the
|
|
25
|
+
// caller did not pass the attribute themselves.
|
|
26
|
+
var TYPE_HINTS = {
|
|
27
|
+
tel: {
|
|
28
|
+
inputMode: "tel",
|
|
29
|
+
autoComplete: "tel"
|
|
30
|
+
},
|
|
31
|
+
email: {
|
|
32
|
+
inputMode: "email",
|
|
33
|
+
autoComplete: "email"
|
|
34
|
+
},
|
|
35
|
+
url: {
|
|
36
|
+
inputMode: "url",
|
|
37
|
+
autoComplete: "url"
|
|
38
|
+
},
|
|
39
|
+
search: {
|
|
40
|
+
inputMode: "search",
|
|
41
|
+
enterKeyHint: "search"
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var OTP_HINTS = {
|
|
45
|
+
inputMode: "numeric",
|
|
46
|
+
autoComplete: "one-time-code"
|
|
47
|
+
};
|
|
48
|
+
// Password toggle / clear: real buttons that reach 44×44 on coarse pointers.
|
|
49
|
+
var ADORNMENT_BUTTON = cn("absolute right-1 top-1/2 -translate-y-1/2 inline-flex h-8 w-8 items-center justify-center rounded-md", "text-primary-500 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300", "transition-colors duration-fast ease-standard motion-reduce:transition-none", "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500", "disabled:cursor-not-allowed disabled:opacity-50", TOUCH_TARGET);
|
|
9
50
|
var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
|
|
10
51
|
var id = props.id,
|
|
11
52
|
className = props.className,
|
|
@@ -34,7 +75,25 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
|
|
|
34
75
|
onChange = props.onChange,
|
|
35
76
|
value = props.value,
|
|
36
77
|
defaultValue = props.defaultValue,
|
|
78
|
+
_props$size = props.size,
|
|
79
|
+
sizeProp = _props$size === void 0 ? "md" : _props$size,
|
|
80
|
+
_props$floatingLabel = props.floatingLabel,
|
|
81
|
+
floatingLabel = _props$floatingLabel === void 0 ? false : _props$floatingLabel,
|
|
82
|
+
_props$otp = props.otp,
|
|
83
|
+
otp = _props$otp === void 0 ? false : _props$otp,
|
|
37
84
|
rest = _objectWithoutProperties(props, _excluded);
|
|
85
|
+
// `size` is the field size (sm|md|lg). A numeric value is the native
|
|
86
|
+
// `<input size>` attribute and is passed through unchanged.
|
|
87
|
+
var size = SIZE_CLASSES[sizeProp] ? sizeProp : "md";
|
|
88
|
+
var nativeSize = SIZE_CLASSES[sizeProp] ? undefined : sizeProp;
|
|
89
|
+
var hints = otp ? OTP_HINTS : TYPE_HINTS[type];
|
|
90
|
+
var hintProps = {};
|
|
91
|
+
if (hints) {
|
|
92
|
+
Object.keys(hints).forEach(function (key) {
|
|
93
|
+
if (!(key in rest)) hintProps[key] = hints[key];
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
var isFloating = Boolean(floatingLabel && label);
|
|
38
97
|
var internalId = useId();
|
|
39
98
|
var inputId = id || "input-".concat(internalId);
|
|
40
99
|
var isSearch = type === "search";
|
|
@@ -88,19 +147,22 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
|
|
|
88
147
|
var leftHasIcon = Boolean(icon && iconPosition === "left") || isSearch;
|
|
89
148
|
var rightHasIcon = Boolean(icon && iconPosition === "right") || isPassword || clearable && currentLength > 0;
|
|
90
149
|
var paddingClass = cn(leftHasIcon ? "pl-10" : "pl-3", rightHasIcon ? "pr-12" : "pr-3");
|
|
91
|
-
var baseClasses = cn("w-full rounded-md border bg-
|
|
92
|
-
//
|
|
93
|
-
paddingClass, error ? "border-
|
|
150
|
+
var baseClasses = cn("w-full rounded-md border bg-surface text-foreground placeholder-gray-400 dark:placeholder-gray-500", "transition-colors duration-fast ease-standard motion-reduce:transition-none", "disabled:cursor-not-allowed disabled:opacity-50", isFloating ? FLOATING_SIZE_CLASSES[size] : SIZE_CLASSES[size],
|
|
151
|
+
// The floated label replaces the placeholder until the field is focused.
|
|
152
|
+
isFloating && "peer placeholder-transparent focus:placeholder-gray-400 dark:focus:placeholder-gray-500", paddingClass, "focus:outline-none focus-visible:ring-2", error ? "border-error-500 focus-visible:ring-error-500 dark:border-error-400" : success ? "border-success-500 focus-visible:ring-success-500 dark:border-success-400" : "border-gray-300 focus-visible:ring-primary-500 dark:border-gray-600", className);
|
|
153
|
+
var labelContent = jsxs(Fragment, {
|
|
154
|
+
children: [label, required && jsx("span", {
|
|
155
|
+
className: "text-error-500 ml-1",
|
|
156
|
+
children: "*"
|
|
157
|
+
})]
|
|
158
|
+
});
|
|
94
159
|
var inputType = isPassword && showPassword ? "text" : type;
|
|
95
160
|
return jsxs("div", {
|
|
96
161
|
className: "w-full",
|
|
97
|
-
children: [label &&
|
|
162
|
+
children: [label && !isFloating && jsx("label", {
|
|
98
163
|
htmlFor: inputId,
|
|
99
|
-
className: "block text-sm font-medium mb-1",
|
|
100
|
-
children:
|
|
101
|
-
className: "text-error-500 ml-1",
|
|
102
|
-
children: "*"
|
|
103
|
-
})]
|
|
164
|
+
className: "block text-sm font-medium mb-1 text-foreground",
|
|
165
|
+
children: labelContent
|
|
104
166
|
}), jsxs("div", {
|
|
105
167
|
className: "relative",
|
|
106
168
|
children: [leftHasIcon && jsx("div", {
|
|
@@ -111,12 +173,13 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
|
|
|
111
173
|
className: "text-primary-400",
|
|
112
174
|
children: icon
|
|
113
175
|
})
|
|
114
|
-
}), jsx("input", _objectSpread2({
|
|
176
|
+
}), jsx("input", _objectSpread2(_objectSpread2({
|
|
115
177
|
id: inputId,
|
|
116
178
|
ref: setRefs,
|
|
117
179
|
type: inputType,
|
|
118
180
|
className: baseClasses,
|
|
119
|
-
placeholder
|
|
181
|
+
// :placeholder-shown drives the floating label, so it needs a placeholder.
|
|
182
|
+
placeholder: isFloating ? placeholder || " " : placeholder,
|
|
120
183
|
disabled: disabled,
|
|
121
184
|
readOnly: readOnly,
|
|
122
185
|
"aria-invalid": !!error,
|
|
@@ -124,13 +187,19 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
|
|
|
124
187
|
maxLength: maxLength,
|
|
125
188
|
onChange: handleChange,
|
|
126
189
|
value: value,
|
|
127
|
-
defaultValue: defaultValue
|
|
128
|
-
|
|
190
|
+
defaultValue: defaultValue,
|
|
191
|
+
size: nativeSize
|
|
192
|
+
}, hintProps), rest)), isFloating && jsx("label", {
|
|
193
|
+
htmlFor: inputId,
|
|
194
|
+
"data-floating-label": "",
|
|
195
|
+
className: cn("pointer-events-none absolute top-1 origin-left text-xs font-medium text-muted", leftHasIcon ? "left-10" : "left-3", "transition-[top,transform,font-size,color] duration-fast ease-standard motion-reduce:transition-none", "peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:text-sm", "peer-focus:top-1 peer-focus:translate-y-0 peer-focus:text-xs peer-focus:text-primary-600 dark:peer-focus:text-primary-400"),
|
|
196
|
+
children: labelContent
|
|
197
|
+
}), icon && iconPosition === "right" && !isPassword && !(clearable && currentLength > 0) && jsx("div", {
|
|
129
198
|
className: "absolute right-3 top-1/2 transform -translate-y-1/2 pointer-events-none text-primary-400",
|
|
130
199
|
children: icon
|
|
131
200
|
}), isPassword && jsx("button", {
|
|
132
201
|
type: "button",
|
|
133
|
-
className:
|
|
202
|
+
className: ADORNMENT_BUTTON,
|
|
134
203
|
onClick: function onClick() {
|
|
135
204
|
return setShowPassword(function (s) {
|
|
136
205
|
return !s;
|
|
@@ -145,7 +214,7 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
|
|
|
145
214
|
})
|
|
146
215
|
}), clearable && currentLength > 0 && !isPassword && jsx("button", {
|
|
147
216
|
type: "button",
|
|
148
|
-
className:
|
|
217
|
+
className: ADORNMENT_BUTTON,
|
|
149
218
|
onClick: handleClear,
|
|
150
219
|
"aria-label": "Clear input",
|
|
151
220
|
disabled: disabled,
|
|
@@ -155,12 +224,12 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
|
|
|
155
224
|
})]
|
|
156
225
|
}), error && jsx("p", {
|
|
157
226
|
id: "".concat(inputId, "-error"),
|
|
158
|
-
className: "mt-1 text-sm text-error-600",
|
|
227
|
+
className: "mt-1 text-sm text-error-600 dark:text-error-400",
|
|
159
228
|
"aria-live": "polite",
|
|
160
229
|
children: error
|
|
161
230
|
}), !error && success && jsx("p", {
|
|
162
231
|
id: "".concat(inputId, "-success"),
|
|
163
|
-
className: "mt-1 text-sm text-success-600",
|
|
232
|
+
className: "mt-1 text-sm text-success-600 dark:text-success-400",
|
|
164
233
|
"aria-live": "polite",
|
|
165
234
|
children: success
|
|
166
235
|
}), !error && !success && helperText && jsx("p", {
|
|
@@ -168,7 +237,7 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
|
|
|
168
237
|
className: "mt-1 text-sm text-muted",
|
|
169
238
|
children: helperText
|
|
170
239
|
}), showCount && maxLength && jsxs("div", {
|
|
171
|
-
className: "mt-1 text-right text-xs text-primary-500",
|
|
240
|
+
className: "mt-1 text-right text-xs text-primary-500 dark:text-primary-400",
|
|
172
241
|
"aria-hidden": true,
|
|
173
242
|
children: [currentLength, "/", maxLength]
|
|
174
243
|
})]
|