@dmsi/wedgekit-react 0.0.550 → 0.0.552
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/dist/{chunk-U3QGZAVS.js → chunk-JADOJNBI.js} +4 -4
- package/dist/{chunk-N2KPADIL.js → chunk-WNGFRQ4Y.js} +7 -7
- package/dist/{chunk-ZVY3TLXL.js → chunk-ZIPJMN2E.js} +4 -4
- package/dist/components/Alert.js +2 -2
- package/dist/components/CalendarRange.js +10 -10
- package/dist/components/DataGrid/ColumnSelectorHeaderCell/ColumnSelectorMenuOption.js +10 -10
- package/dist/components/DataGrid/ColumnSelectorHeaderCell/index.js +10 -10
- package/dist/components/DataGrid/PinnedColumns.js +10 -10
- package/dist/components/DataGrid/TableBody/LoadingCell.js +10 -10
- package/dist/components/DataGrid/TableBody/TableBodyRow.js +10 -10
- package/dist/components/DataGrid/TableBody/index.js +10 -10
- package/dist/components/DataGrid/index.js +10 -10
- package/dist/components/DataGrid/utils.js +10 -10
- package/dist/components/DateInput.js +10 -10
- package/dist/components/DateRangeInput.js +10 -10
- package/dist/components/FilterGroup.js +5 -5
- package/dist/components/MobileDataGrid/ColumnSelector/index.js +10 -10
- package/dist/components/MobileDataGrid/MobileDataGridHeader.js +10 -10
- package/dist/components/MobileDataGrid/RowDetailModalProvider/index.js +5 -5
- package/dist/components/MobileDataGrid/index.js +10 -10
- package/dist/components/Modal.js +4 -4
- package/dist/components/ModalButtons.js +2 -2
- package/dist/components/ModalHeader.js +2 -2
- package/dist/components/NavigationTab.js +2 -2
- package/dist/components/NavigationTabs.js +2 -2
- package/dist/components/NestedMenu.js +3 -3
- package/dist/components/Notification.js +3 -3
- package/dist/components/OptionPill.js +2 -2
- package/dist/components/PDFViewer/DownloadIcon.js +2 -2
- package/dist/components/PDFViewer/PDFNavigation.js +2 -2
- package/dist/components/PDFViewer/index.js +6 -6
- package/dist/components/ProductImagePreview/index.js +1 -1
- package/dist/components/Stepper.js +3 -3
- package/dist/components/Toast.js +3 -3
- package/dist/components/Upload.js +3 -3
- package/dist/components/index.js +16 -16
- package/package.json +8 -9
- package/src/brand.css +0 -125
- package/src/classNames.ts +0 -174
- package/src/components/AccessChangerTabItem.tsx +0 -71
- package/src/components/Accordion.tsx +0 -108
- package/src/components/Alert.tsx +0 -81
- package/src/components/Breadcrumbs.tsx +0 -142
- package/src/components/Button.tsx +0 -216
- package/src/components/CalendarRange.tsx +0 -628
- package/src/components/Caption.tsx +0 -144
- package/src/components/Card.tsx +0 -88
- package/src/components/Checkbox.tsx +0 -206
- package/src/components/CompactImagesPreview.tsx +0 -135
- package/src/components/ContentTab.tsx +0 -84
- package/src/components/ContentTabs.tsx +0 -136
- package/src/components/DMSiLogo.tsx +0 -33
- package/src/components/DataGrid/ColumnSelectorHeaderCell/ColumnSelectorMenuOption.tsx +0 -35
- package/src/components/DataGrid/ColumnSelectorHeaderCell/index.tsx +0 -74
- package/src/components/DataGrid/PinnedColumns.tsx +0 -183
- package/src/components/DataGrid/TableBody/LoadingCell.tsx +0 -44
- package/src/components/DataGrid/TableBody/TableBodyRow.tsx +0 -157
- package/src/components/DataGrid/TableBody/index.tsx +0 -185
- package/src/components/DataGrid/index.tsx +0 -756
- package/src/components/DataGrid/types.ts +0 -98
- package/src/components/DataGrid/utils.tsx +0 -15
- package/src/components/DataGridCell.tsx +0 -526
- package/src/components/DataTable.tsx +0 -881
- package/src/components/DateInput.tsx +0 -306
- package/src/components/DateRangeInput.tsx +0 -758
- package/src/components/DebugJson.tsx +0 -28
- package/src/components/Display.tsx +0 -66
- package/src/components/EditingContext.tsx +0 -43
- package/src/components/EmptyCartIcon.tsx +0 -18
- package/src/components/FilterGroup.tsx +0 -264
- package/src/components/FullViewportBox.tsx +0 -19
- package/src/components/Grid.tsx +0 -97
- package/src/components/Heading.tsx +0 -72
- package/src/components/HorizontalDivider.tsx +0 -22
- package/src/components/Icon.tsx +0 -39
- package/src/components/ImagePlaceholder.tsx +0 -22
- package/src/components/Input.tsx +0 -609
- package/src/components/InputGroup.tsx +0 -59
- package/src/components/Label.tsx +0 -46
- package/src/components/Link.tsx +0 -117
- package/src/components/List.tsx +0 -18
- package/src/components/ListGroup.tsx +0 -82
- package/src/components/LiveChatComponent.tsx +0 -56
- package/src/components/LoadingScrim.tsx +0 -33
- package/src/components/LogoAgilityTopBar.tsx +0 -54
- package/src/components/LogoDMSiTopBar.tsx +0 -33
- package/src/components/LogoMillworkTopBar.tsx +0 -119
- package/src/components/MainBar.tsx +0 -91
- package/src/components/MaxViewportBox.tsx +0 -19
- package/src/components/Menu.tsx +0 -316
- package/src/components/MenuOption.tsx +0 -330
- package/src/components/MobileDataGrid/ColumnList.tsx +0 -66
- package/src/components/MobileDataGrid/ColumnSelector/index.tsx +0 -97
- package/src/components/MobileDataGrid/GridContextProvider/GridContext.tsx +0 -25
- package/src/components/MobileDataGrid/GridContextProvider/index.tsx +0 -132
- package/src/components/MobileDataGrid/GridContextProvider/useGridContext.ts +0 -10
- package/src/components/MobileDataGrid/MobileDataGridCard/MobileDataGridColumn.tsx +0 -27
- package/src/components/MobileDataGrid/MobileDataGridCard/index.tsx +0 -138
- package/src/components/MobileDataGrid/MobileDataGridHeader.tsx +0 -81
- package/src/components/MobileDataGrid/RowDetailModalProvider/ModalContent.tsx +0 -42
- package/src/components/MobileDataGrid/RowDetailModalProvider/index.tsx +0 -68
- package/src/components/MobileDataGrid/dataGridReducer.ts +0 -55
- package/src/components/MobileDataGrid/index.tsx +0 -92
- package/src/components/MobileDataGrid/types.ts +0 -4
- package/src/components/Modal.tsx +0 -312
- package/src/components/ModalButtons.tsx +0 -62
- package/src/components/ModalContent.tsx +0 -31
- package/src/components/ModalHeader.tsx +0 -78
- package/src/components/ModalScrim.tsx +0 -42
- package/src/components/NavigationTab.tsx +0 -95
- package/src/components/NavigationTabs.tsx +0 -70
- package/src/components/NestedMenu.tsx +0 -131
- package/src/components/Notification.tsx +0 -128
- package/src/components/OptionPill.tsx +0 -139
- package/src/components/OrderCheckIcon.tsx +0 -19
- package/src/components/PDFViewer/DownloadIcon.tsx +0 -25
- package/src/components/PDFViewer/PDFElement.tsx +0 -90
- package/src/components/PDFViewer/PDFNavigation.tsx +0 -68
- package/src/components/PDFViewer/PDFPage.tsx +0 -34
- package/src/components/PDFViewer/index.tsx +0 -128
- package/src/components/Pagination.tsx +0 -182
- package/src/components/Paragraph.tsx +0 -55
- package/src/components/Password.tsx +0 -62
- package/src/components/ProductImagePreview/CarouselPagination.tsx +0 -54
- package/src/components/ProductImagePreview/MobileImageCarousel.tsx +0 -226
- package/src/components/ProductImagePreview/ProductPrimaryImage.tsx +0 -219
- package/src/components/ProductImagePreview/Thumbnail.tsx +0 -55
- package/src/components/ProductImagePreview/ZoomWindow.tsx +0 -136
- package/src/components/ProductImagePreview/index.tsx +0 -182
- package/src/components/ProductImagePreview/useProductImagePreview.ts +0 -211
- package/src/components/ProjectBar.tsx +0 -82
- package/src/components/Radio.tsx +0 -146
- package/src/components/Search.tsx +0 -152
- package/src/components/SearchResultImage/index.tsx +0 -39
- package/src/components/Select.tsx +0 -114
- package/src/components/SideMenu.tsx +0 -30
- package/src/components/SideMenuGroup.tsx +0 -95
- package/src/components/SideMenuItem.tsx +0 -109
- package/src/components/SimpleTable.tsx +0 -77
- package/src/components/SkeletonParagraph.tsx +0 -31
- package/src/components/Spinner.tsx +0 -32
- package/src/components/Stack.tsx +0 -347
- package/src/components/StatusPill.tsx +0 -59
- package/src/components/Stepper.tsx +0 -128
- package/src/components/Subheader.tsx +0 -50
- package/src/components/Surface.tsx +0 -37
- package/src/components/Swatch.tsx +0 -1341
- package/src/components/Textarea.tsx +0 -102
- package/src/components/Theme.tsx +0 -27
- package/src/components/Time.tsx +0 -460
- package/src/components/Toast.tsx +0 -268
- package/src/components/Tooltip.tsx +0 -159
- package/src/components/TopBar.tsx +0 -139
- package/src/components/Upload.tsx +0 -107
- package/src/components/WorldpayIframe.tsx +0 -7
- package/src/components/index.ts +0 -34
- package/src/components/useMenuSystem.tsx +0 -456
- package/src/components/useMounted.tsx +0 -14
- package/src/darkmode.css +0 -278
- package/src/fonts.css +0 -23
- package/src/hooks/index.ts +0 -4
- package/src/hooks/useInfiniteScroll.tsx +0 -40
- package/src/hooks/useKeydown.ts +0 -42
- package/src/hooks/useMatchesMedia.ts +0 -18
- package/src/hooks/useTableLayout.ts +0 -106
- package/src/index.css +0 -800
- package/src/index.tsx +0 -5
- package/src/types.ts +0 -150
- package/src/utils/date.ts +0 -236
- package/src/utils/formatting.tsx +0 -81
- package/src/utils/index.ts +0 -4
- package/src/utils/mergeObjectArrays.ts +0 -18
- package/src/utils.ts +0 -24
- package/dist/{chunk-7FQ7PGUF.js → chunk-7COWXCPA.js} +3 -3
- package/dist/{chunk-NKCFYM7A.js → chunk-7SFFUICM.js} +3 -3
- package/dist/{chunk-25RZP3VR.js → chunk-AKJUBFJK.js} +3 -3
- package/dist/{chunk-TAPYQBQU.js → chunk-CMMQTIVM.js} +3 -3
- package/dist/{chunk-GYEXSNFP.js → chunk-FWCVZWE6.js} +3 -3
- package/dist/{chunk-MV6W7OMC.js → chunk-QMMPHXVE.js} +3 -3
- package/dist/{chunk-GG5OZTI5.js → chunk-XRE52QTN.js} +3 -3
|
@@ -1,758 +0,0 @@
|
|
|
1
|
-
import { useRef, useEffect, useState, useLayoutEffect } from "react";
|
|
2
|
-
import { createPortal } from "react-dom";
|
|
3
|
-
import { InputBaseProps, InputBase } from "./Input";
|
|
4
|
-
import { CalendarRange, CalendarRangeProps } from "./CalendarRange";
|
|
5
|
-
import { Icon } from "./Icon";
|
|
6
|
-
import { findDocumentRoot } from "../utils";
|
|
7
|
-
import {
|
|
8
|
-
parseInputDate,
|
|
9
|
-
isValidDate,
|
|
10
|
-
formatInputValue,
|
|
11
|
-
calculateCursorPosition,
|
|
12
|
-
formatDate,
|
|
13
|
-
isValidDateRangeOrder,
|
|
14
|
-
} from "../utils/date";
|
|
15
|
-
|
|
16
|
-
type DateRangeInputProps = Omit<InputBaseProps, "id"> & {
|
|
17
|
-
id?: string;
|
|
18
|
-
testid?: string;
|
|
19
|
-
/**
|
|
20
|
-
* Value in the format "YYYY-MM-DD|YYYY-MM-DD" or empty string
|
|
21
|
-
*/
|
|
22
|
-
value: string;
|
|
23
|
-
/**
|
|
24
|
-
* Called when the range changes. Value is in the format "YYYY-MM-DD|YYYY-MM-DD"
|
|
25
|
-
*/
|
|
26
|
-
onChange: (value: string) => void;
|
|
27
|
-
/**
|
|
28
|
-
* Optional placeholder text
|
|
29
|
-
*/
|
|
30
|
-
placeholder?: string;
|
|
31
|
-
/**
|
|
32
|
-
* Optional disabled state
|
|
33
|
-
*/
|
|
34
|
-
disabled?: boolean;
|
|
35
|
-
/**
|
|
36
|
-
* If true, shows single calendar instead of double calendar
|
|
37
|
-
*/
|
|
38
|
-
single?: boolean;
|
|
39
|
-
/**
|
|
40
|
-
* If true, disables range selection (single date only)
|
|
41
|
-
*/
|
|
42
|
-
disableRange?: boolean;
|
|
43
|
-
readOnly?: boolean; // If true, input is read-only and cannot be focused
|
|
44
|
-
label?: string; // Optional label for the input
|
|
45
|
-
} & Pick<CalendarRangeProps, "isDateAvailable" | "onPendingFromChange">;
|
|
46
|
-
|
|
47
|
-
export const DateRangeInput = ({
|
|
48
|
-
id,
|
|
49
|
-
testid,
|
|
50
|
-
value,
|
|
51
|
-
onChange,
|
|
52
|
-
placeholder = "MM/DD/YYYY - MM/DD/YYYY",
|
|
53
|
-
disabled,
|
|
54
|
-
readOnly = false,
|
|
55
|
-
single = false,
|
|
56
|
-
disableRange = false,
|
|
57
|
-
label,
|
|
58
|
-
isDateAvailable,
|
|
59
|
-
onPendingFromChange,
|
|
60
|
-
...props
|
|
61
|
-
}: DateRangeInputProps) => {
|
|
62
|
-
const [visible, setVisible] = useState(false);
|
|
63
|
-
const [inputValue, setInputValue] = useState("");
|
|
64
|
-
const [isTyping, setIsTyping] = useState(false);
|
|
65
|
-
const popoverRef = useRef<HTMLDivElement | null>(null);
|
|
66
|
-
const rootRef = useRef<HTMLDivElement | null>(null);
|
|
67
|
-
const triggerRef = useRef<HTMLInputElement | null>(null);
|
|
68
|
-
const [calendarPosition, setCalendarPosition] = useState({
|
|
69
|
-
top: 0,
|
|
70
|
-
left: 0,
|
|
71
|
-
width: 0,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const [from, to] = value.split("|");
|
|
75
|
-
|
|
76
|
-
// Update input value when external value changes (but not when typing)
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (!isTyping) {
|
|
79
|
-
// Only update inputValue if the value is valid
|
|
80
|
-
const displayValue = formatDisplayValue(from, to);
|
|
81
|
-
if (displayValue) {
|
|
82
|
-
setInputValue(displayValue);
|
|
83
|
-
}
|
|
84
|
-
// If displayValue is empty, keep previous inputValue unchanged
|
|
85
|
-
}
|
|
86
|
-
}, [from, to, isTyping, disableRange]);
|
|
87
|
-
|
|
88
|
-
useLayoutEffect(() => {
|
|
89
|
-
if (visible) {
|
|
90
|
-
updatePosition();
|
|
91
|
-
}
|
|
92
|
-
}, [visible]);
|
|
93
|
-
|
|
94
|
-
const updatePosition = () => {
|
|
95
|
-
if (rootRef.current) {
|
|
96
|
-
const rect = rootRef.current.getBoundingClientRect();
|
|
97
|
-
setCalendarPosition({
|
|
98
|
-
top: rect.bottom + window.scrollY,
|
|
99
|
-
left: rect.left + window.scrollX,
|
|
100
|
-
width: rect.width,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
updatePosition();
|
|
107
|
-
|
|
108
|
-
const resizeObserver = new ResizeObserver(updatePosition);
|
|
109
|
-
if (triggerRef.current) {
|
|
110
|
-
resizeObserver.observe(triggerRef.current);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
window.addEventListener("scroll", updatePosition);
|
|
114
|
-
|
|
115
|
-
return () => {
|
|
116
|
-
resizeObserver.disconnect();
|
|
117
|
-
window.removeEventListener("scroll", updatePosition);
|
|
118
|
-
};
|
|
119
|
-
}, []);
|
|
120
|
-
|
|
121
|
-
useEffect(() => {
|
|
122
|
-
const handleKeyDown = (event: KeyboardEvent) => {
|
|
123
|
-
if (event.key === "Escape" && popoverRef.current) {
|
|
124
|
-
setVisible(false);
|
|
125
|
-
triggerRef.current?.blur();
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
129
|
-
return () => {
|
|
130
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
131
|
-
};
|
|
132
|
-
}, []);
|
|
133
|
-
|
|
134
|
-
useEffect(() => {
|
|
135
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
136
|
-
if (
|
|
137
|
-
popoverRef.current &&
|
|
138
|
-
!popoverRef.current.contains(event.target as HTMLElement) &&
|
|
139
|
-
triggerRef.current &&
|
|
140
|
-
!triggerRef.current.contains(event.target as HTMLElement)
|
|
141
|
-
) {
|
|
142
|
-
setVisible(false);
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
146
|
-
return () => {
|
|
147
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
148
|
-
};
|
|
149
|
-
}, []);
|
|
150
|
-
|
|
151
|
-
function handleRangeChange(fromValue: string, toValue: string) {
|
|
152
|
-
if (disableRange) {
|
|
153
|
-
onChange(`${fromValue}|${fromValue}`);
|
|
154
|
-
} else {
|
|
155
|
-
onChange(`${fromValue}|${toValue}`);
|
|
156
|
-
}
|
|
157
|
-
setVisible(false);
|
|
158
|
-
setIsTyping(false);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const handleFocus = () => {
|
|
162
|
-
if (readOnly) return;
|
|
163
|
-
setVisible(true);
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const handleClick = () => {
|
|
167
|
-
handleFocus();
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
171
|
-
if (readOnly) return;
|
|
172
|
-
|
|
173
|
-
const rawValue = event.target.value;
|
|
174
|
-
const cursorPosition = event.target.selectionStart || 0;
|
|
175
|
-
|
|
176
|
-
if (shouldPreventManualDash(rawValue)) {
|
|
177
|
-
handleManualDashRemoval(rawValue, cursorPosition);
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
setIsTyping(true);
|
|
182
|
-
|
|
183
|
-
const formattedValue = formatInputValue(rawValue);
|
|
184
|
-
const finalValue = shouldAutoInsertDash(formattedValue, rawValue)
|
|
185
|
-
? `${formattedValue} - `
|
|
186
|
-
: formattedValue;
|
|
187
|
-
|
|
188
|
-
setInputValue(finalValue);
|
|
189
|
-
|
|
190
|
-
const newCursorPosition = calculateNewCursorPosition(
|
|
191
|
-
rawValue,
|
|
192
|
-
formattedValue,
|
|
193
|
-
finalValue,
|
|
194
|
-
cursorPosition,
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
requestAnimationFrame(() => {
|
|
198
|
-
setCursorPosition(newCursorPosition);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
updateParentValue(finalValue);
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const shouldPreventManualDash = (value: string): boolean => {
|
|
205
|
-
return !disableRange && value.includes("-") && !value.includes(" - ");
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
const handleManualDashRemoval = (
|
|
209
|
-
rawValue: string,
|
|
210
|
-
cursorPosition: number,
|
|
211
|
-
) => {
|
|
212
|
-
const cleanValue = rawValue.replace(/-/g, "");
|
|
213
|
-
const formattedCleanValue = formatInputValue(cleanValue);
|
|
214
|
-
setInputValue(formattedCleanValue);
|
|
215
|
-
|
|
216
|
-
requestAnimationFrame(() => {
|
|
217
|
-
const newPosition = Math.min(
|
|
218
|
-
cursorPosition - 1,
|
|
219
|
-
formattedCleanValue.length,
|
|
220
|
-
);
|
|
221
|
-
setCursorPosition(newPosition);
|
|
222
|
-
});
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
const shouldAutoInsertDash = (
|
|
226
|
-
formattedValue: string,
|
|
227
|
-
rawValue: string,
|
|
228
|
-
): boolean => {
|
|
229
|
-
if (disableRange || formattedValue.includes(" - ")) {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const completeDate = formattedValue.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
|
|
234
|
-
if (!completeDate || rawValue.length !== formattedValue.length) {
|
|
235
|
-
return false;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Only add dash if user just completed typing the year
|
|
239
|
-
const prevLength = rawValue.length - 1;
|
|
240
|
-
const prevFormatted = formatInputValue(rawValue.slice(0, prevLength));
|
|
241
|
-
|
|
242
|
-
return !prevFormatted.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
const calculateNewCursorPosition = (
|
|
246
|
-
rawValue: string,
|
|
247
|
-
formattedValue: string,
|
|
248
|
-
finalValue: string,
|
|
249
|
-
originalPosition: number,
|
|
250
|
-
): number => {
|
|
251
|
-
if (finalValue !== formattedValue) {
|
|
252
|
-
return finalValue.length; // Place cursor after auto-inserted dash
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return calculateCursorPositionHelper(
|
|
256
|
-
rawValue,
|
|
257
|
-
finalValue,
|
|
258
|
-
originalPosition,
|
|
259
|
-
disableRange,
|
|
260
|
-
);
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
const setCursorPosition = (position: number) => {
|
|
264
|
-
if (triggerRef.current) {
|
|
265
|
-
triggerRef.current.setSelectionRange(position, position);
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
const updateParentValue = (value: string) => {
|
|
270
|
-
if (disableRange) {
|
|
271
|
-
updateSingleDateValue(value);
|
|
272
|
-
} else {
|
|
273
|
-
updateRangeValue(value);
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const updateSingleDateValue = (value: string) => {
|
|
278
|
-
const parsedDate = parseInputDate(value);
|
|
279
|
-
if (parsedDate && isValidDate(parsedDate)) {
|
|
280
|
-
onChange(`${parsedDate}|${parsedDate}`);
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const updateRangeValue = (value: string) => {
|
|
285
|
-
if (value === "") {
|
|
286
|
-
onChange("");
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const rangeMatch = value.match(/^(.+?)\s*-\s*(.+)$/);
|
|
291
|
-
if (rangeMatch) {
|
|
292
|
-
updateCompleteRange(rangeMatch);
|
|
293
|
-
} else {
|
|
294
|
-
updatePartialRange(value);
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
const updateCompleteRange = (rangeMatch: RegExpMatchArray) => {
|
|
299
|
-
const [, fromStr, toStr] = rangeMatch;
|
|
300
|
-
const fromDate = parseInputDate(fromStr.trim());
|
|
301
|
-
const toDate = parseInputDate(toStr.trim());
|
|
302
|
-
|
|
303
|
-
if (fromDate && toDate && isValidDateRange(fromDate, toDate)) {
|
|
304
|
-
onChange(`${fromDate}|${toDate}`);
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
const updatePartialRange = (value: string) => {
|
|
309
|
-
const singleDate = parseInputDate(value);
|
|
310
|
-
if (singleDate && isValidDate(singleDate)) {
|
|
311
|
-
onChange(`${singleDate}|`);
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
const handleBlur = () => {
|
|
316
|
-
setIsTyping(false);
|
|
317
|
-
|
|
318
|
-
// If the input is invalid, revert to the last valid value or empty
|
|
319
|
-
if (disableRange) {
|
|
320
|
-
const parsedDate = parseInputDate(inputValue);
|
|
321
|
-
if (!parsedDate || !isValidDate(parsedDate)) {
|
|
322
|
-
const lastValidValue = formatDisplayValue(from, to);
|
|
323
|
-
setInputValue(lastValidValue || "");
|
|
324
|
-
}
|
|
325
|
-
} else {
|
|
326
|
-
// For range, validate both parts
|
|
327
|
-
const rangeMatch = inputValue.match(/^(.+?)\s*-\s*(.+)$/);
|
|
328
|
-
if (rangeMatch) {
|
|
329
|
-
const [, fromStr, toStr] = rangeMatch;
|
|
330
|
-
const fromDate = parseInputDate(fromStr.trim());
|
|
331
|
-
const toDate = parseInputDate(toStr.trim());
|
|
332
|
-
|
|
333
|
-
// Only accept if both dates are valid AND the range is valid (to >= from)
|
|
334
|
-
if (!fromDate || !toDate || !isValidDateRange(fromDate, toDate)) {
|
|
335
|
-
// Invalid range - clear the input
|
|
336
|
-
setInputValue("");
|
|
337
|
-
onChange("");
|
|
338
|
-
}
|
|
339
|
-
} else {
|
|
340
|
-
// Check if input is just a dash or incomplete second date
|
|
341
|
-
if (inputValue.includes(" - ")) {
|
|
342
|
-
// Has dash but incomplete second date - clear the input
|
|
343
|
-
setInputValue("");
|
|
344
|
-
onChange("");
|
|
345
|
-
} else {
|
|
346
|
-
// Single date in range mode - check if we have a previous valid state
|
|
347
|
-
const singleDate = parseInputDate(inputValue);
|
|
348
|
-
if (!singleDate || !isValidDate(singleDate)) {
|
|
349
|
-
// Invalid single date - clear the input
|
|
350
|
-
setInputValue("");
|
|
351
|
-
onChange("");
|
|
352
|
-
} else {
|
|
353
|
-
// Valid single date but incomplete range - clear the input
|
|
354
|
-
setInputValue("");
|
|
355
|
-
onChange("");
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
363
|
-
// Prevent manual typing of dash in range mode
|
|
364
|
-
if (!disableRange && event.key === "-") {
|
|
365
|
-
event.preventDefault();
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (event.key === "Backspace") {
|
|
370
|
-
const input = event.target as HTMLInputElement;
|
|
371
|
-
const cursorPosition = input.selectionStart || 0;
|
|
372
|
-
const value = input.value;
|
|
373
|
-
|
|
374
|
-
// If cursor is right after a slash, move it before the slash
|
|
375
|
-
if (cursorPosition > 0 && value[cursorPosition - 1] === "/") {
|
|
376
|
-
event.preventDefault();
|
|
377
|
-
const newValue =
|
|
378
|
-
value.slice(0, cursorPosition - 2) + value.slice(cursorPosition);
|
|
379
|
-
const formattedValue = formatInputValue(newValue);
|
|
380
|
-
setInputValue(formattedValue);
|
|
381
|
-
|
|
382
|
-
// Set cursor position after the deletion
|
|
383
|
-
requestAnimationFrame(() => {
|
|
384
|
-
if (triggerRef.current) {
|
|
385
|
-
const newPosition = Math.max(0, cursorPosition - 2);
|
|
386
|
-
triggerRef.current.setSelectionRange(newPosition, newPosition);
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
setIsTyping(true);
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Handle deletion when cursor is on or near the dash separator
|
|
395
|
-
if (!disableRange && value.includes(" - ")) {
|
|
396
|
-
const dashIndex = value.indexOf(" - ");
|
|
397
|
-
const dashStart = dashIndex;
|
|
398
|
-
const dashEnd = dashIndex + 3; // " - " is 3 characters
|
|
399
|
-
|
|
400
|
-
// If cursor is within the dash area (including spaces)
|
|
401
|
-
if (cursorPosition >= dashStart && cursorPosition <= dashEnd) {
|
|
402
|
-
event.preventDefault();
|
|
403
|
-
|
|
404
|
-
// Remove the entire range and the last digit of the year from the first date
|
|
405
|
-
const beforeDash = value.slice(0, dashStart).trim();
|
|
406
|
-
|
|
407
|
-
// Extract the year part and remove the last digit
|
|
408
|
-
const yearMatch = beforeDash.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
|
|
409
|
-
if (yearMatch) {
|
|
410
|
-
const [, month, day, year] = yearMatch;
|
|
411
|
-
const truncatedYear = year.slice(0, -1); // Remove last digit
|
|
412
|
-
const newValue = `${month}/${day}/${truncatedYear}`;
|
|
413
|
-
const formattedValue = formatInputValue(newValue);
|
|
414
|
-
setInputValue(formattedValue);
|
|
415
|
-
|
|
416
|
-
// Update the value - no valid date yet since year is incomplete
|
|
417
|
-
onChange("");
|
|
418
|
-
|
|
419
|
-
// Position cursor at the end
|
|
420
|
-
requestAnimationFrame(() => {
|
|
421
|
-
if (triggerRef.current) {
|
|
422
|
-
triggerRef.current.setSelectionRange(
|
|
423
|
-
formattedValue.length,
|
|
424
|
-
formattedValue.length,
|
|
425
|
-
);
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
} else {
|
|
429
|
-
// Fallback to original behavior
|
|
430
|
-
const formattedValue = formatInputValue(beforeDash);
|
|
431
|
-
setInputValue(formattedValue);
|
|
432
|
-
|
|
433
|
-
const singleDate = parseInputDate(beforeDash);
|
|
434
|
-
if (singleDate && isValidDate(singleDate)) {
|
|
435
|
-
onChange(`${singleDate}|`);
|
|
436
|
-
} else {
|
|
437
|
-
onChange("");
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
requestAnimationFrame(() => {
|
|
441
|
-
if (triggerRef.current) {
|
|
442
|
-
const newPosition = formattedValue.length;
|
|
443
|
-
triggerRef.current.setSelectionRange(newPosition, newPosition);
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
setIsTyping(true);
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// If cursor is right after the dash, remove the second date
|
|
453
|
-
if (cursorPosition === dashEnd) {
|
|
454
|
-
event.preventDefault();
|
|
455
|
-
|
|
456
|
-
const beforeDash = value.slice(0, dashStart).trim();
|
|
457
|
-
const newValue = `${beforeDash} - `;
|
|
458
|
-
setInputValue(newValue);
|
|
459
|
-
|
|
460
|
-
// Update the value to only have the first date as partial range
|
|
461
|
-
const singleDate = parseInputDate(beforeDash);
|
|
462
|
-
if (singleDate && isValidDate(singleDate)) {
|
|
463
|
-
onChange(`${singleDate}|`);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// Position cursor after the dash
|
|
467
|
-
requestAnimationFrame(() => {
|
|
468
|
-
if (triggerRef.current) {
|
|
469
|
-
triggerRef.current.setSelectionRange(
|
|
470
|
-
newValue.length,
|
|
471
|
-
newValue.length,
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
setIsTyping(true);
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (event.key === "Delete") {
|
|
483
|
-
const input = event.target as HTMLInputElement;
|
|
484
|
-
const cursorPosition = input.selectionStart || 0;
|
|
485
|
-
const value = input.value;
|
|
486
|
-
|
|
487
|
-
// Handle deletion when cursor is on or near the dash separator
|
|
488
|
-
if (!disableRange && value.includes(" - ")) {
|
|
489
|
-
const dashIndex = value.indexOf(" - ");
|
|
490
|
-
const dashStart = dashIndex;
|
|
491
|
-
const dashEnd = dashIndex + 3; // " - " is 3 characters
|
|
492
|
-
|
|
493
|
-
// If cursor is within the dash area or right before it
|
|
494
|
-
if (cursorPosition >= dashStart && cursorPosition <= dashEnd) {
|
|
495
|
-
event.preventDefault();
|
|
496
|
-
|
|
497
|
-
// Remove the dash and second date, and the last digit of the year from the first date
|
|
498
|
-
const beforeDash = value.slice(0, dashStart).trim();
|
|
499
|
-
|
|
500
|
-
// Extract the year part and remove the last digit
|
|
501
|
-
const yearMatch = beforeDash.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
|
|
502
|
-
if (yearMatch) {
|
|
503
|
-
const [, month, day, year] = yearMatch;
|
|
504
|
-
const truncatedYear = year.slice(0, -1); // Remove last digit
|
|
505
|
-
const newValue = `${month}/${day}/${truncatedYear}`;
|
|
506
|
-
const formattedValue = formatInputValue(newValue);
|
|
507
|
-
setInputValue(formattedValue);
|
|
508
|
-
|
|
509
|
-
// Update the value - no valid date yet since year is incomplete
|
|
510
|
-
onChange("");
|
|
511
|
-
|
|
512
|
-
// Position cursor at the end
|
|
513
|
-
requestAnimationFrame(() => {
|
|
514
|
-
if (triggerRef.current) {
|
|
515
|
-
triggerRef.current.setSelectionRange(
|
|
516
|
-
formattedValue.length,
|
|
517
|
-
formattedValue.length,
|
|
518
|
-
);
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
} else {
|
|
522
|
-
// Fallback to original behavior
|
|
523
|
-
const formattedValue = formatInputValue(beforeDash);
|
|
524
|
-
setInputValue(formattedValue);
|
|
525
|
-
|
|
526
|
-
const singleDate = parseInputDate(beforeDash);
|
|
527
|
-
if (singleDate && isValidDate(singleDate)) {
|
|
528
|
-
onChange(`${singleDate}|`);
|
|
529
|
-
} else {
|
|
530
|
-
onChange("");
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
requestAnimationFrame(() => {
|
|
534
|
-
if (triggerRef.current) {
|
|
535
|
-
const newPosition = formattedValue.length;
|
|
536
|
-
triggerRef.current.setSelectionRange(newPosition, newPosition);
|
|
537
|
-
}
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
setIsTyping(true);
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
if (event.key === "Enter") {
|
|
548
|
-
if (disableRange) {
|
|
549
|
-
const parsedDate = parseInputDate(inputValue);
|
|
550
|
-
if (parsedDate && isValidDate(parsedDate)) {
|
|
551
|
-
onChange(`${parsedDate}|${parsedDate}`);
|
|
552
|
-
setVisible(false);
|
|
553
|
-
setIsTyping(false);
|
|
554
|
-
}
|
|
555
|
-
} else {
|
|
556
|
-
// In range mode, only accept complete and valid ranges
|
|
557
|
-
const rangeMatch = inputValue.match(/^(.+?)\s*-\s*(.+)$/);
|
|
558
|
-
if (rangeMatch) {
|
|
559
|
-
const [, fromStr, toStr] = rangeMatch;
|
|
560
|
-
const fromDate = parseInputDate(fromStr.trim());
|
|
561
|
-
const toDate = parseInputDate(toStr.trim());
|
|
562
|
-
|
|
563
|
-
if (fromDate && toDate && isValidDateRange(fromDate, toDate)) {
|
|
564
|
-
onChange(`${fromDate}|${toDate}`);
|
|
565
|
-
setVisible(false);
|
|
566
|
-
setIsTyping(false);
|
|
567
|
-
} else {
|
|
568
|
-
// Invalid range - clear the input
|
|
569
|
-
setInputValue("");
|
|
570
|
-
onChange("");
|
|
571
|
-
setVisible(false);
|
|
572
|
-
setIsTyping(false);
|
|
573
|
-
}
|
|
574
|
-
} else {
|
|
575
|
-
// Single date in range mode - clear the input
|
|
576
|
-
setInputValue("");
|
|
577
|
-
onChange("");
|
|
578
|
-
setVisible(false);
|
|
579
|
-
setIsTyping(false);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
return (
|
|
586
|
-
<>
|
|
587
|
-
<InputBase
|
|
588
|
-
id={id}
|
|
589
|
-
testid={testid}
|
|
590
|
-
ref={(el) => {
|
|
591
|
-
triggerRef.current = el;
|
|
592
|
-
}}
|
|
593
|
-
{...props}
|
|
594
|
-
wrapperRef={rootRef}
|
|
595
|
-
value={inputValue}
|
|
596
|
-
placeholder={disableRange ? "MM/DD/YYYY" : placeholder}
|
|
597
|
-
disabled={disabled}
|
|
598
|
-
readOnly={readOnly}
|
|
599
|
-
after={<Icon name="calendar_month" />}
|
|
600
|
-
onFocus={handleFocus}
|
|
601
|
-
onClick={handleClick}
|
|
602
|
-
onChange={handleInputChange}
|
|
603
|
-
onBlur={handleBlur}
|
|
604
|
-
onKeyDown={handleKeyDown}
|
|
605
|
-
label={label}
|
|
606
|
-
secondaryIconColor
|
|
607
|
-
/>
|
|
608
|
-
{visible &&
|
|
609
|
-
!readOnly &&
|
|
610
|
-
createPortal(
|
|
611
|
-
<div
|
|
612
|
-
ref={(el) => {
|
|
613
|
-
popoverRef.current = el;
|
|
614
|
-
}}
|
|
615
|
-
className="absolute z-40"
|
|
616
|
-
style={{
|
|
617
|
-
top: `${calendarPosition.top + 4}px`,
|
|
618
|
-
left: `${calendarPosition.left}px`,
|
|
619
|
-
minWidth: `${calendarPosition.width}px`,
|
|
620
|
-
}}
|
|
621
|
-
>
|
|
622
|
-
<CalendarRange
|
|
623
|
-
id={id ? `${id}-calendar` : undefined}
|
|
624
|
-
testid={testid ? `${testid}-calendar` : undefined}
|
|
625
|
-
from={from}
|
|
626
|
-
to={to}
|
|
627
|
-
onChange={handleRangeChange}
|
|
628
|
-
cardStyle
|
|
629
|
-
mode={single ? "single" : "double"}
|
|
630
|
-
disableRange={disableRange}
|
|
631
|
-
isDateAvailable={isDateAvailable}
|
|
632
|
-
onPendingFromChange={onPendingFromChange}
|
|
633
|
-
/>
|
|
634
|
-
</div>,
|
|
635
|
-
findDocumentRoot(popoverRef.current),
|
|
636
|
-
)}
|
|
637
|
-
</>
|
|
638
|
-
);
|
|
639
|
-
|
|
640
|
-
function formatInputValue(value: string): string {
|
|
641
|
-
return formatInputValueHelper(value, disableRange);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
function formatDisplayValue(from?: string, to?: string) {
|
|
645
|
-
return formatDisplayValueHelper(from, to, disableRange);
|
|
646
|
-
}
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
DateRangeInput.displayName = "DateRangeInput";
|
|
650
|
-
|
|
651
|
-
function isValidDateRange(fromDate: string, toDate: string): boolean {
|
|
652
|
-
return isValidDateRangeOrder(fromDate, toDate);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
function formatInputValueHelper(value: string, disableRange: boolean): string {
|
|
656
|
-
if (disableRange) {
|
|
657
|
-
return formatInputValue(value);
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
if (value.includes(" - ")) {
|
|
661
|
-
const [from, to] = value.split(" - ");
|
|
662
|
-
const fromFormatted = formatInputValue(from);
|
|
663
|
-
const toFormatted = formatInputValue(to || "");
|
|
664
|
-
return `${fromFormatted} - ${toFormatted}`;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
const cleanValue = value.replace(/-/g, "");
|
|
668
|
-
return formatInputValue(cleanValue);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
function calculateCursorPositionHelper(
|
|
672
|
-
originalValue: string,
|
|
673
|
-
formattedValue: string,
|
|
674
|
-
originalPosition: number,
|
|
675
|
-
disableRange: boolean,
|
|
676
|
-
): number {
|
|
677
|
-
// Handle range input cursor positioning
|
|
678
|
-
if (!disableRange && formattedValue.includes(" - ")) {
|
|
679
|
-
const dashPosition = formattedValue.indexOf(" - ");
|
|
680
|
-
const originalDashPosition = originalValue.indexOf("-");
|
|
681
|
-
|
|
682
|
-
// If cursor was after the dash in original, maintain relative position
|
|
683
|
-
if (
|
|
684
|
-
originalDashPosition !== -1 &&
|
|
685
|
-
originalPosition > originalDashPosition
|
|
686
|
-
) {
|
|
687
|
-
const afterDashDigits = originalValue
|
|
688
|
-
.slice(originalDashPosition + 1)
|
|
689
|
-
.replace(/\D/g, "").length;
|
|
690
|
-
const formattedAfterDash = formattedValue.slice(dashPosition + 3);
|
|
691
|
-
|
|
692
|
-
let newPosition = dashPosition + 3;
|
|
693
|
-
let digitCount = 0;
|
|
694
|
-
|
|
695
|
-
for (let i = 0; i < formattedAfterDash.length; i++) {
|
|
696
|
-
if (/\d/.test(formattedAfterDash[i])) {
|
|
697
|
-
digitCount++;
|
|
698
|
-
if (digitCount >= afterDashDigits) {
|
|
699
|
-
// Check if we're at a position where a slash was auto-inserted in the second date
|
|
700
|
-
// If the next character is a slash, place cursor after it
|
|
701
|
-
if (
|
|
702
|
-
i + 1 < formattedAfterDash.length &&
|
|
703
|
-
formattedAfterDash[i + 1] === "/"
|
|
704
|
-
) {
|
|
705
|
-
newPosition = dashPosition + 3 + i + 2;
|
|
706
|
-
} else {
|
|
707
|
-
newPosition = dashPosition + 3 + i + 1;
|
|
708
|
-
}
|
|
709
|
-
break;
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
if (digitCount < afterDashDigits) {
|
|
713
|
-
newPosition = dashPosition + 3 + i + 1;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
return Math.min(newPosition, formattedValue.length);
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
return calculateCursorPosition(
|
|
722
|
-
originalValue,
|
|
723
|
-
formattedValue,
|
|
724
|
-
originalPosition,
|
|
725
|
-
);
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
function formatDisplayValueHelper(
|
|
729
|
-
from?: string,
|
|
730
|
-
to?: string,
|
|
731
|
-
disableRange?: boolean,
|
|
732
|
-
) {
|
|
733
|
-
if (!from && !to) {
|
|
734
|
-
return "";
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
// Validate dates before displaying
|
|
738
|
-
const fromValid = from ? isValidDate(from) : false;
|
|
739
|
-
const toValid = to ? isValidDate(to) : false;
|
|
740
|
-
|
|
741
|
-
if (disableRange) {
|
|
742
|
-
return fromValid && from ? formatDate(from) : "";
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// Return formatted display if we have a complete, valid range
|
|
746
|
-
if (fromValid && toValid && from && to && isValidDateRange(from, to)) {
|
|
747
|
-
return `${formatDate(from)} - ${formatDate(to)}`;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
// If we have a valid from date but no to date, show the from date only
|
|
751
|
-
// This preserves the previous valid state during editing
|
|
752
|
-
if (fromValid && !to && from) {
|
|
753
|
-
return `${formatDate(from)} - `;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
// Return empty string if no valid dates or invalid range
|
|
757
|
-
return "";
|
|
758
|
-
}
|