@dmsi/wedgekit-react 0.0.231 → 0.0.233

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 (143) hide show
  1. package/dist/{chunk-FYW64H7N.js → chunk-3FOAGRIY.js} +3 -3
  2. package/dist/{chunk-BHJNYWQG.js → chunk-4AHS4CVA.js} +1 -1
  3. package/dist/{chunk-RDLEIAQU.js → chunk-6CTCHYIS.js} +5 -0
  4. package/dist/{chunk-2VRZB2A4.js → chunk-6ZYTR6DU.js} +1 -1
  5. package/dist/{chunk-CJELWEO2.js → chunk-7KLALK3J.js} +1 -1
  6. package/dist/{chunk-E3UOI2D2.js → chunk-DXVLA32J.js} +2 -2
  7. package/dist/{chunk-SYEJVSE4.js → chunk-FRHPFACM.js} +1 -1
  8. package/dist/{chunk-KGVKHWUK.js → chunk-FS2RQE55.js} +1 -1
  9. package/dist/{chunk-REOLWEZG.js → chunk-KEDK5EWD.js} +1 -1
  10. package/dist/{chunk-SK742QNF.js → chunk-KNP4LUFA.js} +1 -1
  11. package/dist/{chunk-A76MF7ZO.js → chunk-MQXOQL3T.js} +3 -3
  12. package/dist/{chunk-ARC7FOL2.js → chunk-NWELJDBW.js} +3 -3
  13. package/dist/{chunk-47KTDBGA.js → chunk-OGMN5SHL.js} +4 -4
  14. package/dist/{chunk-7BBXPM5C.js → chunk-OP5XCT3L.js} +1 -1
  15. package/dist/{chunk-BOWQUDUU.js → chunk-QA3KMRA6.js} +1 -1
  16. package/dist/{chunk-3WNXMCZV.js → chunk-QQ5G773N.js} +1 -1
  17. package/dist/{chunk-LPQ4QMAV.js → chunk-R2FGTCOY.js} +1 -1
  18. package/dist/{chunk-QHHNFGEX.js → chunk-RJBKI53P.js} +3 -3
  19. package/dist/{chunk-ED7FXZRX.js → chunk-UVG2TKTG.js} +5 -5
  20. package/dist/chunk-VDO7VGAD.js +1182 -0
  21. package/dist/{chunk-Q7ETEWBW.js → chunk-VGPOI7UP.js} +3 -3
  22. package/dist/{chunk-CJVTFYI4.js → chunk-VP7HJX24.js} +1 -1
  23. package/dist/{chunk-A5ROZWIH.js → chunk-VR7X3NMG.js} +5 -5
  24. package/dist/chunk-X73BO77J.js +493 -0
  25. package/dist/{chunk-XQILVD56.js → chunk-XO6PXENP.js} +1 -1
  26. package/dist/{chunk-5JVMULVC.js → chunk-YCKRRAJA.js} +2 -2
  27. package/dist/components/AccessChangerTabItem.cjs +5 -0
  28. package/dist/components/AccessChangerTabItem.js +2 -2
  29. package/dist/components/Accordion.cjs +5 -0
  30. package/dist/components/Accordion.js +4 -4
  31. package/dist/components/Breadcrumbs.cjs +15 -4
  32. package/dist/components/Breadcrumbs.js +12 -6
  33. package/dist/components/Button.cjs +5 -0
  34. package/dist/components/Button.js +2 -2
  35. package/dist/components/CalendarRange.cjs +4071 -224
  36. package/dist/components/CalendarRange.css +4830 -0
  37. package/dist/components/CalendarRange.js +24 -2
  38. package/dist/components/Caption.cjs +5 -0
  39. package/dist/components/Caption.js +1 -1
  40. package/dist/components/Checkbox.cjs +5 -0
  41. package/dist/components/Checkbox.js +3 -3
  42. package/dist/components/ContentTab.cjs +5 -0
  43. package/dist/components/ContentTab.js +3 -3
  44. package/dist/components/ContentTabs.cjs +5 -0
  45. package/dist/components/ContentTabs.js +3 -3
  46. package/dist/components/DataGridCell.cjs +5 -0
  47. package/dist/components/DataGridCell.js +7 -7
  48. package/dist/components/DateInput.cjs +3673 -326
  49. package/dist/components/DateInput.css +4830 -0
  50. package/dist/components/DateInput.js +23 -6
  51. package/dist/components/DateRangeInput.cjs +3673 -326
  52. package/dist/components/DateRangeInput.css +4830 -0
  53. package/dist/components/DateRangeInput.js +23 -6
  54. package/dist/components/Display.cjs +5 -0
  55. package/dist/components/Display.js +2 -2
  56. package/dist/components/FilterGroup.cjs +5 -0
  57. package/dist/components/FilterGroup.js +11 -11
  58. package/dist/components/Heading.cjs +5 -0
  59. package/dist/components/Heading.js +2 -2
  60. package/dist/components/Input.cjs +5 -0
  61. package/dist/components/Input.js +3 -3
  62. package/dist/components/InputGroup.cjs +5 -0
  63. package/dist/components/InputGroup.js +2 -2
  64. package/dist/components/Label.cjs +5 -0
  65. package/dist/components/Label.js +2 -2
  66. package/dist/components/Link.cjs +5 -0
  67. package/dist/components/Link.js +2 -2
  68. package/dist/components/MainBar.cjs +5 -0
  69. package/dist/components/MainBar.js +1 -1
  70. package/dist/components/MenuOption.cjs +5 -0
  71. package/dist/components/MenuOption.js +4 -4
  72. package/dist/components/MobileDataGrid.cjs +5 -0
  73. package/dist/components/MobileDataGrid.js +7 -7
  74. package/dist/components/Modal.cjs +5 -0
  75. package/dist/components/Modal.js +7 -7
  76. package/dist/components/ModalButtons.cjs +5 -0
  77. package/dist/components/ModalButtons.js +3 -3
  78. package/dist/components/ModalContent.cjs +5 -0
  79. package/dist/components/ModalContent.js +2 -2
  80. package/dist/components/ModalHeader.cjs +5 -0
  81. package/dist/components/ModalHeader.js +4 -4
  82. package/dist/components/NavigationTab.cjs +5 -0
  83. package/dist/components/NavigationTab.js +3 -3
  84. package/dist/components/NavigationTabs.cjs +5 -0
  85. package/dist/components/NavigationTabs.js +3 -3
  86. package/dist/components/NestedMenu.cjs +5 -0
  87. package/dist/components/NestedMenu.js +4 -4
  88. package/dist/components/Notification.cjs +5 -0
  89. package/dist/components/Notification.js +7 -7
  90. package/dist/components/OptionPill.cjs +5 -0
  91. package/dist/components/OptionPill.js +4 -4
  92. package/dist/components/PDFViewer.cjs +5 -0
  93. package/dist/components/PDFViewer.js +11 -11
  94. package/dist/components/Paragraph.cjs +5 -0
  95. package/dist/components/Paragraph.js +2 -2
  96. package/dist/components/Password.cjs +5 -0
  97. package/dist/components/Password.js +3 -3
  98. package/dist/components/ProjectBar.cjs +5 -0
  99. package/dist/components/ProjectBar.js +1 -1
  100. package/dist/components/Radio.cjs +5 -0
  101. package/dist/components/Radio.js +2 -2
  102. package/dist/components/Search.cjs +5 -0
  103. package/dist/components/Search.js +4 -4
  104. package/dist/components/Select.cjs +5 -0
  105. package/dist/components/Select.js +4 -4
  106. package/dist/components/SideMenuGroup.cjs +5 -0
  107. package/dist/components/SideMenuGroup.js +3 -3
  108. package/dist/components/SideMenuItem.cjs +5 -0
  109. package/dist/components/SideMenuItem.js +3 -3
  110. package/dist/components/Stack.cjs +5 -0
  111. package/dist/components/Stack.js +2 -2
  112. package/dist/components/StatusPill.cjs +5 -0
  113. package/dist/components/StatusPill.js +2 -2
  114. package/dist/components/Stepper.cjs +5 -0
  115. package/dist/components/Stepper.js +6 -6
  116. package/dist/components/Subheader.cjs +5 -0
  117. package/dist/components/Subheader.js +2 -2
  118. package/dist/components/Swatch.cjs +5 -0
  119. package/dist/components/Swatch.js +5 -5
  120. package/dist/components/Textarea.cjs +5 -0
  121. package/dist/components/Textarea.js +1 -1
  122. package/dist/components/Time.cjs +5 -0
  123. package/dist/components/Time.js +4 -4
  124. package/dist/components/Toast.cjs +5 -0
  125. package/dist/components/Toast.js +6 -6
  126. package/dist/components/Tooltip.cjs +5 -0
  127. package/dist/components/Tooltip.js +2 -2
  128. package/dist/components/TopBar.cjs +5 -0
  129. package/dist/components/TopBar.js +1 -1
  130. package/dist/components/Upload.cjs +5 -0
  131. package/dist/components/Upload.js +6 -6
  132. package/dist/components/index.cjs +5 -0
  133. package/dist/components/index.css +15 -13
  134. package/dist/components/index.js +20 -1150
  135. package/dist/index.css +15 -13
  136. package/package.json +1 -1
  137. package/src/classNames.ts +9 -3
  138. package/src/components/Breadcrumbs.tsx +13 -6
  139. package/src/components/CalendarRange.tsx +316 -165
  140. package/src/components/Link.tsx +1 -1
  141. package/src/components/Stack.tsx +1 -1
  142. package/src/index.css +4 -2
  143. package/dist/chunk-SE5DM2IJ.js +0 -350
package/dist/index.css CHANGED
@@ -146,6 +146,8 @@
146
146
  --text-caption-desktop-compact: 12px;
147
147
  --leading-caption-desktop: 1.333;
148
148
  --leading-caption-mobile: 1.333;
149
+ --spacing-mobile-breadcrumb: 24px;
150
+ --spacing-desktop-breadcrumb: 24px;
149
151
  --spacing-desktop-container-gap: 16px;
150
152
  --spacing-mobile-container-gap: 16px;
151
153
  --spacing-desktop-compact-container-gap: 16px;
@@ -1881,12 +1883,12 @@
1881
1883
  .py-4 {
1882
1884
  padding-block: calc(var(--spacing) * 4);
1883
1885
  }
1886
+ .py-\[2px\] {
1887
+ padding-block: 2px;
1888
+ }
1884
1889
  .py-\[15px\] {
1885
1890
  padding-block: 15px;
1886
1891
  }
1887
- .py-\[calc\(var\(--spacing-mobile-component-padding\)_\+_3px\)\] {
1888
- padding-block: calc(var(--spacing-mobile-component-padding) + 3px);
1889
- }
1890
1892
  .py-mobile-component-gap {
1891
1893
  padding-block: var(--spacing-mobile-component-gap);
1892
1894
  }
@@ -2027,6 +2029,10 @@
2027
2029
  --tw-leading: var(--leading-link-mobile);
2028
2030
  line-height: var(--leading-link-mobile);
2029
2031
  }
2032
+ .leading-mobile-breadcrumb {
2033
+ --tw-leading: var(--spacing-mobile-breadcrumb);
2034
+ line-height: var(--spacing-mobile-breadcrumb);
2035
+ }
2030
2036
  .leading-paragraph-mobile {
2031
2037
  --tw-leading: var(--leading-paragraph-mobile);
2032
2038
  line-height: var(--leading-paragraph-mobile);
@@ -3689,11 +3695,6 @@
3689
3695
  padding-inline: var(--spacing-desktop-compact-layout-padding);
3690
3696
  }
3691
3697
  }
3692
- .compact\:py-\[calc\(var\(--spacing-desktop-compact-component-padding\)_\+_3px\)\] {
3693
- &:where([data-compact]) {
3694
- padding-block: calc(var(--spacing-desktop-compact-component-padding) + 3px);
3695
- }
3696
- }
3697
3698
  .compact\:py-desktop-compact-component-gap {
3698
3699
  &:where([data-compact]) {
3699
3700
  padding-block: var(--spacing-desktop-compact-component-gap);
@@ -4005,11 +4006,6 @@
4005
4006
  padding-inline: var(--spacing-desktop-layout-padding);
4006
4007
  }
4007
4008
  }
4008
- .desktop\:py-\[calc\(var\(--spacing-desktop-component-padding\)_\+_3px\)\] {
4009
- @container root (width >= 48rem) {
4010
- padding-block: calc(var(--spacing-desktop-component-padding) + 3px);
4011
- }
4012
- }
4013
4009
  .desktop\:py-desktop-component-gap {
4014
4010
  @container root (width >= 48rem) {
4015
4011
  padding-block: var(--spacing-desktop-component-gap);
@@ -4111,6 +4107,12 @@
4111
4107
  line-height: var(--leading-caption-desktop);
4112
4108
  }
4113
4109
  }
4110
+ .desktop\:leading-desktop-breadcrumb {
4111
+ @container root (width >= 48rem) {
4112
+ --tw-leading: var(--spacing-desktop-breadcrumb);
4113
+ line-height: var(--spacing-desktop-breadcrumb);
4114
+ }
4115
+ }
4114
4116
  .desktop\:leading-display-1-desktop {
4115
4117
  @container root (width >= 48rem) {
4116
4118
  --tw-leading: var(--leading-display-1-desktop);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dmsi/wedgekit-react",
3
3
  "private": false,
4
- "version": "0.0.231",
4
+ "version": "0.0.233",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "tsup",
package/src/classNames.ts CHANGED
@@ -65,6 +65,12 @@ export const typography = {
65
65
  "text-caption-mobile desktop:text-caption-desktop compact:text-caption-desktop-compact",
66
66
  "leading-caption-mobile desktop:leading-caption-desktop",
67
67
  ),
68
+
69
+ breadcrumb: clsx(
70
+ "font-sans font-normal",
71
+ "text-paragraph-mobile desktop:text-paragraph-desktop compact:text-paragraph-desktop-compact",
72
+ "leading-mobile-breadcrumb desktop:leading-desktop-breadcrumb",
73
+ ),
68
74
  };
69
75
 
70
76
  export const baseTransition = clsx(
@@ -96,8 +102,8 @@ export const componentPaddingBottom = clsx(
96
102
  );
97
103
 
98
104
  export const componentPaddingX = clsx(
99
- "px-mobile-component-padding desktop:px-desktop-component-padding compact:px-desktop-compact-component-padding"
100
- )
105
+ "px-mobile-component-padding desktop:px-desktop-component-padding compact:px-desktop-compact-component-padding",
106
+ );
101
107
 
102
108
  export const componentPaddingY = clsx(
103
109
  "py-mobile-component-padding desktop:py-desktop-component-padding compact:py-desktop-compact-component-padding",
@@ -120,7 +126,7 @@ export const componentPaddingMinus2pxBorder = clsx(
120
126
  );
121
127
 
122
128
  export const layoutPaddding = clsx(
123
- "p-mobile-layout-padding desktop:p-desktop-layout-padding compact:p-desktop-compact-layout-padding"
129
+ "p-mobile-layout-padding desktop:p-desktop-layout-padding compact:p-desktop-compact-layout-padding",
124
130
  );
125
131
 
126
132
  export const layoutPaddingBottom = clsx(
@@ -26,13 +26,13 @@ export const Breadcrumb = ({
26
26
  children,
27
27
  onClick,
28
28
  id,
29
- testid
29
+ testid,
30
30
  }: BreadcrumbProps) => {
31
31
  const linkPadding = clsx(
32
32
  "!text-text-link-normal whitespace-nowrap",
33
- "py-[calc(var(--spacing-mobile-component-padding)_+_3px)]",
34
- "desktop:py-[calc(var(--spacing-desktop-component-padding)_+_3px)]",
35
- "compact:py-[calc(var(--spacing-desktop-compact-component-padding)_+_3px)]",
33
+ "py-mobile-component-padding",
34
+ "desktop:py-desktop-component-padding",
35
+ "compact:py-desktop-compact-component-padding",
36
36
  );
37
37
 
38
38
  return (
@@ -42,6 +42,7 @@ export const Breadcrumb = ({
42
42
  data-testid={testid ? `${testid}-link` : undefined}
43
43
  className={linkPadding}
44
44
  href={href}
45
+ typographyStyle="breadcrumb"
45
46
  onClick={onClick}
46
47
  >
47
48
  {children}
@@ -56,7 +57,12 @@ const Chevron = () => (
56
57
  </li>
57
58
  );
58
59
 
59
- export const Breadcrumbs = ({ asCardStyle, id, testid, crumbs }: BreadcrumbsProps) => {
60
+ export const Breadcrumbs = ({
61
+ asCardStyle,
62
+ id,
63
+ testid,
64
+ crumbs,
65
+ }: BreadcrumbsProps) => {
60
66
  const scrollableElement = React.useRef<HTMLElement | null>(null);
61
67
  const [isOverflow, setIsOverflow] = useState(false);
62
68
 
@@ -88,7 +94,8 @@ export const Breadcrumbs = ({ asCardStyle, id, testid, crumbs }: BreadcrumbsProp
88
94
  {
89
95
  "bg-white rounded-base shadow-4 px-mobile-layout-padding desktop:px-desktop-layout-padding compact:px-desktop-compact-layout-padding":
90
96
  asCardStyle,
91
- "px-mobile-container-padding desktop:px-desktop-container-padding compact:px-desktop-compact-container-padding": !asCardStyle,
97
+ "px-mobile-container-padding desktop:px-desktop-container-padding compact:px-desktop-compact-container-padding":
98
+ !asCardStyle,
92
99
  },
93
100
  )}
94
101
  aria-label="Breadcrumb"
@@ -6,8 +6,9 @@ import {
6
6
  layoutGap,
7
7
  } from "../classNames";
8
8
  import { Icon } from "./Icon";
9
- import React, { useEffect, useState } from "react";
9
+ import React, { useEffect, useRef, useState } from "react";
10
10
  import { Temporal } from "@js-temporal/polyfill";
11
+ import { Menu, MenuOption } from ".";
11
12
 
12
13
  export interface CalendarRangeProps {
13
14
  from?: string | number;
@@ -218,6 +219,19 @@ export function CalendarRange({
218
219
  };
219
220
  }
220
221
 
222
+ function getMonthDataWith(monthOffset: number) {
223
+ const monthDate = baseMonth.with({ month: monthOffset }).with({ day: 1 });
224
+ const days = monthDate.daysInMonth;
225
+ const firstDayOffset = monthDate.dayOfWeek % 7;
226
+ return {
227
+ name: monthDate.toLocaleString("en-US", { month: "long" }),
228
+ year: monthDate.year,
229
+ days,
230
+ firstDayOffset,
231
+ date: monthDate,
232
+ };
233
+ }
234
+
221
235
  // Click handlers
222
236
  function handleDayClick(date: Temporal.PlainDate) {
223
237
  if (isDateAvailable && !isDateAvailable(date)) return;
@@ -297,177 +311,314 @@ export function CalendarRange({
297
311
  )}
298
312
  >
299
313
  {(mode === "double" ? [0, 1] : [0]).map((offset, idx) => {
300
- const month = getMonthData(offset);
301
- // Always show 6 weeks (42 days)
302
- const totalCells = 42;
303
- const emptyCells = month.firstDayOffset;
304
314
  return (
305
- <React.Fragment key={month.name + month.year}>
306
- <div
307
- // key={month.name + month.year}
308
- className={clsx("flex flex-col")}
309
- >
310
- <div
311
- className={clsx(
312
- "flex flex-row items-center justify-between",
313
- typography.label,
314
- "text-text-action-primary-normal",
315
- )}
316
- >
317
- {idx === 0 ? (
318
- <button
319
- id={id ? `${id}-prev-month-button` : undefined}
320
- data-testid={
321
- testid ? `${testid}-prev-month-button` : undefined
322
- }
323
- type="button"
324
- className={clsx(
325
- "flex items-center justify-center rounded-base hover:bg-action-100 active:bg-action-300 text-icon-action-primary-normal",
326
- componentPadding,
327
- )}
328
- aria-label="Previous month"
329
- onClick={() =>
330
- setBaseMonth(baseMonth.subtract({ months: 1 }))
331
- }
332
- >
333
- <Icon name="chevron_left" size={24} />
334
- </button>
335
- ) : (
336
- <span className={clsx(componentPadding, "mr-1")} />
337
- )}
338
- <div className="flex gap-desktop-compact-component-padding">
339
- <span className="font-semibold text-text-action-primary-normal text-[14px] leading-[1] truncate">
340
- {month.name}
341
- </span>
342
- <span className="font-semibold text-text-action-primary-normal text-[14px] leading-[1] px-1 truncate">
343
- {month.year}
344
- </span>
345
- </div>
346
- {(mode === "double" ? idx === 1 : true) ? (
347
- <button
348
- id={id ? `${id}-next-month-button` : undefined}
349
- data-testid={
350
- testid ? `${testid}-next-month-button` : undefined
351
- }
352
- type="button"
353
- className={clsx(
354
- "flex items-center justify-center rounded-base hover:bg-action-100 active:bg-action-300 text-icon-action-primary-normal",
355
- componentPadding,
356
- )}
357
- aria-label="Next month"
358
- onClick={() => setBaseMonth(baseMonth.add({ months: 1 }))}
359
- >
360
- <Icon name="chevron_right" size={24} />
361
- </button>
362
- ) : (
363
- <span className={clsx(componentPadding, "ml-1")} />
364
- )}
365
- </div>
366
- <div className={clsx("grid grid-cols-7")}>
367
- {weekDays.map((d) => (
368
- <span
369
- key={d}
370
- className={clsx(
371
- typography.caption,
372
- "text-text-secondary-normal text-center",
373
- "w-10",
374
- )}
375
- >
376
- {d}
377
- </span>
378
- ))}
379
- </div>
380
- <div className={clsx("grid grid-cols-7")}>
381
- {Array.from({ length: totalCells }).map((_, i) => {
382
- const day = i - emptyCells + 1;
383
- const date = month.date.with({ day: 1 }).add({
384
- days: i - emptyCells,
385
- });
386
- const isInMonth = day > 0 && day <= month.days;
387
- const isToday = isInMonth && date.equals(today);
388
- const isSelected =
389
- isInMonth &&
390
- ((!pendingFrom && fromDate && date.equals(fromDate)) ||
391
- (!pendingFrom && toDate && date.equals(toDate)) ||
392
- (pendingFrom && date.equals(pendingFrom)));
393
- const inRange = isInMonth && isInRange(date);
394
- const isDisabled =
395
- !isInMonth ||
396
- (isDateAvailable ? !isDateAvailable(date) : false);
397
- // New: determine if this is the start or end of the range
315
+ <CalendarPane
316
+ key={idx}
317
+ getMonthData={getMonthData}
318
+ getMonthDataWith={getMonthDataWith}
319
+ offset={offset}
320
+ idx={idx}
321
+ id={id}
322
+ testid={testid}
323
+ baseMonth={baseMonth}
324
+ setBaseMonth={setBaseMonth}
325
+ mode={mode}
326
+ pendingFrom={pendingFrom}
327
+ weekDays={weekDays}
328
+ fromDate={fromDate}
329
+ toDate={toDate}
330
+ isDateAvailable={isDateAvailable}
331
+ disableRange={disableRange}
332
+ hoveredDate={hoveredDate}
333
+ isInRange={isInRange}
334
+ today={today}
335
+ setHoveredDate={setHoveredDate}
336
+ handleDayClick={handleDayClick}
337
+ />
338
+ );
339
+ })}
340
+ </div>
341
+ </div>
342
+ );
343
+ }
344
+ function CalendarPane({
345
+ getMonthData,
346
+ getMonthDataWith,
347
+ offset,
348
+ idx,
349
+ id,
350
+ testid,
351
+ baseMonth,
352
+ setBaseMonth,
353
+ mode,
354
+ pendingFrom,
355
+ weekDays,
356
+ fromDate,
357
+ toDate,
358
+ isDateAvailable,
359
+ disableRange,
360
+ hoveredDate,
361
+ isInRange,
362
+ today,
363
+ setHoveredDate,
364
+ handleDayClick,
365
+ }: {
366
+ getMonthData: (offset: number) => {
367
+ name: string;
368
+ year: number;
369
+ days: number;
370
+ firstDayOffset: number;
371
+ date: Temporal.PlainDate;
372
+ };
373
+ getMonthDataWith: (offset: number) => {
374
+ name: string;
375
+ year: number;
376
+ days: number;
377
+ firstDayOffset: number;
378
+ date: Temporal.PlainDate;
379
+ };
380
+ offset: number;
381
+ idx: number;
382
+ id: string | undefined;
383
+ testid: string | undefined;
384
+ baseMonth: Temporal.PlainDate;
385
+ setBaseMonth: (date: Temporal.PlainDate) => void;
386
+ mode: "single" | "range" | "double";
387
+ pendingFrom: Temporal.PlainDate | undefined;
388
+ weekDays: string[];
389
+ fromDate: Temporal.PlainDate | undefined;
390
+ toDate: Temporal.PlainDate | undefined;
391
+ isDateAvailable?: (date: Temporal.PlainDate) => boolean;
392
+ disableRange: boolean;
393
+ hoveredDate: Temporal.PlainDate | undefined;
394
+ isInRange: (date: Temporal.PlainDate) => boolean;
395
+ today: Temporal.PlainDate;
396
+ setHoveredDate: (date: Temporal.PlainDate | undefined) => void;
397
+ handleDayClick: (date: Temporal.PlainDate) => void;
398
+ }) {
399
+ const months = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
400
+ const years = Array.from({ length: 100 }).map(
401
+ (_, i) => baseMonth.year - 50 + i,
402
+ );
403
+ const [monthMenuOpen, setMonthMenuOpen] = useState(false);
404
+ const [yearMenuOpen, setYearMenuOpen] = useState(false);
398
405
 
399
- const hoverDateIsBeforePendingFrom =
400
- hoveredDate &&
401
- pendingFrom &&
402
- Temporal.PlainDate.compare(hoveredDate, pendingFrom) < 0;
406
+ const monthMenuRef = useRef<HTMLButtonElement | null>(null);
407
+ const yearMenuRef = useRef<HTMLButtonElement | null>(null);
408
+ const month = getMonthData(offset);
409
+ // Always show 6 weeks (42 days)
410
+ const totalCells = 42;
411
+ const emptyCells = month.firstDayOffset;
412
+ return (
413
+ <React.Fragment key={month.name + month.year}>
414
+ <div
415
+ // key={month.name + month.year}
416
+ className={clsx("flex flex-col")}
417
+ >
418
+ <div
419
+ className={clsx(
420
+ "flex flex-row items-center justify-between",
421
+ typography.label,
422
+ "text-text-action-primary-normal",
423
+ )}
424
+ >
425
+ {idx === 0 ? (
426
+ <button
427
+ id={id ? `${id}-prev-month-button` : undefined}
428
+ data-testid={testid ? `${testid}-prev-month-button` : undefined}
429
+ type="button"
430
+ className={clsx(
431
+ "flex items-center justify-center rounded-base hover:bg-action-100 active:bg-action-300 text-icon-action-primary-normal",
432
+ componentPadding,
433
+ )}
434
+ aria-label="Previous month"
435
+ onClick={() => setBaseMonth(baseMonth.subtract({ months: 1 }))}
436
+ >
437
+ <Icon name="chevron_left" size={24} />
438
+ </button>
439
+ ) : (
440
+ <span className={clsx(componentPadding, "mr-1")} />
441
+ )}
442
+ <div className="flex gap-desktop-compact-component-padding">
443
+ <button
444
+ ref={(el) => {
445
+ monthMenuRef.current = el;
446
+ }}
447
+ type="button"
448
+ onClick={() => {
449
+ setMonthMenuOpen(true);
450
+ setYearMenuOpen(false);
451
+ }}
452
+ className="font-semibold text-text-action-primary-normal text-[14px] py-[2px] truncate"
453
+ >
454
+ {month.name}
455
+ </button>
456
+ <Menu
457
+ show={monthMenuOpen}
458
+ positionTo={monthMenuRef}
459
+ setShow={() => setMonthMenuOpen(false)}
460
+ >
461
+ {months
462
+ .map((x) => [x, getMonthDataWith(x + 1)] as const)
463
+ .map(([x, m]) => (
464
+ <MenuOption
465
+ key={m.name}
466
+ selected={baseMonth.month === x + 1}
467
+ onClick={() => {
468
+ setBaseMonth(baseMonth.with({ month: x + 1 }));
469
+ setMonthMenuOpen(false);
470
+ }}
471
+ >
472
+ {m.name}
473
+ </MenuOption>
474
+ ))}
475
+ </Menu>
476
+ <button
477
+ ref={(el) => {
478
+ yearMenuRef.current = el;
479
+ }}
480
+ type="button"
481
+ onClick={() => {
482
+ setYearMenuOpen(true);
483
+ setMonthMenuOpen(false);
484
+ }}
485
+ className="font-semibold text-text-action-primary-normal text-[14px] py-[2px] truncate"
486
+ >
487
+ {month.year}
488
+ </button>
489
+ <Menu
490
+ show={yearMenuOpen}
491
+ positionTo={yearMenuRef}
492
+ setShow={() => setYearMenuOpen(false)}
493
+ >
494
+ {years.map((y) => (
495
+ <MenuOption
496
+ key={y}
497
+ selected={baseMonth.year === y}
498
+ onClick={() => {
499
+ setBaseMonth(baseMonth.with({ year: y }));
500
+ setYearMenuOpen(false);
501
+ }}
502
+ >
503
+ {y}
504
+ </MenuOption>
505
+ ))}
506
+ </Menu>
507
+ </div>
508
+ {(mode === "double" ? idx === 1 : true) ? (
509
+ <button
510
+ id={id ? `${id}-next-month-button` : undefined}
511
+ data-testid={testid ? `${testid}-next-month-button` : undefined}
512
+ type="button"
513
+ className={clsx(
514
+ "flex items-center justify-center rounded-base hover:bg-action-100 active:bg-action-300 text-icon-action-primary-normal",
515
+ componentPadding,
516
+ )}
517
+ aria-label="Next month"
518
+ onClick={() => setBaseMonth(baseMonth.add({ months: 1 }))}
519
+ >
520
+ <Icon name="chevron_right" size={24} />
521
+ </button>
522
+ ) : (
523
+ <span className={clsx(componentPadding, "ml-1")} />
524
+ )}
525
+ </div>
526
+ <div className={clsx("grid grid-cols-7")}>
527
+ {weekDays.map((d) => (
528
+ <span
529
+ key={d}
530
+ className={clsx(
531
+ typography.caption,
532
+ "text-text-secondary-normal text-center",
533
+ "w-10",
534
+ )}
535
+ >
536
+ {d}
537
+ </span>
538
+ ))}
539
+ </div>
540
+ <div className={clsx("grid grid-cols-7")}>
541
+ {Array.from({ length: totalCells }).map((_, i) => {
542
+ const day = i - emptyCells + 1;
543
+ const date = month.date.with({ day: 1 }).add({
544
+ days: i - emptyCells,
545
+ });
546
+ const isInMonth = day > 0 && day <= month.days;
547
+ const isToday = isInMonth && date.equals(today);
548
+ const isSelected =
549
+ isInMonth &&
550
+ ((!pendingFrom && fromDate && date.equals(fromDate)) ||
551
+ (!pendingFrom && toDate && date.equals(toDate)) ||
552
+ (pendingFrom && date.equals(pendingFrom)));
553
+ const inRange = isInMonth && isInRange(date);
554
+ const isDisabled =
555
+ !isInMonth || (isDateAvailable ? !isDateAvailable(date) : false);
556
+ // New: determine if this is the start or end of the range
403
557
 
404
- const hoverDateIsAfterPendingFrom =
405
- hoveredDate &&
406
- pendingFrom &&
407
- Temporal.PlainDate.compare(hoveredDate, pendingFrom) >= 0;
558
+ const hoverDateIsBeforePendingFrom =
559
+ hoveredDate &&
560
+ pendingFrom &&
561
+ Temporal.PlainDate.compare(hoveredDate, pendingFrom) < 0;
408
562
 
409
- const isRangeStart =
410
- mode === "single" && disableRange
411
- ? false
412
- : (!pendingFrom &&
413
- isInMonth &&
414
- fromDate &&
415
- date.equals(fromDate)) ||
416
- (hoverDateIsAfterPendingFrom &&
417
- date.equals(pendingFrom));
563
+ const hoverDateIsAfterPendingFrom =
564
+ hoveredDate &&
565
+ pendingFrom &&
566
+ Temporal.PlainDate.compare(hoveredDate, pendingFrom) >= 0;
418
567
 
419
- const isRangeEnd =
420
- mode === "single" && disableRange
421
- ? false
422
- : (!pendingFrom &&
423
- isInMonth &&
424
- toDate &&
425
- date.equals(toDate)) ||
426
- (hoverDateIsBeforePendingFrom &&
427
- date.equals(pendingFrom));
428
- return (
429
- <DateCell
430
- key={i}
431
- id={id ? `${id}-date-${date.toString()}` : undefined}
432
- testid={
433
- testid
434
- ? `${testid}-date-${date.toString()}`
435
- : undefined
436
- }
437
- date={date}
438
- isInMonth={!!isInMonth}
439
- isToday={!!isToday}
440
- isSelected={!!isSelected}
441
- inRange={!!inRange}
442
- isDisabled={!!isDisabled}
443
- onClick={() => handleDayClick(date)}
444
- onMouseEnter={() => setHoveredDate(date)}
445
- onMouseLeave={() => setHoveredDate(undefined)}
446
- isRangeStart={!!isRangeStart}
447
- isRangeEnd={!!isRangeEnd}
448
- isRangeDisabled={mode === "single" && disableRange}
449
- // Add cell padding for spacing
450
- cellPadding={componentPadding}
451
- />
452
- );
453
- })}
454
- </div>
455
- </div>
456
- {mode === "double" && idx === 0 && (
457
- <div
458
- className={clsx(
459
- "self-stretch bg-border-primary-normal rounded-base",
568
+ const isRangeStart =
569
+ mode === "single" && disableRange
570
+ ? false
571
+ : (!pendingFrom &&
572
+ isInMonth &&
573
+ fromDate &&
574
+ date.equals(fromDate)) ||
575
+ (hoverDateIsAfterPendingFrom && date.equals(pendingFrom));
460
576
 
461
- // 1px width, full height, matches Figma divider
462
- "w-px",
463
- )}
464
- />
465
- )}
466
- </React.Fragment>
467
- );
468
- })}
577
+ const isRangeEnd =
578
+ mode === "single" && disableRange
579
+ ? false
580
+ : (!pendingFrom &&
581
+ isInMonth &&
582
+ toDate &&
583
+ date.equals(toDate)) ||
584
+ (hoverDateIsBeforePendingFrom && date.equals(pendingFrom));
585
+ return (
586
+ <DateCell
587
+ key={i}
588
+ id={id ? `${id}-date-${date.toString()}` : undefined}
589
+ testid={
590
+ testid ? `${testid}-date-${date.toString()}` : undefined
591
+ }
592
+ date={date}
593
+ isInMonth={!!isInMonth}
594
+ isToday={!!isToday}
595
+ isSelected={!!isSelected}
596
+ inRange={!!inRange}
597
+ isDisabled={!!isDisabled}
598
+ onClick={() => handleDayClick(date)}
599
+ onMouseEnter={() => setHoveredDate(date)}
600
+ onMouseLeave={() => setHoveredDate(undefined)}
601
+ isRangeStart={!!isRangeStart}
602
+ isRangeEnd={!!isRangeEnd}
603
+ isRangeDisabled={mode === "single" && disableRange}
604
+ // Add cell padding for spacing
605
+ cellPadding={componentPadding}
606
+ />
607
+ );
608
+ })}
609
+ </div>
469
610
  </div>
470
- </div>
611
+ {mode === "double" && idx === 0 && (
612
+ <div
613
+ className={clsx(
614
+ "self-stretch bg-border-primary-normal rounded-base",
615
+
616
+ // 1px width, full height, matches Figma divider
617
+ "w-px",
618
+ )}
619
+ />
620
+ )}
621
+ </React.Fragment>
471
622
  );
472
623
  }
473
624