@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,628 +0,0 @@
|
|
|
1
|
-
import clsx from "clsx";
|
|
2
|
-
import {
|
|
3
|
-
typography,
|
|
4
|
-
componentPadding,
|
|
5
|
-
layoutPaddding,
|
|
6
|
-
layoutGap,
|
|
7
|
-
} from "../classNames";
|
|
8
|
-
import { Icon } from "./Icon";
|
|
9
|
-
import React, { useEffect, useRef, useState } from "react";
|
|
10
|
-
import { Temporal } from "@js-temporal/polyfill";
|
|
11
|
-
import { Menu, MenuOption } from ".";
|
|
12
|
-
|
|
13
|
-
export interface CalendarRangeProps {
|
|
14
|
-
from?: string | number;
|
|
15
|
-
to?: string | number;
|
|
16
|
-
onChange?: (from: string, to: string) => void;
|
|
17
|
-
onPendingFromChange?: (from: string) => void;
|
|
18
|
-
isDateAvailable?: (date: Temporal.PlainDate) => boolean;
|
|
19
|
-
/**
|
|
20
|
-
* Display mode: 'single' for one month, 'double' for two months side by side (default)
|
|
21
|
-
*/
|
|
22
|
-
mode?: "single" | "double";
|
|
23
|
-
cardStyle?: boolean;
|
|
24
|
-
/**
|
|
25
|
-
* Disable range selection in single mode. When true, single mode will only allow single date selection.
|
|
26
|
-
* Only applies when mode is "single".
|
|
27
|
-
*/
|
|
28
|
-
disableRange?: boolean;
|
|
29
|
-
id?: string;
|
|
30
|
-
testid?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Returns true if the given date is a weekend (Saturday or Sunday).
|
|
35
|
-
* @param date Temporal.PlainDate
|
|
36
|
-
*/
|
|
37
|
-
export function isWeekend(date: Temporal.PlainDate): boolean {
|
|
38
|
-
// 6 = Saturday, 7 = Sunday (Temporal: 1=Monday, 7=Sunday)
|
|
39
|
-
return date.dayOfWeek === 6 || date.dayOfWeek === 7;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// DateCell component for rendering a single day
|
|
43
|
-
interface DateCellProps {
|
|
44
|
-
date: Temporal.PlainDate;
|
|
45
|
-
isInMonth: boolean;
|
|
46
|
-
isToday: boolean;
|
|
47
|
-
isSelected: boolean;
|
|
48
|
-
inRange: boolean;
|
|
49
|
-
isDisabled: boolean;
|
|
50
|
-
onClick: () => void;
|
|
51
|
-
onMouseEnter: () => void;
|
|
52
|
-
onMouseLeave: () => void;
|
|
53
|
-
/** True if this cell is the start of the selected range */
|
|
54
|
-
isRangeStart?: boolean;
|
|
55
|
-
/** True if this cell is the end of the selected range */
|
|
56
|
-
isRangeEnd?: boolean;
|
|
57
|
-
/** True if range selection is disabled in single mode */
|
|
58
|
-
isRangeDisabled?: boolean;
|
|
59
|
-
id?: string;
|
|
60
|
-
testid?: string;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function DateCell({
|
|
64
|
-
date,
|
|
65
|
-
isInMonth,
|
|
66
|
-
isToday,
|
|
67
|
-
isSelected,
|
|
68
|
-
inRange,
|
|
69
|
-
isDisabled,
|
|
70
|
-
isRangeStart,
|
|
71
|
-
isRangeEnd,
|
|
72
|
-
onClick,
|
|
73
|
-
onMouseEnter,
|
|
74
|
-
onMouseLeave,
|
|
75
|
-
cellPadding = "",
|
|
76
|
-
isRangeDisabled = false,
|
|
77
|
-
id,
|
|
78
|
-
testid,
|
|
79
|
-
...props
|
|
80
|
-
}: DateCellProps & { cellPadding?: string }) {
|
|
81
|
-
return (
|
|
82
|
-
<span
|
|
83
|
-
{...props}
|
|
84
|
-
id={id}
|
|
85
|
-
data-testid={testid}
|
|
86
|
-
className={clsx(
|
|
87
|
-
"flex items-center justify-center aspect-square select-none transition-colors border duration-100 font-medium",
|
|
88
|
-
typography.caption,
|
|
89
|
-
cellPadding,
|
|
90
|
-
!isToday &&
|
|
91
|
-
!isSelected &&
|
|
92
|
-
!inRange &&
|
|
93
|
-
!isDisabled &&
|
|
94
|
-
!isRangeStart &&
|
|
95
|
-
!isRangeEnd &&
|
|
96
|
-
"border-transparent",
|
|
97
|
-
!isInMonth && "border-transparent",
|
|
98
|
-
// Today: subtle border ring
|
|
99
|
-
isToday &&
|
|
100
|
-
!isSelected &&
|
|
101
|
-
!inRange &&
|
|
102
|
-
"rounded-full border-border-primary-normal ",
|
|
103
|
-
// Selected: Figma blue, white text, strong shadow
|
|
104
|
-
isSelected && "bg-action-400 text-white border-action-400 z-10",
|
|
105
|
-
!isSelected && !inRange && "rounded-base",
|
|
106
|
-
// When range is disabled OR when only 'from' is selected (no range yet), apply rounded corners
|
|
107
|
-
(isRangeDisabled || (!inRange && isSelected)) && "rounded-base",
|
|
108
|
-
inRange && isSelected && "hover:border-action-500",
|
|
109
|
-
// In range: Figma light blue background
|
|
110
|
-
inRange &&
|
|
111
|
-
!isSelected &&
|
|
112
|
-
"bg-action-100 text-text-primary-normal border-y-action-400 border-x-0 ",
|
|
113
|
-
// Disabled: Figma gray, no pointer, no hover
|
|
114
|
-
isDisabled && !inRange
|
|
115
|
-
? "text-text-primary-disabled bg-transparent pointer-events-none opacity-40 border-transparent"
|
|
116
|
-
: [
|
|
117
|
-
"text-text-primary-normal cursor-pointer",
|
|
118
|
-
// Figma hover: blue bg, blue text (or red text if selected)
|
|
119
|
-
isSelected
|
|
120
|
-
? "hover:bg-background-action-primary-hover hover:text-white"
|
|
121
|
-
: "hover:bg-action-100 hover:text-text-action-primary-hover",
|
|
122
|
-
// Figma active: darker blue bg, white text
|
|
123
|
-
"active:bg-action-300 active:text-white",
|
|
124
|
-
// Figma focus: ring
|
|
125
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-400",
|
|
126
|
-
],
|
|
127
|
-
isRangeStart && "rounded-l",
|
|
128
|
-
isRangeEnd && "rounded-r",
|
|
129
|
-
)}
|
|
130
|
-
tabIndex={isDisabled ? -1 : 0}
|
|
131
|
-
aria-disabled={isDisabled}
|
|
132
|
-
onClick={() => !isDisabled && isInMonth && onClick()}
|
|
133
|
-
onMouseEnter={() => isInMonth && onMouseEnter()}
|
|
134
|
-
onMouseLeave={() => isInMonth && onMouseLeave()}
|
|
135
|
-
>
|
|
136
|
-
{isInMonth ? date.day : ""}
|
|
137
|
-
</span>
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function CalendarRange({
|
|
142
|
-
from,
|
|
143
|
-
to,
|
|
144
|
-
onChange,
|
|
145
|
-
onPendingFromChange,
|
|
146
|
-
isDateAvailable,
|
|
147
|
-
mode = "double",
|
|
148
|
-
cardStyle = false,
|
|
149
|
-
disableRange = false,
|
|
150
|
-
id,
|
|
151
|
-
testid,
|
|
152
|
-
}: CalendarRangeProps) {
|
|
153
|
-
const weekDays = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
154
|
-
// Parse from/to props
|
|
155
|
-
const parseDate = (d?: string | number) => {
|
|
156
|
-
if (!d) {
|
|
157
|
-
return undefined;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
if (typeof d === "number") {
|
|
162
|
-
return Temporal.PlainDate.from(new Date(d).toISOString().slice(0, 10));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (typeof d === "string") {
|
|
166
|
-
return Temporal.PlainDate.from(d);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return undefined;
|
|
170
|
-
} catch (error) {
|
|
171
|
-
// If parsing fails, return current date instead of throwing
|
|
172
|
-
console.error("Invalid date format:", d, error);
|
|
173
|
-
return Temporal.Now.plainDateISO();
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
const fromDate = parseDate(from);
|
|
177
|
-
const toDate = parseDate(to);
|
|
178
|
-
|
|
179
|
-
// Internal state for visible months and selection
|
|
180
|
-
const today = Temporal.Now.plainDateISO();
|
|
181
|
-
const [baseMonth, setBaseMonth] = useState(
|
|
182
|
-
fromDate ?? today.with({ day: 1 }),
|
|
183
|
-
);
|
|
184
|
-
const [selecting, setSelecting] = useState<"from" | "to">("from");
|
|
185
|
-
const [pendingFrom, setPendingFrom] = useState<
|
|
186
|
-
Temporal.PlainDate | undefined
|
|
187
|
-
>(undefined);
|
|
188
|
-
const [hoveredDate, setHoveredDate] = useState<
|
|
189
|
-
Temporal.PlainDate | undefined
|
|
190
|
-
>(undefined);
|
|
191
|
-
|
|
192
|
-
// Update base month when from/to props change
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
if (fromDate) {
|
|
195
|
-
setBaseMonth(fromDate.with({ day: 1 }));
|
|
196
|
-
} else if (toDate) {
|
|
197
|
-
setBaseMonth(toDate.with({ day: 1 }));
|
|
198
|
-
}
|
|
199
|
-
}, [from, to]);
|
|
200
|
-
|
|
201
|
-
// Reset selection state when from/to props change externally
|
|
202
|
-
useEffect(() => {
|
|
203
|
-
if (fromDate && toDate) {
|
|
204
|
-
setSelecting("from");
|
|
205
|
-
setPendingFrom(undefined);
|
|
206
|
-
setHoveredDate(undefined);
|
|
207
|
-
}
|
|
208
|
-
}, [from, to]);
|
|
209
|
-
|
|
210
|
-
// Helper to get month data
|
|
211
|
-
function getMonthData(monthOffset: number) {
|
|
212
|
-
const monthDate = baseMonth.add({ months: monthOffset }).with({ day: 1 });
|
|
213
|
-
const days = monthDate.daysInMonth;
|
|
214
|
-
const firstDayOffset = monthDate.dayOfWeek % 7;
|
|
215
|
-
return {
|
|
216
|
-
name: monthDate.toLocaleString("en-US", { month: "long" }),
|
|
217
|
-
year: monthDate.year,
|
|
218
|
-
days,
|
|
219
|
-
firstDayOffset,
|
|
220
|
-
date: monthDate,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function getMonthDataWith(monthOffset: number) {
|
|
225
|
-
const monthDate = baseMonth.with({ month: monthOffset }).with({ day: 1 });
|
|
226
|
-
const days = monthDate.daysInMonth;
|
|
227
|
-
const firstDayOffset = monthDate.dayOfWeek % 7;
|
|
228
|
-
return {
|
|
229
|
-
name: monthDate.toLocaleString("en-US", { month: "long" }),
|
|
230
|
-
year: monthDate.year,
|
|
231
|
-
days,
|
|
232
|
-
firstDayOffset,
|
|
233
|
-
date: monthDate,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Click handlers
|
|
238
|
-
function handleDayClick(date: Temporal.PlainDate) {
|
|
239
|
-
if (isDateAvailable && !isDateAvailable(date)) return;
|
|
240
|
-
|
|
241
|
-
// Single mode with range disabled: just select the date directly
|
|
242
|
-
if (mode === "single" && disableRange) {
|
|
243
|
-
if (onChange) {
|
|
244
|
-
onChange(date.toString(), date.toString());
|
|
245
|
-
}
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Range mode (or single mode with range enabled): handle two-step selection
|
|
250
|
-
if (selecting === "from") {
|
|
251
|
-
setPendingFrom(date);
|
|
252
|
-
setSelecting("to");
|
|
253
|
-
setHoveredDate(undefined);
|
|
254
|
-
onPendingFromChange?.(date.toString());
|
|
255
|
-
// Hide current from/to selection when starting a new selection
|
|
256
|
-
} else if (pendingFrom) {
|
|
257
|
-
if (onChange) {
|
|
258
|
-
const [start, end] =
|
|
259
|
-
Temporal.PlainDate.compare(date, pendingFrom) < 0
|
|
260
|
-
? [date, pendingFrom]
|
|
261
|
-
: [pendingFrom, date];
|
|
262
|
-
onChange(start.toString(), end.toString());
|
|
263
|
-
}
|
|
264
|
-
setPendingFrom(undefined);
|
|
265
|
-
setSelecting("from");
|
|
266
|
-
setHoveredDate(undefined);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Range highlighting
|
|
271
|
-
function isInRange(date: Temporal.PlainDate) {
|
|
272
|
-
// No range highlighting in single mode when range is disabled
|
|
273
|
-
if (mode === "single" && disableRange) {
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (pendingFrom && selecting === "to" && hoveredDate) {
|
|
278
|
-
const [start, end] =
|
|
279
|
-
Temporal.PlainDate.compare(hoveredDate, pendingFrom) < 0
|
|
280
|
-
? [hoveredDate, pendingFrom]
|
|
281
|
-
: [pendingFrom, hoveredDate];
|
|
282
|
-
return (
|
|
283
|
-
Temporal.PlainDate.compare(date, start) >= 0 &&
|
|
284
|
-
Temporal.PlainDate.compare(date, end) <= 0
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
if (!pendingFrom && fromDate && toDate) {
|
|
288
|
-
return (
|
|
289
|
-
Temporal.PlainDate.compare(date, fromDate) >= 0 &&
|
|
290
|
-
Temporal.PlainDate.compare(date, toDate) <= 0
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
return false;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Render
|
|
297
|
-
return (
|
|
298
|
-
<div
|
|
299
|
-
id={id}
|
|
300
|
-
data-testid={testid}
|
|
301
|
-
className={clsx(
|
|
302
|
-
"relative bg-background-grouped-primary-normal rounded-base w-fit",
|
|
303
|
-
layoutPaddding,
|
|
304
|
-
layoutGap,
|
|
305
|
-
cardStyle && "shadow-4",
|
|
306
|
-
// baseTransition,
|
|
307
|
-
"overflow-hidden",
|
|
308
|
-
)}
|
|
309
|
-
>
|
|
310
|
-
<div
|
|
311
|
-
className={clsx(
|
|
312
|
-
"flex flex-row items-start justify-start bg-background-primary-normal overflow-clip",
|
|
313
|
-
layoutGap,
|
|
314
|
-
)}
|
|
315
|
-
>
|
|
316
|
-
{(mode === "double" ? [0, 1] : [0]).map((offset, idx) => {
|
|
317
|
-
return (
|
|
318
|
-
<CalendarPane
|
|
319
|
-
key={idx}
|
|
320
|
-
getMonthData={getMonthData}
|
|
321
|
-
getMonthDataWith={getMonthDataWith}
|
|
322
|
-
offset={offset}
|
|
323
|
-
idx={idx}
|
|
324
|
-
id={id}
|
|
325
|
-
testid={testid}
|
|
326
|
-
baseMonth={baseMonth}
|
|
327
|
-
setBaseMonth={setBaseMonth}
|
|
328
|
-
mode={mode}
|
|
329
|
-
pendingFrom={pendingFrom}
|
|
330
|
-
weekDays={weekDays}
|
|
331
|
-
fromDate={fromDate}
|
|
332
|
-
toDate={toDate}
|
|
333
|
-
isDateAvailable={isDateAvailable}
|
|
334
|
-
disableRange={disableRange}
|
|
335
|
-
hoveredDate={hoveredDate}
|
|
336
|
-
isInRange={isInRange}
|
|
337
|
-
today={today}
|
|
338
|
-
setHoveredDate={setHoveredDate}
|
|
339
|
-
handleDayClick={handleDayClick}
|
|
340
|
-
/>
|
|
341
|
-
);
|
|
342
|
-
})}
|
|
343
|
-
</div>
|
|
344
|
-
</div>
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
function CalendarPane({
|
|
348
|
-
getMonthData,
|
|
349
|
-
getMonthDataWith,
|
|
350
|
-
offset,
|
|
351
|
-
idx,
|
|
352
|
-
id,
|
|
353
|
-
testid,
|
|
354
|
-
baseMonth,
|
|
355
|
-
setBaseMonth,
|
|
356
|
-
mode,
|
|
357
|
-
pendingFrom,
|
|
358
|
-
weekDays,
|
|
359
|
-
fromDate,
|
|
360
|
-
toDate,
|
|
361
|
-
isDateAvailable,
|
|
362
|
-
disableRange,
|
|
363
|
-
hoveredDate,
|
|
364
|
-
isInRange,
|
|
365
|
-
today,
|
|
366
|
-
setHoveredDate,
|
|
367
|
-
handleDayClick,
|
|
368
|
-
}: {
|
|
369
|
-
getMonthData: (offset: number) => {
|
|
370
|
-
name: string;
|
|
371
|
-
year: number;
|
|
372
|
-
days: number;
|
|
373
|
-
firstDayOffset: number;
|
|
374
|
-
date: Temporal.PlainDate;
|
|
375
|
-
};
|
|
376
|
-
getMonthDataWith: (offset: number) => {
|
|
377
|
-
name: string;
|
|
378
|
-
year: number;
|
|
379
|
-
days: number;
|
|
380
|
-
firstDayOffset: number;
|
|
381
|
-
date: Temporal.PlainDate;
|
|
382
|
-
};
|
|
383
|
-
offset: number;
|
|
384
|
-
idx: number;
|
|
385
|
-
id: string | undefined;
|
|
386
|
-
testid: string | undefined;
|
|
387
|
-
baseMonth: Temporal.PlainDate;
|
|
388
|
-
setBaseMonth: (date: Temporal.PlainDate) => void;
|
|
389
|
-
mode: "single" | "range" | "double";
|
|
390
|
-
pendingFrom: Temporal.PlainDate | undefined;
|
|
391
|
-
weekDays: string[];
|
|
392
|
-
fromDate: Temporal.PlainDate | undefined;
|
|
393
|
-
toDate: Temporal.PlainDate | undefined;
|
|
394
|
-
isDateAvailable?: (date: Temporal.PlainDate) => boolean;
|
|
395
|
-
disableRange: boolean;
|
|
396
|
-
hoveredDate: Temporal.PlainDate | undefined;
|
|
397
|
-
isInRange: (date: Temporal.PlainDate) => boolean;
|
|
398
|
-
today: Temporal.PlainDate;
|
|
399
|
-
setHoveredDate: (date: Temporal.PlainDate | undefined) => void;
|
|
400
|
-
handleDayClick: (date: Temporal.PlainDate) => void;
|
|
401
|
-
}) {
|
|
402
|
-
const months = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
|
403
|
-
const years = Array.from({ length: 100 }).map(
|
|
404
|
-
(_, i) => baseMonth.year - 50 + i,
|
|
405
|
-
);
|
|
406
|
-
const [monthMenuOpen, setMonthMenuOpen] = useState(false);
|
|
407
|
-
const [yearMenuOpen, setYearMenuOpen] = useState(false);
|
|
408
|
-
|
|
409
|
-
const monthMenuRef = useRef<HTMLButtonElement | null>(null);
|
|
410
|
-
const yearMenuRef = useRef<HTMLButtonElement | null>(null);
|
|
411
|
-
const month = getMonthData(offset);
|
|
412
|
-
// Always show 6 weeks (42 days)
|
|
413
|
-
const totalCells = 42;
|
|
414
|
-
const emptyCells = month.firstDayOffset;
|
|
415
|
-
return (
|
|
416
|
-
<React.Fragment key={month.name + month.year}>
|
|
417
|
-
<div
|
|
418
|
-
// key={month.name + month.year}
|
|
419
|
-
className={clsx("flex flex-col")}
|
|
420
|
-
>
|
|
421
|
-
<div
|
|
422
|
-
className={clsx(
|
|
423
|
-
"flex flex-row items-center justify-between",
|
|
424
|
-
typography.label,
|
|
425
|
-
"text-text-action-primary-normal",
|
|
426
|
-
)}
|
|
427
|
-
>
|
|
428
|
-
{idx === 0 ? (
|
|
429
|
-
<button
|
|
430
|
-
id={id ? `${id}-prev-month-button` : undefined}
|
|
431
|
-
data-testid={testid ? `${testid}-prev-month-button` : undefined}
|
|
432
|
-
type="button"
|
|
433
|
-
className={clsx(
|
|
434
|
-
"flex items-center justify-center rounded-base hover:bg-action-100 active:bg-action-300 text-icon-action-primary-normal",
|
|
435
|
-
componentPadding,
|
|
436
|
-
)}
|
|
437
|
-
aria-label="Previous month"
|
|
438
|
-
onClick={() => setBaseMonth(baseMonth.subtract({ months: 1 }))}
|
|
439
|
-
>
|
|
440
|
-
<Icon name="chevron_left" size={24} />
|
|
441
|
-
</button>
|
|
442
|
-
) : (
|
|
443
|
-
<span className={clsx(componentPadding, "mr-1")} />
|
|
444
|
-
)}
|
|
445
|
-
<div className="flex gap-desktop-compact-component-padding">
|
|
446
|
-
<button
|
|
447
|
-
ref={(el) => {
|
|
448
|
-
monthMenuRef.current = el;
|
|
449
|
-
}}
|
|
450
|
-
type="button"
|
|
451
|
-
onClick={() => {
|
|
452
|
-
setMonthMenuOpen(true);
|
|
453
|
-
setYearMenuOpen(false);
|
|
454
|
-
}}
|
|
455
|
-
className="font-semibold text-text-action-primary-normal text-[14px] py-[2px] truncate"
|
|
456
|
-
>
|
|
457
|
-
{month.name}
|
|
458
|
-
</button>
|
|
459
|
-
<Menu
|
|
460
|
-
show={monthMenuOpen}
|
|
461
|
-
positionTo={monthMenuRef}
|
|
462
|
-
setShow={() => setMonthMenuOpen(false)}
|
|
463
|
-
>
|
|
464
|
-
{months
|
|
465
|
-
.map((x) => [x, getMonthDataWith(x + 1)] as const)
|
|
466
|
-
.map(([x, m]) => (
|
|
467
|
-
<MenuOption
|
|
468
|
-
key={m.name}
|
|
469
|
-
selected={baseMonth.month === x + 1}
|
|
470
|
-
onClick={() => {
|
|
471
|
-
setBaseMonth(baseMonth.with({ month: x + 1 }));
|
|
472
|
-
setMonthMenuOpen(false);
|
|
473
|
-
}}
|
|
474
|
-
>
|
|
475
|
-
{m.name}
|
|
476
|
-
</MenuOption>
|
|
477
|
-
))}
|
|
478
|
-
</Menu>
|
|
479
|
-
<button
|
|
480
|
-
ref={(el) => {
|
|
481
|
-
yearMenuRef.current = el;
|
|
482
|
-
}}
|
|
483
|
-
type="button"
|
|
484
|
-
onClick={() => {
|
|
485
|
-
setYearMenuOpen(true);
|
|
486
|
-
setMonthMenuOpen(false);
|
|
487
|
-
}}
|
|
488
|
-
className="font-semibold text-text-action-primary-normal text-[14px] py-[2px] truncate"
|
|
489
|
-
>
|
|
490
|
-
{month.year}
|
|
491
|
-
</button>
|
|
492
|
-
<Menu
|
|
493
|
-
show={yearMenuOpen}
|
|
494
|
-
positionTo={yearMenuRef}
|
|
495
|
-
setShow={() => setYearMenuOpen(false)}
|
|
496
|
-
>
|
|
497
|
-
{years.map((y) => (
|
|
498
|
-
<MenuOption
|
|
499
|
-
key={y}
|
|
500
|
-
selected={baseMonth.year === y}
|
|
501
|
-
onClick={() => {
|
|
502
|
-
setBaseMonth(baseMonth.with({ year: y }));
|
|
503
|
-
setYearMenuOpen(false);
|
|
504
|
-
}}
|
|
505
|
-
>
|
|
506
|
-
{y}
|
|
507
|
-
</MenuOption>
|
|
508
|
-
))}
|
|
509
|
-
</Menu>
|
|
510
|
-
</div>
|
|
511
|
-
{(mode === "double" ? idx === 1 : true) ? (
|
|
512
|
-
<button
|
|
513
|
-
id={id ? `${id}-next-month-button` : undefined}
|
|
514
|
-
data-testid={testid ? `${testid}-next-month-button` : undefined}
|
|
515
|
-
type="button"
|
|
516
|
-
className={clsx(
|
|
517
|
-
"flex items-center justify-center rounded-base hover:bg-action-100 active:bg-action-300 text-icon-action-primary-normal",
|
|
518
|
-
componentPadding,
|
|
519
|
-
)}
|
|
520
|
-
aria-label="Next month"
|
|
521
|
-
onClick={() => setBaseMonth(baseMonth.add({ months: 1 }))}
|
|
522
|
-
>
|
|
523
|
-
<Icon name="chevron_right" size={24} />
|
|
524
|
-
</button>
|
|
525
|
-
) : (
|
|
526
|
-
<span className={clsx(componentPadding, "ml-1")} />
|
|
527
|
-
)}
|
|
528
|
-
</div>
|
|
529
|
-
<div className={clsx("grid grid-cols-7")}>
|
|
530
|
-
{weekDays.map((d) => (
|
|
531
|
-
<span
|
|
532
|
-
key={d}
|
|
533
|
-
className={clsx(
|
|
534
|
-
typography.caption,
|
|
535
|
-
"text-text-secondary-normal text-center",
|
|
536
|
-
"w-10",
|
|
537
|
-
)}
|
|
538
|
-
>
|
|
539
|
-
{d}
|
|
540
|
-
</span>
|
|
541
|
-
))}
|
|
542
|
-
</div>
|
|
543
|
-
<div className={clsx("grid grid-cols-7")}>
|
|
544
|
-
{Array.from({ length: totalCells }).map((_, i) => {
|
|
545
|
-
const day = i - emptyCells + 1;
|
|
546
|
-
const date = month.date.with({ day: 1 }).add({
|
|
547
|
-
days: i - emptyCells,
|
|
548
|
-
});
|
|
549
|
-
const isInMonth = day > 0 && day <= month.days;
|
|
550
|
-
const isToday = isInMonth && date.equals(today);
|
|
551
|
-
const isSelected =
|
|
552
|
-
isInMonth &&
|
|
553
|
-
((!pendingFrom && fromDate && date.equals(fromDate)) ||
|
|
554
|
-
(!pendingFrom && toDate && date.equals(toDate)) ||
|
|
555
|
-
(pendingFrom && date.equals(pendingFrom)));
|
|
556
|
-
const inRange = isInMonth && isInRange(date);
|
|
557
|
-
const isDisabled =
|
|
558
|
-
!isInMonth || (isDateAvailable ? !isDateAvailable(date) : false);
|
|
559
|
-
// New: determine if this is the start or end of the range
|
|
560
|
-
|
|
561
|
-
const hoverDateIsBeforePendingFrom =
|
|
562
|
-
hoveredDate &&
|
|
563
|
-
pendingFrom &&
|
|
564
|
-
Temporal.PlainDate.compare(hoveredDate, pendingFrom) < 0;
|
|
565
|
-
|
|
566
|
-
const hoverDateIsAfterPendingFrom =
|
|
567
|
-
hoveredDate &&
|
|
568
|
-
pendingFrom &&
|
|
569
|
-
Temporal.PlainDate.compare(hoveredDate, pendingFrom) >= 0;
|
|
570
|
-
|
|
571
|
-
const isRangeStart =
|
|
572
|
-
mode === "single" && disableRange
|
|
573
|
-
? false
|
|
574
|
-
: (!pendingFrom &&
|
|
575
|
-
isInMonth &&
|
|
576
|
-
fromDate &&
|
|
577
|
-
date.equals(fromDate)) ||
|
|
578
|
-
(hoverDateIsAfterPendingFrom && date.equals(pendingFrom));
|
|
579
|
-
|
|
580
|
-
const isRangeEnd =
|
|
581
|
-
mode === "single" && disableRange
|
|
582
|
-
? false
|
|
583
|
-
: (!pendingFrom &&
|
|
584
|
-
isInMonth &&
|
|
585
|
-
toDate &&
|
|
586
|
-
date.equals(toDate)) ||
|
|
587
|
-
(hoverDateIsBeforePendingFrom && date.equals(pendingFrom));
|
|
588
|
-
return (
|
|
589
|
-
<DateCell
|
|
590
|
-
key={i}
|
|
591
|
-
id={id ? `${id}-date-${date.toString()}` : undefined}
|
|
592
|
-
testid={
|
|
593
|
-
testid ? `${testid}-date-${date.toString()}` : undefined
|
|
594
|
-
}
|
|
595
|
-
date={date}
|
|
596
|
-
isInMonth={!!isInMonth}
|
|
597
|
-
isToday={!!isToday}
|
|
598
|
-
isSelected={!!isSelected}
|
|
599
|
-
inRange={!!inRange}
|
|
600
|
-
isDisabled={!!isDisabled}
|
|
601
|
-
onClick={() => handleDayClick(date)}
|
|
602
|
-
onMouseEnter={() => setHoveredDate(date)}
|
|
603
|
-
onMouseLeave={() => setHoveredDate(undefined)}
|
|
604
|
-
isRangeStart={!!isRangeStart}
|
|
605
|
-
isRangeEnd={!!isRangeEnd}
|
|
606
|
-
isRangeDisabled={mode === "single" && disableRange}
|
|
607
|
-
// Add cell padding for spacing
|
|
608
|
-
cellPadding={componentPadding}
|
|
609
|
-
/>
|
|
610
|
-
);
|
|
611
|
-
})}
|
|
612
|
-
</div>
|
|
613
|
-
</div>
|
|
614
|
-
{mode === "double" && idx === 0 && (
|
|
615
|
-
<div
|
|
616
|
-
className={clsx(
|
|
617
|
-
"self-stretch bg-border-primary-normal rounded-base",
|
|
618
|
-
|
|
619
|
-
// 1px width, full height, matches Figma divider
|
|
620
|
-
"w-px",
|
|
621
|
-
)}
|
|
622
|
-
/>
|
|
623
|
-
)}
|
|
624
|
-
</React.Fragment>
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
export default CalendarRange;
|