@lekoala/date-picker 0.3.0 → 0.4.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/README.md +8 -6
- package/custom-elements.json +20 -0
- package/dist/date-picker.css +32 -32
- package/dist/date-picker.js +493 -96
- package/dist/date-picker.min.css +1 -1
- package/dist/date-picker.min.js +13 -13
- package/dist/date-picker.standalone.min.js +46 -46
- package/dist/types/date-calendar.d.ts +34 -3
- package/dist/types/date-calendar.d.ts.map +1 -1
- package/dist/types/date-field.d.ts +12 -4
- package/dist/types/date-field.d.ts.map +1 -1
- package/dist/types/date-picker.d.ts +91 -9
- package/dist/types/date-picker.d.ts.map +1 -1
- package/dist/types/date-range.d.ts +102 -20
- package/dist/types/date-range.d.ts.map +1 -1
- package/dist/types/date.d.ts +10 -0
- package/dist/types/date.d.ts.map +1 -1
- package/dist/types/intl.d.ts.map +1 -1
- package/docs/ACCESSIBILITY.md +6 -2
- package/docs/API.md +51 -1
- package/docs/DECISIONS.md +17 -4
- package/docs/MIGRATION-FLATPICKR.md +1 -1
- package/docs/TESTING.md +1 -1
- package/docs/USE_CASES.md +3 -0
- package/package.json +6 -6
- package/src/date-calendar.js +145 -42
- package/src/date-field.js +8 -4
- package/src/date-picker.css +32 -32
- package/src/date-picker.js +345 -37
- package/src/date-range.js +184 -30
- package/src/date.js +50 -1
- package/src/intl.js +13 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Small, native-first date picker and inline calendar primitives.
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> The interaction contract, event names, public styling tokens and the component API are frozen — see [docs/DECISIONS.md](docs/DECISIONS.md). Internal DOM and class names are `.dp-*` implementation details and stay private.
|
|
6
6
|
|
|
7
7
|
This project fills the gap between a normal editable date field and a full scheduling calendar. A plain date input remains the right answer for simple CMS/editing forms. This component becomes useful when a date has context: availability, disabled days, annotations, remote state, start/end relationships, or a mini calendar that navigates another view.
|
|
8
8
|
|
|
@@ -164,6 +164,8 @@ The picker popup uses native Popover for the top layer and `@lekoala/floating` f
|
|
|
164
164
|
|
|
165
165
|
The calendar trigger is a real button painted inside the field's own box (like the native `input[type=time]` indicator): the field reserves its place with `padding-inline-end`, keeps its own border and background, and its focus ring wraps the trigger too. The trigger draws no chrome of its own and shows a small inner ring when focused.
|
|
166
166
|
|
|
167
|
+
Fields, header controls and days share an inner outline that survives forced colors. Customize its color with `--dp-focus` (default `var(--dp-accent)`) and thickness with `--dp-focus-width` (default `2px`) on `date-picker` and `date-calendar`. The trigger keeps a `-5px` inset around its icon; selected days and range endpoints use `--dp-accent-fg` for contrast. Fields and header controls now show this solid outline instead of the previous soft halo.
|
|
168
|
+
|
|
167
169
|
When the popup opens on the field's focus, grid navigation is reachable via `ArrowDown` (or the trigger button) so the focus-restore/Escape flow stays predictable. Closing returns focus to the control that opened the popover: the field when it opened by focus, the trigger when it was activated. In range mode focus follows the active bound instead, because a first pick moves the workflow to the other bound.
|
|
168
170
|
|
|
169
171
|
See [docs/ACCESSIBILITY.md](docs/ACCESSIBILITY.md).
|
|
@@ -179,10 +181,10 @@ If a previous `bun run dev` is still holding port `4859`, clear it with `bun run
|
|
|
179
181
|
|
|
180
182
|
Tooling is aligned with the current LeKoala component repos:
|
|
181
183
|
|
|
182
|
-
- Bun 1.4.
|
|
184
|
+
- Bun 1.4.3
|
|
183
185
|
- TypeScript 7.0.2 (`checkJs` + declaration emit)
|
|
184
|
-
- Biome 2.5.
|
|
185
|
-
- Playwright 1.
|
|
186
|
+
- Biome 2.5.15
|
|
187
|
+
- Playwright 1.64.0
|
|
186
188
|
- `@lekoala/floating ^0.2.0`
|
|
187
189
|
|
|
188
190
|
Useful commands:
|
|
@@ -214,7 +216,7 @@ The default ESM exports point at `src/`, matching the native-first development s
|
|
|
214
216
|
|
|
215
217
|
## Scope
|
|
216
218
|
|
|
217
|
-
Included
|
|
219
|
+
Included:
|
|
218
220
|
|
|
219
221
|
- inline single-date selection;
|
|
220
222
|
- mini-calendar/navigation-only use;
|
|
@@ -236,7 +238,7 @@ Included in v0.2:
|
|
|
236
238
|
- ISO week numbers;
|
|
237
239
|
- keyboard and focus contracts.
|
|
238
240
|
|
|
239
|
-
Deliberately out of scope
|
|
241
|
+
Deliberately out of scope:
|
|
240
242
|
|
|
241
243
|
- custom time-picker UI and combined datetime values (only native `input[type=time]` companions are supported);
|
|
242
244
|
- timezones;
|
package/custom-elements.json
CHANGED
|
@@ -139,6 +139,11 @@
|
|
|
139
139
|
"kind": "method",
|
|
140
140
|
"name": "focusGrid"
|
|
141
141
|
},
|
|
142
|
+
{
|
|
143
|
+
"kind": "method",
|
|
144
|
+
"name": "activateDate",
|
|
145
|
+
"description": "Run one explicit activation: availability check, then the cancelable `dateactivate` seam, then selection. Click, keyboard and any other input device share this single path, so an application that cancels `dateactivate` cannot be bypassed by a new interaction."
|
|
146
|
+
},
|
|
142
147
|
{
|
|
143
148
|
"kind": "method",
|
|
144
149
|
"name": "render"
|
|
@@ -151,6 +156,9 @@
|
|
|
151
156
|
{
|
|
152
157
|
"name": "datechange"
|
|
153
158
|
},
|
|
159
|
+
{
|
|
160
|
+
"name": "datefocus"
|
|
161
|
+
},
|
|
154
162
|
{
|
|
155
163
|
"name": "dateinvalid"
|
|
156
164
|
},
|
|
@@ -189,6 +197,12 @@
|
|
|
189
197
|
{
|
|
190
198
|
"name": "--dp-accent-fg"
|
|
191
199
|
},
|
|
200
|
+
{
|
|
201
|
+
"name": "--dp-focus"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"name": "--dp-focus-width"
|
|
205
|
+
},
|
|
192
206
|
{
|
|
193
207
|
"name": "--dp-radius"
|
|
194
208
|
},
|
|
@@ -375,6 +389,12 @@
|
|
|
375
389
|
{
|
|
376
390
|
"name": "--dp-accent-fg"
|
|
377
391
|
},
|
|
392
|
+
{
|
|
393
|
+
"name": "--dp-focus"
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
"name": "--dp-focus-width"
|
|
397
|
+
},
|
|
378
398
|
{
|
|
379
399
|
"name": "--dp-radius"
|
|
380
400
|
},
|
package/dist/date-picker.css
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
--dp-hover: color-mix(in srgb, var(--dp-fg) 5%, var(--dp-bg));
|
|
7
7
|
--dp-accent: #2563eb;
|
|
8
8
|
--dp-accent-fg: #fff;
|
|
9
|
+
--dp-focus: var(--dp-accent);
|
|
10
|
+
--dp-focus-width: 2px;
|
|
9
11
|
--dp-radius: 0.5rem;
|
|
10
12
|
--dp-day-size: 2.75rem;
|
|
11
13
|
--dp-gap: 0.375rem;
|
|
@@ -137,14 +139,6 @@ html[dir="rtl"] .dp-month-select {
|
|
|
137
139
|
pointer-events: none;
|
|
138
140
|
}
|
|
139
141
|
|
|
140
|
-
/* Inherited RTL mirrors the header layout via the grid; mirror the nav glyphs
|
|
141
|
-
so previous/next keep their visual sense. Actions stay chronological: no
|
|
142
|
-
button swap, no keyboard remapping. */
|
|
143
|
-
[dir="rtl"] .dp-nav svg,
|
|
144
|
-
html[dir="rtl"] .dp-nav svg {
|
|
145
|
-
transform: scaleX(-1);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
142
|
.dp-calendar-header :where(button, select, input):disabled,
|
|
149
143
|
.dp-picker-button:disabled {
|
|
150
144
|
cursor: not-allowed;
|
|
@@ -155,12 +149,13 @@ html[dir="rtl"] .dp-nav svg {
|
|
|
155
149
|
background: var(--dp-hover);
|
|
156
150
|
}
|
|
157
151
|
|
|
158
|
-
/*
|
|
159
|
-
|
|
160
|
-
.dp-calendar-header :where(button, select, input):focus-visible
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
152
|
+
/* Inner outlines survive forced colors and keep focus inside each control.
|
|
153
|
+
The picker trigger uses a tighter inset around its icon below. */
|
|
154
|
+
.dp-calendar-header :where(button, select, input):focus-visible,
|
|
155
|
+
date-picker > input:not([type="hidden"]):focus-visible,
|
|
156
|
+
.dp-day:focus-visible {
|
|
157
|
+
outline: var(--dp-focus-width) solid var(--dp-focus);
|
|
158
|
+
outline-offset: calc(var(--dp-focus-width) * -1);
|
|
164
159
|
}
|
|
165
160
|
|
|
166
161
|
.dp-day:hover:not([data-disabled="true"]):not([data-selected="true"]):not([data-range-start="true"]):not(
|
|
@@ -205,15 +200,6 @@ html[dir="rtl"] .dp-nav svg {
|
|
|
205
200
|
font-size: 0.875rem;
|
|
206
201
|
}
|
|
207
202
|
|
|
208
|
-
/* Inner ring: keyboard focus stays inside the cell instead of reading as a
|
|
209
|
-
second selected state or bleeding onto a neighbouring range band. It is an
|
|
210
|
-
`outline` (not an inset box-shadow) because forced colors computes
|
|
211
|
-
box-shadow to `none`, which would erase the keyboard indicator entirely. */
|
|
212
|
-
.dp-day:focus-visible {
|
|
213
|
-
outline: 2px solid var(--dp-accent);
|
|
214
|
-
outline-offset: -2px;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
203
|
/* On an accent-filled cell the ring inverts so it stays readable. */
|
|
218
204
|
.dp-day:is([data-selected="true"], [data-range-start="true"], [data-range-end="true"]):focus-visible {
|
|
219
205
|
outline-color: var(--dp-accent-fg);
|
|
@@ -275,6 +261,28 @@ html[dir="rtl"] .dp-nav svg {
|
|
|
275
261
|
border-end-end-radius: var(--dp-day-radius);
|
|
276
262
|
}
|
|
277
263
|
|
|
264
|
+
/* Projected band: the range the next commit would produce. It reads as one
|
|
265
|
+
step short of the committed band, so a hovered, keyboard or dragged preview
|
|
266
|
+
is never mistaken for the selection itself. */
|
|
267
|
+
[data-range-preview] .dp-day:is([data-range-start="true"], [data-range-end="true"]) {
|
|
268
|
+
background: color-mix(in srgb, var(--dp-accent) 58%, var(--dp-bg));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
[data-range-preview] .dp-day[data-in-range="true"] {
|
|
272
|
+
background: color-mix(in srgb, var(--dp-accent) 11%, var(--dp-bg));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Endpoint handles are draggable only while the committed range is complete
|
|
276
|
+
and its two ends sit on different days. */
|
|
277
|
+
[data-range-drag] .dp-day:is([data-range-start="true"], [data-range-end="true"]) {
|
|
278
|
+
cursor: grab;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
[data-range-drag="active"],
|
|
282
|
+
[data-range-drag="active"] .dp-day {
|
|
283
|
+
cursor: grabbing;
|
|
284
|
+
}
|
|
285
|
+
|
|
278
286
|
.dp-day-number {
|
|
279
287
|
display: block;
|
|
280
288
|
font-variant-numeric: tabular-nums;
|
|
@@ -320,14 +328,6 @@ date-picker {
|
|
|
320
328
|
max-inline-size: 100%;
|
|
321
329
|
}
|
|
322
330
|
|
|
323
|
-
/* Visible focus is normalized to --dp-accent so the component holds together
|
|
324
|
-
without a consumer stylesheet; the field's own look stays consumer-owned. */
|
|
325
|
-
date-picker > input:not([type="hidden"]):focus-visible {
|
|
326
|
-
border-color: var(--dp-accent);
|
|
327
|
-
outline: none;
|
|
328
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--dp-accent) 20%, transparent);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
331
|
/* Every authored control is a grid item. */
|
|
332
332
|
date-picker > input:not([type="hidden"]) {
|
|
333
333
|
min-inline-size: 0;
|
|
@@ -368,7 +368,7 @@ date-picker > input:not([type="hidden"]):not([type="time"]) {
|
|
|
368
368
|
field already shows its own ring through :focus-visible). Same radius as
|
|
369
369
|
everywhere else: no bespoke shape. */
|
|
370
370
|
.dp-picker-button:focus-visible {
|
|
371
|
-
outline:
|
|
371
|
+
outline: var(--dp-focus-width) solid var(--dp-focus);
|
|
372
372
|
outline-offset: -5px;
|
|
373
373
|
border-radius: var(--dp-radius);
|
|
374
374
|
}
|