@dreamtree-org/twreact-ui 1.2.1 → 1.3.1
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/DESIGN.md +118 -0
- package/README.md +231 -44
- package/ai-skills/dreamtree-ui.md +98 -12
- package/dist/ai-schema.json +452 -17
- package/dist/components/blocks/AppShell.d.ts +4 -0
- package/dist/components/blocks/AppShell.d.ts.map +1 -0
- package/dist/components/blocks/ConfirmDialog.d.ts +4 -0
- package/dist/components/blocks/ConfirmDialog.d.ts.map +1 -0
- package/dist/components/blocks/EmptyState.d.ts +4 -0
- package/dist/components/blocks/EmptyState.d.ts.map +1 -0
- package/dist/components/blocks/ErrorState.d.ts +4 -0
- package/dist/components/blocks/ErrorState.d.ts.map +1 -0
- package/dist/components/blocks/LoadingState.d.ts +9 -0
- package/dist/components/blocks/LoadingState.d.ts.map +1 -0
- package/dist/components/blocks/PageHeader.d.ts +4 -0
- package/dist/components/blocks/PageHeader.d.ts.map +1 -0
- package/dist/components/blocks/StatCard.d.ts +4 -0
- package/dist/components/blocks/StatCard.d.ts.map +1 -0
- package/dist/components/blocks/StatusPill.d.ts +5 -0
- package/dist/components/blocks/StatusPill.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/DateRangePicker.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/LocationPicker.d.ts.map +1 -1
- package/dist/components/core/Popover.d.ts +4 -0
- package/dist/components/core/Popover.d.ts.map +1 -0
- 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/Select.d.ts.map +1 -1
- package/dist/components/core/Skeleton.d.ts.map +1 -1
- package/dist/components/core/Slider.d.ts +4 -0
- package/dist/components/core/Slider.d.ts.map +1 -0
- 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/ActionMenu.d.ts.map +1 -1
- package/dist/components/core/Table/ColumnMenu.d.ts +2 -1
- package/dist/components/core/Table/ColumnMenu.d.ts.map +1 -1
- package/dist/components/core/Table/MobileCard.d.ts +2 -2
- package/dist/components/core/Table/MobileCard.d.ts.map +1 -1
- package/dist/components/core/Table/Table.d.ts.map +1 -1
- package/dist/components/core/Table/TableRow.d.ts +4 -2
- package/dist/components/core/Table/TableRow.d.ts.map +1 -1
- package/dist/components/core/Tabs.d.ts.map +1 -1
- package/dist/components/core/Textarea.d.ts +4 -0
- package/dist/components/core/Textarea.d.ts.map +1 -0
- package/dist/components/core/ThreeDotPopover.d.ts.map +1 -1
- package/dist/components/core/Tooltip.d.ts.map +1 -1
- package/dist/components/feedback/Dialog.d.ts.map +1 -1
- package/dist/components/feedback/Drawer.d.ts +4 -0
- package/dist/components/feedback/Drawer.d.ts.map +1 -0
- package/dist/components/feedback/Toast.d.ts +14 -5
- package/dist/components/feedback/Toast.d.ts.map +1 -1
- package/dist/components/feedback/Tour.d.ts +4 -0
- package/dist/components/feedback/Tour.d.ts.map +1 -0
- package/dist/components/feedback/TourSpotlight.d.ts +9 -0
- package/dist/components/feedback/TourSpotlight.d.ts.map +1 -0
- package/dist/components/navigation/Breadcrumbs.d.ts.map +1 -1
- package/dist/components/navigation/FootNav.d.ts.map +1 -1
- package/dist/components/navigation/Navbar.d.ts.map +1 -1
- package/dist/components/navigation/Sidebar.d.ts +8 -2
- package/dist/components/navigation/Sidebar.d.ts.map +1 -1
- package/dist/components/utility/Badge.d.ts.map +1 -1
- package/dist/components/utility/FileUpload.d.ts.map +1 -1
- package/dist/components/utility/Sortable.d.ts +4 -0
- package/dist/components/utility/Sortable.d.ts.map +1 -0
- package/dist/esm/_vendor/object-assign/index.js +99 -0
- package/dist/esm/_vendor/prop-types/_vendor/react-is/cjs/react-is.development.js +190 -0
- package/dist/esm/_vendor/prop-types/_vendor/react-is/cjs/react-is.production.min.js +26 -0
- package/dist/esm/_vendor/prop-types/_vendor/react-is/index.js +19 -0
- package/dist/esm/_vendor/prop-types/checkPropTypes.js +115 -0
- package/dist/esm/_vendor/prop-types/factoryWithThrowingShims.js +74 -0
- package/dist/esm/_vendor/prop-types/factoryWithTypeCheckers.js +625 -0
- package/dist/esm/_vendor/prop-types/index.js +30 -0
- package/dist/esm/_vendor/prop-types/lib/ReactPropTypesSecret.js +21 -0
- package/dist/esm/_vendor/prop-types/lib/has.js +11 -0
- package/dist/esm/_virtual/_commonjsHelpers.js +5 -0
- package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +329 -0
- package/dist/esm/_virtual/index.js +3 -0
- package/dist/esm/_virtual/index2.js +3 -0
- package/dist/esm/_virtual/react-is.development.js +3 -0
- package/dist/esm/_virtual/react-is.production.min.js +3 -0
- package/dist/esm/components/blocks/AppShell.js +205 -0
- package/dist/esm/components/blocks/ConfirmDialog.js +57 -0
- package/dist/esm/components/blocks/EmptyState.js +44 -0
- package/dist/esm/components/blocks/ErrorState.js +53 -0
- package/dist/esm/components/blocks/LoadingState.js +134 -0
- package/dist/esm/components/blocks/PageHeader.js +51 -0
- package/dist/esm/components/blocks/StatCard.js +79 -0
- package/dist/esm/components/blocks/StatusPill.js +55 -0
- package/dist/esm/components/core/Accordion.js +260 -0
- package/dist/esm/components/core/Button.js +59 -0
- package/dist/esm/components/core/Checkbox.js +188 -0
- package/dist/esm/components/core/ColorPicker.js +348 -0
- package/dist/esm/components/core/DatePicker.js +436 -0
- package/dist/esm/components/core/DateRangePicker.js +494 -0
- package/dist/esm/components/core/Form.js +522 -0
- package/dist/esm/components/core/Input.js +179 -0
- package/dist/esm/components/core/Loader.js +141 -0
- package/dist/esm/components/core/LocationPicker.js +161 -0
- package/dist/esm/components/core/Popover.js +171 -0
- package/dist/esm/components/core/PriceRangePicker.js +376 -0
- package/dist/esm/components/core/ProgressBar.js +152 -0
- package/dist/esm/components/core/Radio.js +156 -0
- package/dist/esm/components/core/Rate.js +197 -0
- package/dist/esm/components/core/RoundedTag.js +118 -0
- package/dist/esm/components/core/Select.js +857 -0
- package/dist/esm/components/core/Skeleton.js +100 -0
- package/dist/esm/components/core/Slider.js +134 -0
- package/dist/esm/components/core/SpeechToText.js +226 -0
- package/dist/esm/components/core/Switch.js +207 -0
- package/dist/esm/components/core/Table/ActionMenu.js +251 -0
- package/dist/esm/components/core/Table/ColumnMenu.js +55 -0
- package/dist/esm/components/core/Table/FieldInput.js +157 -0
- package/dist/esm/components/core/Table/MobileCard.js +187 -0
- package/dist/esm/components/core/Table/MobileFilters.js +72 -0
- package/dist/esm/components/core/Table/Pagination.js +157 -0
- package/dist/esm/components/core/Table/Table.js +1533 -0
- package/dist/esm/components/core/Table/TableRow.js +254 -0
- package/dist/esm/components/core/Table/constants.js +35 -0
- package/dist/esm/components/core/Table/helpers.js +124 -0
- package/dist/esm/components/core/Table/useClickOutside.js +26 -0
- package/dist/esm/components/core/Tabs.js +289 -0
- package/dist/esm/components/core/TextToSpeech.js +52 -0
- package/dist/esm/components/core/Textarea.js +125 -0
- package/dist/esm/components/core/ThreeDotPopover.js +160 -0
- package/dist/esm/components/core/Tooltip.js +372 -0
- package/dist/esm/components/feedback/Alert.js +96 -0
- package/dist/esm/components/feedback/Dialog.js +200 -0
- package/dist/esm/components/feedback/Drawer.js +171 -0
- package/dist/esm/components/feedback/Toast.js +362 -0
- package/dist/esm/components/feedback/Tour.js +214 -0
- package/dist/esm/components/feedback/TourSpotlight.js +71 -0
- package/dist/esm/components/navigation/Breadcrumbs.js +75 -0
- package/dist/esm/components/navigation/Dropdown.js +53 -0
- package/dist/esm/components/navigation/FootNav.js +51 -0
- package/dist/esm/components/navigation/Navbar.js +437 -0
- package/dist/esm/components/navigation/Sidebar.js +567 -0
- package/dist/esm/components/utility/Avatar.js +89 -0
- package/dist/esm/components/utility/Badge.js +47 -0
- package/dist/esm/components/utility/Card.js +85 -0
- package/dist/esm/components/utility/Carousel.js +97 -0
- package/dist/esm/components/utility/Condition.js +9 -0
- package/dist/esm/components/utility/FileUpload.js +177 -0
- package/dist/esm/components/utility/Pagination.js +111 -0
- package/dist/esm/components/utility/Sortable.js +190 -0
- package/dist/esm/components/utility/Stepper.js +101 -0
- package/dist/esm/hooks/useApi.js +169 -0
- package/dist/esm/hooks/useControllableState.js +32 -0
- package/dist/esm/hooks/useFocusTrap.js +85 -0
- package/dist/esm/hooks/useLatestRef.js +20 -0
- package/dist/esm/hooks/useMixins.js +100 -0
- package/dist/esm/hooks/useScrollLock.js +26 -0
- package/dist/esm/hooks/useSliderValues.js +135 -0
- package/dist/esm/hooks/useSortable.js +188 -0
- package/dist/esm/hooks/useTheme.js +53 -0
- package/dist/esm/hooks/useTourTarget.js +92 -0
- package/dist/esm/index.js +65 -0
- package/dist/esm/store/index.js +26 -0
- package/dist/esm/store/store.js +107 -0
- package/dist/esm/store/storeSlice.js +45 -0
- package/dist/esm/utils/cn.js +11 -0
- package/dist/esm/utils/emitter.js +169 -0
- package/dist/esm/utils/helpers.js +161 -0
- package/dist/esm/utils/position.js +134 -0
- package/dist/hooks/useApi.d.ts +2 -1
- package/dist/hooks/useApi.d.ts.map +1 -1
- package/dist/hooks/useControllableState.d.ts +11 -0
- package/dist/hooks/useControllableState.d.ts.map +1 -0
- package/dist/hooks/useFocusTrap.d.ts +15 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -0
- package/dist/hooks/useLatestRef.d.ts +8 -0
- package/dist/hooks/useLatestRef.d.ts.map +1 -0
- package/dist/hooks/useMixins.d.ts.map +1 -1
- package/dist/hooks/useScrollLock.d.ts +2 -0
- package/dist/hooks/useScrollLock.d.ts.map +1 -0
- package/dist/hooks/useSliderValues.d.ts +43 -0
- package/dist/hooks/useSliderValues.d.ts.map +1 -0
- package/dist/hooks/useSortable.d.ts +32 -0
- package/dist/hooks/useSortable.d.ts.map +1 -0
- package/dist/hooks/useTheme.d.ts.map +1 -1
- package/dist/hooks/useTourTarget.d.ts +15 -0
- package/dist/hooks/useTourTarget.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +55 -39
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +7937 -4659
- package/dist/index.js +7950 -4657
- package/dist/tokens/brands/default.css +1 -0
- package/dist/tokens/brands/default.json +5 -0
- package/dist/tokens/brands/practicepro.css +17 -0
- package/dist/tokens/brands/practicepro.json +27 -0
- package/dist/tokens/tailwind3-preset.cjs +214 -0
- package/dist/tokens/theme.css +193 -0
- package/dist/tokens/tokens.json +373 -0
- package/dist/tokens/vars.css +98 -0
- package/dist/types/blocks.d.ts +109 -0
- package/dist/types/blocks.d.ts.map +1 -0
- package/dist/types/index.d.ts +194 -5
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/position.d.ts +30 -0
- package/dist/utils/position.d.ts.map +1 -0
- package/doc/README.md +26 -1
- package/mcp/catalog.snapshot.json +1643 -549
- package/package.json +24 -6
- package/tokens/brands/default.json +5 -0
- package/tokens/brands/practicepro.json +27 -0
- package/tokens/lib.cjs +128 -0
- package/tokens/tokens.json +133 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
import { forwardRef, useId, useMemo, useState, useRef, useEffect, useLayoutEffect } from 'react';
|
|
5
|
+
import ReactDOM from 'react-dom';
|
|
6
|
+
import { startOfDay, startOfMonth, startOfWeek, endOfMonth, endOfWeek, eachDayOfInterval, subMonths, format, addMonths, isSameDay, isSameMonth, isToday, parseISO, isAfter, isBefore } from 'date-fns';
|
|
7
|
+
import { cn } from '../../utils/cn.js';
|
|
8
|
+
import { ChevronLeft, ChevronRight, Calendar, X } from 'lucide-react';
|
|
9
|
+
|
|
10
|
+
var _excluded = ["value", "onChange", "placeholder", "label", "error", "helperText", "disabled", "required", "minDate", "maxDate", "className", "weekStartsOn", "portal", "displayFormat", "locale", "showClear", "closeOnSelect"];
|
|
11
|
+
/*
|
|
12
|
+
Single-calendar DateRangePicker with hover-preview and space-aware popup positioning
|
|
13
|
+
- Enforces mandatory range selection until both start and end are chosen.
|
|
14
|
+
- Popup flips above/below and clamps horizontally to avoid clipping.
|
|
15
|
+
- Popup won't close while only a start date is selected (Clear cancels in-progress selection).
|
|
16
|
+
*/
|
|
17
|
+
var normalizeDate = function normalizeDate(d) {
|
|
18
|
+
if (!d) return null;
|
|
19
|
+
if (typeof d === "string") {
|
|
20
|
+
try {
|
|
21
|
+
return parseISO(d);
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return d instanceof Date ? d : null;
|
|
27
|
+
};
|
|
28
|
+
var DateRangePicker = /*#__PURE__*/forwardRef(function DateRangePicker(_ref, ref) {
|
|
29
|
+
var value = _ref.value,
|
|
30
|
+
onChange = _ref.onChange,
|
|
31
|
+
_ref$placeholder = _ref.placeholder,
|
|
32
|
+
placeholder = _ref$placeholder === void 0 ? "Select date range..." : _ref$placeholder,
|
|
33
|
+
label = _ref.label,
|
|
34
|
+
error = _ref.error,
|
|
35
|
+
helperText = _ref.helperText,
|
|
36
|
+
disabled = _ref.disabled,
|
|
37
|
+
required = _ref.required,
|
|
38
|
+
minDate = _ref.minDate,
|
|
39
|
+
maxDate = _ref.maxDate,
|
|
40
|
+
className = _ref.className,
|
|
41
|
+
_ref$weekStartsOn = _ref.weekStartsOn,
|
|
42
|
+
weekStartsOn = _ref$weekStartsOn === void 0 ? 0 : _ref$weekStartsOn,
|
|
43
|
+
_ref$portal = _ref.portal,
|
|
44
|
+
portal = _ref$portal === void 0 ? false : _ref$portal,
|
|
45
|
+
_ref$displayFormat = _ref.displayFormat,
|
|
46
|
+
displayFormat = _ref$displayFormat === void 0 ? "MMM dd, yyyy" : _ref$displayFormat,
|
|
47
|
+
_ref$locale = _ref.locale,
|
|
48
|
+
locale = _ref$locale === void 0 ? undefined : _ref$locale,
|
|
49
|
+
_ref$showClear = _ref.showClear,
|
|
50
|
+
showClear = _ref$showClear === void 0 ? true : _ref$showClear,
|
|
51
|
+
_ref$closeOnSelect = _ref.closeOnSelect,
|
|
52
|
+
closeOnSelect = _ref$closeOnSelect === void 0 ? true : _ref$closeOnSelect,
|
|
53
|
+
props = _objectWithoutProperties(_ref, _excluded);
|
|
54
|
+
// Stable ids wiring label / helper / error to the trigger button.
|
|
55
|
+
// A consumer `id` (spread onto the trigger via ...props) wins.
|
|
56
|
+
var reactId = useId();
|
|
57
|
+
var fieldId = props.id || "daterangepicker-".concat(reactId);
|
|
58
|
+
var labelId = "".concat(fieldId, "-label");
|
|
59
|
+
var valueId = "".concat(fieldId, "-value");
|
|
60
|
+
var errorId = "".concat(fieldId, "-error");
|
|
61
|
+
var helperId = "".concat(fieldId, "-helper");
|
|
62
|
+
var start = useMemo(function () {
|
|
63
|
+
return normalizeDate(value === null || value === void 0 ? void 0 : value.start);
|
|
64
|
+
}, [value]);
|
|
65
|
+
var end = useMemo(function () {
|
|
66
|
+
return normalizeDate(value === null || value === void 0 ? void 0 : value.end);
|
|
67
|
+
}, [value]);
|
|
68
|
+
var _useState = useState(false),
|
|
69
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
70
|
+
isOpen = _useState2[0],
|
|
71
|
+
setIsOpen = _useState2[1];
|
|
72
|
+
var _useState3 = useState(start || new Date()),
|
|
73
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
74
|
+
currentMonth = _useState4[0],
|
|
75
|
+
setCurrentMonth = _useState4[1];
|
|
76
|
+
// local selection state so user can pick start then end before commit
|
|
77
|
+
var _useState5 = useState(start),
|
|
78
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
79
|
+
localStart = _useState6[0],
|
|
80
|
+
setLocalStart = _useState6[1];
|
|
81
|
+
var _useState7 = useState(end),
|
|
82
|
+
_useState8 = _slicedToArray(_useState7, 2),
|
|
83
|
+
localEnd = _useState8[0],
|
|
84
|
+
setLocalEnd = _useState8[1];
|
|
85
|
+
// hover preview while picking second date
|
|
86
|
+
var _useState9 = useState(null),
|
|
87
|
+
_useState0 = _slicedToArray(_useState9, 2),
|
|
88
|
+
hoverDate = _useState0[0],
|
|
89
|
+
setHoverDate = _useState0[1];
|
|
90
|
+
// Re-sync local selection when the external value changes
|
|
91
|
+
// (adjust-during-render pattern).
|
|
92
|
+
var _useState1 = useState(value),
|
|
93
|
+
_useState10 = _slicedToArray(_useState1, 2),
|
|
94
|
+
prevValue = _useState10[0],
|
|
95
|
+
setPrevValue = _useState10[1];
|
|
96
|
+
if (value !== prevValue) {
|
|
97
|
+
setPrevValue(value);
|
|
98
|
+
setLocalStart(start);
|
|
99
|
+
setLocalEnd(end);
|
|
100
|
+
if (start) setCurrentMonth(start);
|
|
101
|
+
}
|
|
102
|
+
var datePickerRef = useRef(null);
|
|
103
|
+
var popupRef = useRef(null);
|
|
104
|
+
var _useState11 = useState({}),
|
|
105
|
+
_useState12 = _slicedToArray(_useState11, 2),
|
|
106
|
+
popupStyle = _useState12[0],
|
|
107
|
+
setPopupStyle = _useState12[1];
|
|
108
|
+
// Outside click: only close if there is NOT an in-progress single-date selection
|
|
109
|
+
useEffect(function () {
|
|
110
|
+
var handleClickOutside = function handleClickOutside(e) {
|
|
111
|
+
if (datePickerRef.current && !datePickerRef.current.contains(e.target) && (!popupRef.current || !popupRef.current.contains(e.target))) {
|
|
112
|
+
// If user has selected a start but no end, prevent closing to force range selection
|
|
113
|
+
if (localStart && !localEnd) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
setIsOpen(false);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
120
|
+
return function () {
|
|
121
|
+
return document.removeEventListener("mousedown", handleClickOutside);
|
|
122
|
+
};
|
|
123
|
+
}, [localStart, localEnd]);
|
|
124
|
+
// Keyboard handling: Escape should not close when only start selected
|
|
125
|
+
useEffect(function () {
|
|
126
|
+
var handleKey = function handleKey(e) {
|
|
127
|
+
if (!isOpen) return;
|
|
128
|
+
if (e.key === "Escape") {
|
|
129
|
+
if (localStart && !localEnd) return; // ignore escape while selecting range
|
|
130
|
+
setIsOpen(false);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
document.addEventListener("keydown", handleKey);
|
|
134
|
+
return function () {
|
|
135
|
+
return document.removeEventListener("keydown", handleKey);
|
|
136
|
+
};
|
|
137
|
+
}, [isOpen, localStart, localEnd]);
|
|
138
|
+
var min = minDate ? startOfDay(normalizeDate(minDate)) : null;
|
|
139
|
+
var max = maxDate ? startOfDay(normalizeDate(maxDate)) : null;
|
|
140
|
+
var isDisabled = function isDisabled(d) {
|
|
141
|
+
var dd = startOfDay(d);
|
|
142
|
+
if (min && isBefore(dd, min)) return true;
|
|
143
|
+
if (max && isAfter(dd, max)) return true;
|
|
144
|
+
return false;
|
|
145
|
+
};
|
|
146
|
+
var monthStart = startOfMonth(currentMonth);
|
|
147
|
+
var monthGridStart = startOfWeek(monthStart, {
|
|
148
|
+
weekStartsOn: weekStartsOn
|
|
149
|
+
});
|
|
150
|
+
var monthEnd = endOfMonth(currentMonth);
|
|
151
|
+
var monthGridEnd = endOfWeek(monthEnd, {
|
|
152
|
+
weekStartsOn: weekStartsOn
|
|
153
|
+
});
|
|
154
|
+
var days = useMemo(function () {
|
|
155
|
+
return eachDayOfInterval({
|
|
156
|
+
start: monthGridStart,
|
|
157
|
+
end: monthGridEnd
|
|
158
|
+
});
|
|
159
|
+
}, [monthGridStart, monthGridEnd]);
|
|
160
|
+
// Compute whether a day is considered 'in range' for highlighting.
|
|
161
|
+
// If user has selected start and is hovering (no end yet) we preview using hoverDate.
|
|
162
|
+
var dayInRange = function dayInRange(d) {
|
|
163
|
+
var s = localStart ? startOfDay(localStart) : null;
|
|
164
|
+
var e = localEnd ? startOfDay(localEnd) : null;
|
|
165
|
+
var hover = hoverDate ? startOfDay(hoverDate) : null;
|
|
166
|
+
if (s && e) {
|
|
167
|
+
return isSameDay(d, s) || isSameDay(d, e) || isAfter(startOfDay(d), s) && isBefore(startOfDay(d), e);
|
|
168
|
+
}
|
|
169
|
+
if (s && !e && hover) {
|
|
170
|
+
var a = isBefore(hover, s) ? hover : s;
|
|
171
|
+
var b = isBefore(hover, s) ? s : hover;
|
|
172
|
+
return isSameDay(d, a) || isSameDay(d, b) || isAfter(startOfDay(d), a) && isBefore(startOfDay(d), b);
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
};
|
|
176
|
+
var handleDayClick = function handleDayClick(d) {
|
|
177
|
+
if (isDisabled(d)) return;
|
|
178
|
+
// No start selected -> pick start
|
|
179
|
+
if (!localStart && !localEnd) {
|
|
180
|
+
setLocalStart(d);
|
|
181
|
+
setLocalEnd(null);
|
|
182
|
+
setHoverDate(null);
|
|
183
|
+
setCurrentMonth(d);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
// Have start but no end -> pick end (swap if before)
|
|
187
|
+
if (localStart && !localEnd) {
|
|
188
|
+
if (isBefore(d, localStart)) {
|
|
189
|
+
var newStart = d;
|
|
190
|
+
var newEnd = localStart;
|
|
191
|
+
setLocalStart(newStart);
|
|
192
|
+
setLocalEnd(newEnd);
|
|
193
|
+
// commit only when both present
|
|
194
|
+
onChange === null || onChange === void 0 || onChange({
|
|
195
|
+
start: newStart,
|
|
196
|
+
end: newEnd
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
setLocalEnd(d);
|
|
200
|
+
onChange === null || onChange === void 0 || onChange({
|
|
201
|
+
start: localStart,
|
|
202
|
+
end: d
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
setHoverDate(null);
|
|
206
|
+
// Close the popup only after the second date is selected
|
|
207
|
+
if (closeOnSelect) setIsOpen(false);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Both present -> start a new selection with clicked date as start
|
|
211
|
+
if (localStart && localEnd) {
|
|
212
|
+
setLocalStart(d);
|
|
213
|
+
setLocalEnd(null);
|
|
214
|
+
setHoverDate(null);
|
|
215
|
+
setCurrentMonth(d);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
var clearSelection = function clearSelection() {
|
|
220
|
+
setLocalStart(null);
|
|
221
|
+
setLocalEnd(null);
|
|
222
|
+
setHoverDate(null);
|
|
223
|
+
onChange === null || onChange === void 0 || onChange(null);
|
|
224
|
+
};
|
|
225
|
+
// A disabled picker cannot be cleared (the old nested button could not be
|
|
226
|
+
// clicked inside a disabled trigger either).
|
|
227
|
+
var showClearButton = Boolean(showClear && !disabled && (localStart || localEnd));
|
|
228
|
+
// Display placeholder until both start and end are selected (mandatory range)
|
|
229
|
+
var formatDisplay = function formatDisplay() {
|
|
230
|
+
if (!localStart || !localEnd) return placeholder;
|
|
231
|
+
return "".concat(format(localStart, displayFormat, {
|
|
232
|
+
locale: locale
|
|
233
|
+
}), " \u2014 ").concat(format(localEnd, displayFormat, {
|
|
234
|
+
locale: locale
|
|
235
|
+
}));
|
|
236
|
+
};
|
|
237
|
+
var header = jsxs("div", {
|
|
238
|
+
className: "mb-2 flex items-center justify-between",
|
|
239
|
+
children: [jsx("button", {
|
|
240
|
+
type: "button",
|
|
241
|
+
className: "rounded-md p-1 hover:bg-gray-100",
|
|
242
|
+
onClick: function onClick() {
|
|
243
|
+
return setCurrentMonth(function (m) {
|
|
244
|
+
return subMonths(m, 1);
|
|
245
|
+
});
|
|
246
|
+
},
|
|
247
|
+
"aria-label": "Previous month",
|
|
248
|
+
children: jsx(ChevronLeft, {
|
|
249
|
+
className: "h-4 w-4"
|
|
250
|
+
})
|
|
251
|
+
}), jsx("h4", {
|
|
252
|
+
className: "text-sm font-medium",
|
|
253
|
+
children: format(currentMonth, "MMMM yyyy")
|
|
254
|
+
}), jsx("button", {
|
|
255
|
+
type: "button",
|
|
256
|
+
className: "rounded-md p-1 hover:bg-gray-100",
|
|
257
|
+
onClick: function onClick() {
|
|
258
|
+
return setCurrentMonth(function (m) {
|
|
259
|
+
return addMonths(m, 1);
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
"aria-label": "Next month",
|
|
263
|
+
children: jsx(ChevronRight, {
|
|
264
|
+
className: "h-4 w-4"
|
|
265
|
+
})
|
|
266
|
+
})]
|
|
267
|
+
});
|
|
268
|
+
// compute popup position so it's aware of viewport edges (top/bottom/left/right)
|
|
269
|
+
var computePopupPosition = function computePopupPosition() {
|
|
270
|
+
var margin = 8;
|
|
271
|
+
var trigger = datePickerRef.current;
|
|
272
|
+
var popup = popupRef.current;
|
|
273
|
+
if (!trigger || !popup) return;
|
|
274
|
+
var triggerRect = trigger.getBoundingClientRect();
|
|
275
|
+
// measure popup by temporarily making it visible if hidden
|
|
276
|
+
var popupRect = popup.getBoundingClientRect();
|
|
277
|
+
// If the popup is not yet fully measured (width/height 0), try to read offsetWidth/Height
|
|
278
|
+
if (popupRect.width === 0 || popupRect.height === 0) {
|
|
279
|
+
popupRect = {
|
|
280
|
+
width: popup.offsetWidth || 320,
|
|
281
|
+
height: popup.offsetHeight || 300
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
// Decide vertical placement
|
|
285
|
+
var spaceBelow = window.innerHeight - triggerRect.bottom;
|
|
286
|
+
var spaceAbove = triggerRect.top;
|
|
287
|
+
var placeBelow = spaceBelow >= popupRect.height + margin;
|
|
288
|
+
if (!placeBelow && spaceAbove >= popupRect.height + margin) placeBelow = false;
|
|
289
|
+
// If neither side has full space, pick the side with more space
|
|
290
|
+
if (!placeBelow && spaceBelow < popupRect.height + margin && spaceAbove < popupRect.height + margin) {
|
|
291
|
+
placeBelow = spaceBelow >= spaceAbove;
|
|
292
|
+
}
|
|
293
|
+
// compute top coordinate (fixed positioning)
|
|
294
|
+
var top;
|
|
295
|
+
if (placeBelow) {
|
|
296
|
+
top = Math.min(window.innerHeight - margin - popupRect.height, triggerRect.bottom + margin);
|
|
297
|
+
} else {
|
|
298
|
+
top = Math.max(margin, triggerRect.top - popupRect.height - margin);
|
|
299
|
+
}
|
|
300
|
+
// Decide horizontal placement (try align left to trigger)
|
|
301
|
+
var left = triggerRect.left;
|
|
302
|
+
// If overflowing right, shift left
|
|
303
|
+
if (left + popupRect.width > window.innerWidth - margin) {
|
|
304
|
+
left = Math.max(margin, window.innerWidth - margin - popupRect.width);
|
|
305
|
+
}
|
|
306
|
+
// If overflowing left, clamp
|
|
307
|
+
if (left < margin) left = margin;
|
|
308
|
+
var transformOrigin = placeBelow ? "top left" : "bottom left";
|
|
309
|
+
setPopupStyle({
|
|
310
|
+
position: "fixed",
|
|
311
|
+
top: "".concat(Math.round(top), "px"),
|
|
312
|
+
left: "".concat(Math.round(left), "px"),
|
|
313
|
+
zIndex: 9999,
|
|
314
|
+
transformOrigin: transformOrigin
|
|
315
|
+
});
|
|
316
|
+
};
|
|
317
|
+
// recompute on open, on resize/scroll, and when popup content changes (currentMonth)
|
|
318
|
+
useLayoutEffect(function () {
|
|
319
|
+
if (!isOpen) return;
|
|
320
|
+
// compute on next frame to ensure popup ref exists in DOM (especially with portal)
|
|
321
|
+
// Measuring the DOM and positioning the popup before paint is the
|
|
322
|
+
// intended use of a layout effect, so the setState inside is deliberate.
|
|
323
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
324
|
+
computePopupPosition();
|
|
325
|
+
var handleResize = function handleResize() {
|
|
326
|
+
return computePopupPosition();
|
|
327
|
+
};
|
|
328
|
+
window.addEventListener("resize", handleResize);
|
|
329
|
+
window.addEventListener("scroll", handleResize, true); // capture scrolling in ancestors
|
|
330
|
+
return function () {
|
|
331
|
+
window.removeEventListener("resize", handleResize);
|
|
332
|
+
window.removeEventListener("scroll", handleResize, true);
|
|
333
|
+
};
|
|
334
|
+
}, [isOpen, currentMonth]);
|
|
335
|
+
var calendarPopup = jsxs("div", {
|
|
336
|
+
ref: popupRef,
|
|
337
|
+
className: "w-80 rounded-md border border-gray-300 bg-white p-4 shadow-lg",
|
|
338
|
+
role: "dialog",
|
|
339
|
+
"aria-modal": "false",
|
|
340
|
+
style: popupStyle,
|
|
341
|
+
children: [header, jsx("div", {
|
|
342
|
+
className: "mb-2 grid grid-cols-7 gap-1",
|
|
343
|
+
children: Array.from({
|
|
344
|
+
length: 7
|
|
345
|
+
}).map(function (_, i) {
|
|
346
|
+
var dayIndex = (i + weekStartsOn) % 7;
|
|
347
|
+
var label = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"][dayIndex];
|
|
348
|
+
return jsx("div", {
|
|
349
|
+
className: "p-2 text-center text-xs font-medium text-primary-500",
|
|
350
|
+
children: label
|
|
351
|
+
}, label);
|
|
352
|
+
})
|
|
353
|
+
}), jsx("div", {
|
|
354
|
+
className: "grid grid-cols-7 gap-1",
|
|
355
|
+
role: "grid",
|
|
356
|
+
children: days.map(function (d) {
|
|
357
|
+
var isSelStart = localStart && isSameDay(d, localStart);
|
|
358
|
+
var isSelEnd = localEnd && isSameDay(d, localEnd);
|
|
359
|
+
var inRange = dayInRange(d);
|
|
360
|
+
var isCurrentMonth = isSameMonth(d, currentMonth);
|
|
361
|
+
var today = isToday(d);
|
|
362
|
+
var disabled = isDisabled(d);
|
|
363
|
+
return jsx("button", {
|
|
364
|
+
type: "button",
|
|
365
|
+
onClick: function onClick() {
|
|
366
|
+
return handleDayClick(d);
|
|
367
|
+
},
|
|
368
|
+
onMouseEnter: function onMouseEnter() {
|
|
369
|
+
// only set hover when picking end (start selected, end not)
|
|
370
|
+
if (localStart && !localEnd) setHoverDate(d);
|
|
371
|
+
},
|
|
372
|
+
onMouseLeave: function onMouseLeave() {
|
|
373
|
+
if (localStart && !localEnd) setHoverDate(null);
|
|
374
|
+
},
|
|
375
|
+
disabled: disabled,
|
|
376
|
+
className: cn("h-8 w-8 rounded-md text-sm transition-colors focus:outline-2 focus:outline-offset-1", {
|
|
377
|
+
"text-primary-400": !isCurrentMonth,
|
|
378
|
+
"bg-primary-600 text-white": isSelStart || isSelEnd,
|
|
379
|
+
"bg-primary-100 text-primary-600": today && !(isSelStart || isSelEnd),
|
|
380
|
+
"bg-primary-200": inRange && !(isSelStart || isSelEnd),
|
|
381
|
+
"hover:bg-gray-100": isCurrentMonth && !(isSelStart || isSelEnd) && !disabled,
|
|
382
|
+
"cursor-not-allowed opacity-50": disabled
|
|
383
|
+
}),
|
|
384
|
+
children: format(d, "d")
|
|
385
|
+
}, d.toISOString());
|
|
386
|
+
})
|
|
387
|
+
}), jsxs("div", {
|
|
388
|
+
className: "mt-3 flex items-center justify-between",
|
|
389
|
+
children: [jsxs("div", {
|
|
390
|
+
className: "flex gap-2",
|
|
391
|
+
children: [showClear && jsx("button", {
|
|
392
|
+
type: "button",
|
|
393
|
+
className: "rounded-md px-2 py-1 text-xs hover:bg-gray-100",
|
|
394
|
+
onClick: clearSelection,
|
|
395
|
+
children: "Clear"
|
|
396
|
+
}), jsx("button", {
|
|
397
|
+
type: "button",
|
|
398
|
+
className: "rounded-md px-2 py-1 text-xs hover:bg-gray-100",
|
|
399
|
+
onClick: function onClick() {
|
|
400
|
+
var t = new Date();
|
|
401
|
+
setLocalStart(t);
|
|
402
|
+
setLocalEnd(t);
|
|
403
|
+
onChange === null || onChange === void 0 || onChange({
|
|
404
|
+
start: t,
|
|
405
|
+
end: t
|
|
406
|
+
});
|
|
407
|
+
if (closeOnSelect) setIsOpen(false);
|
|
408
|
+
},
|
|
409
|
+
children: "Today"
|
|
410
|
+
})]
|
|
411
|
+
}), jsx("div", {
|
|
412
|
+
className: "flex items-center gap-2",
|
|
413
|
+
children: jsx("div", {
|
|
414
|
+
className: "text-xs text-primary-500",
|
|
415
|
+
children: !localStart || !localEnd ? placeholder : "".concat(format(localStart, displayFormat, {
|
|
416
|
+
locale: locale
|
|
417
|
+
}), " \u2014 ").concat(format(localEnd, displayFormat, {
|
|
418
|
+
locale: locale
|
|
419
|
+
}))
|
|
420
|
+
})
|
|
421
|
+
})]
|
|
422
|
+
})]
|
|
423
|
+
});
|
|
424
|
+
return jsxs("div", {
|
|
425
|
+
ref: ref,
|
|
426
|
+
className: "w-full",
|
|
427
|
+
children: [label && jsxs("label", {
|
|
428
|
+
id: labelId,
|
|
429
|
+
htmlFor: fieldId,
|
|
430
|
+
className: "block text-sm font-medium mb-1",
|
|
431
|
+
children: [label, required && jsx("span", {
|
|
432
|
+
className: "text-error-500 ml-1",
|
|
433
|
+
children: "*"
|
|
434
|
+
})]
|
|
435
|
+
}), jsxs("div", {
|
|
436
|
+
ref: datePickerRef,
|
|
437
|
+
className: "relative",
|
|
438
|
+
children: [jsxs("button", _objectSpread2(_objectSpread2({
|
|
439
|
+
type: "button",
|
|
440
|
+
id: fieldId,
|
|
441
|
+
"aria-labelledby": label ? "".concat(labelId, " ").concat(valueId) : valueId,
|
|
442
|
+
"aria-describedby": error ? errorId : helperText ? helperId : undefined,
|
|
443
|
+
"aria-invalid": error ? true : undefined,
|
|
444
|
+
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", {
|
|
445
|
+
"border-error-500 focus:ring-error-500": error,
|
|
446
|
+
"text-primary-500": !localStart || !localEnd,
|
|
447
|
+
"pr-16": showClearButton
|
|
448
|
+
}, className),
|
|
449
|
+
onClick: function onClick() {
|
|
450
|
+
// Prevent closing the popup when a start is selected but end isn't — force user to finish range
|
|
451
|
+
if (isOpen && localStart && !localEnd) return;
|
|
452
|
+
setIsOpen(function (v) {
|
|
453
|
+
return !v;
|
|
454
|
+
});
|
|
455
|
+
},
|
|
456
|
+
disabled: disabled,
|
|
457
|
+
"aria-haspopup": "dialog",
|
|
458
|
+
"aria-expanded": isOpen
|
|
459
|
+
}, props), {}, {
|
|
460
|
+
children: [jsx("span", {
|
|
461
|
+
className: "truncate",
|
|
462
|
+
id: valueId,
|
|
463
|
+
children: formatDisplay()
|
|
464
|
+
}), jsx(Calendar, {
|
|
465
|
+
className: "h-4 w-4 shrink-0 text-primary-400",
|
|
466
|
+
"aria-hidden": "true"
|
|
467
|
+
})]
|
|
468
|
+
})), showClearButton && jsx("button", {
|
|
469
|
+
type: "button",
|
|
470
|
+
onClick: function onClick(e) {
|
|
471
|
+
e.stopPropagation();
|
|
472
|
+
clearSelection();
|
|
473
|
+
},
|
|
474
|
+
"aria-label": "Clear range",
|
|
475
|
+
className: "absolute right-8 top-1/2 -translate-y-1/2 rounded p-1 focus:outline-none focus:ring-2 focus:ring-primary-500",
|
|
476
|
+
children: jsx(X, {
|
|
477
|
+
className: "h-4 w-4 text-primary-400",
|
|
478
|
+
"aria-hidden": "true"
|
|
479
|
+
})
|
|
480
|
+
}), isOpen && (portal ? /*#__PURE__*/ReactDOM.createPortal(calendarPopup, document.body) : calendarPopup)]
|
|
481
|
+
}), error ? jsx("p", {
|
|
482
|
+
id: errorId,
|
|
483
|
+
className: "mt-1 text-sm text-error-500",
|
|
484
|
+
children: error
|
|
485
|
+
}) : helperText ? jsx("p", {
|
|
486
|
+
id: helperId,
|
|
487
|
+
className: "mt-1 text-sm text-muted",
|
|
488
|
+
children: helperText
|
|
489
|
+
}) : null]
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
DateRangePicker.displayName = "DateRangePicker";
|
|
493
|
+
|
|
494
|
+
export { DateRangePicker as default };
|