temporal-react-datepicker 1.0.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.
- package/LICENSE +21 -0
- package/README.md +440 -0
- package/dist/index.css +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +533 -0
- package/dist/index.umd.cjs +1 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 temporal-react-datepicker contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
# temporal-react-datepicker
|
|
2
|
+
|
|
3
|
+
A high-performance, accessible React date picker built on the [JavaScript Temporal API](https://tc39.es/proposal-temporal/docs/). Uses `Temporal.PlainDate` for all date logic — immutable, correct calendar arithmetic, no legacy `Date` object.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- React 19+
|
|
8
|
+
- `@js-temporal/polyfill` 0.5.1+
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install temporal-react-datepicker @js-temporal/polyfill
|
|
14
|
+
# or
|
|
15
|
+
pnpm add temporal-react-datepicker @js-temporal/polyfill
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Basic usage
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { useState } from 'react'
|
|
22
|
+
import { Temporal } from '@js-temporal/polyfill'
|
|
23
|
+
import { TemporalDatePicker } from 'temporal-react-datepicker'
|
|
24
|
+
|
|
25
|
+
function App() {
|
|
26
|
+
const [date, setDate] = useState<Temporal.PlainDate | undefined>(undefined)
|
|
27
|
+
|
|
28
|
+
return <TemporalDatePicker value={date} onChange={setDate} />
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Date range selection
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import type { DateRange } from 'temporal-react-datepicker'
|
|
36
|
+
|
|
37
|
+
const [range, setRange] = useState<DateRange | undefined>(undefined)
|
|
38
|
+
|
|
39
|
+
<TemporalDatePicker
|
|
40
|
+
mode="range"
|
|
41
|
+
clearable
|
|
42
|
+
value={range}
|
|
43
|
+
onChange={setRange}
|
|
44
|
+
/>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`DateRange` is `{ start: Temporal.PlainDate; end: Temporal.PlainDate | null }`. After the first click `end` is `null`; after the second click both dates are set.
|
|
48
|
+
|
|
49
|
+
## Props
|
|
50
|
+
|
|
51
|
+
The component uses a discriminated union — the available props depend on `mode` and `clearable`.
|
|
52
|
+
|
|
53
|
+
### Shared props
|
|
54
|
+
|
|
55
|
+
| Prop | Type | Default | Description |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `mode` | `'single' \| 'range'` | `'single'` | Selection mode. |
|
|
58
|
+
| `clearable` | `boolean` | `false` | Show a `×` button to reset the selection when a value is set. |
|
|
59
|
+
| `isDateDisabled` | `(date: Temporal.PlainDate) => boolean` | `undefined` | Return `true` to disable (and block) a specific date. |
|
|
60
|
+
| `showWeekNumbers` | `boolean` | `false` | Show ISO week numbers as an extra column on the left. |
|
|
61
|
+
| `className` | `string` | `''` | Additional CSS class on the root element. |
|
|
62
|
+
| `locale` | `string` | `navigator.language` | BCP 47 locale tag for month/weekday labels. |
|
|
63
|
+
| `labels` | `Partial<Labels>` | English strings | Override any UI string (aria-labels, panel titles, range context). |
|
|
64
|
+
| `renderDayContent` | `(date, state: DayState) => ReactNode` | `undefined` | Custom renderer for each day cell. Falls back to the day number. |
|
|
65
|
+
|
|
66
|
+
### `mode="single"` props
|
|
67
|
+
|
|
68
|
+
| Prop | Type | Description |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `value` | `Temporal.PlainDate \| undefined` | Currently selected date. |
|
|
71
|
+
| `onChange` | `(date: Temporal.PlainDate) => void` | Called on selection. |
|
|
72
|
+
|
|
73
|
+
When `clearable={true}`, `onChange` is widened to `(date: Temporal.PlainDate \| undefined) => void`.
|
|
74
|
+
|
|
75
|
+
### `mode="range"` props
|
|
76
|
+
|
|
77
|
+
| Prop | Type | Description |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| `value` | `DateRange \| undefined` | Currently selected range. |
|
|
80
|
+
| `onChange` | `(range: DateRange) => void` | Called on each click. Fires with `end: null` after the first click. |
|
|
81
|
+
|
|
82
|
+
When `clearable={true}`, `onChange` is `(range: DateRange \| undefined) => void`.
|
|
83
|
+
|
|
84
|
+
## DayState
|
|
85
|
+
|
|
86
|
+
The `renderDayContent` callback receives a `DayState` object as its second argument:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
interface DayState {
|
|
90
|
+
selected: boolean // true in single mode when this date === value
|
|
91
|
+
inRange: boolean // true for days between range.start and range.end
|
|
92
|
+
isRangeStart: boolean // true when this date === range.start
|
|
93
|
+
isRangeEnd: boolean // true when this date === range.end
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
// Bold weight for selected day
|
|
99
|
+
<TemporalDatePicker
|
|
100
|
+
value={date}
|
|
101
|
+
onChange={setDate}
|
|
102
|
+
renderDayContent={(d, { selected }) => (
|
|
103
|
+
<span style={{ fontWeight: selected ? 800 : 400 }}>{d.day}</span>
|
|
104
|
+
)}
|
|
105
|
+
/>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
// Dot indicator on Fridays (payday)
|
|
110
|
+
<TemporalDatePicker
|
|
111
|
+
value={date}
|
|
112
|
+
onChange={setDate}
|
|
113
|
+
renderDayContent={(d, { selected }) => (
|
|
114
|
+
<span style={{ position: 'relative', display: 'inline-flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
|
|
115
|
+
{d.day}
|
|
116
|
+
{d.dayOfWeek === 5 && (
|
|
117
|
+
<span style={{
|
|
118
|
+
width: 3, height: 3, borderRadius: '50%',
|
|
119
|
+
background: selected ? 'currentColor' : '#38bdf8'
|
|
120
|
+
}} />
|
|
121
|
+
)}
|
|
122
|
+
</span>
|
|
123
|
+
)}
|
|
124
|
+
/>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
// Custom badge for range mode (shows "S" / "E" on start and end)
|
|
129
|
+
<TemporalDatePicker
|
|
130
|
+
mode="range"
|
|
131
|
+
value={range}
|
|
132
|
+
onChange={setRange}
|
|
133
|
+
renderDayContent={(d, { isRangeStart, isRangeEnd, inRange }) => (
|
|
134
|
+
<span style={{ position: 'relative' }}>
|
|
135
|
+
{d.day}
|
|
136
|
+
{isRangeStart && (
|
|
137
|
+
<span style={{ position: 'absolute', top: -4, right: -4, fontSize: 8, fontWeight: 700 }}>S</span>
|
|
138
|
+
)}
|
|
139
|
+
{isRangeEnd && (
|
|
140
|
+
<span style={{ position: 'absolute', top: -4, right: -4, fontSize: 8, fontWeight: 700 }}>E</span>
|
|
141
|
+
)}
|
|
142
|
+
</span>
|
|
143
|
+
)}
|
|
144
|
+
/>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
// Highlight days from an external data source (e.g. events)
|
|
149
|
+
const EVENTS = new Set(['2026-03-10', '2026-03-18', '2026-03-25'])
|
|
150
|
+
|
|
151
|
+
<TemporalDatePicker
|
|
152
|
+
value={date}
|
|
153
|
+
onChange={setDate}
|
|
154
|
+
renderDayContent={(d, { selected }) => (
|
|
155
|
+
<span style={{ position: 'relative', display: 'inline-flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
|
|
156
|
+
{d.day}
|
|
157
|
+
{EVENTS.has(d.toString()) && (
|
|
158
|
+
<span style={{
|
|
159
|
+
width: 4, height: 4, borderRadius: '50%',
|
|
160
|
+
background: selected ? 'white' : '#f472b6'
|
|
161
|
+
}} />
|
|
162
|
+
)}
|
|
163
|
+
</span>
|
|
164
|
+
)}
|
|
165
|
+
/>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Disabled dates
|
|
169
|
+
|
|
170
|
+
Return `true` from `isDateDisabled` to block a date. Disabled days are not clickable, not keyboard-focusable, and in range mode they block the range from crossing them.
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
// Weekends only
|
|
174
|
+
<TemporalDatePicker
|
|
175
|
+
value={date}
|
|
176
|
+
onChange={setDate}
|
|
177
|
+
isDateDisabled={d => d.dayOfWeek >= 6}
|
|
178
|
+
/>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
// Past dates (relative to today)
|
|
183
|
+
const today = Temporal.Now.plainDateISO()
|
|
184
|
+
<TemporalDatePicker
|
|
185
|
+
value={date}
|
|
186
|
+
onChange={setDate}
|
|
187
|
+
isDateDisabled={d => Temporal.PlainDate.compare(d, today) < 0}
|
|
188
|
+
/>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
// Future dates — only allow selecting up to today
|
|
193
|
+
<TemporalDatePicker
|
|
194
|
+
value={date}
|
|
195
|
+
onChange={setDate}
|
|
196
|
+
isDateDisabled={d => Temporal.PlainDate.compare(d, today) > 0}
|
|
197
|
+
/>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
// Specific blocked dates (e.g. bank holidays)
|
|
202
|
+
const HOLIDAYS = new Set(['2026-01-01', '2026-12-25', '2026-12-26'])
|
|
203
|
+
<TemporalDatePicker
|
|
204
|
+
value={date}
|
|
205
|
+
onChange={setDate}
|
|
206
|
+
isDateDisabled={d => HOLIDAYS.has(d.toString())}
|
|
207
|
+
/>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
// Combined: no weekends AND no past dates
|
|
212
|
+
<TemporalDatePicker
|
|
213
|
+
value={date}
|
|
214
|
+
onChange={setDate}
|
|
215
|
+
isDateDisabled={d =>
|
|
216
|
+
d.dayOfWeek >= 6 ||
|
|
217
|
+
Temporal.PlainDate.compare(d, Temporal.Now.plainDateISO()) < 0
|
|
218
|
+
}
|
|
219
|
+
/>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
// Range mode with disabled dates — crossing a disabled date is blocked
|
|
224
|
+
<TemporalDatePicker
|
|
225
|
+
mode="range"
|
|
226
|
+
value={range}
|
|
227
|
+
onChange={setRange}
|
|
228
|
+
isDateDisabled={d => d.dayOfWeek >= 6}
|
|
229
|
+
/>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Navigation
|
|
233
|
+
|
|
234
|
+
Click the **month name** or **year** in the header to jump directly to a month or year selector panel. Use `Escape` to return to the calendar without making a selection.
|
|
235
|
+
|
|
236
|
+
## Locale
|
|
237
|
+
|
|
238
|
+
The `locale` prop accepts any [BCP 47 language tag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locales_argument). It controls month names, weekday labels, and day `aria-label` strings. Defaults to `navigator.language`.
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
// Spanish (Spain)
|
|
242
|
+
<TemporalDatePicker value={date} onChange={setDate} locale="es-ES" />
|
|
243
|
+
|
|
244
|
+
// English (US)
|
|
245
|
+
<TemporalDatePicker value={date} onChange={setDate} locale="en-US" />
|
|
246
|
+
|
|
247
|
+
// French
|
|
248
|
+
<TemporalDatePicker value={date} onChange={setDate} locale="fr-FR" />
|
|
249
|
+
|
|
250
|
+
// Japanese
|
|
251
|
+
<TemporalDatePicker value={date} onChange={setDate} locale="ja-JP" />
|
|
252
|
+
|
|
253
|
+
// Arabic (right-to-left script — layout RTL must be handled by the consumer)
|
|
254
|
+
<TemporalDatePicker value={date} onChange={setDate} locale="ar-SA" />
|
|
255
|
+
|
|
256
|
+
// Portuguese (Brazil)
|
|
257
|
+
<TemporalDatePicker value={date} onChange={setDate} locale="pt-BR" />
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
To follow the browser locale automatically (default behaviour):
|
|
261
|
+
|
|
262
|
+
```tsx
|
|
263
|
+
<TemporalDatePicker value={date} onChange={setDate} />
|
|
264
|
+
// equivalent to:
|
|
265
|
+
<TemporalDatePicker value={date} onChange={setDate} locale={navigator.language} />
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
To let the user switch locale at runtime, just pass a state variable:
|
|
269
|
+
|
|
270
|
+
```tsx
|
|
271
|
+
const [locale, setLocale] = useState('en-US')
|
|
272
|
+
|
|
273
|
+
<select onChange={e => setLocale(e.target.value)}>
|
|
274
|
+
<option value="en-US">English</option>
|
|
275
|
+
<option value="es-ES">Español</option>
|
|
276
|
+
<option value="fr-FR">Français</option>
|
|
277
|
+
<option value="de-DE">Deutsch</option>
|
|
278
|
+
</select>
|
|
279
|
+
|
|
280
|
+
<TemporalDatePicker value={date} onChange={setDate} locale={locale} />
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Labels (i18n for UI strings)
|
|
284
|
+
|
|
285
|
+
The `locale` prop controls date formatting (month names, weekday headers, day `aria-label`s) via the browser's `Intl` API. UI strings such as button labels, panel titles, and range context suffixes are controlled separately via the `labels` prop.
|
|
286
|
+
|
|
287
|
+
The default language is **English**. Pass `Partial<Labels>` to override any string:
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import type { Labels } from 'temporal-react-datepicker'
|
|
291
|
+
|
|
292
|
+
const spanishLabels: Partial<Labels> = {
|
|
293
|
+
prevMonth: 'Mes anterior',
|
|
294
|
+
nextMonth: 'Mes siguiente',
|
|
295
|
+
clearSelection: 'Limpiar selección',
|
|
296
|
+
selectMonth: name => `Seleccionar mes, actualmente ${name}`,
|
|
297
|
+
selectYear: year => `Seleccionar año, actualmente ${year}`,
|
|
298
|
+
weekNumberHeader: 'Semana',
|
|
299
|
+
weekNumber: n => `Semana ${n}`,
|
|
300
|
+
rangeStart: ', inicio de rango',
|
|
301
|
+
rangeEnd: ', fin de rango',
|
|
302
|
+
inRange: ', dentro del rango',
|
|
303
|
+
monthPanelAnnouncement: 'Selector de mes',
|
|
304
|
+
yearPanelAnnouncement: 'Selector de año',
|
|
305
|
+
prevYear: 'Año anterior',
|
|
306
|
+
nextYear: 'Año siguiente',
|
|
307
|
+
monthPanelTitle: 'Seleccionar mes',
|
|
308
|
+
prevYearWindow: 'Ventana anterior',
|
|
309
|
+
nextYearWindow: 'Ventana siguiente',
|
|
310
|
+
yearPanelTitle: 'Seleccionar año',
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
<TemporalDatePicker value={date} onChange={setDate} locale="es-ES" labels={spanishLabels} />
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
You only need to supply the strings you want to override — the rest fall back to English defaults. For example, to only translate the range context suffixes for French:
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
<TemporalDatePicker
|
|
320
|
+
mode="range"
|
|
321
|
+
value={range}
|
|
322
|
+
onChange={setRange}
|
|
323
|
+
locale="fr-FR"
|
|
324
|
+
labels={{
|
|
325
|
+
rangeStart: ', début de la plage',
|
|
326
|
+
rangeEnd: ', fin de la plage',
|
|
327
|
+
inRange: ', dans la plage',
|
|
328
|
+
}}
|
|
329
|
+
/>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### `Labels` interface
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
interface Labels {
|
|
336
|
+
// Calendar header buttons
|
|
337
|
+
prevMonth: string
|
|
338
|
+
nextMonth: string
|
|
339
|
+
clearSelection: string
|
|
340
|
+
selectMonth: (monthName: string) => string // e.g. "Select month, currently March"
|
|
341
|
+
selectYear: (year: number) => string // e.g. "Select year, currently 2026"
|
|
342
|
+
// Week number column
|
|
343
|
+
weekNumberHeader: string // column header aria-label
|
|
344
|
+
weekNumber: (weekNum: number) => string // row aria-label, e.g. "Week 12"
|
|
345
|
+
// Range context (appended to day aria-label)
|
|
346
|
+
rangeStart: string // e.g. ", range start"
|
|
347
|
+
rangeEnd: string // e.g. ", range end"
|
|
348
|
+
inRange: string // e.g. ", in range"
|
|
349
|
+
// aria-live announcements on panel switch
|
|
350
|
+
monthPanelAnnouncement: string
|
|
351
|
+
yearPanelAnnouncement: string
|
|
352
|
+
// Month panel (jump nav)
|
|
353
|
+
prevYear: string
|
|
354
|
+
nextYear: string
|
|
355
|
+
monthPanelTitle: string
|
|
356
|
+
// Year panel (jump nav)
|
|
357
|
+
prevYearWindow: string
|
|
358
|
+
nextYearWindow: string
|
|
359
|
+
yearPanelTitle: string
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Theming
|
|
364
|
+
|
|
365
|
+
Override CSS custom properties on `.temporal-datepicker` or any ancestor:
|
|
366
|
+
|
|
367
|
+
```css
|
|
368
|
+
/* Emerald theme */
|
|
369
|
+
.my-app .temporal-datepicker {
|
|
370
|
+
--tdp-accent: #10b981;
|
|
371
|
+
--tdp-accent-fg: #ffffff;
|
|
372
|
+
--tdp-today-text: #10b981;
|
|
373
|
+
--tdp-today-ring: #10b981;
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### CSS tokens
|
|
378
|
+
|
|
379
|
+
| Token | Default (dark) | Description |
|
|
380
|
+
|---|---|---|
|
|
381
|
+
| `--tdp-accent` | `#38bdf8` | Accent color (selected day, focus ring, range edges). |
|
|
382
|
+
| `--tdp-accent-fg` | `#0f172a` | Foreground on accent. |
|
|
383
|
+
| `--tdp-bg` | `#1e293b` | Calendar background. |
|
|
384
|
+
| `--tdp-border` | `#334155` | Border color. |
|
|
385
|
+
| `--tdp-text` | `#e2e8f0` | Primary text. |
|
|
386
|
+
| `--tdp-text-muted` | `#64748b` | Muted text (weekday headers, week numbers). |
|
|
387
|
+
| `--tdp-day-hover-bg` | `rgba(255,255,255,0.06)` | Day hover background. |
|
|
388
|
+
| `--tdp-today-text` | `#38bdf8` | Today text color. |
|
|
389
|
+
| `--tdp-today-ring` | `#38bdf8` | Today indicator dot. |
|
|
390
|
+
| `--tdp-range-bg` | `rgba(56,189,248,0.15)` | Background for days inside a range. |
|
|
391
|
+
| `--tdp-range-edge-bg` | `var(--tdp-accent)` | Background for range start/end. |
|
|
392
|
+
| `--tdp-range-edge-text` | `var(--tdp-accent-fg)` | Foreground for range start/end. |
|
|
393
|
+
| `--tdp-disabled-text` | `#334155` | Disabled day text. |
|
|
394
|
+
| `--tdp-weeknum-text` | `var(--tdp-text-muted)` | Week number column text. |
|
|
395
|
+
| `--tdp-radius` | `12px` | Container border radius. |
|
|
396
|
+
| `--tdp-day-radius` | `8px` | Day button border radius. |
|
|
397
|
+
| `--tdp-day-size` | `36px` | Day button size. |
|
|
398
|
+
| `--tdp-shadow` | (dark shadow) | Container box shadow. |
|
|
399
|
+
|
|
400
|
+
The default theme is dark. Light mode is applied automatically via `@media (prefers-color-scheme: light)`.
|
|
401
|
+
|
|
402
|
+
## Keyboard navigation
|
|
403
|
+
|
|
404
|
+
| Key | Action |
|
|
405
|
+
|---|---|
|
|
406
|
+
| `←` / `→` | Previous / next day |
|
|
407
|
+
| `↑` / `↓` | Same day, previous / next week |
|
|
408
|
+
| `Page Up` / `Page Down` | Previous / next month |
|
|
409
|
+
| `Home` | First day of the current week |
|
|
410
|
+
| `End` | Last day of the current week |
|
|
411
|
+
| `Enter` / `Space` | Select the focused date |
|
|
412
|
+
| `Tab` | Exit the calendar grid |
|
|
413
|
+
|
|
414
|
+
In month/year panels: `← → ↑ ↓` navigate between items, `Enter`/`Space` confirms, `Escape` returns to calendar.
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
## Accessibility
|
|
418
|
+
|
|
419
|
+
- `role="grid"` on the calendar with `role="columnheader"` / `role="gridcell"` per cell.
|
|
420
|
+
- Full `aria-label` on every day button (e.g. "lunes, 17 de marzo de 2026").
|
|
421
|
+
- Range days include context: "inicio de rango", "fin de rango", "dentro del rango".
|
|
422
|
+
- Today marked with `aria-current="date"`.
|
|
423
|
+
- Selected dates use `aria-selected="true"`.
|
|
424
|
+
- Disabled days use the native `disabled` attribute.
|
|
425
|
+
- Panel switches announced via `aria-live="polite"`.
|
|
426
|
+
- All interactive elements expose `:focus-visible` styles.
|
|
427
|
+
|
|
428
|
+
## Development
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
pnpm install # install dependencies
|
|
432
|
+
pnpm dev # start demo app at http://localhost:5173
|
|
433
|
+
pnpm build # build library to /dist
|
|
434
|
+
pnpm type-check # TypeScript compiler check
|
|
435
|
+
pnpm lint # ESLint
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## License
|
|
439
|
+
|
|
440
|
+
MIT
|
package/dist/index.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.temporal-datepicker{--tdp-accent:#38bdf8;--tdp-accent-fg:#0f172a;--tdp-bg:#1e293b;--tdp-border:#334155;--tdp-text:#e2e8f0;--tdp-text-muted:#64748b;--tdp-day-hover-bg:#ffffff0f;--tdp-today-text:#38bdf8;--tdp-today-ring:#38bdf8;--tdp-selected-bg:var(--tdp-accent);--tdp-selected-text:var(--tdp-accent-fg);--tdp-range-bg:#38bdf826;--tdp-range-edge-bg:var(--tdp-accent);--tdp-range-edge-text:var(--tdp-accent-fg);--tdp-disabled-text:#334155;--tdp-disabled-bg:transparent;--tdp-weeknum-text:var(--tdp-text-muted);--tdp-panel-item-hover-bg:var(--tdp-day-hover-bg);--tdp-panel-item-selected-bg:var(--tdp-accent);--tdp-panel-item-selected-text:var(--tdp-accent-fg);--tdp-radius:12px;--tdp-day-radius:8px;--tdp-width:100%;--tdp-day-size:36px;--tdp-shadow:0 8px 32px #0006, 0 2px 8px #0000004d}@media (prefers-color-scheme:light){.temporal-datepicker{--tdp-bg:#fff;--tdp-border:#e2e8f0;--tdp-text:#0f172a;--tdp-text-muted:#94a3b8;--tdp-day-hover-bg:#f1f5f9;--tdp-accent:#0ea5e9;--tdp-accent-fg:#fff;--tdp-today-text:#0ea5e9;--tdp-today-ring:#0ea5e9;--tdp-shadow:0 4px 24px #00000014, 0 1px 4px #0000000f;--tdp-range-bg:#0ea5e91f;--tdp-disabled-text:#cbd5e1}}.temporal-datepicker{width:var(--tdp-width);background:var(--tdp-bg);border:1px solid var(--tdp-border);border-radius:var(--tdp-radius);color:var(--tdp-text);box-sizing:border-box;box-shadow:var(--tdp-shadow);padding:16px;font-family:inherit;font-size:14px;line-height:1;transition:border-color .15s}.tdp-header{justify-content:space-between;align-items:center;gap:8px;margin-bottom:14px;display:flex}.tdp-month-label{color:var(--tdp-text);text-align:center;letter-spacing:.015em;text-transform:capitalize;flex:1;font-size:13px;font-weight:600}.tdp-nav{border:1px solid var(--tdp-border);border-radius:calc(var(--tdp-day-radius) - 2px);width:28px;height:28px;color:var(--tdp-text-muted);cursor:pointer;background:0 0;flex-shrink:0;justify-content:center;align-items:center;padding:0;font-size:16px;line-height:1;transition:background .12s,color .12s,border-color .12s;display:inline-flex}.tdp-nav:hover{background:var(--tdp-day-hover-bg);color:var(--tdp-text);border-color:var(--tdp-text-muted)}.tdp-nav:focus-visible{outline:2px solid var(--tdp-accent);outline-offset:2px;border-color:#0000}.tdp-grid{border-collapse:collapse;table-layout:fixed;width:100%}.tdp-weekday{text-align:center;height:28px;color:var(--tdp-text-muted);text-transform:uppercase;letter-spacing:.07em;padding:0 0 4px;font-size:10px;font-weight:600}.tdp-cell{text-align:center;padding:2px}.tdp-day{width:var(--tdp-day-size);height:var(--tdp-day-size);border-radius:var(--tdp-day-radius);color:var(--tdp-text);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:0;font-family:inherit;font-size:13px;transition:background .1s,color .1s,transform .1s;display:inline-flex;position:relative}.tdp-day:hover:not(:disabled){background:var(--tdp-day-hover-bg);transform:scale(1.08)}.tdp-day:focus-visible{outline:2px solid var(--tdp-accent);outline-offset:1px}.tdp-day--today{color:var(--tdp-today-text);font-weight:700}.tdp-day--today:after{content:"";background:var(--tdp-today-ring);border-radius:50%;width:3px;height:3px;position:absolute;bottom:4px;left:50%;transform:translate(-50%)}.tdp-day--selected{background:var(--tdp-selected-bg);color:var(--tdp-selected-text);font-weight:600}.tdp-day--selected:hover{background:var(--tdp-selected-bg);opacity:.88;transform:scale(1.08)}.tdp-day--selected.tdp-day--today:after{background:var(--tdp-selected-text);opacity:.5}.tdp-day--range-start,.tdp-day--range-end{background:var(--tdp-range-edge-bg);color:var(--tdp-range-edge-text);font-weight:600}.tdp-day--range-start:hover:not(:disabled),.tdp-day--range-end:hover:not(:disabled){background:var(--tdp-range-edge-bg);opacity:.88;transform:scale(1.08)}.tdp-day--in-range{background:var(--tdp-range-bg);color:var(--tdp-text);border-radius:0}.tdp-day--in-range:hover:not(:disabled){background:var(--tdp-range-bg);transform:none}.tdp-day:disabled,.tdp-day--disabled{color:var(--tdp-disabled-text);background:var(--tdp-disabled-bg);cursor:not-allowed;opacity:.4;transform:none}.tdp-weeknum-header,.tdp-weeknum{color:var(--tdp-weeknum-text);text-align:right;-webkit-user-select:none;user-select:none;pointer-events:none;width:22px;padding-right:6px;font-size:10px;font-weight:500}.tdp-month-btn,.tdp-year-btn{color:var(--tdp-text);cursor:pointer;text-transform:capitalize;letter-spacing:.015em;background:0 0;border:none;border-radius:4px;padding:2px 4px;font-family:inherit;font-size:13px;font-weight:600;line-height:1;transition:background .12s,color .12s}.tdp-month-btn:hover,.tdp-year-btn:hover{background:var(--tdp-day-hover-bg);color:var(--tdp-text)}.tdp-month-btn:focus-visible,.tdp-year-btn:focus-visible{outline:2px solid var(--tdp-accent);outline-offset:2px}.tdp-sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.tdp-panel-grid{flex-direction:column;gap:2px;margin-top:8px;display:flex}.tdp-panel-row{grid-template-columns:repeat(3,1fr);gap:2px;display:grid}.tdp-panel-item{border-radius:var(--tdp-day-radius);width:100%;color:var(--tdp-text);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:9px 4px;font-family:inherit;font-size:13px;transition:background .1s,color .1s;display:inline-flex}.tdp-panel-item:hover{background:var(--tdp-panel-item-hover-bg)}.tdp-panel-item:focus-visible{outline:2px solid var(--tdp-accent);outline-offset:2px}.tdp-panel-item--selected{background:var(--tdp-panel-item-selected-bg);color:var(--tdp-panel-item-selected-text);font-weight:600}.tdp-panel-item--selected:hover{background:var(--tdp-panel-item-selected-bg);opacity:.88}
|
|
2
|
+
/*$vite$:1*/
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
import { useEffect as e, useRef as t, useState as n } from "react";
|
|
2
|
+
import { Temporal as r } from "@js-temporal/polyfill";
|
|
3
|
+
import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
4
|
+
//#region src/lib/panels/MonthPanel.tsx
|
|
5
|
+
function s(e) {
|
|
6
|
+
return Array.from({ length: 12 }, (t, n) => new Date(2024, n, 1).toLocaleString(e, { month: "short" }));
|
|
7
|
+
}
|
|
8
|
+
var c = ({ viewDate: r, locale: i, labels: c, onSelect: l, onClose: u }) => {
|
|
9
|
+
let [d, f] = n(r.year), [p, m] = n(r.month - 1), h = t(Array(12).fill(null)), g = s(i);
|
|
10
|
+
e(() => {
|
|
11
|
+
h.current[p]?.focus();
|
|
12
|
+
}, []);
|
|
13
|
+
function _(e) {
|
|
14
|
+
m(e), h.current[e]?.focus();
|
|
15
|
+
}
|
|
16
|
+
function v(e, t) {
|
|
17
|
+
switch (e.key) {
|
|
18
|
+
case "ArrowLeft":
|
|
19
|
+
e.preventDefault(), _((t - 1 + 12) % 12);
|
|
20
|
+
break;
|
|
21
|
+
case "ArrowRight":
|
|
22
|
+
e.preventDefault(), _((t + 1) % 12);
|
|
23
|
+
break;
|
|
24
|
+
case "ArrowUp":
|
|
25
|
+
e.preventDefault(), _((t - 3 + 12) % 12);
|
|
26
|
+
break;
|
|
27
|
+
case "ArrowDown":
|
|
28
|
+
e.preventDefault(), _((t + 3) % 12);
|
|
29
|
+
break;
|
|
30
|
+
case "Enter":
|
|
31
|
+
case " ":
|
|
32
|
+
e.preventDefault(), l(d, t + 1);
|
|
33
|
+
break;
|
|
34
|
+
case "Escape":
|
|
35
|
+
e.preventDefault(), u();
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return /* @__PURE__ */ o("div", {
|
|
40
|
+
className: "tdp-panel",
|
|
41
|
+
children: [/* @__PURE__ */ o("div", {
|
|
42
|
+
className: "tdp-header",
|
|
43
|
+
children: [
|
|
44
|
+
/* @__PURE__ */ a("button", {
|
|
45
|
+
type: "button",
|
|
46
|
+
className: "tdp-nav",
|
|
47
|
+
onClick: () => f((e) => e - 1),
|
|
48
|
+
"aria-label": c.prevYear,
|
|
49
|
+
children: "‹"
|
|
50
|
+
}),
|
|
51
|
+
/* @__PURE__ */ a("span", {
|
|
52
|
+
className: "tdp-month-label",
|
|
53
|
+
children: d
|
|
54
|
+
}),
|
|
55
|
+
/* @__PURE__ */ a("button", {
|
|
56
|
+
type: "button",
|
|
57
|
+
className: "tdp-nav",
|
|
58
|
+
onClick: () => f((e) => e + 1),
|
|
59
|
+
"aria-label": c.nextYear,
|
|
60
|
+
children: "›"
|
|
61
|
+
})
|
|
62
|
+
]
|
|
63
|
+
}), /* @__PURE__ */ a("div", {
|
|
64
|
+
role: "grid",
|
|
65
|
+
className: "tdp-panel-grid",
|
|
66
|
+
"aria-label": c.monthPanelTitle,
|
|
67
|
+
children: [
|
|
68
|
+
0,
|
|
69
|
+
1,
|
|
70
|
+
2,
|
|
71
|
+
3
|
|
72
|
+
].map((e) => /* @__PURE__ */ a("div", {
|
|
73
|
+
role: "row",
|
|
74
|
+
className: "tdp-panel-row",
|
|
75
|
+
children: [
|
|
76
|
+
0,
|
|
77
|
+
1,
|
|
78
|
+
2
|
|
79
|
+
].map((t) => {
|
|
80
|
+
let n = e * 3 + t, i = n + 1, o = d === r.year && i === r.month;
|
|
81
|
+
return /* @__PURE__ */ a("button", {
|
|
82
|
+
ref: (e) => {
|
|
83
|
+
h.current[n] = e;
|
|
84
|
+
},
|
|
85
|
+
role: "gridcell",
|
|
86
|
+
type: "button",
|
|
87
|
+
className: `tdp-panel-item${o ? " tdp-panel-item--selected" : ""}`,
|
|
88
|
+
tabIndex: p === n ? 0 : -1,
|
|
89
|
+
"aria-selected": o,
|
|
90
|
+
onClick: () => l(d, i),
|
|
91
|
+
onKeyDown: (e) => v(e, n),
|
|
92
|
+
onFocus: () => m(n),
|
|
93
|
+
children: g[n]
|
|
94
|
+
}, n);
|
|
95
|
+
})
|
|
96
|
+
}, e))
|
|
97
|
+
})]
|
|
98
|
+
});
|
|
99
|
+
}, l = ({ viewDate: r, labels: i, onSelect: s, onClose: c }) => {
|
|
100
|
+
let [l, u] = n(r.year - 5), [d, f] = n(Math.max(0, Math.min(11, r.year - (r.year - 5)))), p = t(Array(12).fill(null));
|
|
101
|
+
e(() => {
|
|
102
|
+
p.current[d]?.focus();
|
|
103
|
+
}, []);
|
|
104
|
+
function m(e) {
|
|
105
|
+
f(e), p.current[e]?.focus();
|
|
106
|
+
}
|
|
107
|
+
function h(e, t) {
|
|
108
|
+
switch (e.key) {
|
|
109
|
+
case "ArrowLeft":
|
|
110
|
+
e.preventDefault(), m((t - 1 + 12) % 12);
|
|
111
|
+
break;
|
|
112
|
+
case "ArrowRight":
|
|
113
|
+
e.preventDefault(), m((t + 1) % 12);
|
|
114
|
+
break;
|
|
115
|
+
case "ArrowUp":
|
|
116
|
+
e.preventDefault(), m((t - 3 + 12) % 12);
|
|
117
|
+
break;
|
|
118
|
+
case "ArrowDown":
|
|
119
|
+
e.preventDefault(), m((t + 3) % 12);
|
|
120
|
+
break;
|
|
121
|
+
case "Enter":
|
|
122
|
+
case " ":
|
|
123
|
+
e.preventDefault(), s(l + t);
|
|
124
|
+
break;
|
|
125
|
+
case "Escape":
|
|
126
|
+
e.preventDefault(), c();
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return /* @__PURE__ */ o("div", {
|
|
131
|
+
className: "tdp-panel",
|
|
132
|
+
children: [/* @__PURE__ */ o("div", {
|
|
133
|
+
className: "tdp-header",
|
|
134
|
+
children: [
|
|
135
|
+
/* @__PURE__ */ a("button", {
|
|
136
|
+
type: "button",
|
|
137
|
+
className: "tdp-nav",
|
|
138
|
+
onClick: () => u((e) => e - 12),
|
|
139
|
+
"aria-label": i.prevYearWindow,
|
|
140
|
+
children: "‹"
|
|
141
|
+
}),
|
|
142
|
+
/* @__PURE__ */ o("span", {
|
|
143
|
+
className: "tdp-month-label",
|
|
144
|
+
children: [
|
|
145
|
+
l,
|
|
146
|
+
" – ",
|
|
147
|
+
l + 11
|
|
148
|
+
]
|
|
149
|
+
}),
|
|
150
|
+
/* @__PURE__ */ a("button", {
|
|
151
|
+
type: "button",
|
|
152
|
+
className: "tdp-nav",
|
|
153
|
+
onClick: () => u((e) => e + 12),
|
|
154
|
+
"aria-label": i.nextYearWindow,
|
|
155
|
+
children: "›"
|
|
156
|
+
})
|
|
157
|
+
]
|
|
158
|
+
}), /* @__PURE__ */ a("div", {
|
|
159
|
+
role: "grid",
|
|
160
|
+
className: "tdp-panel-grid",
|
|
161
|
+
"aria-label": i.yearPanelTitle,
|
|
162
|
+
children: [
|
|
163
|
+
0,
|
|
164
|
+
1,
|
|
165
|
+
2,
|
|
166
|
+
3
|
|
167
|
+
].map((e) => /* @__PURE__ */ a("div", {
|
|
168
|
+
role: "row",
|
|
169
|
+
className: "tdp-panel-row",
|
|
170
|
+
children: [
|
|
171
|
+
0,
|
|
172
|
+
1,
|
|
173
|
+
2
|
|
174
|
+
].map((t) => {
|
|
175
|
+
let n = e * 3 + t, i = l + n, o = i === r.year;
|
|
176
|
+
return /* @__PURE__ */ a("button", {
|
|
177
|
+
ref: (e) => {
|
|
178
|
+
p.current[n] = e;
|
|
179
|
+
},
|
|
180
|
+
role: "gridcell",
|
|
181
|
+
type: "button",
|
|
182
|
+
className: `tdp-panel-item${o ? " tdp-panel-item--selected" : ""}`,
|
|
183
|
+
tabIndex: d === n ? 0 : -1,
|
|
184
|
+
"aria-selected": o,
|
|
185
|
+
onClick: () => s(i),
|
|
186
|
+
onKeyDown: (e) => h(e, n),
|
|
187
|
+
onFocus: () => f(n),
|
|
188
|
+
children: i
|
|
189
|
+
}, n);
|
|
190
|
+
})
|
|
191
|
+
}, e))
|
|
192
|
+
})]
|
|
193
|
+
});
|
|
194
|
+
}, u = {
|
|
195
|
+
CALENDAR: "calendar",
|
|
196
|
+
MONTH: "month",
|
|
197
|
+
YEAR: "year"
|
|
198
|
+
};
|
|
199
|
+
function d() {
|
|
200
|
+
let [e, t] = n(u.CALENDAR);
|
|
201
|
+
return {
|
|
202
|
+
view: e,
|
|
203
|
+
setView: t
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/lib/hooks/useDateRange.ts
|
|
208
|
+
function f(e, t, n) {
|
|
209
|
+
let [i, a] = r.PlainDate.compare(e, t) < 0 ? [e, t] : [t, e], o = i.add({ days: 1 });
|
|
210
|
+
for (; r.PlainDate.compare(o, a) < 0;) {
|
|
211
|
+
if (n(o)) return !0;
|
|
212
|
+
o = o.add({ days: 1 });
|
|
213
|
+
}
|
|
214
|
+
return !1;
|
|
215
|
+
}
|
|
216
|
+
function p(e, t, n) {
|
|
217
|
+
let i = r.PlainDate.compare(t, e) > 0, a = e;
|
|
218
|
+
for (let o = 1;; o++) {
|
|
219
|
+
let s = e.add({ days: i ? o : -o });
|
|
220
|
+
if (n(s)) return a;
|
|
221
|
+
if (r.PlainDate.compare(s, t) === 0) return t;
|
|
222
|
+
a = s;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function m(e) {
|
|
226
|
+
let [t, i] = n(null);
|
|
227
|
+
function a(t, n, i) {
|
|
228
|
+
if (e?.(t)) return;
|
|
229
|
+
if (!n || n.end !== null) {
|
|
230
|
+
i({
|
|
231
|
+
start: t,
|
|
232
|
+
end: null
|
|
233
|
+
});
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
let [a, o] = r.PlainDate.compare(t, n.start) >= 0 ? [n.start, t] : [t, n.start];
|
|
237
|
+
e && f(a, o, e) || i({
|
|
238
|
+
start: a,
|
|
239
|
+
end: o
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function o(n) {
|
|
243
|
+
return t ? e ? p(n, t, e) : t : null;
|
|
244
|
+
}
|
|
245
|
+
function s(e, t) {
|
|
246
|
+
if (!t) return {
|
|
247
|
+
isRangeStart: !1,
|
|
248
|
+
isRangeEnd: !1,
|
|
249
|
+
inRange: !1,
|
|
250
|
+
isPreview: !1
|
|
251
|
+
};
|
|
252
|
+
if (t.end !== null) return {
|
|
253
|
+
isRangeStart: r.PlainDate.compare(e, t.start) === 0,
|
|
254
|
+
isRangeEnd: r.PlainDate.compare(e, t.end) === 0,
|
|
255
|
+
inRange: r.PlainDate.compare(e, t.start) > 0 && r.PlainDate.compare(e, t.end) < 0,
|
|
256
|
+
isPreview: !1
|
|
257
|
+
};
|
|
258
|
+
let n = o(t.start);
|
|
259
|
+
if (!n) return {
|
|
260
|
+
isRangeStart: r.PlainDate.compare(e, t.start) === 0,
|
|
261
|
+
isRangeEnd: !1,
|
|
262
|
+
inRange: !1,
|
|
263
|
+
isPreview: !0
|
|
264
|
+
};
|
|
265
|
+
let [i, a] = r.PlainDate.compare(n, t.start) >= 0 ? [t.start, n] : [n, t.start];
|
|
266
|
+
return {
|
|
267
|
+
isRangeStart: r.PlainDate.compare(e, i) === 0,
|
|
268
|
+
isRangeEnd: r.PlainDate.compare(e, a) === 0,
|
|
269
|
+
inRange: r.PlainDate.compare(e, i) >= 0 && r.PlainDate.compare(e, a) <= 0,
|
|
270
|
+
isPreview: !0
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
setHoverDate: i,
|
|
275
|
+
handleRangeClick: a,
|
|
276
|
+
getDayRangeState: s
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
//#endregion
|
|
280
|
+
//#region src/lib/temporal-date-picker.tsx
|
|
281
|
+
var h = {
|
|
282
|
+
prevMonth: "Previous month",
|
|
283
|
+
nextMonth: "Next month",
|
|
284
|
+
clearSelection: "Clear selection",
|
|
285
|
+
selectMonth: (e) => `Select month, currently ${e}`,
|
|
286
|
+
selectYear: (e) => `Select year, currently ${e}`,
|
|
287
|
+
weekNumberHeader: "Week",
|
|
288
|
+
weekNumber: (e) => `Week ${e}`,
|
|
289
|
+
rangeStart: ", range start",
|
|
290
|
+
rangeEnd: ", range end",
|
|
291
|
+
inRange: ", in range",
|
|
292
|
+
monthPanelAnnouncement: "Month selector",
|
|
293
|
+
yearPanelAnnouncement: "Year selector",
|
|
294
|
+
prevYear: "Previous year",
|
|
295
|
+
nextYear: "Next year",
|
|
296
|
+
monthPanelTitle: "Select month",
|
|
297
|
+
prevYearWindow: "Previous window",
|
|
298
|
+
nextYearWindow: "Next window",
|
|
299
|
+
yearPanelTitle: "Select year"
|
|
300
|
+
};
|
|
301
|
+
function g(e) {
|
|
302
|
+
let t = [];
|
|
303
|
+
for (let n = 1; n <= 7; n++) {
|
|
304
|
+
let r = new Date(2024, 0, n);
|
|
305
|
+
t.push({
|
|
306
|
+
short: r.toLocaleString(e, { weekday: "narrow" }),
|
|
307
|
+
long: r.toLocaleString(e, { weekday: "long" })
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
return t;
|
|
311
|
+
}
|
|
312
|
+
function _(e) {
|
|
313
|
+
let t = e.with({ day: 1 }), n = e.daysInMonth, r = t.dayOfWeek - 1, i = [...Array.from({ length: r }, () => null), ...Array.from({ length: n }, (e, n) => t.add({ days: n }))], a = [];
|
|
314
|
+
for (let e = 0; e < i.length; e += 7) {
|
|
315
|
+
let t = i.slice(e, e + 7);
|
|
316
|
+
for (; t.length < 7;) t.push(null);
|
|
317
|
+
a.push(t);
|
|
318
|
+
}
|
|
319
|
+
return a;
|
|
320
|
+
}
|
|
321
|
+
var v = (e) => {
|
|
322
|
+
let { className: s = "", locale: u, labels: f, renderDayContent: p, showWeekNumbers: v, isDateDisabled: y } = e, b = {
|
|
323
|
+
...h,
|
|
324
|
+
...f
|
|
325
|
+
}, x = u ?? (typeof navigator < "u" ? navigator.language : "en"), [S, C] = n(() => e.mode === "range" ? e.value?.start ?? r.Now.plainDateISO() : e.value ?? r.Now.plainDateISO()), [w, T] = n(null), E = t(null), { view: D, setView: O } = d(), { setHoverDate: k, handleRangeClick: A, getDayRangeState: j } = m(y), M = r.Now.plainDateISO(), N = (e) => r.PlainDate.compare(M, e) === 0, P = e.value !== void 0;
|
|
326
|
+
function F() {
|
|
327
|
+
e.clearable && e.onChange(void 0);
|
|
328
|
+
}
|
|
329
|
+
function I(t) {
|
|
330
|
+
e.mode === "range" ? A(t, e.value, e.onChange) : (e.onChange(t), T(t));
|
|
331
|
+
}
|
|
332
|
+
let L = (e) => {
|
|
333
|
+
T(e), (e.year !== S.year || e.month !== S.month) && C(e);
|
|
334
|
+
}, R = (t) => {
|
|
335
|
+
let n = e.mode === "range" ? void 0 : e.value, r = w ?? n ?? M;
|
|
336
|
+
switch (t.key) {
|
|
337
|
+
case "ArrowLeft":
|
|
338
|
+
t.preventDefault(), L(r.subtract({ days: 1 }));
|
|
339
|
+
break;
|
|
340
|
+
case "ArrowRight":
|
|
341
|
+
t.preventDefault(), L(r.add({ days: 1 }));
|
|
342
|
+
break;
|
|
343
|
+
case "ArrowUp":
|
|
344
|
+
t.preventDefault(), L(r.subtract({ weeks: 1 }));
|
|
345
|
+
break;
|
|
346
|
+
case "ArrowDown":
|
|
347
|
+
t.preventDefault(), L(r.add({ weeks: 1 }));
|
|
348
|
+
break;
|
|
349
|
+
case "Home":
|
|
350
|
+
t.preventDefault(), L(r.subtract({ days: r.dayOfWeek - 1 }));
|
|
351
|
+
break;
|
|
352
|
+
case "End":
|
|
353
|
+
t.preventDefault(), L(r.add({ days: 7 - r.dayOfWeek }));
|
|
354
|
+
break;
|
|
355
|
+
case "PageUp":
|
|
356
|
+
t.preventDefault(), L(r.subtract({ months: 1 }));
|
|
357
|
+
break;
|
|
358
|
+
case "PageDown":
|
|
359
|
+
t.preventDefault(), L(r.add({ months: 1 }));
|
|
360
|
+
break;
|
|
361
|
+
case "Enter":
|
|
362
|
+
case " ":
|
|
363
|
+
t.preventDefault(), w && I(w);
|
|
364
|
+
break;
|
|
365
|
+
default: break;
|
|
366
|
+
}
|
|
367
|
+
}, z = () => {
|
|
368
|
+
C((e) => e.subtract({ months: 1 })), T(null);
|
|
369
|
+
}, B = () => {
|
|
370
|
+
C((e) => e.add({ months: 1 })), T(null);
|
|
371
|
+
}, V = S.toLocaleString(x, {
|
|
372
|
+
month: "long",
|
|
373
|
+
year: "numeric"
|
|
374
|
+
}), H = S.toLocaleString(x, { month: "long" }), U = g(x), W = _(S), G = e.mode === "range" ? e.value?.start : void 0, K = w ?? (e.mode === "range" ? G : e.value) ?? M, q = D === "month" ? b.monthPanelAnnouncement : D === "year" ? b.yearPanelAnnouncement : V;
|
|
375
|
+
function J(t) {
|
|
376
|
+
if (e.mode === "range") {
|
|
377
|
+
let n = j(t, e.value);
|
|
378
|
+
return {
|
|
379
|
+
selected: !1,
|
|
380
|
+
inRange: n.inRange,
|
|
381
|
+
isRangeStart: n.isRangeStart,
|
|
382
|
+
isRangeEnd: n.isRangeEnd
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
selected: e.value !== void 0 && r.PlainDate.compare(e.value, t) === 0,
|
|
387
|
+
inRange: !1,
|
|
388
|
+
isRangeStart: !1,
|
|
389
|
+
isRangeEnd: !1
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function Y(e, t) {
|
|
393
|
+
let n = ["tdp-day"];
|
|
394
|
+
return t.selected && n.push("tdp-day--selected"), N(e) && n.push("tdp-day--today"), t.isRangeStart && n.push("tdp-day--range-start"), t.isRangeEnd && n.push("tdp-day--range-end"), t.inRange && !t.isRangeStart && !t.isRangeEnd && n.push("tdp-day--in-range"), n.join(" ");
|
|
395
|
+
}
|
|
396
|
+
return /* @__PURE__ */ o("div", {
|
|
397
|
+
className: `temporal-datepicker ${s}`.trim(),
|
|
398
|
+
children: [
|
|
399
|
+
/* @__PURE__ */ a("div", {
|
|
400
|
+
"aria-live": "polite",
|
|
401
|
+
"aria-atomic": "true",
|
|
402
|
+
className: "tdp-sr-only",
|
|
403
|
+
children: q
|
|
404
|
+
}),
|
|
405
|
+
D === "calendar" && /* @__PURE__ */ o(i, { children: [/* @__PURE__ */ o("div", {
|
|
406
|
+
className: "tdp-header",
|
|
407
|
+
children: [
|
|
408
|
+
/* @__PURE__ */ a("button", {
|
|
409
|
+
type: "button",
|
|
410
|
+
className: "tdp-nav",
|
|
411
|
+
onClick: z,
|
|
412
|
+
"aria-label": b.prevMonth,
|
|
413
|
+
children: "‹"
|
|
414
|
+
}),
|
|
415
|
+
/* @__PURE__ */ o("div", {
|
|
416
|
+
className: "tdp-month-label",
|
|
417
|
+
children: [/* @__PURE__ */ a("button", {
|
|
418
|
+
type: "button",
|
|
419
|
+
className: "tdp-month-btn",
|
|
420
|
+
onClick: () => O("month"),
|
|
421
|
+
"aria-label": b.selectMonth(H),
|
|
422
|
+
children: H
|
|
423
|
+
}), /* @__PURE__ */ a("button", {
|
|
424
|
+
type: "button",
|
|
425
|
+
className: "tdp-year-btn",
|
|
426
|
+
onClick: () => O("year"),
|
|
427
|
+
"aria-label": b.selectYear(S.year),
|
|
428
|
+
children: S.year
|
|
429
|
+
})]
|
|
430
|
+
}),
|
|
431
|
+
e.clearable && P && /* @__PURE__ */ a("button", {
|
|
432
|
+
type: "button",
|
|
433
|
+
className: "tdp-nav tdp-clear",
|
|
434
|
+
onClick: F,
|
|
435
|
+
"aria-label": b.clearSelection,
|
|
436
|
+
children: "×"
|
|
437
|
+
}),
|
|
438
|
+
/* @__PURE__ */ a("button", {
|
|
439
|
+
type: "button",
|
|
440
|
+
className: "tdp-nav",
|
|
441
|
+
onClick: B,
|
|
442
|
+
"aria-label": b.nextMonth,
|
|
443
|
+
children: "›"
|
|
444
|
+
})
|
|
445
|
+
]
|
|
446
|
+
}), /* @__PURE__ */ o("table", {
|
|
447
|
+
ref: E,
|
|
448
|
+
role: "grid",
|
|
449
|
+
"aria-label": V,
|
|
450
|
+
className: "tdp-grid",
|
|
451
|
+
onKeyDown: R,
|
|
452
|
+
children: [/* @__PURE__ */ a("thead", { children: /* @__PURE__ */ o("tr", {
|
|
453
|
+
role: "row",
|
|
454
|
+
children: [v && /* @__PURE__ */ a("th", {
|
|
455
|
+
role: "columnheader",
|
|
456
|
+
scope: "col",
|
|
457
|
+
className: "tdp-weekday tdp-weeknum-header",
|
|
458
|
+
"aria-label": b.weekNumberHeader
|
|
459
|
+
}), U.map(({ short: e, long: t }) => /* @__PURE__ */ a("th", {
|
|
460
|
+
role: "columnheader",
|
|
461
|
+
scope: "col",
|
|
462
|
+
className: "tdp-weekday",
|
|
463
|
+
abbr: t,
|
|
464
|
+
children: e
|
|
465
|
+
}, t))]
|
|
466
|
+
}) }), /* @__PURE__ */ a("tbody", { children: W.map((t, n) => {
|
|
467
|
+
let i = t.find((e) => e !== null)?.weekOfYear;
|
|
468
|
+
return /* @__PURE__ */ o("tr", {
|
|
469
|
+
role: "row",
|
|
470
|
+
children: [v && /* @__PURE__ */ a("th", {
|
|
471
|
+
scope: "row",
|
|
472
|
+
className: "tdp-weeknum",
|
|
473
|
+
"aria-label": i == null ? b.weekNumberHeader : b.weekNumber(i),
|
|
474
|
+
children: i ?? ""
|
|
475
|
+
}), t.map((t, n) => {
|
|
476
|
+
if (!t) return /* @__PURE__ */ a("td", {
|
|
477
|
+
role: "gridcell",
|
|
478
|
+
className: "tdp-cell"
|
|
479
|
+
}, `empty-${n}`);
|
|
480
|
+
let i = y?.(t) ?? !1, o = J(t), s = N(t), c = r.PlainDate.compare(K, t) === 0, l = Y(t, o), u = t.toLocaleString(x, {
|
|
481
|
+
weekday: "long",
|
|
482
|
+
year: "numeric",
|
|
483
|
+
month: "long",
|
|
484
|
+
day: "numeric"
|
|
485
|
+
});
|
|
486
|
+
return o.isRangeStart && !o.isRangeEnd && (u += b.rangeStart), o.isRangeEnd && !o.isRangeStart && (u += b.rangeEnd), o.inRange && !o.isRangeStart && !o.isRangeEnd && (u += b.inRange), /* @__PURE__ */ a("td", {
|
|
487
|
+
role: "gridcell",
|
|
488
|
+
"aria-selected": o.selected || o.isRangeStart || o.isRangeEnd,
|
|
489
|
+
className: "tdp-cell",
|
|
490
|
+
children: /* @__PURE__ */ a("button", {
|
|
491
|
+
type: "button",
|
|
492
|
+
className: l,
|
|
493
|
+
onClick: () => I(t),
|
|
494
|
+
onFocus: () => T(t),
|
|
495
|
+
onMouseEnter: () => e.mode === "range" && k(t),
|
|
496
|
+
onMouseLeave: () => e.mode === "range" && k(null),
|
|
497
|
+
tabIndex: c ? 0 : -1,
|
|
498
|
+
disabled: i,
|
|
499
|
+
"aria-label": u,
|
|
500
|
+
"aria-current": s ? "date" : void 0,
|
|
501
|
+
"aria-pressed": e.mode === "range" ? void 0 : o.selected,
|
|
502
|
+
children: p ? p(t, o) : t.day
|
|
503
|
+
})
|
|
504
|
+
}, t.toString());
|
|
505
|
+
})]
|
|
506
|
+
}, n);
|
|
507
|
+
}) })]
|
|
508
|
+
})] }),
|
|
509
|
+
D === "month" && /* @__PURE__ */ a(c, {
|
|
510
|
+
viewDate: S,
|
|
511
|
+
locale: x,
|
|
512
|
+
labels: b,
|
|
513
|
+
onSelect: (e, t) => {
|
|
514
|
+
C(S.with({
|
|
515
|
+
year: e,
|
|
516
|
+
month: t
|
|
517
|
+
})), O("calendar");
|
|
518
|
+
},
|
|
519
|
+
onClose: () => O("calendar")
|
|
520
|
+
}),
|
|
521
|
+
D === "year" && /* @__PURE__ */ a(l, {
|
|
522
|
+
viewDate: S,
|
|
523
|
+
labels: b,
|
|
524
|
+
onSelect: (e) => {
|
|
525
|
+
C(S.with({ year: e })), O("calendar");
|
|
526
|
+
},
|
|
527
|
+
onClose: () => O("calendar")
|
|
528
|
+
})
|
|
529
|
+
]
|
|
530
|
+
});
|
|
531
|
+
};
|
|
532
|
+
//#endregion
|
|
533
|
+
export { v as TemporalDatePicker };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`react`),require(`@js-temporal/polyfill`),require(`react/jsx-runtime`)):typeof define==`function`&&define.amd?define([`exports`,`react`,`@js-temporal/polyfill`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.TemporalDatePicker={},e.React,e.Temporal,e.ReactJsxRuntime))})(this,function(e,t,n,r){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function i(e){return Array.from({length:12},(t,n)=>new Date(2024,n,1).toLocaleString(e,{month:`short`}))}var a=({viewDate:e,locale:n,labels:a,onSelect:o,onClose:s})=>{let[c,l]=(0,t.useState)(e.year),[u,d]=(0,t.useState)(e.month-1),f=(0,t.useRef)(Array(12).fill(null)),p=i(n);(0,t.useEffect)(()=>{f.current[u]?.focus()},[]);function m(e){d(e),f.current[e]?.focus()}function h(e,t){switch(e.key){case`ArrowLeft`:e.preventDefault(),m((t-1+12)%12);break;case`ArrowRight`:e.preventDefault(),m((t+1)%12);break;case`ArrowUp`:e.preventDefault(),m((t-3+12)%12);break;case`ArrowDown`:e.preventDefault(),m((t+3)%12);break;case`Enter`:case` `:e.preventDefault(),o(c,t+1);break;case`Escape`:e.preventDefault(),s();break}}return(0,r.jsxs)(`div`,{className:`tdp-panel`,children:[(0,r.jsxs)(`div`,{className:`tdp-header`,children:[(0,r.jsx)(`button`,{type:`button`,className:`tdp-nav`,onClick:()=>l(e=>e-1),"aria-label":a.prevYear,children:`‹`}),(0,r.jsx)(`span`,{className:`tdp-month-label`,children:c}),(0,r.jsx)(`button`,{type:`button`,className:`tdp-nav`,onClick:()=>l(e=>e+1),"aria-label":a.nextYear,children:`›`})]}),(0,r.jsx)(`div`,{role:`grid`,className:`tdp-panel-grid`,"aria-label":a.monthPanelTitle,children:[0,1,2,3].map(t=>(0,r.jsx)(`div`,{role:`row`,className:`tdp-panel-row`,children:[0,1,2].map(n=>{let i=t*3+n,a=i+1,s=c===e.year&&a===e.month;return(0,r.jsx)(`button`,{ref:e=>{f.current[i]=e},role:`gridcell`,type:`button`,className:`tdp-panel-item${s?` tdp-panel-item--selected`:``}`,tabIndex:u===i?0:-1,"aria-selected":s,onClick:()=>o(c,a),onKeyDown:e=>h(e,i),onFocus:()=>d(i),children:p[i]},i)})},t))})]})},o=({viewDate:e,labels:n,onSelect:i,onClose:a})=>{let[o,s]=(0,t.useState)(e.year-5),[c,l]=(0,t.useState)(Math.max(0,Math.min(11,e.year-(e.year-5)))),u=(0,t.useRef)(Array(12).fill(null));(0,t.useEffect)(()=>{u.current[c]?.focus()},[]);function d(e){l(e),u.current[e]?.focus()}function f(e,t){switch(e.key){case`ArrowLeft`:e.preventDefault(),d((t-1+12)%12);break;case`ArrowRight`:e.preventDefault(),d((t+1)%12);break;case`ArrowUp`:e.preventDefault(),d((t-3+12)%12);break;case`ArrowDown`:e.preventDefault(),d((t+3)%12);break;case`Enter`:case` `:e.preventDefault(),i(o+t);break;case`Escape`:e.preventDefault(),a();break}}return(0,r.jsxs)(`div`,{className:`tdp-panel`,children:[(0,r.jsxs)(`div`,{className:`tdp-header`,children:[(0,r.jsx)(`button`,{type:`button`,className:`tdp-nav`,onClick:()=>s(e=>e-12),"aria-label":n.prevYearWindow,children:`‹`}),(0,r.jsxs)(`span`,{className:`tdp-month-label`,children:[o,` – `,o+11]}),(0,r.jsx)(`button`,{type:`button`,className:`tdp-nav`,onClick:()=>s(e=>e+12),"aria-label":n.nextYearWindow,children:`›`})]}),(0,r.jsx)(`div`,{role:`grid`,className:`tdp-panel-grid`,"aria-label":n.yearPanelTitle,children:[0,1,2,3].map(t=>(0,r.jsx)(`div`,{role:`row`,className:`tdp-panel-row`,children:[0,1,2].map(n=>{let a=t*3+n,s=o+a,d=s===e.year;return(0,r.jsx)(`button`,{ref:e=>{u.current[a]=e},role:`gridcell`,type:`button`,className:`tdp-panel-item${d?` tdp-panel-item--selected`:``}`,tabIndex:c===a?0:-1,"aria-selected":d,onClick:()=>i(s),onKeyDown:e=>f(e,a),onFocus:()=>l(a),children:s},a)})},t))})]})},s={CALENDAR:`calendar`,MONTH:`month`,YEAR:`year`};function c(){let[e,n]=(0,t.useState)(s.CALENDAR);return{view:e,setView:n}}function l(e,t,r){let[i,a]=n.Temporal.PlainDate.compare(e,t)<0?[e,t]:[t,e],o=i.add({days:1});for(;n.Temporal.PlainDate.compare(o,a)<0;){if(r(o))return!0;o=o.add({days:1})}return!1}function u(e,t,r){let i=n.Temporal.PlainDate.compare(t,e)>0,a=e;for(let o=1;;o++){let s=e.add({days:i?o:-o});if(r(s))return a;if(n.Temporal.PlainDate.compare(s,t)===0)return t;a=s}}function d(e){let[r,i]=(0,t.useState)(null);function a(t,r,i){if(e?.(t))return;if(!r||r.end!==null){i({start:t,end:null});return}let[a,o]=n.Temporal.PlainDate.compare(t,r.start)>=0?[r.start,t]:[t,r.start];e&&l(a,o,e)||i({start:a,end:o})}function o(t){return r?e?u(t,r,e):r:null}function s(e,t){if(!t)return{isRangeStart:!1,isRangeEnd:!1,inRange:!1,isPreview:!1};if(t.end!==null)return{isRangeStart:n.Temporal.PlainDate.compare(e,t.start)===0,isRangeEnd:n.Temporal.PlainDate.compare(e,t.end)===0,inRange:n.Temporal.PlainDate.compare(e,t.start)>0&&n.Temporal.PlainDate.compare(e,t.end)<0,isPreview:!1};let r=o(t.start);if(!r)return{isRangeStart:n.Temporal.PlainDate.compare(e,t.start)===0,isRangeEnd:!1,inRange:!1,isPreview:!0};let[i,a]=n.Temporal.PlainDate.compare(r,t.start)>=0?[t.start,r]:[r,t.start];return{isRangeStart:n.Temporal.PlainDate.compare(e,i)===0,isRangeEnd:n.Temporal.PlainDate.compare(e,a)===0,inRange:n.Temporal.PlainDate.compare(e,i)>=0&&n.Temporal.PlainDate.compare(e,a)<=0,isPreview:!0}}return{setHoverDate:i,handleRangeClick:a,getDayRangeState:s}}var f={prevMonth:`Previous month`,nextMonth:`Next month`,clearSelection:`Clear selection`,selectMonth:e=>`Select month, currently ${e}`,selectYear:e=>`Select year, currently ${e}`,weekNumberHeader:`Week`,weekNumber:e=>`Week ${e}`,rangeStart:`, range start`,rangeEnd:`, range end`,inRange:`, in range`,monthPanelAnnouncement:`Month selector`,yearPanelAnnouncement:`Year selector`,prevYear:`Previous year`,nextYear:`Next year`,monthPanelTitle:`Select month`,prevYearWindow:`Previous window`,nextYearWindow:`Next window`,yearPanelTitle:`Select year`};function p(e){let t=[];for(let n=1;n<=7;n++){let r=new Date(2024,0,n);t.push({short:r.toLocaleString(e,{weekday:`narrow`}),long:r.toLocaleString(e,{weekday:`long`})})}return t}function m(e){let t=e.with({day:1}),n=e.daysInMonth,r=t.dayOfWeek-1,i=[...Array.from({length:r},()=>null),...Array.from({length:n},(e,n)=>t.add({days:n}))],a=[];for(let e=0;e<i.length;e+=7){let t=i.slice(e,e+7);for(;t.length<7;)t.push(null);a.push(t)}return a}e.TemporalDatePicker=e=>{let{className:i=``,locale:s,labels:l,renderDayContent:u,showWeekNumbers:h,isDateDisabled:g}=e,_={...f,...l},v=s??(typeof navigator<`u`?navigator.language:`en`),[y,b]=(0,t.useState)(()=>e.mode===`range`?e.value?.start??n.Temporal.Now.plainDateISO():e.value??n.Temporal.Now.plainDateISO()),[x,S]=(0,t.useState)(null),C=(0,t.useRef)(null),{view:w,setView:T}=c(),{setHoverDate:E,handleRangeClick:D,getDayRangeState:O}=d(g),k=n.Temporal.Now.plainDateISO(),A=e=>n.Temporal.PlainDate.compare(k,e)===0,j=e.value!==void 0;function M(){e.clearable&&e.onChange(void 0)}function N(t){e.mode===`range`?D(t,e.value,e.onChange):(e.onChange(t),S(t))}let P=e=>{S(e),(e.year!==y.year||e.month!==y.month)&&b(e)},F=t=>{let n=e.mode===`range`?void 0:e.value,r=x??n??k;switch(t.key){case`ArrowLeft`:t.preventDefault(),P(r.subtract({days:1}));break;case`ArrowRight`:t.preventDefault(),P(r.add({days:1}));break;case`ArrowUp`:t.preventDefault(),P(r.subtract({weeks:1}));break;case`ArrowDown`:t.preventDefault(),P(r.add({weeks:1}));break;case`Home`:t.preventDefault(),P(r.subtract({days:r.dayOfWeek-1}));break;case`End`:t.preventDefault(),P(r.add({days:7-r.dayOfWeek}));break;case`PageUp`:t.preventDefault(),P(r.subtract({months:1}));break;case`PageDown`:t.preventDefault(),P(r.add({months:1}));break;case`Enter`:case` `:t.preventDefault(),x&&N(x);break;default:break}},I=()=>{b(e=>e.subtract({months:1})),S(null)},L=()=>{b(e=>e.add({months:1})),S(null)},R=y.toLocaleString(v,{month:`long`,year:`numeric`}),z=y.toLocaleString(v,{month:`long`}),B=p(v),V=m(y),H=e.mode===`range`?e.value?.start:void 0,U=x??(e.mode===`range`?H:e.value)??k,W=w===`month`?_.monthPanelAnnouncement:w===`year`?_.yearPanelAnnouncement:R;function G(t){if(e.mode===`range`){let n=O(t,e.value);return{selected:!1,inRange:n.inRange,isRangeStart:n.isRangeStart,isRangeEnd:n.isRangeEnd}}return{selected:e.value!==void 0&&n.Temporal.PlainDate.compare(e.value,t)===0,inRange:!1,isRangeStart:!1,isRangeEnd:!1}}function K(e,t){let n=[`tdp-day`];return t.selected&&n.push(`tdp-day--selected`),A(e)&&n.push(`tdp-day--today`),t.isRangeStart&&n.push(`tdp-day--range-start`),t.isRangeEnd&&n.push(`tdp-day--range-end`),t.inRange&&!t.isRangeStart&&!t.isRangeEnd&&n.push(`tdp-day--in-range`),n.join(` `)}return(0,r.jsxs)(`div`,{className:`temporal-datepicker ${i}`.trim(),children:[(0,r.jsx)(`div`,{"aria-live":`polite`,"aria-atomic":`true`,className:`tdp-sr-only`,children:W}),w===`calendar`&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)(`div`,{className:`tdp-header`,children:[(0,r.jsx)(`button`,{type:`button`,className:`tdp-nav`,onClick:I,"aria-label":_.prevMonth,children:`‹`}),(0,r.jsxs)(`div`,{className:`tdp-month-label`,children:[(0,r.jsx)(`button`,{type:`button`,className:`tdp-month-btn`,onClick:()=>T(`month`),"aria-label":_.selectMonth(z),children:z}),(0,r.jsx)(`button`,{type:`button`,className:`tdp-year-btn`,onClick:()=>T(`year`),"aria-label":_.selectYear(y.year),children:y.year})]}),e.clearable&&j&&(0,r.jsx)(`button`,{type:`button`,className:`tdp-nav tdp-clear`,onClick:M,"aria-label":_.clearSelection,children:`×`}),(0,r.jsx)(`button`,{type:`button`,className:`tdp-nav`,onClick:L,"aria-label":_.nextMonth,children:`›`})]}),(0,r.jsxs)(`table`,{ref:C,role:`grid`,"aria-label":R,className:`tdp-grid`,onKeyDown:F,children:[(0,r.jsx)(`thead`,{children:(0,r.jsxs)(`tr`,{role:`row`,children:[h&&(0,r.jsx)(`th`,{role:`columnheader`,scope:`col`,className:`tdp-weekday tdp-weeknum-header`,"aria-label":_.weekNumberHeader}),B.map(({short:e,long:t})=>(0,r.jsx)(`th`,{role:`columnheader`,scope:`col`,className:`tdp-weekday`,abbr:t,children:e},t))]})}),(0,r.jsx)(`tbody`,{children:V.map((t,i)=>{let a=t.find(e=>e!==null)?.weekOfYear;return(0,r.jsxs)(`tr`,{role:`row`,children:[h&&(0,r.jsx)(`th`,{scope:`row`,className:`tdp-weeknum`,"aria-label":a==null?_.weekNumberHeader:_.weekNumber(a),children:a??``}),t.map((t,i)=>{if(!t)return(0,r.jsx)(`td`,{role:`gridcell`,className:`tdp-cell`},`empty-${i}`);let a=g?.(t)??!1,o=G(t),s=A(t),c=n.Temporal.PlainDate.compare(U,t)===0,l=K(t,o),d=t.toLocaleString(v,{weekday:`long`,year:`numeric`,month:`long`,day:`numeric`});return o.isRangeStart&&!o.isRangeEnd&&(d+=_.rangeStart),o.isRangeEnd&&!o.isRangeStart&&(d+=_.rangeEnd),o.inRange&&!o.isRangeStart&&!o.isRangeEnd&&(d+=_.inRange),(0,r.jsx)(`td`,{role:`gridcell`,"aria-selected":o.selected||o.isRangeStart||o.isRangeEnd,className:`tdp-cell`,children:(0,r.jsx)(`button`,{type:`button`,className:l,onClick:()=>N(t),onFocus:()=>S(t),onMouseEnter:()=>e.mode===`range`&&E(t),onMouseLeave:()=>e.mode===`range`&&E(null),tabIndex:c?0:-1,disabled:a,"aria-label":d,"aria-current":s?`date`:void 0,"aria-pressed":e.mode===`range`?void 0:o.selected,children:u?u(t,o):t.day})},t.toString())})]},i)})})]})]}),w===`month`&&(0,r.jsx)(a,{viewDate:y,locale:v,labels:_,onSelect:(e,t)=>{b(y.with({year:e,month:t})),T(`calendar`)},onClose:()=>T(`calendar`)}),w===`year`&&(0,r.jsx)(o,{viewDate:y,labels:_,onSelect:e=>{b(y.with({year:e})),T(`calendar`)},onClose:()=>T(`calendar`)})]})}});
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "temporal-react-datepicker",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "High-performance React date picker built on the Temporal API. Immutable dates, range selection, accessible.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"datepicker",
|
|
8
|
+
"date-picker",
|
|
9
|
+
"temporal",
|
|
10
|
+
"temporal-api",
|
|
11
|
+
"date-range",
|
|
12
|
+
"accessible",
|
|
13
|
+
"a11y"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"homepage": "https://github.com/PabloViniegra/temporal-react-datepicker#readme",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/PabloViniegra/temporal-react-datepicker.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/PabloViniegra/temporal-react-datepicker/issues"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.umd.cjs",
|
|
26
|
+
"module": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"require": "./dist/index.umd.cjs",
|
|
32
|
+
"types": "./dist/index.d.ts"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@js-temporal/polyfill": ">=0.5.1",
|
|
40
|
+
"react": ">=19.0.0 <20.0.0",
|
|
41
|
+
"react-dom": ">=19.0.0 <20.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@babel/core": "^7.29.0",
|
|
45
|
+
"@eslint/js": "^9.39.4",
|
|
46
|
+
"@js-temporal/polyfill": "^0.5.1",
|
|
47
|
+
"@rolldown/plugin-babel": "^0.2.0",
|
|
48
|
+
"@types/babel__core": "^7.20.5",
|
|
49
|
+
"@types/node": "^24.12.0",
|
|
50
|
+
"@types/react": "^19.2.14",
|
|
51
|
+
"@types/react-dom": "^19.2.3",
|
|
52
|
+
"@vitejs/plugin-react": "^6.0.0",
|
|
53
|
+
"babel-plugin-react-compiler": "^1.0.0",
|
|
54
|
+
"eslint": "^9.39.4",
|
|
55
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
56
|
+
"eslint-plugin-react-refresh": "^0.5.2",
|
|
57
|
+
"globals": "^17.4.0",
|
|
58
|
+
"react": "^19.2.4",
|
|
59
|
+
"react-dom": "^19.2.4",
|
|
60
|
+
"typescript": "~5.9.3",
|
|
61
|
+
"typescript-eslint": "^8.56.1",
|
|
62
|
+
"vite": "^8.0.0",
|
|
63
|
+
"vite-plugin-dts": "^4.5.4"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"dev": "vite",
|
|
67
|
+
"build": "vite build",
|
|
68
|
+
"preview": "vite preview",
|
|
69
|
+
"lint": "eslint .",
|
|
70
|
+
"type-check": "tsc --noEmit"
|
|
71
|
+
}
|
|
72
|
+
}
|