@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.
Files changed (180) hide show
  1. package/dist/{chunk-U3QGZAVS.js → chunk-JADOJNBI.js} +4 -4
  2. package/dist/{chunk-N2KPADIL.js → chunk-WNGFRQ4Y.js} +7 -7
  3. package/dist/{chunk-ZVY3TLXL.js → chunk-ZIPJMN2E.js} +4 -4
  4. package/dist/components/Alert.js +2 -2
  5. package/dist/components/CalendarRange.js +10 -10
  6. package/dist/components/DataGrid/ColumnSelectorHeaderCell/ColumnSelectorMenuOption.js +10 -10
  7. package/dist/components/DataGrid/ColumnSelectorHeaderCell/index.js +10 -10
  8. package/dist/components/DataGrid/PinnedColumns.js +10 -10
  9. package/dist/components/DataGrid/TableBody/LoadingCell.js +10 -10
  10. package/dist/components/DataGrid/TableBody/TableBodyRow.js +10 -10
  11. package/dist/components/DataGrid/TableBody/index.js +10 -10
  12. package/dist/components/DataGrid/index.js +10 -10
  13. package/dist/components/DataGrid/utils.js +10 -10
  14. package/dist/components/DateInput.js +10 -10
  15. package/dist/components/DateRangeInput.js +10 -10
  16. package/dist/components/FilterGroup.js +5 -5
  17. package/dist/components/MobileDataGrid/ColumnSelector/index.js +10 -10
  18. package/dist/components/MobileDataGrid/MobileDataGridHeader.js +10 -10
  19. package/dist/components/MobileDataGrid/RowDetailModalProvider/index.js +5 -5
  20. package/dist/components/MobileDataGrid/index.js +10 -10
  21. package/dist/components/Modal.js +4 -4
  22. package/dist/components/ModalButtons.js +2 -2
  23. package/dist/components/ModalHeader.js +2 -2
  24. package/dist/components/NavigationTab.js +2 -2
  25. package/dist/components/NavigationTabs.js +2 -2
  26. package/dist/components/NestedMenu.js +3 -3
  27. package/dist/components/Notification.js +3 -3
  28. package/dist/components/OptionPill.js +2 -2
  29. package/dist/components/PDFViewer/DownloadIcon.js +2 -2
  30. package/dist/components/PDFViewer/PDFNavigation.js +2 -2
  31. package/dist/components/PDFViewer/index.js +6 -6
  32. package/dist/components/ProductImagePreview/index.js +1 -1
  33. package/dist/components/Stepper.js +3 -3
  34. package/dist/components/Toast.js +3 -3
  35. package/dist/components/Upload.js +3 -3
  36. package/dist/components/index.js +16 -16
  37. package/package.json +8 -9
  38. package/src/brand.css +0 -125
  39. package/src/classNames.ts +0 -174
  40. package/src/components/AccessChangerTabItem.tsx +0 -71
  41. package/src/components/Accordion.tsx +0 -108
  42. package/src/components/Alert.tsx +0 -81
  43. package/src/components/Breadcrumbs.tsx +0 -142
  44. package/src/components/Button.tsx +0 -216
  45. package/src/components/CalendarRange.tsx +0 -628
  46. package/src/components/Caption.tsx +0 -144
  47. package/src/components/Card.tsx +0 -88
  48. package/src/components/Checkbox.tsx +0 -206
  49. package/src/components/CompactImagesPreview.tsx +0 -135
  50. package/src/components/ContentTab.tsx +0 -84
  51. package/src/components/ContentTabs.tsx +0 -136
  52. package/src/components/DMSiLogo.tsx +0 -33
  53. package/src/components/DataGrid/ColumnSelectorHeaderCell/ColumnSelectorMenuOption.tsx +0 -35
  54. package/src/components/DataGrid/ColumnSelectorHeaderCell/index.tsx +0 -74
  55. package/src/components/DataGrid/PinnedColumns.tsx +0 -183
  56. package/src/components/DataGrid/TableBody/LoadingCell.tsx +0 -44
  57. package/src/components/DataGrid/TableBody/TableBodyRow.tsx +0 -157
  58. package/src/components/DataGrid/TableBody/index.tsx +0 -185
  59. package/src/components/DataGrid/index.tsx +0 -756
  60. package/src/components/DataGrid/types.ts +0 -98
  61. package/src/components/DataGrid/utils.tsx +0 -15
  62. package/src/components/DataGridCell.tsx +0 -526
  63. package/src/components/DataTable.tsx +0 -881
  64. package/src/components/DateInput.tsx +0 -306
  65. package/src/components/DateRangeInput.tsx +0 -758
  66. package/src/components/DebugJson.tsx +0 -28
  67. package/src/components/Display.tsx +0 -66
  68. package/src/components/EditingContext.tsx +0 -43
  69. package/src/components/EmptyCartIcon.tsx +0 -18
  70. package/src/components/FilterGroup.tsx +0 -264
  71. package/src/components/FullViewportBox.tsx +0 -19
  72. package/src/components/Grid.tsx +0 -97
  73. package/src/components/Heading.tsx +0 -72
  74. package/src/components/HorizontalDivider.tsx +0 -22
  75. package/src/components/Icon.tsx +0 -39
  76. package/src/components/ImagePlaceholder.tsx +0 -22
  77. package/src/components/Input.tsx +0 -609
  78. package/src/components/InputGroup.tsx +0 -59
  79. package/src/components/Label.tsx +0 -46
  80. package/src/components/Link.tsx +0 -117
  81. package/src/components/List.tsx +0 -18
  82. package/src/components/ListGroup.tsx +0 -82
  83. package/src/components/LiveChatComponent.tsx +0 -56
  84. package/src/components/LoadingScrim.tsx +0 -33
  85. package/src/components/LogoAgilityTopBar.tsx +0 -54
  86. package/src/components/LogoDMSiTopBar.tsx +0 -33
  87. package/src/components/LogoMillworkTopBar.tsx +0 -119
  88. package/src/components/MainBar.tsx +0 -91
  89. package/src/components/MaxViewportBox.tsx +0 -19
  90. package/src/components/Menu.tsx +0 -316
  91. package/src/components/MenuOption.tsx +0 -330
  92. package/src/components/MobileDataGrid/ColumnList.tsx +0 -66
  93. package/src/components/MobileDataGrid/ColumnSelector/index.tsx +0 -97
  94. package/src/components/MobileDataGrid/GridContextProvider/GridContext.tsx +0 -25
  95. package/src/components/MobileDataGrid/GridContextProvider/index.tsx +0 -132
  96. package/src/components/MobileDataGrid/GridContextProvider/useGridContext.ts +0 -10
  97. package/src/components/MobileDataGrid/MobileDataGridCard/MobileDataGridColumn.tsx +0 -27
  98. package/src/components/MobileDataGrid/MobileDataGridCard/index.tsx +0 -138
  99. package/src/components/MobileDataGrid/MobileDataGridHeader.tsx +0 -81
  100. package/src/components/MobileDataGrid/RowDetailModalProvider/ModalContent.tsx +0 -42
  101. package/src/components/MobileDataGrid/RowDetailModalProvider/index.tsx +0 -68
  102. package/src/components/MobileDataGrid/dataGridReducer.ts +0 -55
  103. package/src/components/MobileDataGrid/index.tsx +0 -92
  104. package/src/components/MobileDataGrid/types.ts +0 -4
  105. package/src/components/Modal.tsx +0 -312
  106. package/src/components/ModalButtons.tsx +0 -62
  107. package/src/components/ModalContent.tsx +0 -31
  108. package/src/components/ModalHeader.tsx +0 -78
  109. package/src/components/ModalScrim.tsx +0 -42
  110. package/src/components/NavigationTab.tsx +0 -95
  111. package/src/components/NavigationTabs.tsx +0 -70
  112. package/src/components/NestedMenu.tsx +0 -131
  113. package/src/components/Notification.tsx +0 -128
  114. package/src/components/OptionPill.tsx +0 -139
  115. package/src/components/OrderCheckIcon.tsx +0 -19
  116. package/src/components/PDFViewer/DownloadIcon.tsx +0 -25
  117. package/src/components/PDFViewer/PDFElement.tsx +0 -90
  118. package/src/components/PDFViewer/PDFNavigation.tsx +0 -68
  119. package/src/components/PDFViewer/PDFPage.tsx +0 -34
  120. package/src/components/PDFViewer/index.tsx +0 -128
  121. package/src/components/Pagination.tsx +0 -182
  122. package/src/components/Paragraph.tsx +0 -55
  123. package/src/components/Password.tsx +0 -62
  124. package/src/components/ProductImagePreview/CarouselPagination.tsx +0 -54
  125. package/src/components/ProductImagePreview/MobileImageCarousel.tsx +0 -226
  126. package/src/components/ProductImagePreview/ProductPrimaryImage.tsx +0 -219
  127. package/src/components/ProductImagePreview/Thumbnail.tsx +0 -55
  128. package/src/components/ProductImagePreview/ZoomWindow.tsx +0 -136
  129. package/src/components/ProductImagePreview/index.tsx +0 -182
  130. package/src/components/ProductImagePreview/useProductImagePreview.ts +0 -211
  131. package/src/components/ProjectBar.tsx +0 -82
  132. package/src/components/Radio.tsx +0 -146
  133. package/src/components/Search.tsx +0 -152
  134. package/src/components/SearchResultImage/index.tsx +0 -39
  135. package/src/components/Select.tsx +0 -114
  136. package/src/components/SideMenu.tsx +0 -30
  137. package/src/components/SideMenuGroup.tsx +0 -95
  138. package/src/components/SideMenuItem.tsx +0 -109
  139. package/src/components/SimpleTable.tsx +0 -77
  140. package/src/components/SkeletonParagraph.tsx +0 -31
  141. package/src/components/Spinner.tsx +0 -32
  142. package/src/components/Stack.tsx +0 -347
  143. package/src/components/StatusPill.tsx +0 -59
  144. package/src/components/Stepper.tsx +0 -128
  145. package/src/components/Subheader.tsx +0 -50
  146. package/src/components/Surface.tsx +0 -37
  147. package/src/components/Swatch.tsx +0 -1341
  148. package/src/components/Textarea.tsx +0 -102
  149. package/src/components/Theme.tsx +0 -27
  150. package/src/components/Time.tsx +0 -460
  151. package/src/components/Toast.tsx +0 -268
  152. package/src/components/Tooltip.tsx +0 -159
  153. package/src/components/TopBar.tsx +0 -139
  154. package/src/components/Upload.tsx +0 -107
  155. package/src/components/WorldpayIframe.tsx +0 -7
  156. package/src/components/index.ts +0 -34
  157. package/src/components/useMenuSystem.tsx +0 -456
  158. package/src/components/useMounted.tsx +0 -14
  159. package/src/darkmode.css +0 -278
  160. package/src/fonts.css +0 -23
  161. package/src/hooks/index.ts +0 -4
  162. package/src/hooks/useInfiniteScroll.tsx +0 -40
  163. package/src/hooks/useKeydown.ts +0 -42
  164. package/src/hooks/useMatchesMedia.ts +0 -18
  165. package/src/hooks/useTableLayout.ts +0 -106
  166. package/src/index.css +0 -800
  167. package/src/index.tsx +0 -5
  168. package/src/types.ts +0 -150
  169. package/src/utils/date.ts +0 -236
  170. package/src/utils/formatting.tsx +0 -81
  171. package/src/utils/index.ts +0 -4
  172. package/src/utils/mergeObjectArrays.ts +0 -18
  173. package/src/utils.ts +0 -24
  174. package/dist/{chunk-7FQ7PGUF.js → chunk-7COWXCPA.js} +3 -3
  175. package/dist/{chunk-NKCFYM7A.js → chunk-7SFFUICM.js} +3 -3
  176. package/dist/{chunk-25RZP3VR.js → chunk-AKJUBFJK.js} +3 -3
  177. package/dist/{chunk-TAPYQBQU.js → chunk-CMMQTIVM.js} +3 -3
  178. package/dist/{chunk-GYEXSNFP.js → chunk-FWCVZWE6.js} +3 -3
  179. package/dist/{chunk-MV6W7OMC.js → chunk-QMMPHXVE.js} +3 -3
  180. 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
- }