@neuronection/assistant-ui 0.1.0 → 0.3.0

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 (131) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +40 -9
  3. package/dist/badge.js +1 -1
  4. package/dist/button.js +1 -1
  5. package/dist/check-indicator.d.ts +12 -0
  6. package/dist/check-indicator.js +3 -0
  7. package/dist/check-indicator.js.map +1 -0
  8. package/dist/chip-input.d.ts +14 -0
  9. package/dist/chip-input.js +3 -0
  10. package/dist/chip-input.js.map +1 -0
  11. package/dist/chip-list.d.ts +15 -0
  12. package/dist/chip-list.js +3 -0
  13. package/dist/chip-list.js.map +1 -0
  14. package/dist/chunk-2KO2YIZN.js +357 -0
  15. package/dist/chunk-2KO2YIZN.js.map +1 -0
  16. package/dist/chunk-3522NABN.js +452 -0
  17. package/dist/chunk-3522NABN.js.map +1 -0
  18. package/dist/chunk-4SW3ESVI.js +111 -0
  19. package/dist/chunk-4SW3ESVI.js.map +1 -0
  20. package/dist/chunk-5AVBKEDQ.js +74 -0
  21. package/dist/chunk-5AVBKEDQ.js.map +1 -0
  22. package/dist/chunk-5X3RYDPC.js +54 -0
  23. package/dist/chunk-5X3RYDPC.js.map +1 -0
  24. package/dist/chunk-BJOY3XL7.js +27 -0
  25. package/dist/chunk-BJOY3XL7.js.map +1 -0
  26. package/dist/chunk-BPTGPF3L.js +3 -0
  27. package/dist/chunk-BPTGPF3L.js.map +1 -0
  28. package/dist/chunk-DV3MXDLN.js +46 -0
  29. package/dist/chunk-DV3MXDLN.js.map +1 -0
  30. package/dist/chunk-ESUTPQDO.js +21 -0
  31. package/dist/chunk-ESUTPQDO.js.map +1 -0
  32. package/dist/{chunk-TLTKWLMN.js → chunk-GA3XIDVX.js} +3 -3
  33. package/dist/{chunk-TLTKWLMN.js.map → chunk-GA3XIDVX.js.map} +1 -1
  34. package/dist/chunk-GPIN2ITX.js +63 -0
  35. package/dist/chunk-GPIN2ITX.js.map +1 -0
  36. package/dist/{chunk-56VRWCLJ.js → chunk-GTXGTQ6F.js} +2 -2
  37. package/dist/{chunk-56VRWCLJ.js.map → chunk-GTXGTQ6F.js.map} +1 -1
  38. package/dist/chunk-H5U4KNXF.js +63 -0
  39. package/dist/chunk-H5U4KNXF.js.map +1 -0
  40. package/dist/chunk-HSUCCANX.js +44 -0
  41. package/dist/chunk-HSUCCANX.js.map +1 -0
  42. package/dist/chunk-KXLMTAZ5.js +109 -0
  43. package/dist/chunk-KXLMTAZ5.js.map +1 -0
  44. package/dist/chunk-LEDQQBVV.js +184 -0
  45. package/dist/chunk-LEDQQBVV.js.map +1 -0
  46. package/dist/chunk-MXOBOS4Y.js +60 -0
  47. package/dist/chunk-MXOBOS4Y.js.map +1 -0
  48. package/dist/chunk-N5KQRHXU.js +107 -0
  49. package/dist/chunk-N5KQRHXU.js.map +1 -0
  50. package/dist/chunk-OAR74ML4.js +3 -0
  51. package/dist/chunk-OAR74ML4.js.map +1 -0
  52. package/dist/chunk-OUHUVACM.js +45 -0
  53. package/dist/chunk-OUHUVACM.js.map +1 -0
  54. package/dist/chunk-PPH3G7AL.js +3 -0
  55. package/dist/chunk-PPH3G7AL.js.map +1 -0
  56. package/dist/chunk-RU3DEF2O.js +186 -0
  57. package/dist/chunk-RU3DEF2O.js.map +1 -0
  58. package/dist/{chunk-NQQNTK6L.js → chunk-SKTLC2XY.js} +2 -2
  59. package/dist/{chunk-NQQNTK6L.js.map → chunk-SKTLC2XY.js.map} +1 -1
  60. package/dist/chunk-TPLQL62V.js +124 -0
  61. package/dist/chunk-TPLQL62V.js.map +1 -0
  62. package/dist/chunk-UIUALJNR.js +110 -0
  63. package/dist/chunk-UIUALJNR.js.map +1 -0
  64. package/dist/chunk-WZSAUESL.js +58 -0
  65. package/dist/chunk-WZSAUESL.js.map +1 -0
  66. package/dist/chunk-XE4TFW63.js +34 -0
  67. package/dist/chunk-XE4TFW63.js.map +1 -0
  68. package/dist/chunk-XLD2BPTT.js +82 -0
  69. package/dist/chunk-XLD2BPTT.js.map +1 -0
  70. package/dist/combobox.d.ts +54 -0
  71. package/dist/combobox.js +3 -0
  72. package/dist/combobox.js.map +1 -0
  73. package/dist/confirmation-modal.js +1 -1
  74. package/dist/context-menu.d.ts +22 -0
  75. package/dist/context-menu.js +3 -0
  76. package/dist/context-menu.js.map +1 -0
  77. package/dist/date-picker.d.ts +26 -0
  78. package/dist/date-picker.js +3 -0
  79. package/dist/date-picker.js.map +1 -0
  80. package/dist/error-banner.d.ts +10 -0
  81. package/dist/error-banner.js +3 -0
  82. package/dist/error-banner.js.map +1 -0
  83. package/dist/expandable-search.d.ts +16 -0
  84. package/dist/expandable-search.js +3 -0
  85. package/dist/expandable-search.js.map +1 -0
  86. package/dist/field-label.d.ts +13 -0
  87. package/dist/field-label.js +3 -0
  88. package/dist/field-label.js.map +1 -0
  89. package/dist/form-modal.d.ts +23 -0
  90. package/dist/form-modal.js +3 -0
  91. package/dist/form-modal.js.map +1 -0
  92. package/dist/index.d.ts +23 -0
  93. package/dist/index.js +30 -8
  94. package/dist/info-button.d.ts +13 -0
  95. package/dist/info-button.js +3 -0
  96. package/dist/info-button.js.map +1 -0
  97. package/dist/marquee.d.ts +39 -0
  98. package/dist/marquee.js +3 -0
  99. package/dist/marquee.js.map +1 -0
  100. package/dist/menu.d.ts +38 -0
  101. package/dist/menu.js +3 -0
  102. package/dist/menu.js.map +1 -0
  103. package/dist/popover-button.d.ts +23 -0
  104. package/dist/popover-button.js +3 -0
  105. package/dist/popover-button.js.map +1 -0
  106. package/dist/range-bar.d.ts +15 -0
  107. package/dist/range-bar.js +3 -0
  108. package/dist/range-bar.js.map +1 -0
  109. package/dist/scale-slider.d.ts +18 -0
  110. package/dist/scale-slider.js +3 -0
  111. package/dist/scale-slider.js.map +1 -0
  112. package/dist/search-input.d.ts +15 -0
  113. package/dist/search-input.js +3 -0
  114. package/dist/search-input.js.map +1 -0
  115. package/dist/selection-bar.d.ts +13 -0
  116. package/dist/selection-bar.js +3 -0
  117. package/dist/selection-bar.js.map +1 -0
  118. package/dist/styles.css +1 -1
  119. package/dist/table.d.ts +10 -0
  120. package/dist/table.js +3 -0
  121. package/dist/table.js.map +1 -0
  122. package/dist/undo-notice.d.ts +14 -0
  123. package/dist/undo-notice.js +3 -0
  124. package/dist/undo-notice.js.map +1 -0
  125. package/dist/view-toggle.d.ts +13 -0
  126. package/dist/view-toggle.js +3 -0
  127. package/dist/view-toggle.js.map +1 -0
  128. package/dist/wizard.d.ts +57 -0
  129. package/dist/wizard.js +3 -0
  130. package/dist/wizard.js.map +1 -0
  131. package/package.json +97 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # @neuronection/assistant-ui
2
+
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`e8a56d3`](https://github.com/neuronection/assistant-ui/commit/e8a56d33427fc2e8e03784fe7959037f24efb95a) Thanks [@constLiakos](https://github.com/constLiakos)! - Add the six components career-assistant's Phase-2 adoption needs, all shared
8
+ with health-assistant (catalog T1): `ChipInput`, `ChipList` (superset with
9
+ variants + clickable chips), `Table` (tokenized, with empty state), `RangeBar`
10
+ (with optional `label`/`valueLabel`), `ScaleSlider` (+ `scaleColorForValue`),
11
+ and `DatePicker` (Radix popover calendar with days/months/years views, min/max,
12
+ allowClear, and a roving-tabindex arrow-key day grid — an a11y upgrade over
13
+ both app copies). New dependency: `date-fns`.
14
+
15
+ ### Patch Changes
16
+
17
+ - [`e5252f2`](https://github.com/neuronection/assistant-ui/commit/e5252f26d6f8732a2f5ba4a3fe849b4df50b30c2) Thanks [@constLiakos](https://github.com/constLiakos)! - Ship CHANGELOG.md inside the published package so consuming apps (and their agents) can read what changed without visiting GitHub.
18
+
19
+ ## 0.2.0
20
+
21
+ ### Minor Changes
22
+
23
+ - [`ec2a8bf`](https://github.com/neuronection/assistant-ui/commit/ec2a8bf7d49bb96435990e9b8cca4d74fc31748b) Thanks [@constLiakos](https://github.com/constLiakos)! - Phase 1: 16 new modules for study-assistant full adoption
24
+
25
+ - PopoverButton: self-contained trigger+panel popover on Radix (hover-open, lazy children, closeSignal, controlled mode)
26
+ - InfoButton + FieldLabel: info affordances with popover help
27
+ - Menu: Radix DropdownMenu primitives (Menu, MenuItem, MenuContent…) + items-driven ActionMenu
28
+ - ContextMenu: coordinate-anchored menu with full Radix keyboard/typeahead semantics
29
+ - Combobox + ComboboxMulti: WAI-ARIA listbox pattern, grouping, async mode (onSearchChange + loading), multi-select, clearable
30
+ - FormModal: form-in-modal shell (submit/cancel/busy/disabled) on Modal primitives
31
+ - Wizard + Stepper: wizard state machine with per-step validation gates, modal and drawer variants; dots/labels steppers
32
+ - Ported from study-assistant ui/: CheckIndicator, SearchInput, ExpandableSearch, SelectionBar, ViewToggle, ErrorBanner, UndoNotice, Marquee (useMarquee/MarqueeBand/MarqueeSurface)
33
+
34
+ All components: forwardRef where DOM-emitting, semantic --as-\* tokens only, data-as hooks, English-default label props, keyboard-nav + axe tests.
35
+
36
+ ## 0.1.0
37
+
38
+ ### Minor Changes
39
+
40
+ - [`a211003`](https://github.com/neuronection/assistant-ui/commit/a211003348a5248f65a7995806500325408b6f92) Thanks [@constLiakos](https://github.com/constLiakos)! - Initial release (Phase 0): Button, Badge, Card, Modal, Popover, Tooltip + InfoTooltip, Input, Spinner, EmptyState, ConfirmationModal, Portal, ThemeScope and `cn()` — CSS-variable design tokens, precompiled `styles.css`, React 18/19 and Tailwind 3/4 compatible.
package/README.md CHANGED
@@ -55,13 +55,41 @@ Tailwind is **not** required — but if your app uses Tailwind 3 or 4, both work
55
55
  without configuration. Do **not** add this package to your Tailwind
56
56
  `content`/`@source`.
57
57
 
58
+ ## Documentation
59
+
60
+ - [Local development](./docs/local-development.md) — live-test library changes in the family apps without publishing
61
+ - [Adopting in an app](./docs/adoption.md) — the install/switchover recipe each family app follows
62
+ - [Theming](./docs/theming.md) — tokens, app themes, styling hooks
63
+ - [Publishing](./docs/publishing.md) — the Changesets release flow and CI setup
64
+ - [Adding a component](./docs/adding-a-component.md) — rules, checklist, required companions
65
+
58
66
  ## Components
59
67
 
60
- Button, Badge, Card (+Header/Title/Description/Content/Footer), Modal
61
- (+Trigger/Header/Title/Description/Footer/Close), Popover, Tooltip +
62
- InfoTooltip, Input, Spinner, EmptyState, ConfirmationModal, Portal,
63
- ThemeScope, `cn()`. Full props in the
64
- [gallery](https://neuronection.github.io/assistant-ui/) (Ladle build).
68
+ Every module has its own entry point (`import { Menu } from
69
+ '@neuronection/assistant-ui/menu'`) and everything is re-exported from the
70
+ package root. Authoritative props for your installed version: the `.d.ts`
71
+ files in the package's `dist/`. Visual reference:
72
+ [gallery](https://neuronection.github.io/assistant-ui/).
73
+
74
+ | Module | Exports |
75
+ |---|---|
76
+ | `button` / `badge` / `card` | `Button`, `buttonVariants` · `Badge`, `badgeVariants` · `Card`, `CardHeader/Title/Description/Content/Footer` |
77
+ | `modal` / `confirmation-modal` / `form-modal` | `Modal` + parts, `modalContentVariants` · `ConfirmationModal` · `FormModal` (form-in-modal shell) |
78
+ | `popover` / `popover-button` | compound Radix `Popover` + parts · `PopoverButton` (self-contained trigger + panel, hover-open, lazy children, `closeSignal`) |
79
+ | `menu` / `context-menu` | `Menu`, `MenuTrigger/Content/Item/Separator/Label`, `ActionMenu` (items-driven) · `ContextMenu` (coordinate-anchored, `{x, y, items, onClose}`) |
80
+ | `combobox` | `Combobox`, `ComboboxMulti` — async mode (`onSearchChange` + `loading`), grouping, full keyboard nav |
81
+ | `tooltip` | `Tooltip` + parts, `InfoTooltip` |
82
+ | `wizard` | `Wizard` (steps config + `renderStep`, per-step validation gates, modal/drawer variants), `Stepper` (dots/labels) |
83
+ | `input` / `search-input` / `expandable-search` | `Input` (label/hint/error wiring) · `SearchInput` · `ExpandableSearch` |
84
+ | `chip-input` / `chip-list` | `ChipInput` (Enter/comma commit, paste-splits, Backspace-removes) · `ChipList` (variant pills, clickable, removable) |
85
+ | `date-picker` | `DatePicker` (popover calendar: days/months/years views, min/max, `allowClear`, arrow-key day grid) |
86
+ | `range-bar` / `scale-slider` | `RangeBar` (low–high band + value dot) · `ScaleSlider` (+ `scaleColorForValue`) |
87
+ | `table` | `Table` (headers/rows, built-in empty state) |
88
+ | `check-indicator` / `selection-bar` / `view-toggle` | `CheckIndicator` (tri-state) · `SelectionBar` (bulk-select bar) · `ViewToggle` (grid/list) |
89
+ | `error-banner` / `undo-notice` / `empty-state` / `spinner` | presentational feedback: alert banner with `action` slot · undo toast · `EmptyState` · `Spinner` |
90
+ | `info-button` / `field-label` | `InfoButton` (info popover) · `FieldLabel` (label + info affordance) |
91
+ | `marquee` | `useMarquee`, `MarqueeSurface`, `MarqueeBand` (rubber-band selection) |
92
+ | `portal` / `theme-scope` / `tokens` | `Portal` · `ThemeScope` · token name lists + types |
65
93
 
66
94
  API rules: controlled-first, `className` merges (never replaces), `asChild`
67
95
  where it makes sense, refs forwarded everywhere, English label props with
@@ -77,11 +105,14 @@ pnpm test --watch
77
105
  pnpm verify # lint + typecheck + test + build
78
106
  ```
79
107
 
80
- Testing library changes inside a real app (temporary, never commit):
108
+ Testing library changes inside a real app (no publishing needed) — full
109
+ guide in [docs/local-development.md](./docs/local-development.md):
81
110
 
82
- ```jsonc
83
- // app package.json pnpm.overrides
84
- { "@neuronection/assistant-ui": "link:../assistant-ui/dist" }
111
+ ```bash
112
+ pnpm watch # library: rebuild TS + CSS on change
113
+ node scripts/dev-link.mjs link <app-dir> # wire app (pnpm override / npm file:)
114
+ # ...run the app dev server; reload picks up library changes
115
+ node scripts/dev-link.mjs unlink <app-dir> # restore before committing in the app
85
116
  ```
86
117
 
87
118
  Releases happen via [Changesets](./CONTRIBUTING.md) — CI publishes, never a
package/dist/badge.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Badge, badgeVariants } from './chunk-NQQNTK6L.js';
1
+ export { Badge, badgeVariants } from './chunk-SKTLC2XY.js';
2
2
  //# sourceMappingURL=badge.js.map
3
3
  //# sourceMappingURL=badge.js.map
package/dist/button.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Button, buttonVariants } from './chunk-56VRWCLJ.js';
1
+ export { Button, buttonVariants } from './chunk-GTXGTQ6F.js';
2
2
  //# sourceMappingURL=button.js.map
3
3
  //# sourceMappingURL=button.js.map
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+
3
+ interface CheckIndicatorProps {
4
+ checked: boolean;
5
+ label: string;
6
+ onToggle: () => void;
7
+ mixed?: boolean;
8
+ className?: string;
9
+ }
10
+ declare const CheckIndicator: React.ForwardRefExoticComponent<CheckIndicatorProps & React.RefAttributes<HTMLButtonElement>>;
11
+
12
+ export { CheckIndicator, type CheckIndicatorProps };
@@ -0,0 +1,3 @@
1
+ export { CheckIndicator } from './chunk-XE4TFW63.js';
2
+ //# sourceMappingURL=check-indicator.js.map
3
+ //# sourceMappingURL=check-indicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"check-indicator.js"}
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+
3
+ interface ChipInputProps extends Omit<React.ComponentProps<'div'>, 'onChange'> {
4
+ value: string[];
5
+ onChange: (value: string[]) => void;
6
+ placeholder?: string;
7
+ separators?: string[];
8
+ inputLabel?: string;
9
+ removeLabel?: string;
10
+ disabled?: boolean;
11
+ }
12
+ declare const ChipInput: React.ForwardRefExoticComponent<Omit<ChipInputProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
13
+
14
+ export { ChipInput, type ChipInputProps };
@@ -0,0 +1,3 @@
1
+ export { ChipInput } from './chunk-TPLQL62V.js';
2
+ //# sourceMappingURL=chip-input.js.map
3
+ //# sourceMappingURL=chip-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chip-input.js"}
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+
3
+ type ChipVariant = 'neutral' | 'primary' | 'success' | 'warning' | 'danger';
4
+ interface ChipListProps extends React.ComponentProps<'div'> {
5
+ items: ReadonlyArray<string | null | undefined>;
6
+ onRemove?: (item: string) => void;
7
+ onItemClick?: (value: string, index: number) => void;
8
+ variant?: ChipVariant;
9
+ emptyText?: string;
10
+ showChevron?: boolean;
11
+ removeLabel?: string;
12
+ }
13
+ declare const ChipList: React.ForwardRefExoticComponent<Omit<ChipListProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
14
+
15
+ export { ChipList, type ChipListProps, type ChipVariant };
@@ -0,0 +1,3 @@
1
+ export { ChipList } from './chunk-XLD2BPTT.js';
2
+ //# sourceMappingURL=chip-list.js.map
3
+ //# sourceMappingURL=chip-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chip-list.js"}
@@ -0,0 +1,357 @@
1
+ import { Popover, PopoverTrigger, PopoverContent } from './chunk-CI56SSO2.js';
2
+ import { cn } from './chunk-BLJWR4ZV.js';
3
+ import * as React from 'react';
4
+ import { parseISO, isValid, startOfDay, startOfMonth, endOfMonth, startOfWeek, endOfWeek, addDays, format, isSameDay, isSameMonth, setMonth, setYear, subMonths, subYears, addMonths, addYears } from 'date-fns';
5
+ import { Calendar, X, ChevronLeft, ChevronDown, ChevronRight } from 'lucide-react';
6
+ import { jsx, jsxs } from 'react/jsx-runtime';
7
+
8
+ function dayKey(date) {
9
+ return format(date, "yyyy-MM-dd");
10
+ }
11
+ var DatePicker = React.forwardRef(
12
+ function DatePicker2({
13
+ value,
14
+ onChange,
15
+ onClear,
16
+ allowClear = false,
17
+ clearLabel = "Clear",
18
+ placeholder = "Select date",
19
+ displayFormat = "dd/MM/yyyy",
20
+ minDate,
21
+ maxDate,
22
+ weekStartsOn = 1,
23
+ disabled = false,
24
+ required,
25
+ id,
26
+ name,
27
+ label = "Choose date",
28
+ prevLabel = "Previous",
29
+ nextLabel = "Next",
30
+ className,
31
+ panelClassName
32
+ }, ref) {
33
+ const autoId = React.useId();
34
+ const hiddenId = id ?? `date-picker-${autoId}`;
35
+ const [open, setOpen] = React.useState(false);
36
+ const [viewMode, setViewMode] = React.useState("days");
37
+ const [currentDate, setCurrentDate] = React.useState(() => /* @__PURE__ */ new Date());
38
+ const [focusedDay, setFocusedDay] = React.useState(() => /* @__PURE__ */ new Date());
39
+ const [yearPage, setYearPage] = React.useState(() => (/* @__PURE__ */ new Date()).getFullYear());
40
+ const gridRef = React.useRef(null);
41
+ const selectedDate = React.useMemo(() => {
42
+ if (!value) return null;
43
+ const parsed = parseISO(value);
44
+ return isValid(parsed) ? parsed : null;
45
+ }, [value]);
46
+ const isDisabled = React.useCallback(
47
+ (date) => Boolean(minDate && date < startOfDay(minDate)) || Boolean(maxDate && date > startOfDay(maxDate)),
48
+ [minDate, maxDate]
49
+ );
50
+ const handleOpenChange = (next) => {
51
+ setOpen(next);
52
+ if (next) {
53
+ const base = selectedDate ?? /* @__PURE__ */ new Date();
54
+ setCurrentDate(base);
55
+ setFocusedDay(base);
56
+ setYearPage(base.getFullYear());
57
+ setViewMode("days");
58
+ }
59
+ };
60
+ const handleSelect = (date) => {
61
+ if (isDisabled(date)) return;
62
+ onChange(format(date, "yyyy-MM-dd"));
63
+ setOpen(false);
64
+ };
65
+ const handleClear = (event) => {
66
+ event.stopPropagation();
67
+ onChange("");
68
+ onClear?.();
69
+ setOpen(false);
70
+ };
71
+ const moveFocus = (next) => {
72
+ if (isDisabled(next)) return;
73
+ setFocusedDay(next);
74
+ setCurrentDate(
75
+ (current) => isSameMonth(current, next) ? current : startOfMonth(next)
76
+ );
77
+ };
78
+ const handleGridKeyDown = (event) => {
79
+ const step = event.key === "ArrowLeft" ? -1 : event.key === "ArrowRight" ? 1 : event.key === "ArrowUp" ? -7 : event.key === "ArrowDown" ? 7 : 0;
80
+ if (step !== 0) {
81
+ event.preventDefault();
82
+ moveFocus(addDays(focusedDay, step));
83
+ return;
84
+ }
85
+ if (event.key === "PageUp" || event.key === "PageDown") {
86
+ event.preventDefault();
87
+ const direction = event.key === "PageUp" ? -1 : 1;
88
+ moveFocus(addMonths(focusedDay, direction));
89
+ return;
90
+ }
91
+ if (event.key === "Home") {
92
+ event.preventDefault();
93
+ moveFocus(startOfMonth(focusedDay));
94
+ } else if (event.key === "End") {
95
+ event.preventDefault();
96
+ moveFocus(endOfMonth(focusedDay));
97
+ }
98
+ };
99
+ React.useEffect(() => {
100
+ if (!open || viewMode !== "days") return;
101
+ const target = gridRef.current?.querySelector(
102
+ `[data-day="${dayKey(focusedDay)}"]`
103
+ );
104
+ target?.focus();
105
+ }, [open, viewMode, focusedDay]);
106
+ const monthStart = startOfMonth(currentDate);
107
+ const monthEnd = endOfMonth(monthStart);
108
+ const gridStart = startOfWeek(monthStart, { weekStartsOn });
109
+ const gridEnd = endOfWeek(monthEnd, { weekStartsOn });
110
+ const weeks = [];
111
+ let cursor = gridStart;
112
+ while (cursor <= gridEnd) {
113
+ const week = [];
114
+ for (let i = 0; i < 7; i++) {
115
+ week.push(cursor);
116
+ cursor = addDays(cursor, 1);
117
+ }
118
+ weeks.push(week);
119
+ }
120
+ const today = /* @__PURE__ */ new Date();
121
+ const weekdayStart = startOfWeek(/* @__PURE__ */ new Date(), { weekStartsOn });
122
+ const headerTitle = viewMode === "days" ? format(currentDate, "MMMM yyyy") : viewMode === "months" ? format(currentDate, "yyyy") : `${Math.floor(yearPage / 12) * 12} \u2013 ${Math.floor(yearPage / 12) * 12 + 11}`;
123
+ const handlePrev = () => {
124
+ if (viewMode === "days") setCurrentDate(subMonths(currentDate, 1));
125
+ else if (viewMode === "months") setCurrentDate(subYears(currentDate, 1));
126
+ else setYearPage(yearPage - 12);
127
+ };
128
+ const handleNext = () => {
129
+ if (viewMode === "days") setCurrentDate(addMonths(currentDate, 1));
130
+ else if (viewMode === "months") setCurrentDate(addYears(currentDate, 1));
131
+ else setYearPage(yearPage + 12);
132
+ };
133
+ const toggleViewMode = () => {
134
+ if (viewMode !== "years") setViewMode("years");
135
+ };
136
+ const navButtonClass = "cursor-pointer rounded-[var(--as-radius-sm)] p-1.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]";
137
+ return /* @__PURE__ */ jsx(
138
+ "div",
139
+ {
140
+ ref,
141
+ "data-as": "date-picker",
142
+ className: cn("relative w-full", className),
143
+ children: /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
144
+ /* @__PURE__ */ jsxs("span", { className: "relative inline-flex w-full", children: [
145
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, disabled, children: /* @__PURE__ */ jsxs(
146
+ "button",
147
+ {
148
+ type: "button",
149
+ "aria-label": label,
150
+ className: cn(
151
+ "flex h-9 w-full items-center gap-2 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-left text-sm text-[var(--as-fg)] transition-colors hover:border-[var(--as-fg)]/30 focus-visible:border-[var(--as-focus-ring)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50",
152
+ allowClear && selectedDate && !disabled && "pr-8"
153
+ ),
154
+ children: [
155
+ /* @__PURE__ */ jsx(
156
+ Calendar,
157
+ {
158
+ className: "size-4 shrink-0 text-[var(--as-muted-fg)]",
159
+ "aria-hidden": true
160
+ }
161
+ ),
162
+ /* @__PURE__ */ jsx(
163
+ "span",
164
+ {
165
+ className: cn(
166
+ "flex-1 truncate text-left",
167
+ !selectedDate && "text-[var(--as-muted-fg)]"
168
+ ),
169
+ children: selectedDate ? format(selectedDate, displayFormat) : placeholder
170
+ }
171
+ )
172
+ ]
173
+ }
174
+ ) }),
175
+ allowClear && selectedDate && !disabled ? /* @__PURE__ */ jsx(
176
+ "button",
177
+ {
178
+ type: "button",
179
+ "aria-label": clearLabel,
180
+ onClick: handleClear,
181
+ className: "absolute right-2 top-1/2 -translate-y-1/2 cursor-pointer rounded-full p-0.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
182
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5", "aria-hidden": true })
183
+ }
184
+ ) : null
185
+ ] }),
186
+ /* @__PURE__ */ jsx(
187
+ "input",
188
+ {
189
+ type: "hidden",
190
+ id: hiddenId,
191
+ name: name ?? hiddenId,
192
+ value: value ?? "",
193
+ required
194
+ }
195
+ ),
196
+ /* @__PURE__ */ jsxs(
197
+ PopoverContent,
198
+ {
199
+ align: "start",
200
+ sideOffset: 4,
201
+ collisionPadding: 8,
202
+ "aria-label": label,
203
+ onOpenAutoFocus: (event) => {
204
+ event.preventDefault();
205
+ const panel = event.currentTarget;
206
+ const target = panel?.querySelector(
207
+ '[data-day][tabindex="0"]'
208
+ );
209
+ target?.focus();
210
+ },
211
+ className: cn(
212
+ "as-anim-pop z-50 w-[280px] rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-2 text-[var(--as-fg)] shadow-[var(--as-shadow-3)] outline-none",
213
+ panelClassName
214
+ ),
215
+ children: [
216
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between px-1 pt-1", children: [
217
+ /* @__PURE__ */ jsx(
218
+ "button",
219
+ {
220
+ type: "button",
221
+ "aria-label": prevLabel,
222
+ onClick: handlePrev,
223
+ className: navButtonClass,
224
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-5", "aria-hidden": true })
225
+ }
226
+ ),
227
+ /* @__PURE__ */ jsxs(
228
+ "button",
229
+ {
230
+ type: "button",
231
+ onClick: toggleViewMode,
232
+ className: "flex cursor-pointer items-center rounded-[var(--as-radius-sm)] px-3 py-1.5 text-sm font-semibold transition-colors hover:bg-[var(--as-muted)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
233
+ children: [
234
+ /* @__PURE__ */ jsx("span", { children: headerTitle }),
235
+ viewMode !== "years" ? /* @__PURE__ */ jsx(ChevronDown, { className: "ml-1 size-4", "aria-hidden": true }) : null
236
+ ]
237
+ }
238
+ ),
239
+ /* @__PURE__ */ jsx(
240
+ "button",
241
+ {
242
+ type: "button",
243
+ "aria-label": nextLabel,
244
+ onClick: handleNext,
245
+ className: navButtonClass,
246
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-5", "aria-hidden": true })
247
+ }
248
+ )
249
+ ] }),
250
+ viewMode === "days" ? /* @__PURE__ */ jsx("div", { className: "px-2 pb-2", children: /* @__PURE__ */ jsxs(
251
+ "div",
252
+ {
253
+ ref: gridRef,
254
+ role: "grid",
255
+ "aria-label": label,
256
+ onKeyDown: handleGridKeyDown,
257
+ children: [
258
+ /* @__PURE__ */ jsx("div", { role: "row", className: "mb-2 grid grid-cols-7 gap-1", children: Array.from({ length: 7 }, (_, i) => /* @__PURE__ */ jsx(
259
+ "div",
260
+ {
261
+ role: "columnheader",
262
+ className: "py-1 text-center text-xs font-medium text-[var(--as-muted-fg)]",
263
+ children: format(addDays(weekdayStart, i), "EEEEE")
264
+ },
265
+ i
266
+ )) }),
267
+ weeks.map((week, weekIndex) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-1", children: week.map((day) => {
268
+ const isSelected = selectedDate ? isSameDay(day, selectedDate) : false;
269
+ const isFocused = isSameDay(day, focusedDay);
270
+ const outside = !isSameMonth(day, monthStart);
271
+ const isToday = isSameDay(day, today);
272
+ const dayDisabled = isDisabled(day);
273
+ return /* @__PURE__ */ jsx(
274
+ "div",
275
+ {
276
+ role: "gridcell",
277
+ "aria-selected": isSelected,
278
+ className: "p-0",
279
+ children: /* @__PURE__ */ jsx(
280
+ "button",
281
+ {
282
+ type: "button",
283
+ "data-day": dayKey(day),
284
+ tabIndex: isFocused ? 0 : -1,
285
+ disabled: dayDisabled,
286
+ "aria-current": isToday ? "date" : void 0,
287
+ onClick: () => handleSelect(day),
288
+ className: cn(
289
+ "flex size-8 cursor-pointer items-center justify-center rounded-full text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
290
+ outside ? "text-[var(--as-muted-fg)] opacity-50" : "text-[var(--as-fg)]",
291
+ isSelected && "bg-[var(--as-primary)] font-medium text-[var(--as-primary-fg)] hover:brightness-110",
292
+ !isSelected && isToday && "bg-[color-mix(in_srgb,var(--as-primary)_12%,transparent)] font-medium text-[var(--as-primary)]",
293
+ !isSelected && !dayDisabled && "hover:bg-[var(--as-muted)]",
294
+ dayDisabled && "cursor-not-allowed opacity-30 hover:bg-transparent"
295
+ ),
296
+ children: format(day, "d")
297
+ }
298
+ )
299
+ },
300
+ dayKey(day)
301
+ );
302
+ }) }, weekIndex))
303
+ ]
304
+ }
305
+ ) }) : null,
306
+ viewMode === "months" ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 p-2", children: Array.from({ length: 12 }, (_, i) => {
307
+ const isSelected = selectedDate?.getMonth() === i && selectedDate?.getFullYear() === currentDate.getFullYear();
308
+ return /* @__PURE__ */ jsx(
309
+ "button",
310
+ {
311
+ type: "button",
312
+ onClick: () => {
313
+ setCurrentDate(setMonth(currentDate, i));
314
+ setViewMode("days");
315
+ },
316
+ className: cn(
317
+ "cursor-pointer rounded-[var(--as-radius)] py-3 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
318
+ isSelected ? "bg-[var(--as-primary)] text-[var(--as-primary-fg)]" : "text-[var(--as-fg)] hover:bg-[var(--as-muted)]"
319
+ ),
320
+ children: format(setMonth(/* @__PURE__ */ new Date(), i), "MMM")
321
+ },
322
+ i
323
+ );
324
+ }) }) : null,
325
+ viewMode === "years" ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 p-2", children: Array.from({ length: 12 }, (_, i) => {
326
+ const year = Math.floor(yearPage / 12) * 12 + i;
327
+ const isSelected = selectedDate?.getFullYear() === year;
328
+ return /* @__PURE__ */ jsx(
329
+ "button",
330
+ {
331
+ type: "button",
332
+ onClick: () => {
333
+ setCurrentDate(setYear(currentDate, year));
334
+ setFocusedDay((current) => setYear(current, year));
335
+ setViewMode("months");
336
+ },
337
+ className: cn(
338
+ "cursor-pointer rounded-[var(--as-radius)] py-3 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
339
+ isSelected ? "bg-[var(--as-primary)] text-[var(--as-primary-fg)]" : "text-[var(--as-fg)] hover:bg-[var(--as-muted)]"
340
+ ),
341
+ children: year
342
+ },
343
+ year
344
+ );
345
+ }) }) : null
346
+ ]
347
+ }
348
+ )
349
+ ] })
350
+ }
351
+ );
352
+ }
353
+ );
354
+
355
+ export { DatePicker };
356
+ //# sourceMappingURL=chunk-2KO2YIZN.js.map
357
+ //# sourceMappingURL=chunk-2KO2YIZN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/date-picker/DatePicker.tsx"],"names":["DatePicker","CalendarIcon"],"mappings":"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","file":"chunk-2KO2YIZN.js","sourcesContent":["import * as React from 'react'\nimport {\n addDays,\n addMonths,\n addYears,\n endOfMonth,\n endOfWeek,\n format,\n isSameDay,\n isSameMonth,\n isValid,\n parseISO,\n setMonth,\n setYear,\n startOfDay,\n startOfMonth,\n startOfWeek,\n subMonths,\n subYears,\n} from 'date-fns'\nimport {\n Calendar as CalendarIcon,\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n X,\n} from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '../popover'\n\ntype ViewMode = 'days' | 'months' | 'years'\n\nexport interface DatePickerProps {\n value: string | null | undefined\n onChange: (value: string) => void\n onClear?: () => void\n allowClear?: boolean\n clearLabel?: string\n placeholder?: string\n displayFormat?: string\n minDate?: Date\n maxDate?: Date\n weekStartsOn?: 0 | 1\n disabled?: boolean\n required?: boolean\n id?: string\n name?: string\n label?: string\n prevLabel?: string\n nextLabel?: string\n className?: string\n panelClassName?: string\n}\n\nfunction dayKey(date: Date): string {\n return format(date, 'yyyy-MM-dd')\n}\n\nexport const DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>(\n function DatePicker(\n {\n value,\n onChange,\n onClear,\n allowClear = false,\n clearLabel = 'Clear',\n placeholder = 'Select date',\n displayFormat = 'dd/MM/yyyy',\n minDate,\n maxDate,\n weekStartsOn = 1,\n disabled = false,\n required,\n id,\n name,\n label = 'Choose date',\n prevLabel = 'Previous',\n nextLabel = 'Next',\n className,\n panelClassName,\n },\n ref,\n ) {\n const autoId = React.useId()\n const hiddenId = id ?? `date-picker-${autoId}`\n const [open, setOpen] = React.useState(false)\n const [viewMode, setViewMode] = React.useState<ViewMode>('days')\n const [currentDate, setCurrentDate] = React.useState<Date>(() => new Date())\n const [focusedDay, setFocusedDay] = React.useState<Date>(() => new Date())\n const [yearPage, setYearPage] = React.useState(() => new Date().getFullYear())\n const gridRef = React.useRef<HTMLDivElement>(null)\n\n const selectedDate = React.useMemo(() => {\n if (!value) return null\n const parsed = parseISO(value)\n return isValid(parsed) ? parsed : null\n }, [value])\n\n const isDisabled = React.useCallback(\n (date: Date) =>\n Boolean(minDate && date < startOfDay(minDate)) ||\n Boolean(maxDate && date > startOfDay(maxDate)),\n [minDate, maxDate],\n )\n\n const handleOpenChange = (next: boolean) => {\n setOpen(next)\n if (next) {\n const base = selectedDate ?? new Date()\n setCurrentDate(base)\n setFocusedDay(base)\n setYearPage(base.getFullYear())\n setViewMode('days')\n }\n }\n\n const handleSelect = (date: Date) => {\n if (isDisabled(date)) return\n onChange(format(date, 'yyyy-MM-dd'))\n setOpen(false)\n }\n\n const handleClear = (event: React.MouseEvent) => {\n event.stopPropagation()\n onChange('')\n onClear?.()\n setOpen(false)\n }\n\n const moveFocus = (next: Date) => {\n if (isDisabled(next)) return\n setFocusedDay(next)\n setCurrentDate((current) =>\n isSameMonth(current, next) ? current : startOfMonth(next),\n )\n }\n\n const handleGridKeyDown = (event: React.KeyboardEvent) => {\n const step =\n event.key === 'ArrowLeft'\n ? -1\n : event.key === 'ArrowRight'\n ? 1\n : event.key === 'ArrowUp'\n ? -7\n : event.key === 'ArrowDown'\n ? 7\n : 0\n if (step !== 0) {\n event.preventDefault()\n moveFocus(addDays(focusedDay, step))\n return\n }\n if (event.key === 'PageUp' || event.key === 'PageDown') {\n event.preventDefault()\n const direction = event.key === 'PageUp' ? -1 : 1\n moveFocus(addMonths(focusedDay, direction))\n return\n }\n if (event.key === 'Home') {\n event.preventDefault()\n moveFocus(startOfMonth(focusedDay))\n } else if (event.key === 'End') {\n event.preventDefault()\n moveFocus(endOfMonth(focusedDay))\n }\n }\n\n React.useEffect(() => {\n if (!open || viewMode !== 'days') return\n const target = gridRef.current?.querySelector<HTMLButtonElement>(\n `[data-day=\"${dayKey(focusedDay)}\"]`,\n )\n target?.focus()\n }, [open, viewMode, focusedDay])\n\n const monthStart = startOfMonth(currentDate)\n const monthEnd = endOfMonth(monthStart)\n const gridStart = startOfWeek(monthStart, { weekStartsOn })\n const gridEnd = endOfWeek(monthEnd, { weekStartsOn })\n\n const weeks: Date[][] = []\n let cursor = gridStart\n while (cursor <= gridEnd) {\n const week: Date[] = []\n for (let i = 0; i < 7; i++) {\n week.push(cursor)\n cursor = addDays(cursor, 1)\n }\n weeks.push(week)\n }\n\n const today = new Date()\n const weekdayStart = startOfWeek(new Date(), { weekStartsOn })\n\n const headerTitle =\n viewMode === 'days'\n ? format(currentDate, 'MMMM yyyy')\n : viewMode === 'months'\n ? format(currentDate, 'yyyy')\n : `${Math.floor(yearPage / 12) * 12} – ${Math.floor(yearPage / 12) * 12 + 11}`\n\n const handlePrev = () => {\n if (viewMode === 'days') setCurrentDate(subMonths(currentDate, 1))\n else if (viewMode === 'months') setCurrentDate(subYears(currentDate, 1))\n else setYearPage(yearPage - 12)\n }\n\n const handleNext = () => {\n if (viewMode === 'days') setCurrentDate(addMonths(currentDate, 1))\n else if (viewMode === 'months') setCurrentDate(addYears(currentDate, 1))\n else setYearPage(yearPage + 12)\n }\n\n const toggleViewMode = () => {\n if (viewMode !== 'years') setViewMode('years')\n }\n\n const navButtonClass =\n 'cursor-pointer rounded-[var(--as-radius-sm)] p-1.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]'\n\n return (\n <div\n ref={ref}\n data-as=\"date-picker\"\n className={cn('relative w-full', className)}\n >\n <Popover open={open} onOpenChange={handleOpenChange}>\n <span className=\"relative inline-flex w-full\">\n <PopoverTrigger asChild disabled={disabled}>\n <button\n type=\"button\"\n aria-label={label}\n className={cn(\n 'flex h-9 w-full items-center gap-2 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-left text-sm text-[var(--as-fg)] transition-colors hover:border-[var(--as-fg)]/30 focus-visible:border-[var(--as-focus-ring)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50',\n allowClear && selectedDate && !disabled && 'pr-8',\n )}\n >\n <CalendarIcon\n className=\"size-4 shrink-0 text-[var(--as-muted-fg)]\"\n aria-hidden\n />\n <span\n className={cn(\n 'flex-1 truncate text-left',\n !selectedDate && 'text-[var(--as-muted-fg)]',\n )}\n >\n {selectedDate\n ? format(selectedDate, displayFormat)\n : placeholder}\n </span>\n </button>\n </PopoverTrigger>\n {allowClear && selectedDate && !disabled ? (\n <button\n type=\"button\"\n aria-label={clearLabel}\n onClick={handleClear}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 cursor-pointer rounded-full p-0.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <X className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n </span>\n\n <input\n type=\"hidden\"\n id={hiddenId}\n name={name ?? hiddenId}\n value={value ?? ''}\n required={required}\n />\n\n <PopoverContent\n align=\"start\"\n sideOffset={4}\n collisionPadding={8}\n aria-label={label}\n onOpenAutoFocus={(event) => {\n event.preventDefault()\n const panel = event.currentTarget as HTMLElement | null\n const target = panel?.querySelector<HTMLButtonElement>(\n '[data-day][tabindex=\"0\"]',\n )\n target?.focus()\n }}\n className={cn(\n 'as-anim-pop z-50 w-[280px] rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-2 text-[var(--as-fg)] shadow-[var(--as-shadow-3)] outline-none',\n panelClassName,\n )}\n >\n <div className=\"mb-2 flex items-center justify-between px-1 pt-1\">\n <button\n type=\"button\"\n aria-label={prevLabel}\n onClick={handlePrev}\n className={navButtonClass}\n >\n <ChevronLeft className=\"size-5\" aria-hidden />\n </button>\n <button\n type=\"button\"\n onClick={toggleViewMode}\n className=\"flex cursor-pointer items-center rounded-[var(--as-radius-sm)] px-3 py-1.5 text-sm font-semibold transition-colors hover:bg-[var(--as-muted)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <span>{headerTitle}</span>\n {viewMode !== 'years' ? (\n <ChevronDown className=\"ml-1 size-4\" aria-hidden />\n ) : null}\n </button>\n <button\n type=\"button\"\n aria-label={nextLabel}\n onClick={handleNext}\n className={navButtonClass}\n >\n <ChevronRight className=\"size-5\" aria-hidden />\n </button>\n </div>\n\n {viewMode === 'days' ? (\n <div className=\"px-2 pb-2\">\n <div\n ref={gridRef}\n role=\"grid\"\n aria-label={label}\n onKeyDown={handleGridKeyDown}\n >\n <div role=\"row\" className=\"mb-2 grid grid-cols-7 gap-1\">\n {Array.from({ length: 7 }, (_, i) => (\n <div\n key={i}\n role=\"columnheader\"\n className=\"py-1 text-center text-xs font-medium text-[var(--as-muted-fg)]\"\n >\n {format(addDays(weekdayStart, i), 'EEEEE')}\n </div>\n ))}\n </div>\n {weeks.map((week, weekIndex) => (\n <div key={weekIndex} role=\"row\" className=\"grid grid-cols-7 gap-1\">\n {week.map((day) => {\n const isSelected = selectedDate\n ? isSameDay(day, selectedDate)\n : false\n const isFocused = isSameDay(day, focusedDay)\n const outside = !isSameMonth(day, monthStart)\n const isToday = isSameDay(day, today)\n const dayDisabled = isDisabled(day)\n return (\n <div\n key={dayKey(day)}\n role=\"gridcell\"\n aria-selected={isSelected}\n className=\"p-0\"\n >\n <button\n type=\"button\"\n data-day={dayKey(day)}\n tabIndex={isFocused ? 0 : -1}\n disabled={dayDisabled}\n aria-current={isToday ? 'date' : undefined}\n onClick={() => handleSelect(day)}\n className={cn(\n 'flex size-8 cursor-pointer items-center justify-center rounded-full text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n outside\n ? 'text-[var(--as-muted-fg)] opacity-50'\n : 'text-[var(--as-fg)]',\n isSelected &&\n 'bg-[var(--as-primary)] font-medium text-[var(--as-primary-fg)] hover:brightness-110',\n !isSelected &&\n isToday &&\n 'bg-[color-mix(in_srgb,var(--as-primary)_12%,transparent)] font-medium text-[var(--as-primary)]',\n !isSelected && !dayDisabled && 'hover:bg-[var(--as-muted)]',\n dayDisabled &&\n 'cursor-not-allowed opacity-30 hover:bg-transparent',\n )}\n >\n {format(day, 'd')}\n </button>\n </div>\n )\n })}\n </div>\n ))}\n </div>\n </div>\n ) : null}\n\n {viewMode === 'months' ? (\n <div className=\"grid grid-cols-3 gap-2 p-2\">\n {Array.from({ length: 12 }, (_, i) => {\n const isSelected =\n selectedDate?.getMonth() === i &&\n selectedDate?.getFullYear() === currentDate.getFullYear()\n return (\n <button\n key={i}\n type=\"button\"\n onClick={() => {\n setCurrentDate(setMonth(currentDate, i))\n setViewMode('days')\n }}\n className={cn(\n 'cursor-pointer rounded-[var(--as-radius)] py-3 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n isSelected\n ? 'bg-[var(--as-primary)] text-[var(--as-primary-fg)]'\n : 'text-[var(--as-fg)] hover:bg-[var(--as-muted)]',\n )}\n >\n {format(setMonth(new Date(), i), 'MMM')}\n </button>\n )\n })}\n </div>\n ) : null}\n\n {viewMode === 'years' ? (\n <div className=\"grid grid-cols-3 gap-2 p-2\">\n {Array.from({ length: 12 }, (_, i) => {\n const year = Math.floor(yearPage / 12) * 12 + i\n const isSelected = selectedDate?.getFullYear() === year\n return (\n <button\n key={year}\n type=\"button\"\n onClick={() => {\n setCurrentDate(setYear(currentDate, year))\n setFocusedDay((current) => setYear(current, year))\n setViewMode('months')\n }}\n className={cn(\n 'cursor-pointer rounded-[var(--as-radius)] py-3 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n isSelected\n ? 'bg-[var(--as-primary)] text-[var(--as-primary-fg)]'\n : 'text-[var(--as-fg)] hover:bg-[var(--as-muted)]',\n )}\n >\n {year}\n </button>\n )\n })}\n </div>\n ) : null}\n </PopoverContent>\n </Popover>\n </div>\n )\n },\n)\n"]}