plass-ui 1.0.0 → 1.0.2

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 (165) hide show
  1. package/README.md +165 -0
  2. package/dist/components/accordion/PlAccordion.js +1 -131
  3. package/dist/components/accordion/index.js +1 -1
  4. package/dist/components/alert/PlAlert.js +1 -109
  5. package/dist/components/alert/index.js +1 -1
  6. package/dist/components/animate-appear/PlAnimateAppear.js +1 -71
  7. package/dist/components/animate-appear/index.js +1 -1
  8. package/dist/components/animate-blink/PlAnimateBlink.js +1 -47
  9. package/dist/components/animate-blink/index.js +1 -1
  10. package/dist/components/animate-fade/PlAnimateFade.js +1 -48
  11. package/dist/components/animate-fade/index.js +1 -1
  12. package/dist/components/animate-grow/PlAnimateGrow.js +1 -49
  13. package/dist/components/animate-grow/index.js +1 -1
  14. package/dist/components/animate-headline/PlAnimateHeadline.js +1 -120
  15. package/dist/components/animate-headline/index.js +1 -1
  16. package/dist/components/animate-lighting/PlAnimateLighting.js +1 -65
  17. package/dist/components/animate-lighting/index.js +1 -1
  18. package/dist/components/animate-marquee/PlAnimateMarquee.js +1 -94
  19. package/dist/components/animate-marquee/index.js +1 -1
  20. package/dist/components/animate-rotate/PlAnimateRotate.js +1 -50
  21. package/dist/components/animate-rotate/index.js +1 -1
  22. package/dist/components/animate-slide/PlAnimateSlide.js +1 -49
  23. package/dist/components/animate-slide/index.js +1 -1
  24. package/dist/components/animate-typing/PlAnimateTyping.js +1 -177
  25. package/dist/components/animate-typing/index.js +1 -1
  26. package/dist/components/animate-zoom/PlAnimateZoom.js +1 -45
  27. package/dist/components/animate-zoom/index.js +1 -1
  28. package/dist/components/aspect-ratio/PlAspectRatio.js +1 -58
  29. package/dist/components/aspect-ratio/index.js +1 -1
  30. package/dist/components/avatar/PlAvatar.js +1 -121
  31. package/dist/components/avatar/index.js +1 -1
  32. package/dist/components/badge/PlBadge.js +1 -168
  33. package/dist/components/badge/index.js +1 -1
  34. package/dist/components/blockquote/PlBlockquote.js +1 -112
  35. package/dist/components/blockquote/index.js +1 -1
  36. package/dist/components/bottom-navigation/PlBottomNavigation.js +1 -141
  37. package/dist/components/bottom-navigation/index.js +1 -1
  38. package/dist/components/box/PlBox.js +1 -46
  39. package/dist/components/box/index.js +1 -1
  40. package/dist/components/breadcrumb/PlBreadcrumb.js +1 -201
  41. package/dist/components/breadcrumb/index.js +1 -1
  42. package/dist/components/button/PlButton.js +1 -196
  43. package/dist/components/button/index.js +1 -1
  44. package/dist/components/button-group/PlButtonGroup.js +1 -64
  45. package/dist/components/button-group/index.js +1 -1
  46. package/dist/components/card/PlCard.js +1 -102
  47. package/dist/components/card/index.js +1 -1
  48. package/dist/components/carousel/PlCarousel.js +1 -177
  49. package/dist/components/carousel/index.js +1 -1
  50. package/dist/components/chat-bubble/PlChatBubble.js +1 -206
  51. package/dist/components/chat-bubble/index.js +1 -1
  52. package/dist/components/checkbox/PlCheckbox.js +1 -100
  53. package/dist/components/checkbox/index.js +1 -1
  54. package/dist/components/chip/PlChip.js +1 -144
  55. package/dist/components/chip/index.js +1 -1
  56. package/dist/components/collapsible/PlCollapsible.js +1 -65
  57. package/dist/components/collapsible/index.js +1 -1
  58. package/dist/components/combobox/PlCombobox.js +1 -199
  59. package/dist/components/combobox/index.js +1 -1
  60. package/dist/components/container/PlContainer.js +1 -47
  61. package/dist/components/container/index.js +1 -1
  62. package/dist/components/date-picker/PlDatePicker.js +1 -87
  63. package/dist/components/date-picker/index.js +1 -1
  64. package/dist/components/date-range-picker/PlDateRangePicker.js +1 -152
  65. package/dist/components/date-range-picker/index.js +1 -1
  66. package/dist/components/date-time-picker/PlDateTimePicker.js +1 -106
  67. package/dist/components/date-time-picker/index.js +1 -1
  68. package/dist/components/divider/PlDivider.js +1 -107
  69. package/dist/components/divider/index.js +1 -1
  70. package/dist/components/drawer/PlDrawer.js +1 -190
  71. package/dist/components/drawer/index.js +1 -1
  72. package/dist/components/file-picker/PlFilePicker.js +1 -274
  73. package/dist/components/file-picker/index.js +1 -1
  74. package/dist/components/floating-bottom-navigation/PlFloatingBottomNavigation.js +1 -291
  75. package/dist/components/floating-bottom-navigation/index.js +1 -1
  76. package/dist/components/grid/PlGrid.js +1 -51
  77. package/dist/components/grid/PlGridItem.js +1 -34
  78. package/dist/components/grid/index.js +1 -2
  79. package/dist/components/highlight/PlHighlight.js +1 -185
  80. package/dist/components/highlight/index.js +1 -1
  81. package/dist/components/hot-keys/PlHotKeys.js +1 -294
  82. package/dist/components/hot-keys/index.js +1 -1
  83. package/dist/components/icon/PlIcon.js +1 -58
  84. package/dist/components/icon/index.js +1 -1
  85. package/dist/components/icon-button/PlIconButton.js +1 -37
  86. package/dist/components/icon-button/index.js +1 -1
  87. package/dist/components/list/PlList.js +1 -160
  88. package/dist/components/list/index.js +1 -1
  89. package/dist/components/menu/PlMenu.js +1 -209
  90. package/dist/components/menu/index.js +1 -1
  91. package/dist/components/modal/PlModal.js +1 -150
  92. package/dist/components/modal/index.js +1 -1
  93. package/dist/components/number-field/PlNumberField.js +1 -111
  94. package/dist/components/number-field/index.js +1 -1
  95. package/dist/components/otp-field/PlOtpField.js +1 -109
  96. package/dist/components/otp-field/index.js +1 -1
  97. package/dist/components/overlay/PlOverlay.js +1 -77
  98. package/dist/components/overlay/index.js +1 -1
  99. package/dist/components/pagination/PlPagination.js +1 -159
  100. package/dist/components/pagination/index.js +1 -1
  101. package/dist/components/panes/PlPanes.js +1 -324
  102. package/dist/components/panes/index.js +1 -1
  103. package/dist/components/pill/PlPill.js +1 -154
  104. package/dist/components/pill/index.js +1 -1
  105. package/dist/components/popover/PlPopover.js +1 -113
  106. package/dist/components/popover/index.js +1 -1
  107. package/dist/components/progress-box/PlProgressBox.js +1 -34
  108. package/dist/components/progress-box/index.js +1 -1
  109. package/dist/components/progress-circular/PlProgressCircular.js +1 -47
  110. package/dist/components/progress-circular/index.js +1 -1
  111. package/dist/components/progress-linear/PlProgressLinear.js +1 -40
  112. package/dist/components/progress-linear/index.js +1 -1
  113. package/dist/components/radio-group/PlRadioGroup.js +1 -99
  114. package/dist/components/radio-group/index.js +1 -1
  115. package/dist/components/rating/PlRating.js +1 -101
  116. package/dist/components/rating/index.js +1 -1
  117. package/dist/components/scroll-zone/PlScrollZone.js +1 -411
  118. package/dist/components/scroll-zone/index.js +1 -1
  119. package/dist/components/segmented-button/PlSegmentedButton.js +1 -206
  120. package/dist/components/segmented-button/index.js +1 -1
  121. package/dist/components/select/PlSelect.js +1 -115
  122. package/dist/components/select/index.js +1 -1
  123. package/dist/components/skeleton/PlSkeleton.js +1 -142
  124. package/dist/components/skeleton/index.js +1 -1
  125. package/dist/components/slider/PlSlider.js +1 -132
  126. package/dist/components/slider/index.js +1 -1
  127. package/dist/components/spoiler/PlSpoiler.js +1 -74
  128. package/dist/components/spoiler/index.js +1 -1
  129. package/dist/components/switch/PlSwitch.js +1 -131
  130. package/dist/components/switch/index.js +1 -1
  131. package/dist/components/table/PlTable.js +1 -210
  132. package/dist/components/table/index.js +1 -1
  133. package/dist/components/tabs/PlTabs.js +1 -182
  134. package/dist/components/tabs/index.js +1 -1
  135. package/dist/components/text-field/PlTextField.js +1 -126
  136. package/dist/components/text-field/index.js +1 -1
  137. package/dist/components/text-link/PlTextLink.js +1 -136
  138. package/dist/components/text-link/index.js +1 -1
  139. package/dist/components/time-picker/PlTimePicker.js +1 -97
  140. package/dist/components/time-picker/index.js +1 -1
  141. package/dist/components/timeline/PlTimeline.js +1 -207
  142. package/dist/components/timeline/index.js +1 -1
  143. package/dist/components/toast/PlToast.js +1 -174
  144. package/dist/components/toast/index.js +1 -1
  145. package/dist/components/toolbar/PlToolbar.js +1 -65
  146. package/dist/components/toolbar/index.js +1 -1
  147. package/dist/components/tooltip/PlTooltip.js +1 -117
  148. package/dist/components/tooltip/index.js +1 -1
  149. package/dist/components/typography/PlTypography.js +1 -143
  150. package/dist/components/typography/index.js +1 -1
  151. package/dist/index.js +1 -75
  152. package/dist/internal/animate.js +1 -318
  153. package/dist/internal/button-group.js +2 -19
  154. package/dist/internal/calendar.js +1 -501
  155. package/dist/internal/date.js +1 -353
  156. package/dist/internal/format.js +1 -41
  157. package/dist/internal/grid.js +1 -143
  158. package/dist/internal/icons.js +1 -138
  159. package/dist/internal/menu.js +1 -18
  160. package/dist/internal/picker.js +1 -139
  161. package/dist/internal/progress.js +1 -167
  162. package/dist/internal/sizer.js +1 -35
  163. package/dist/internal/styles.js +1 -636
  164. package/dist/types.js +0 -8
  165. package/package.json +5 -3
@@ -1,501 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import * as React from 'react';
3
- import { PlButton } from '../components/button/PlButton.js';
4
- import { ChevronIcon } from './icons.js';
5
- import { dateFormatter } from './format.js';
6
- import { addDays, addMonths, addYears, calendarWeeks, compareDay, daysInMonth, isDayOutside, isMonthBeforeYear, isSameDay, isSameMonth, isValidDate, makeDate, meridiemLabels, monthLabels, startOfDay, startOfMonth, today, weekdayLabels, withTime, YEAR_PAGE_SIZE, yearPageStart } from './date.js';
7
- import { controlHeightClasses, controlTextClasses, cx, gapClasses, metaTextClasses, srOnlyClasses, transitionClasses } from './styles.js';
8
- export const defaultPickerLabels = {
9
- previousMonth: 'Previous month',
10
- nextMonth: 'Next month',
11
- previousYear: 'Previous year',
12
- nextYear: 'Next year',
13
- previousYears: 'Previous years',
14
- nextYears: 'Next years',
15
- chooseMonth: 'Choose a month',
16
- chooseYear: 'Choose a year',
17
- today: 'Today',
18
- now: 'Now',
19
- clear: 'Clear',
20
- done: 'Done',
21
- hour: 'Hour',
22
- minute: 'Minute',
23
- second: 'Second',
24
- meridiem: 'AM/PM',
25
- start: 'Start',
26
- end: 'End'
27
- };
28
- /** Merges a caller's overrides over the defaults, once per render. */
29
- export function usePickerLabels(overrides) {
30
- return React.useMemo(() => (overrides ? { ...defaultPickerLabels, ...overrides } : defaultPickerLabels), [overrides]);
31
- }
32
- /* ---------------------------------------------------------------------------
33
- * Scale
34
- * ------------------------------------------------------------------------- */
35
- /**
36
- * The width of one day cell, as a length rather than as a class.
37
- *
38
- * It goes in an inline `--p-cell` slot for the reason the design language gives
39
- * for every per-instance value: Tailwind only sees class names written out
40
- * literally, and the grid needs this number in places that are not a `size-*`
41
- * utility — the panel's own width (`7 × cell`), the height the three views share
42
- * so switching between them does not resize the popup, and the clock's columns.
43
- *
44
- * The numbers are `controlHeightClasses` as lengths: a `md` day cell is 40px,
45
- * which is a `md` PlButton, which is a `md` PlTextField. A calendar dropped
46
- * beside a form is on the form's grid.
47
- */
48
- const cellSizes = {
49
- xs: '1.5rem',
50
- sm: '2rem',
51
- md: '2.5rem',
52
- lg: '3rem',
53
- xl: '3.5rem'
54
- };
55
- /** A cell's corner, one step *down* the radius ladder from the popup it sits in. */
56
- const cellRadiusClasses = {
57
- xs: 'rounded-(--plass-radius-xs)',
58
- sm: 'rounded-(--plass-radius-xs)',
59
- md: 'rounded-(--plass-radius-sm)',
60
- lg: 'rounded-(--plass-radius-md)',
61
- xl: 'rounded-(--plass-radius-lg)'
62
- };
63
- /** The same corner on one side only, for where a range band stops. */
64
- const cellRadiusStartClasses = {
65
- xs: 'rounded-s-(--plass-radius-xs)',
66
- sm: 'rounded-s-(--plass-radius-xs)',
67
- md: 'rounded-s-(--plass-radius-sm)',
68
- lg: 'rounded-s-(--plass-radius-md)',
69
- xl: 'rounded-s-(--plass-radius-lg)'
70
- };
71
- const cellRadiusEndClasses = {
72
- xs: 'rounded-e-(--plass-radius-xs)',
73
- sm: 'rounded-e-(--plass-radius-xs)',
74
- md: 'rounded-e-(--plass-radius-sm)',
75
- lg: 'rounded-e-(--plass-radius-md)',
76
- xl: 'rounded-e-(--plass-radius-lg)'
77
- };
78
- /**
79
- * What every cell in every one of the three views is drawn on.
80
- *
81
- * The house transition, unchanged, and **no `transform` anywhere near it**: a
82
- * grid of forty-two cells that each grew a pixel under the pointer would be the
83
- * single worst place in the library to break that rule.
84
- *
85
- * The ring is written as the `outline` shorthand — never `outline-none`, which
86
- * zeroes the variable Tailwind routes the style through — and pulled *inside*
87
- * the cell rather than sitting outside it, because a ring drawn outside a cell
88
- * in a gapless grid is a ring drawn on the neighbours.
89
- */
90
- const cellBaseClasses = /* @__PURE__ */ [
91
- 'relative flex items-center justify-center tabular-nums select-none',
92
- '[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]',
93
- transitionClasses,
94
- 'focus-visible:z-10 focus-visible:[outline:2px_solid_var(--p-ring)]',
95
- 'focus-visible:[outline-offset:-2px]'
96
- ].join(' ');
97
- /**
98
- * One pressable square in a grid.
99
- *
100
- * The state branch is an if/else chain rather than a stack of Tailwind
101
- * variants, which the design language asks for by name: two utilities of equal
102
- * specificity resolve by their order in the generated stylesheet, and "chosen"
103
- * beating "inside the range" is not something a component may leave to that.
104
- *
105
- * The order is the order of importance. Unavailable first — a blocked day still
106
- * wearing the range's tint would be advertising that it is part of a range it
107
- * cannot join. Then chosen, then inside the range, then today, then the days
108
- * belonging to the month next door.
109
- *
110
- * A chosen cell is the family's **gradient**, which makes it the one filled
111
- * token in the popup: the sheet under it is undyed glass, so what a reader is
112
- * looking for in a grid of forty-two is the only coloured thing on it.
113
- */
114
- function Cell({ children, label, selected, inRange = false, rangeEdge = null, current = false, muted = false, disabled = false, focused = false, size, className, onClick, onPointerEnter, onKeyDown }) {
115
- const stateClasses = disabled
116
- ? 'cursor-not-allowed text-(--plass-muted-fg) opacity-50'
117
- : selected
118
- ? 'cursor-pointer font-semibold text-(--p-on-solid) [background-image:var(--p-fill)] hover:brightness-105 active:brightness-95'
119
- : inRange
120
- ? 'cursor-pointer bg-(--p-soft) text-(--plass-fg) hover:bg-(--p-soft-hover) active:bg-(--p-soft-press)'
121
- : current
122
- ? 'cursor-pointer font-semibold text-(--p-accent) hover:bg-(--p-soft) active:bg-(--p-soft-hover)'
123
- : muted
124
- ? 'cursor-pointer text-(--plass-muted-fg) hover:bg-(--p-soft) active:bg-(--p-soft-hover)'
125
- : 'cursor-pointer text-(--plass-fg) hover:bg-(--p-soft) active:bg-(--p-soft-hover)';
126
- // Square through the middle of a run and rounded where the run stops, so a
127
- // week of banded days reads as one shape rather than as seven tokens.
128
- const shapeClasses = rangeEdge === 'start'
129
- ? cellRadiusStartClasses[size]
130
- : rangeEdge === 'end'
131
- ? cellRadiusEndClasses[size]
132
- : rangeEdge === 'middle'
133
- ? 'rounded-none'
134
- : cellRadiusClasses[size];
135
- return (_jsxs("button", { type: "button", role: "gridcell", "aria-label": label, "aria-selected": selected, "aria-current": current ? 'date' : undefined, "aria-disabled": disabled || undefined,
136
- // Not the `disabled` attribute. A disabled button leaves the tab order and
137
- // the grid's arrow-key path with it, so a reader arrowing across a month
138
- // would fall into a hole at every blocked day.
139
- tabIndex: focused ? 0 : -1, "data-focus-target": focused ? 'true' : undefined, className: cx(cellBaseClasses, shapeClasses, stateClasses, className), onClick: () => {
140
- if (!disabled) {
141
- onClick();
142
- }
143
- }, onPointerEnter: onPointerEnter, onKeyDown: onKeyDown, children: [children, current ? (_jsx("span", { "aria-hidden": "true", className: "absolute bottom-[0.18em] size-[0.22em] rounded-full bg-current" })) : null] }));
144
- }
145
- /**
146
- * The two steppers and, between them, the way into the other two views.
147
- *
148
- * This is what the component is for. A calendar that only steps a month at a
149
- * time puts a birthday thirty years back a hundred and eighty clicks away, so
150
- * the month and the year are each a button that opens a grid of its own: two
151
- * clicks to any month of the year on screen, three to any year at all.
152
- *
153
- * The two buttons are printed in the order the locale writes them — `July 2026`
154
- * in English, `2026년 7월` in Korean. `Intl` is asked which part comes first
155
- * rather than being guessed at, because a header in the wrong order reads as
156
- * broken to exactly the readers it is wrong for.
157
- */
158
- function Header({ size, color, view, month, locale, labels, showPreviousButton, showNextButton, onStep, onViewChange }) {
159
- const monthName = monthLabels(locale, 'long')[month.getMonth()];
160
- const yearName = String(month.getFullYear());
161
- const monthFirst = isMonthBeforeYear(locale);
162
- const stepLabels = view === 'day'
163
- ? [labels.previousMonth, labels.nextMonth]
164
- : view === 'month'
165
- ? [labels.previousYear, labels.nextYear]
166
- : [labels.previousYears, labels.nextYears];
167
- const stepper = (direction, shown) => shown ? (_jsx(PlButton, { variant: "ghost", size: size, color: color, density: "compact", "aria-label": stepLabels[direction === -1 ? 0 : 1], onClick: () => onStep(direction), startIcon: _jsx("span", { className: cx('flex items-center', direction === -1 ? 'rotate-90 rtl:-rotate-90' : '-rotate-90 rtl:rotate-90'), children: _jsx(ChevronIcon, {}) }) })) : (_jsx("span", { "aria-hidden": "true", className: cx(controlHeightClasses[size], 'w-(--p-cell)') }));
168
- const disclosure = (open) => (_jsx("span", { className: cx('flex items-center text-(--plass-muted-fg)', '[transition:rotate_var(--plass-duration)_var(--plass-ease)]', open && 'rotate-180'), children: _jsx(ChevronIcon, {}) }));
169
- const monthButton = (_jsx(PlButton, { variant: "ghost", size: size, color: color, density: "compact", "aria-label": labels.chooseMonth, "aria-expanded": view === 'month', onClick: () => onViewChange(view === 'month' ? 'day' : 'month'), endIcon: disclosure(view === 'month'), children: monthName }, "month"));
170
- const yearButton = (_jsx(PlButton, { variant: "ghost", size: size, color: color, density: "compact", className: "tabular-nums", "aria-label": labels.chooseYear, "aria-expanded": view === 'year', onClick: () => onViewChange(view === 'year' ? 'day' : 'year'), endIcon: disclosure(view === 'year'), children: yearName }, "year"));
171
- const pageStart = yearPageStart(month.getFullYear());
172
- return (_jsxs("div", { className: "flex items-center justify-between gap-1", children: [stepper(-1, showPreviousButton), _jsx("div", { className: cx('flex min-w-0 flex-1 items-center justify-center', gapClasses[size]), children: view === 'year' ? (_jsx("span", { className: cx('flex items-center font-semibold tabular-nums text-(--plass-fg)', controlHeightClasses[size], controlTextClasses[size]), children: `${pageStart}–${pageStart + YEAR_PAGE_SIZE - 1}` })) : view === 'month' ? (yearButton) : monthFirst ? (_jsxs(React.Fragment, { children: [monthButton, yearButton] })) : (_jsxs(React.Fragment, { children: [yearButton, monthButton] })) }), stepper(1, showNextButton)] }));
173
- }
174
- /**
175
- * One month, with a way to reach every other one.
176
- *
177
- * Three views on the same footprint: the days of a month, the twelve months of
178
- * a year, twelve years at a time. They are deliberately the same width *and*
179
- * the same height — the day view is seven rows counting its header, and the
180
- * other two stretch four rows and three rows across that same height — so
181
- * switching view never resizes the popup under the pointer that opened it.
182
- *
183
- * Arrow keys move by one cell, `PageUp`/`PageDown` by a month (a year with
184
- * Shift), `Home`/`End` to the ends of the week, and running off an edge steps
185
- * the calendar rather than stopping. One roving tab stop, so `Tab` leaves the
186
- * grid instead of walking forty-two cells — the pattern the ARIA date-picker
187
- * practice describes, and the reason none of the cells is a `disabled` button.
188
- */
189
- export function Calendar({ size, color, locale, weekStartsOn, month, onMonthChange, selected, rangeStart = null, rangeEnd = null, onSelect, onPreviewChange, minDate, maxDate, shouldDisableDate, showOutsideDays = true, autoFocus = false, showPreviousButton = true, showNextButton = true, labels, className }) {
190
- const [view, setView] = React.useState('day');
191
- const chosen = React.useMemo(() => selected.filter(isValidDate), [selected]);
192
- // The one cell that carries the tab stop. It starts on the chosen day, or on
193
- // today when today is on screen, or on the 1st — never nowhere, because a grid
194
- // whose tab stop is nowhere cannot be reached by a keyboard at all.
195
- const [focusedDate, setFocusedDate] = React.useState(() => {
196
- const preferred = chosen.find((date) => isSameMonth(date, month));
197
- if (preferred) {
198
- return startOfDay(preferred);
199
- }
200
- return isSameMonth(today(), month) ? today() : startOfMonth(month);
201
- });
202
- // Set only by the interactions that *move* the focus — an arrow key, a view
203
- // change — so the effect below never yanks focus out from under a pointer user
204
- // doing something else on the page.
205
- const pendingFocus = React.useRef(autoFocus);
206
- const rootRef = React.useRef(null);
207
- // Following the month keeps the tab stop inside the grid the reader is looking
208
- // at: stepping a month and then pressing an arrow lands somewhere sensible
209
- // instead of scrolling the panel back where it came from.
210
- React.useEffect(() => {
211
- // The tab stop follows a prop, and the updater is a no-op unless the month
212
- // actually changed — so this settles in one pass rather than cascading.
213
- // eslint-disable-next-line react-hooks/set-state-in-effect
214
- setFocusedDate((current) => (isSameMonth(current, month) ? current : startOfMonth(month)));
215
- }, [month]);
216
- React.useLayoutEffect(() => {
217
- if (!pendingFocus.current) {
218
- return;
219
- }
220
- pendingFocus.current = false;
221
- // `preventScroll`, because on the very first pass this runs before the popup
222
- // has been positioned — it is still at the top-left of the page, and the
223
- // browser's own "scroll the focused element into view" would drag the
224
- // document up there with it.
225
- rootRef.current
226
- ?.querySelector('[data-focus-target="true"]')
227
- ?.focus({ preventScroll: true });
228
- });
229
- const isDisabled = React.useCallback((date) => isDayOutside(date, minDate, maxDate) || (shouldDisableDate?.(date) ?? false), [minDate, maxDate, shouldDisableDate]);
230
- /** Moves the tab stop, pulling the month along when it lands outside. */
231
- const moveFocus = (next) => {
232
- pendingFocus.current = true;
233
- setFocusedDate(next);
234
- if (!isSameMonth(next, month)) {
235
- onMonthChange(startOfMonth(next));
236
- }
237
- };
238
- const step = (direction) => {
239
- if (view === 'day') {
240
- onMonthChange(addMonths(month, direction));
241
- }
242
- else if (view === 'month') {
243
- onMonthChange(addYears(month, direction));
244
- }
245
- else {
246
- onMonthChange(addYears(month, direction * YEAR_PAGE_SIZE));
247
- }
248
- };
249
- const changeView = (next) => {
250
- pendingFocus.current = true;
251
- setView(next);
252
- };
253
- /** In month and year view the header's `month` *is* the cursor. */
254
- const moveCursor = (next) => {
255
- pendingFocus.current = true;
256
- onMonthChange(next);
257
- };
258
- return (_jsxs("div", { ref: rootRef, className: cx('flex flex-col', gapClasses[size], className), style: { '--p-cell': cellSizes[size] }, onPointerLeave: () => onPreviewChange?.(null), children: [_jsx(Header, { size: size, color: color, view: view, month: month, locale: locale, labels: labels, showPreviousButton: showPreviousButton, showNextButton: showNextButton, onStep: step, onViewChange: changeView }), _jsx("div", { className: "h-[calc(var(--p-cell)*7)] w-[calc(var(--p-cell)*7)]", children: view === 'day' ? (_jsx(DayGrid, { size: size, locale: locale, weekStartsOn: weekStartsOn, month: month, chosen: chosen, rangeStart: rangeStart, rangeEnd: rangeEnd, focusedDate: focusedDate, showOutsideDays: showOutsideDays, isDisabled: isDisabled, onSelect: (date) => {
259
- setFocusedDate(date);
260
- onSelect(date);
261
- }, onPreviewChange: onPreviewChange, onMoveFocus: moveFocus })) : view === 'month' ? (_jsx(MonthGrid, { size: size, locale: locale, month: month, chosen: chosen, minDate: minDate, maxDate: maxDate, onMoveCursor: moveCursor, onPick: (index) => {
262
- onMonthChange(makeDate(month.getFullYear(), index, 1));
263
- changeView('day');
264
- } })) : (_jsx(YearGrid, { size: size, month: month, chosen: chosen, minDate: minDate, maxDate: maxDate, onMoveCursor: moveCursor, onPick: (year) => {
265
- onMonthChange(makeDate(year, month.getMonth(), 1));
266
- changeView('month');
267
- } })) })] }));
268
- }
269
- /* ---------------------------------------------------------------------------
270
- * The three grids
271
- * ------------------------------------------------------------------------- */
272
- /** The two ends of a band, smallest first, whichever way round they arrived. */
273
- function orderedRange(a, b) {
274
- if (!isValidDate(a) || !isValidDate(b)) {
275
- return null;
276
- }
277
- return compareDay(a, b) <= 0 ? [a, b] : [b, a];
278
- }
279
- function DayGrid({ size, locale, weekStartsOn, month, chosen, rangeStart, rangeEnd, focusedDate, showOutsideDays, isDisabled, onSelect, onPreviewChange, onMoveFocus }) {
280
- const weeks = calendarWeeks(month, weekStartsOn);
281
- const short = weekdayLabels(locale, weekStartsOn, 'short');
282
- const long = weekdayLabels(locale, weekStartsOn, 'long');
283
- const fullDate = dateFormatter(locale, { dateStyle: 'full' });
284
- const band = orderedRange(rangeStart, rangeEnd);
285
- const now = today();
286
- const onKeyDown = (event, date) => {
287
- const offsetInWeek = (date.getDay() - weekStartsOn + 7) % 7;
288
- const moves = {
289
- ArrowLeft: () => addDays(date, -1),
290
- ArrowRight: () => addDays(date, 1),
291
- ArrowUp: () => addDays(date, -7),
292
- ArrowDown: () => addDays(date, 7),
293
- Home: () => addDays(date, -offsetInWeek),
294
- End: () => addDays(date, 6 - offsetInWeek),
295
- PageUp: () => addMonths(date, event.shiftKey ? -12 : -1),
296
- PageDown: () => addMonths(date, event.shiftKey ? 12 : 1)
297
- };
298
- const move = moves[event.key];
299
- if (!move) {
300
- return;
301
- }
302
- event.preventDefault();
303
- onMoveFocus(move());
304
- };
305
- return (_jsxs("div", { role: "grid", className: "flex h-full flex-col", children: [_jsx("div", { role: "row", className: "grid grid-cols-7", children: short.map((label, index) => (_jsx("span", { role: "columnheader", "aria-label": long[index], className: cx('flex h-(--p-cell) items-center justify-center font-semibold text-(--plass-muted-fg) select-none', metaTextClasses[size]), children: label }, index))) }), weeks.map((week, weekIndex) => (_jsx("div", { role: "row", className: "grid grid-cols-7", children: week.map((date) => {
306
- const outside = !isSameMonth(date, month);
307
- // A hole the size of a cell rather than a missing one: the grid has
308
- // to keep its seven columns and six rows whatever month it is on.
309
- if (outside && !showOutsideDays) {
310
- return (_jsx("span", { role: "gridcell", "aria-hidden": "true", className: "size-(--p-cell)" }, date.getTime()));
311
- }
312
- const isChosen = chosen.some((entry) => isSameDay(entry, date));
313
- const within = band !== null && compareDay(date, band[0]) >= 0 && compareDay(date, band[1]) <= 0;
314
- const atStart = band !== null && within && isSameDay(date, band[0]);
315
- const atEnd = band !== null && within && isSameDay(date, band[1]);
316
- return (_jsx(Cell, { size: size, label: fullDate.format(date), selected: isChosen, inRange: within && !isChosen, rangeEdge: !within
317
- ? null
318
- : atStart && atEnd
319
- ? 'both'
320
- : atStart
321
- ? 'start'
322
- : atEnd
323
- ? 'end'
324
- : 'middle', current: isSameDay(date, now) && !isChosen, muted: outside, disabled: isDisabled(date), focused: isSameDay(date, focusedDate), className: cx('size-(--p-cell)', controlTextClasses[size]), onClick: () => onSelect(date), onPointerEnter: () => onPreviewChange?.(date), onKeyDown: (event) => onKeyDown(event, date), children: date.getDate() }, date.getTime()));
325
- }) }, weekIndex)))] }));
326
- }
327
- /**
328
- * Twelve months, three across.
329
- *
330
- * The tab stop is the header's own month, so moving it *is* moving the header —
331
- * arrowing right off December lands on January of the next year and the year
332
- * button follows, which is one fewer thing for the reader to keep track of.
333
- */
334
- function MonthGrid({ size, locale, month, chosen, minDate, maxDate, onMoveCursor, onPick }) {
335
- const short = monthLabels(locale, 'short');
336
- const long = monthLabels(locale, 'long');
337
- const year = month.getFullYear();
338
- const now = new Date();
339
- const onKeyDown = (event) => {
340
- const steps = {
341
- ArrowLeft: -1,
342
- ArrowRight: 1,
343
- ArrowUp: -3,
344
- ArrowDown: 3,
345
- PageUp: -12,
346
- PageDown: 12
347
- };
348
- const step = steps[event.key];
349
- if (step === undefined) {
350
- return;
351
- }
352
- event.preventDefault();
353
- onMoveCursor(addMonths(startOfMonth(month), step));
354
- };
355
- return (_jsx("div", { role: "grid", className: "flex h-full flex-col justify-evenly", children: [0, 1, 2, 3].map((row) => (_jsx("div", { role: "row", className: "grid grid-cols-3 gap-1", children: [0, 1, 2].map((column) => {
356
- const index = row * 3 + column;
357
- // A month is out of bounds only when every day in it is: the month a
358
- // `minDate` falls in is still reachable, it just starts late.
359
- const first = makeDate(year, index, 1);
360
- const last = makeDate(year, index, daysInMonth(year, index));
361
- return (_jsx(Cell, { size: size, label: `${long[index]} ${year}`, selected: chosen.some((entry) => entry.getFullYear() === year && entry.getMonth() === index), current: now.getFullYear() === year && now.getMonth() === index, disabled: (isValidDate(minDate) && compareDay(last, minDate) < 0) ||
362
- (isValidDate(maxDate) && compareDay(first, maxDate) > 0), focused: index === month.getMonth(), className: cx('h-(--p-cell) w-full', controlTextClasses[size]), onClick: () => onPick(index), onKeyDown: onKeyDown, children: short[index] }, index));
363
- }) }, row))) }));
364
- }
365
- /** Twelve years, four across, and the same trick with the cursor. */
366
- function YearGrid({ size, month, chosen, minDate, maxDate, onMoveCursor, onPick }) {
367
- const pageStart = yearPageStart(month.getFullYear());
368
- const now = new Date().getFullYear();
369
- const onKeyDown = (event) => {
370
- const steps = {
371
- ArrowLeft: -1,
372
- ArrowRight: 1,
373
- ArrowUp: -4,
374
- ArrowDown: 4,
375
- PageUp: -YEAR_PAGE_SIZE,
376
- PageDown: YEAR_PAGE_SIZE
377
- };
378
- const step = steps[event.key];
379
- if (step === undefined) {
380
- return;
381
- }
382
- event.preventDefault();
383
- onMoveCursor(addYears(startOfMonth(month), step));
384
- };
385
- return (_jsx("div", { role: "grid", className: "flex h-full flex-col justify-evenly", children: [0, 1, 2].map((row) => (_jsx("div", { role: "row", className: "grid grid-cols-4 gap-1", children: [0, 1, 2, 3].map((column) => {
386
- const year = pageStart + row * 4 + column;
387
- return (_jsx(Cell, { size: size, label: String(year), selected: chosen.some((entry) => entry.getFullYear() === year), current: year === now, disabled: (isValidDate(minDate) && year < minDate.getFullYear()) ||
388
- (isValidDate(maxDate) && year > maxDate.getFullYear()), focused: year === month.getFullYear(), className: cx('h-(--p-cell) w-full', controlTextClasses[size]), onClick: () => onPick(year), onKeyDown: onKeyDown, children: year }, year));
389
- }) }, row))) }));
390
- }
391
- /**
392
- * Brings a row into view *inside its own column*, and nowhere else.
393
- *
394
- * `scrollIntoView` walks every scrollable ancestor up to the document, and the
395
- * popup this runs in has not been positioned yet when the effect fires — it is
396
- * still at the top-left of the page. So the browser dutifully scrolled the whole
397
- * document to the top to reveal a row that was about to move anyway, which is
398
- * the "opening a picker jumps the page" bug. Setting `scrollTop` on the column
399
- * cannot touch anything above it.
400
- */
401
- function revealInColumn(row) {
402
- const column = row.parentElement;
403
- if (!column) {
404
- return;
405
- }
406
- // Measured rather than read off `offsetTop`, which is relative to whichever
407
- // ancestor happens to be positioned and not necessarily to the column.
408
- const rowBox = row.getBoundingClientRect();
409
- const columnBox = column.getBoundingClientRect();
410
- const top = rowBox.top - columnBox.top - column.clientTop + column.scrollTop;
411
- const bottom = top + rowBox.height;
412
- if (top < column.scrollTop) {
413
- column.scrollTop = top;
414
- }
415
- else if (bottom > column.scrollTop + column.clientHeight) {
416
- column.scrollTop = bottom - column.clientHeight;
417
- }
418
- }
419
- /**
420
- * Hours, minutes and — when asked for — seconds, as columns you scroll rather
421
- * than as a dial you drag.
422
- *
423
- * Columns because they are the shape that answers what a time picker is actually
424
- * asked: "half past nine" is two glances, and "any time at all, on the hour" is
425
- * a column you never touch. A clock face is prettier and needs a `transform` to
426
- * read, which this library does not have.
427
- *
428
- * The chosen row in each column is scrolled into view once, on open. That is the
429
- * only imperative work here and it is not optional: a column of sixty minutes
430
- * that opens at `00` while the value is `45` has hidden its own answer.
431
- */
432
- export function TimeGrid({ size, density, locale, value, referenceDate, onChange, hour12, showSeconds, hourStep, minuteStep, secondStep, shouldDisableTime, autoFocus = false, labels, className }) {
433
- const base = value ?? referenceDate;
434
- const rootRef = React.useRef(null);
435
- const [am, pm] = React.useMemo(() => meridiemLabels(locale), [locale]);
436
- React.useEffect(() => {
437
- const root = rootRef.current;
438
- root?.querySelectorAll('[data-chosen="true"]').forEach(revealInColumn);
439
- if (autoFocus) {
440
- const first = root?.querySelector('[role="listbox"]');
441
- (first?.querySelector('[data-chosen="true"]') ??
442
- first?.querySelector('[role="option"]'))?.focus({ preventScroll: true });
443
- }
444
- // Once, on open. Re-running it on every change would drag a column back
445
- // under the pointer that is scrolling it.
446
- }, [autoFocus]);
447
- const hours = React.useMemo(() => {
448
- const count = Math.ceil((hour12 ? 12 : 24) / hourStep);
449
- const raw = Array.from({ length: count }, (_, index) => index * hourStep);
450
- // 12, 1, 2 … 11 — the order a 12-hour dial is read in, not 0…11.
451
- return hour12 ? raw.map((hour) => (hour === 0 ? 12 : hour)) : raw;
452
- }, [hour12, hourStep]);
453
- const minutes = React.useMemo(() => Array.from({ length: Math.ceil(60 / minuteStep) }, (_, index) => index * minuteStep), [minuteStep]);
454
- const seconds = React.useMemo(() => Array.from({ length: Math.ceil(60 / secondStep) }, (_, index) => index * secondStep), [secondStep]);
455
- /** The instant choosing this row would produce. */
456
- const candidate = (unit, raw) => {
457
- if (unit === 'hour') {
458
- return withTime(base, {
459
- hours: hour12 ? (raw % 12) + (base.getHours() >= 12 ? 12 : 0) : raw
460
- });
461
- }
462
- if (unit === 'minute') {
463
- return withTime(base, { minutes: raw });
464
- }
465
- if (unit === 'second') {
466
- return withTime(base, { seconds: raw });
467
- }
468
- // `raw` is 0 for the first half of the day and 1 for the second.
469
- return withTime(base, { hours: (base.getHours() % 12) + raw * 12 });
470
- };
471
- const pad = (raw) => String(raw).padStart(2, '0');
472
- const displayHour = hour12 ? base.getHours() % 12 || 12 : base.getHours();
473
- const column = (unit, name, rows, isChosen, render) => (_jsx("div", { role: "listbox", "aria-label": name, className: cx('flex flex-col gap-0.5 overflow-y-auto overscroll-contain',
474
- // The same height as the calendar beside it, so a PlDateTimePicker's
475
- // popup is one rectangle rather than two of different heights.
476
- 'h-[calc(var(--p-cell)*7)] w-[calc(var(--p-cell)*1.75)]', 'scroll-py-0.5 [scrollbar-width:thin]'), children: rows.map((raw) => {
477
- const at = candidate(unit, raw);
478
- const chosen = value !== null && isChosen(raw);
479
- const disabled = shouldDisableTime?.(at, unit) ?? false;
480
- return (_jsx("button", { type: "button", role: "option", "aria-selected": chosen, "aria-disabled": disabled || undefined, "data-chosen": chosen ? 'true' : undefined, className: cx(cellBaseClasses, cellRadiusClasses[size], controlHeightClasses[size], controlTextClasses[size], 'w-full shrink-0', disabled
481
- ? 'cursor-not-allowed text-(--plass-muted-fg) opacity-50'
482
- : chosen
483
- ? 'cursor-pointer font-semibold text-(--p-on-solid) [background-image:var(--p-fill)] hover:brightness-105 active:brightness-95'
484
- : 'cursor-pointer text-(--plass-fg) hover:bg-(--p-soft) active:bg-(--p-soft-hover)'), onClick: () => {
485
- if (!disabled) {
486
- onChange(at);
487
- }
488
- }, children: render(raw) }, raw));
489
- }) }, unit));
490
- return (_jsxs("div", { ref: rootRef, className: cx('flex', density === 'compact' ? 'gap-0.5' : 'gap-1', className), style: { '--p-cell': cellSizes[size] }, children: [column('hour', labels.hour, hours, (raw) => raw === displayHour, (raw) => (hour12 ? String(raw) : pad(raw))), column('minute', labels.minute, minutes, (raw) => raw === base.getMinutes(), pad), showSeconds
491
- ? column('second', labels.second, seconds, (raw) => raw === base.getSeconds(), pad)
492
- : null, hour12
493
- ? column('meridiem', labels.meridiem, [0, 1], (raw) => (base.getHours() >= 12 ? 1 : 0) === raw, (raw) => (raw === 0 ? am : pm))
494
- : null, _jsx("span", { className: srOnlyClasses, "aria-live": "polite", children: value === null
495
- ? ''
496
- : dateFormatter(locale, {
497
- hour: 'numeric',
498
- minute: '2-digit',
499
- ...(showSeconds ? { second: '2-digit' } : {})
500
- }).format(value) })] }));
501
- }
1
+ import{jsx as e,jsxs as o}from"react/jsx-runtime";import*as t from"react";import{PlButton as r}from"../components/button/PlButton.js";import{ChevronIcon as s}from"./icons.js";import{dateFormatter as n}from"./format.js";import{addDays as l,addMonths as a,addYears as i,calendarWeeks as c,compareDay as u,daysInMonth as d,isDayOutside as m,isMonthBeforeYear as h,isSameDay as p,isSameMonth as g,isValidDate as f,makeDate as v,meridiemLabels as x,monthLabels as b,startOfDay as y,startOfMonth as w,today as D,weekdayLabels as N,withTime as C,YEAR_PAGE_SIZE as M,yearPageStart as S}from"./date.js";import{controlHeightClasses as P,controlTextClasses as k,cx as Y,gapClasses as z,metaTextClasses as A,srOnlyClasses as F,transitionClasses as j}from"./styles.js";export const defaultPickerLabels={previousMonth:"Previous month",nextMonth:"Next month",previousYear:"Previous year",nextYear:"Next year",previousYears:"Previous years",nextYears:"Next years",chooseMonth:"Choose a month",chooseYear:"Choose a year",today:"Today",now:"Now",clear:"Clear",done:"Done",hour:"Hour",minute:"Minute",second:"Second",meridiem:"AM/PM",start:"Start",end:"End"};export function usePickerLabels(e){return t.useMemo(()=>e?{...defaultPickerLabels,...e}:defaultPickerLabels,[e])}const E={xs:"1.5rem",sm:"2rem",md:"2.5rem",lg:"3rem",xl:"3.5rem"},H={xs:"rounded-(--plass-radius-xs)",sm:"rounded-(--plass-radius-xs)",md:"rounded-(--plass-radius-sm)",lg:"rounded-(--plass-radius-md)",xl:"rounded-(--plass-radius-lg)"},R={xs:"rounded-s-(--plass-radius-xs)",sm:"rounded-s-(--plass-radius-xs)",md:"rounded-s-(--plass-radius-sm)",lg:"rounded-s-(--plass-radius-md)",xl:"rounded-s-(--plass-radius-lg)"},T={xs:"rounded-e-(--plass-radius-xs)",sm:"rounded-e-(--plass-radius-xs)",md:"rounded-e-(--plass-radius-sm)",lg:"rounded-e-(--plass-radius-md)",xl:"rounded-e-(--plass-radius-lg)"},B=/* @__PURE__ */["relative flex items-center justify-center tabular-nums select-none","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",j,"focus-visible:z-10 focus-visible:[outline:2px_solid_var(--p-ring)]","focus-visible:[outline-offset:-2px]"].join(" ");function Cell({children:t,label:r,selected:s,inRange:n=!1,rangeEdge:l=null,current:a=!1,muted:i=!1,disabled:c=!1,focused:u=!1,size:d,className:m,onClick:h,onPointerEnter:p,onKeyDown:g}){return o("button",{type:"button",role:"gridcell","aria-label":r,"aria-selected":s,"aria-current":a?"date":void 0,"aria-disabled":c||void 0,tabIndex:u?0:-1,"data-focus-target":u?"true":void 0,className:Y(B,"start"===l?R[d]:"end"===l?T[d]:"middle"===l?"rounded-none":H[d],c?"cursor-not-allowed text-(--plass-muted-fg) opacity-50":s?"cursor-pointer font-semibold text-(--p-on-solid) [background-image:var(--p-fill)] hover:brightness-105 active:brightness-95":n?"cursor-pointer bg-(--p-soft) text-(--plass-fg) hover:bg-(--p-soft-hover) active:bg-(--p-soft-press)":a?"cursor-pointer font-semibold text-(--p-accent) hover:bg-(--p-soft) active:bg-(--p-soft-hover)":i?"cursor-pointer text-(--plass-muted-fg) hover:bg-(--p-soft) active:bg-(--p-soft-hover)":"cursor-pointer text-(--plass-fg) hover:bg-(--p-soft) active:bg-(--p-soft-hover)",m),onClick:()=>{c||h()},onPointerEnter:p,onKeyDown:g,children:[t,a?e("span",{"aria-hidden":"true",className:"absolute bottom-[0.18em] size-[0.22em] rounded-full bg-current"}):null]})}function Header({size:n,color:l,view:a,month:i,locale:c,labels:u,showPreviousButton:d,showNextButton:m,onStep:p,onViewChange:g}){const f=b(c,"long")[i.getMonth()],v=String(i.getFullYear()),x=h(c),y="day"===a?[u.previousMonth,u.nextMonth]:"month"===a?[u.previousYear,u.nextYear]:[u.previousYears,u.nextYears],stepper=(o,t)=>t?e(r,{variant:"ghost",size:n,color:l,density:"compact","aria-label":y[-1===o?0:1],onClick:()=>p(o),startIcon:e("span",{className:Y("flex items-center",-1===o?"rotate-90 rtl:-rotate-90":"-rotate-90 rtl:rotate-90"),children:e(s,{})})}):e("span",{"aria-hidden":"true",className:Y(P[n],"w-(--p-cell)")}),disclosure=o=>e("span",{className:Y("flex items-center text-(--plass-muted-fg)","[transition:rotate_var(--plass-duration)_var(--plass-ease)]",o&&"rotate-180"),children:e(s,{})}),w=e(r,{variant:"ghost",size:n,color:l,density:"compact","aria-label":u.chooseMonth,"aria-expanded":"month"===a,onClick:()=>g("month"===a?"day":"month"),endIcon:disclosure("month"===a),children:f},"month"),D=e(r,{variant:"ghost",size:n,color:l,density:"compact",className:"tabular-nums","aria-label":u.chooseYear,"aria-expanded":"year"===a,onClick:()=>g("year"===a?"day":"year"),endIcon:disclosure("year"===a),children:v},"year"),N=S(i.getFullYear());return o("div",{className:"flex items-center justify-between gap-1",children:[stepper(-1,d),e("div",{className:Y("flex min-w-0 flex-1 items-center justify-center",z[n]),children:"year"===a?e("span",{className:Y("flex items-center font-semibold tabular-nums text-(--plass-fg)",P[n],k[n]),children:`${N}–${N+M-1}`}):"month"===a?D:o(t.Fragment,x?{children:[w,D]}:{children:[D,w]})}),stepper(1,m)]})}export function Calendar({size:r,color:s,locale:n,weekStartsOn:l,month:c,onMonthChange:u,selected:d,rangeStart:h=null,rangeEnd:p=null,onSelect:x,onPreviewChange:b,minDate:N,maxDate:C,shouldDisableDate:S,showOutsideDays:P=!0,autoFocus:k=!1,showPreviousButton:A=!0,showNextButton:F=!0,labels:j,className:H}){const[R,T]=t.useState("day"),B=t.useMemo(()=>d.filter(f),[d]),[L,G]=t.useState(()=>{const e=B.find(e=>g(e,c));return e?y(e):g(D(),c)?D():w(c)}),K=t.useRef(k),I=t.useRef(null);t.useEffect(()=>{G(e=>g(e,c)?e:w(c))},[c]),t.useLayoutEffect(()=>{K.current&&(K.current=!1,I.current?.querySelector('[data-focus-target="true"]')?.focus({preventScroll:!0}))});const O=t.useCallback(e=>m(e,N,C)||(S?.(e)??!1),[N,C,S]),changeView=e=>{K.current=!0,T(e)},moveCursor=e=>{K.current=!0,u(e)};return o("div",{ref:I,className:Y("flex flex-col",z[r],H),style:{"--p-cell":E[r]},onPointerLeave:()=>b?.(null),children:[e(Header,{size:r,color:s,view:R,month:c,locale:n,labels:j,showPreviousButton:A,showNextButton:F,onStep:e=>{u("day"===R?a(c,e):i(c,"month"===R?e:e*M))},onViewChange:changeView}),e("div",{className:"h-[calc(var(--p-cell)*7)] w-[calc(var(--p-cell)*7)]",children:"day"===R?e(DayGrid,{size:r,locale:n,weekStartsOn:l,month:c,chosen:B,rangeStart:h,rangeEnd:p,focusedDate:L,showOutsideDays:P,isDisabled:O,onSelect:e=>{G(e),x(e)},onPreviewChange:b,onMoveFocus:e=>{K.current=!0,G(e),g(e,c)||u(w(e))}}):"month"===R?e(MonthGrid,{size:r,locale:n,month:c,chosen:B,minDate:N,maxDate:C,onMoveCursor:moveCursor,onPick:e=>{u(v(c.getFullYear(),e,1)),changeView("day")}}):e(YearGrid,{size:r,month:c,chosen:B,minDate:N,maxDate:C,onMoveCursor:moveCursor,onPick:e=>{u(v(e,c.getMonth(),1)),changeView("month")}})})]})}function DayGrid({size:t,locale:r,weekStartsOn:s,month:i,chosen:d,rangeStart:m,rangeEnd:h,focusedDate:v,showOutsideDays:x,isDisabled:b,onSelect:y,onPreviewChange:w,onMoveFocus:C}){const M=c(i,s),S=N(r,s,"short"),P=N(r,s,"long"),z=n(r,{dateStyle:"full"}),F=function orderedRange(e,o){return f(e)&&f(o)?u(e,o)<=0?[e,o]:[o,e]:null}(m,h),j=D();return o("div",{role:"grid",className:"flex h-full flex-col",children:[e("div",{role:"row",className:"grid grid-cols-7",children:S.map((o,r)=>e("span",{role:"columnheader","aria-label":P[r],className:Y("flex h-(--p-cell) items-center justify-center font-semibold text-(--plass-muted-fg) select-none",A[t]),children:o},r))}),M.map((o,r)=>e("div",{role:"row",className:"grid grid-cols-7",children:o.map(o=>{const r=!g(o,i);if(r&&!x)return e("span",{role:"gridcell","aria-hidden":"true",className:"size-(--p-cell)"},o.getTime());const n=d.some(e=>p(e,o)),c=null!==F&&u(o,F[0])>=0&&u(o,F[1])<=0,m=null!==F&&c&&p(o,F[0]),h=null!==F&&c&&p(o,F[1]);return e(Cell,{size:t,label:z.format(o),selected:n,inRange:c&&!n,rangeEdge:c?m&&h?"both":m?"start":h?"end":"middle":null,current:p(o,j)&&!n,muted:r,disabled:b(o),focused:p(o,v),className:Y("size-(--p-cell)",k[t]),onClick:()=>y(o),onPointerEnter:()=>w?.(o),onKeyDown:e=>((e,o)=>{const t=(o.getDay()-s+7)%7,r={ArrowLeft:()=>l(o,-1),ArrowRight:()=>l(o,1),ArrowUp:()=>l(o,-7),ArrowDown:()=>l(o,7),Home:()=>l(o,-t),End:()=>l(o,6-t),PageUp:()=>a(o,e.shiftKey?-12:-1),PageDown:()=>a(o,e.shiftKey?12:1)}[e.key];r&&(e.preventDefault(),C(r()))})(e,o),children:o.getDate()},o.getTime())})},r))]})}function MonthGrid({size:o,locale:t,month:r,chosen:s,minDate:n,maxDate:l,onMoveCursor:i,onPick:c}){const m=b(t,"short"),h=b(t,"long"),p=r.getFullYear(),g=new Date,onKeyDown=e=>{const o={ArrowLeft:-1,ArrowRight:1,ArrowUp:-3,ArrowDown:3,PageUp:-12,PageDown:12}[e.key];void 0!==o&&(e.preventDefault(),i(a(w(r),o)))};return e("div",{role:"grid",className:"flex h-full flex-col justify-evenly",children:[0,1,2,3].map(t=>e("div",{role:"row",className:"grid grid-cols-3 gap-1",children:[0,1,2].map(a=>{const i=3*t+a,x=v(p,i,1),b=v(p,i,d(p,i));return e(Cell,{size:o,label:`${h[i]} ${p}`,selected:s.some(e=>e.getFullYear()===p&&e.getMonth()===i),current:g.getFullYear()===p&&g.getMonth()===i,disabled:f(n)&&u(b,n)<0||f(l)&&u(x,l)>0,focused:i===r.getMonth(),className:Y("h-(--p-cell) w-full",k[o]),onClick:()=>c(i),onKeyDown,children:m[i]},i)})},t))})}function YearGrid({size:o,month:t,chosen:r,minDate:s,maxDate:n,onMoveCursor:l,onPick:a}){const c=S(t.getFullYear()),u=(new Date).getFullYear(),onKeyDown=e=>{const o={ArrowLeft:-1,ArrowRight:1,ArrowUp:-4,ArrowDown:4,PageUp:-M,PageDown:M}[e.key];void 0!==o&&(e.preventDefault(),l(i(w(t),o)))};return e("div",{role:"grid",className:"flex h-full flex-col justify-evenly",children:[0,1,2].map(l=>e("div",{role:"row",className:"grid grid-cols-4 gap-1",children:[0,1,2,3].map(i=>{const d=c+4*l+i;return e(Cell,{size:o,label:String(d),selected:r.some(e=>e.getFullYear()===d),current:d===u,disabled:f(s)&&d<s.getFullYear()||f(n)&&d>n.getFullYear(),focused:d===t.getFullYear(),className:Y("h-(--p-cell) w-full",k[o]),onClick:()=>a(d),onKeyDown,children:d},d)})},l))})}function revealInColumn(e){const o=e.parentElement;if(!o)return;const t=e.getBoundingClientRect(),r=o.getBoundingClientRect(),s=t.top-r.top-o.clientTop+o.scrollTop,n=s+t.height;s<o.scrollTop?o.scrollTop=s:n>o.scrollTop+o.clientHeight&&(o.scrollTop=n-o.clientHeight)}export function TimeGrid({size:r,density:s,locale:l,value:a,referenceDate:i,onChange:c,hour12:u,showSeconds:d,hourStep:m,minuteStep:h,secondStep:p,shouldDisableTime:g,autoFocus:f=!1,labels:v,className:b}){const y=a??i,w=t.useRef(null),[D,N]=t.useMemo(()=>x(l),[l]);t.useEffect(()=>{const e=w.current;if(e?.querySelectorAll('[data-chosen="true"]').forEach(revealInColumn),f){const o=e?.querySelector('[role="listbox"]');(o?.querySelector('[data-chosen="true"]')??o?.querySelector('[role="option"]'))?.focus({preventScroll:!0})}},[f]);const M=t.useMemo(()=>{const e=Math.ceil((u?12:24)/m),o=Array.from({length:e},(e,o)=>o*m);return u?o.map(e=>0===e?12:e):o},[u,m]),S=t.useMemo(()=>Array.from({length:Math.ceil(60/h)},(e,o)=>o*h),[h]),z=t.useMemo(()=>Array.from({length:Math.ceil(60/p)},(e,o)=>o*p),[p]),pad=e=>String(e).padStart(2,"0"),A=u?y.getHours()%12||12:y.getHours(),column=(o,t,s,n,l)=>e("div",{role:"listbox","aria-label":t,className:Y("flex flex-col gap-0.5 overflow-y-auto overscroll-contain","h-[calc(var(--p-cell)*7)] w-[calc(var(--p-cell)*1.75)]","scroll-py-0.5 [scrollbar-width:thin]"),children:s.map(t=>{const s=((e,o)=>C(y,"hour"===e?{hours:u?o%12+(y.getHours()>=12?12:0):o}:"minute"===e?{minutes:o}:"second"===e?{seconds:o}:{hours:y.getHours()%12+12*o}))(o,t),i=null!==a&&n(t),d=g?.(s,o)??!1;return e("button",{type:"button",role:"option","aria-selected":i,"aria-disabled":d||void 0,"data-chosen":i?"true":void 0,className:Y(B,H[r],P[r],k[r],"w-full shrink-0",d?"cursor-not-allowed text-(--plass-muted-fg) opacity-50":i?"cursor-pointer font-semibold text-(--p-on-solid) [background-image:var(--p-fill)] hover:brightness-105 active:brightness-95":"cursor-pointer text-(--plass-fg) hover:bg-(--p-soft) active:bg-(--p-soft-hover)"),onClick:()=>{d||c(s)},children:l(t)},t)})},o);return o("div",{ref:w,className:Y("flex","compact"===s?"gap-0.5":"gap-1",b),style:{"--p-cell":E[r]},children:[column("hour",v.hour,M,e=>e===A,e=>u?String(e):pad(e)),column("minute",v.minute,S,e=>e===y.getMinutes(),pad),d?column("second",v.second,z,e=>e===y.getSeconds(),pad):null,u?column("meridiem",v.meridiem,[0,1],e=>(y.getHours()>=12?1:0)===e,e=>0===e?D:N):null,e("span",{className:F,"aria-live":"polite",children:null===a?"":n(l,{hour:"numeric",minute:"2-digit",...d?{second:"2-digit"}:{}}).format(a)})]})}