@pine-ds/core 4.10.0 → 4.11.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/components/pds-datepicker.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/pds-datepicker.cjs.entry.js +253 -75
- package/dist/cjs/pine-core.cjs.js +1 -1
- package/dist/collection/components/pds-datepicker/date-utils.js +42 -0
- package/dist/collection/components/pds-datepicker/pds-datepicker.js +163 -64
- package/dist/collection/components/pds-datepicker/stories/pds-datepicker.stories.js +11 -0
- package/dist/collection/components/pds-datepicker/value.js +76 -18
- package/dist/docs.json +35 -6
- package/dist/esm/loader.js +1 -1
- package/dist/esm/pds-datepicker.entry.js +253 -75
- package/dist/esm/pine-core.js +1 -1
- package/dist/esm-es5/loader.js +1 -1
- package/dist/esm-es5/pds-datepicker.entry.js +1 -1
- package/dist/esm-es5/pine-core.js +1 -1
- package/dist/pine-core/p-3359a935.entry.js +1 -0
- package/dist/pine-core/p-XK45dzWR.system.js +1 -1
- package/dist/pine-core/p-d4968f9d.system.entry.js +1 -0
- package/dist/pine-core/pine-core.esm.js +1 -1
- package/dist/types/components/pds-datepicker/date-utils.d.ts +19 -0
- package/dist/types/components/pds-datepicker/datepicker-interface.d.ts +2 -1
- package/dist/types/components/pds-datepicker/pds-datepicker.d.ts +30 -5
- package/dist/types/components/pds-datepicker/value.d.ts +14 -2
- package/dist/types/components.d.ts +25 -10
- package/dist/vscode.html-data.json +11 -4
- package/hydrate/index.js +256 -76
- package/hydrate/index.mjs +256 -76
- package/package.json +2 -2
- package/dist/pine-core/p-8436cbfb.entry.js +0 -1
- package/dist/pine-core/p-d47b2a6a.system.entry.js +0 -1
|
@@ -141,6 +141,48 @@ function dateFormatter(locale, options) {
|
|
|
141
141
|
function formatDate(date, locale, options) {
|
|
142
142
|
return dateFormatter(locale, options).format(date);
|
|
143
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Month-day mode's year. A leap year, so February has its 29th; every date the mode holds is in it,
|
|
146
|
+
* and the value drops it (`--MM-DD`).
|
|
147
|
+
*/
|
|
148
|
+
const MONTH_DAY_YEAR = 2000;
|
|
149
|
+
const MONTH_DAY = /^--(\d{2})-(\d{2})$/;
|
|
150
|
+
/** The same month and day in month-day mode's year. */
|
|
151
|
+
function inMonthDayYear(date) {
|
|
152
|
+
return new Date(Date.UTC(MONTH_DAY_YEAR, date.getUTCMonth(), date.getUTCDate()));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Parses a month and day, `--MM-DD` (ISO 8601 without a year), to a date in month-day mode's year.
|
|
156
|
+
* A full `YYYY-MM-DD` reads as its month and day. `null` when it isn't a real month and day.
|
|
157
|
+
*/
|
|
158
|
+
function parseMonthDay(value) {
|
|
159
|
+
const text = (value !== null && value !== void 0 ? value : '').trim();
|
|
160
|
+
const match = MONTH_DAY.exec(text);
|
|
161
|
+
if (match)
|
|
162
|
+
return parseIsoDate(`${MONTH_DAY_YEAR}-${match[1]}-${match[2]}`);
|
|
163
|
+
const date = parseIsoDate(text);
|
|
164
|
+
return date && inMonthDayYear(date);
|
|
165
|
+
}
|
|
166
|
+
/** A date's month and day as `--MM-DD`. */
|
|
167
|
+
function toMonthDay(date) {
|
|
168
|
+
return `-${toIsoDate(date).slice(4)}`;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* A month's days for month-day mode, which has no weekdays: rows of seven from the 1st, with the
|
|
172
|
+
* last row filled out with `null`.
|
|
173
|
+
*/
|
|
174
|
+
function monthDayRows(month) {
|
|
175
|
+
const cells = [];
|
|
176
|
+
for (let day = 1; day <= daysInMonth(MONTH_DAY_YEAR, month); day++) {
|
|
177
|
+
cells.push(new Date(Date.UTC(MONTH_DAY_YEAR, month, day)));
|
|
178
|
+
}
|
|
179
|
+
while (cells.length % 7 !== 0)
|
|
180
|
+
cells.push(null);
|
|
181
|
+
const rows = [];
|
|
182
|
+
for (let i = 0; i < cells.length; i += 7)
|
|
183
|
+
rows.push(cells.slice(i, i + 7));
|
|
184
|
+
return rows;
|
|
185
|
+
}
|
|
144
186
|
|
|
145
187
|
/**
|
|
146
188
|
* Where the open calendar lives: a `div` in `document.body`, or in the nearest `<dialog>` so it
|
|
@@ -384,32 +426,50 @@ const wordsCache = new Map();
|
|
|
384
426
|
function localeWords(locale) {
|
|
385
427
|
let words = wordsCache.get(locale);
|
|
386
428
|
if (!words) {
|
|
387
|
-
const month = (style) => {
|
|
388
|
-
const format = new Intl.DateTimeFormat(locale, {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
429
|
+
const month = (style, withDay) => {
|
|
430
|
+
const format = new Intl.DateTimeFormat(locale, Object.assign(Object.assign({ month: style }, (withDay ? { day: 'numeric' } : {})), { timeZone: 'UTC', calendar: 'gregory' }));
|
|
431
|
+
return Array.from({ length: 12 }, (_, m) => {
|
|
432
|
+
const date = new Date(Date.UTC(2023, m, 1));
|
|
433
|
+
if (!withDay)
|
|
434
|
+
return format.format(date);
|
|
435
|
+
const parts = format.formatToParts(date);
|
|
436
|
+
const index = parts.findIndex((part) => part.type === 'month');
|
|
437
|
+
if (index === -1)
|
|
438
|
+
return format.format(date);
|
|
439
|
+
// CJK locales split a counter word, as Japanese `月`, into its own literal right after the
|
|
440
|
+
// month; a literal with no space is that word, not the separator before the day.
|
|
441
|
+
const suffix = parts[index + 1];
|
|
442
|
+
return (suffix === null || suffix === void 0 ? void 0 : suffix.type) === 'literal' && !/\s/.test(suffix.value) ? parts[index].value + suffix.value : parts[index].value;
|
|
392
443
|
});
|
|
393
|
-
return Array.from({ length: 12 }, (_, m) => format.format(new Date(Date.UTC(2023, m, 1))));
|
|
394
444
|
};
|
|
395
445
|
const clock = clockFor(locale);
|
|
396
|
-
words = {
|
|
446
|
+
words = {
|
|
447
|
+
months: month('long', false),
|
|
448
|
+
shortMonths: month('short', false),
|
|
449
|
+
monthsWithDay: month('long', true),
|
|
450
|
+
shortMonthsWithDay: month('short', true),
|
|
451
|
+
am: clock.am,
|
|
452
|
+
pm: clock.pm,
|
|
453
|
+
};
|
|
397
454
|
wordsCache.set(locale, words);
|
|
398
455
|
}
|
|
399
456
|
return words;
|
|
400
457
|
}
|
|
458
|
+
/** Whether a format's tokens (see `tokensOnly`) have a day, so a month name should inflect for one. */
|
|
459
|
+
const hasDayToken = (tokens) => /D/.test(tokens);
|
|
401
460
|
/** Writes a date in a format of tokens (see `FORMAT_PARTS`), with month names and AM/PM in the locale. */
|
|
402
461
|
function formatWith(value, format, locale = 'en-US') {
|
|
403
462
|
var _a;
|
|
404
463
|
const words = localeWords(locale);
|
|
464
|
+
const withDay = hasDayToken(tokensOnly(format));
|
|
405
465
|
const [hours, minutes] = splitTime((_a = value.time) !== null && _a !== void 0 ? _a : '00:00');
|
|
406
466
|
const hour12 = hours % 12 || 12;
|
|
407
467
|
const month = value.date.getUTCMonth();
|
|
408
468
|
const period = hours < 12 ? words.am : words.pm;
|
|
409
469
|
const parts = {
|
|
410
470
|
YYYY: pad(value.date.getUTCFullYear(), 4),
|
|
411
|
-
MMMM: words.months[month],
|
|
412
|
-
MMM: words.shortMonths[month],
|
|
471
|
+
MMMM: withDay ? words.monthsWithDay[month] : words.months[month],
|
|
472
|
+
MMM: withDay ? words.shortMonthsWithDay[month] : words.shortMonths[month],
|
|
413
473
|
MM: pad(month + 1),
|
|
414
474
|
M: String(month + 1),
|
|
415
475
|
DD: pad(value.date.getUTCDate()),
|
|
@@ -431,17 +491,23 @@ const alternatives = (words) => `(${words.map(escapeRegExp).join('|')})`;
|
|
|
431
491
|
/**
|
|
432
492
|
* Reads typed text in a format of tokens, as written by `formatWith`. Leading zeros are optional,
|
|
433
493
|
* spaces are loose, and month names and AM/PM are matched without regard to case, in the locale or
|
|
434
|
-
* in English.
|
|
494
|
+
* in English. A format without `YYYY` reads into `year`, and without `year` it reads nothing.
|
|
495
|
+
* Returns `null` for anything that isn't a real date and time in that format.
|
|
435
496
|
*/
|
|
436
|
-
function parseWith(text, format, locale = 'en-US') {
|
|
497
|
+
function parseWith(text, format, locale = 'en-US', year) {
|
|
437
498
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
438
499
|
const words = localeWords(locale);
|
|
439
500
|
const english = localeWords('en-US');
|
|
501
|
+
const withDay = hasDayToken(tokensOnly(format));
|
|
502
|
+
const months = withDay ? words.monthsWithDay : words.months;
|
|
503
|
+
const shortMonths = withDay ? words.shortMonthsWithDay : words.shortMonths;
|
|
504
|
+
const englishMonths = withDay ? english.monthsWithDay : english.months;
|
|
505
|
+
const englishShortMonths = withDay ? english.shortMonthsWithDay : english.shortMonths;
|
|
440
506
|
const periods = [words.am, words.pm, english.am, english.pm];
|
|
441
507
|
const patterns = {
|
|
442
508
|
YYYY: '(\\d{4})',
|
|
443
|
-
MMMM: alternatives([...
|
|
444
|
-
MMM: alternatives([...
|
|
509
|
+
MMMM: alternatives([...months, ...englishMonths]),
|
|
510
|
+
MMM: alternatives([...shortMonths, ...englishShortMonths]),
|
|
445
511
|
MM: '(\\d{1,2})',
|
|
446
512
|
M: '(\\d{1,2})',
|
|
447
513
|
DD: '(\\d{1,2})',
|
|
@@ -488,7 +554,7 @@ function parseWith(text, format, locale = 'en-US') {
|
|
|
488
554
|
const monthIndex = (name, long) => {
|
|
489
555
|
if (name === undefined)
|
|
490
556
|
return undefined;
|
|
491
|
-
const lists = long ? [
|
|
557
|
+
const lists = long ? [months, englishMonths] : [shortMonths, englishShortMonths];
|
|
492
558
|
for (const list of lists) {
|
|
493
559
|
const index = list.findIndex((candidate) => candidate.toLowerCase() === name.toLowerCase());
|
|
494
560
|
if (index >= 0)
|
|
@@ -496,12 +562,12 @@ function parseWith(text, format, locale = 'en-US') {
|
|
|
496
562
|
}
|
|
497
563
|
return undefined;
|
|
498
564
|
};
|
|
499
|
-
const
|
|
565
|
+
const readYear = read.YYYY ? Number(read.YYYY) : year;
|
|
500
566
|
const month = (_b = (_a = monthIndex(read.MMMM, true)) !== null && _a !== void 0 ? _a : monthIndex(read.MMM, false)) !== null && _b !== void 0 ? _b : Number((_c = read.MM) !== null && _c !== void 0 ? _c : read.M);
|
|
501
567
|
const day = Number((_d = read.DD) !== null && _d !== void 0 ? _d : read.D);
|
|
502
|
-
if (
|
|
568
|
+
if (readYear === undefined || !month || !day)
|
|
503
569
|
return null;
|
|
504
|
-
const date = parseIsoDate(`${pad(
|
|
570
|
+
const date = parseIsoDate(`${pad(readYear, 4)}-${pad(month)}-${pad(day)}`);
|
|
505
571
|
if (!date)
|
|
506
572
|
return null;
|
|
507
573
|
const hourText = (_g = (_f = (_e = read.HH) !== null && _e !== void 0 ? _e : read.H) !== null && _f !== void 0 ? _f : read.hh) !== null && _g !== void 0 ? _g : read.h;
|
|
@@ -523,6 +589,40 @@ function parseFieldText(text, format = DEFAULT_FORMAT, locale = 'en-US') {
|
|
|
523
589
|
var _a;
|
|
524
590
|
return (_a = parseWith(text, format, locale)) !== null && _a !== void 0 ? _a : parseDateTime(text);
|
|
525
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* The locale's way of writing a month and day, as a format of tokens: `MMMM D` in English, `D MMMM`
|
|
594
|
+
* in French, `M月D日` in Japanese, or with `numeric`, `M/D` and `D/M`.
|
|
595
|
+
*/
|
|
596
|
+
function monthDayFormat(locale = 'en-US', month = 'long') {
|
|
597
|
+
const parts = dateFormatter(locale, { month, day: 'numeric' }).formatToParts(new Date(Date.UTC(MONTH_DAY_YEAR, 11, 31)));
|
|
598
|
+
return parts
|
|
599
|
+
.map((part) => {
|
|
600
|
+
// Some locales write even the long month in digits, as Japanese `12月31日`.
|
|
601
|
+
if (part.type === 'month')
|
|
602
|
+
return /^\d+$/.test(part.value) ? 'M' : 'MMMM';
|
|
603
|
+
if (part.type === 'day')
|
|
604
|
+
return 'D';
|
|
605
|
+
// Bracketed, spaces included, only when it has letters a token could be read from, as
|
|
606
|
+
// Spanish `D[ de ]MMMM`.
|
|
607
|
+
return /[A-Za-z]/.test(part.value) ? `[${part.value}]` : part.value;
|
|
608
|
+
})
|
|
609
|
+
.join('');
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Reads a typed month and day, into month-day mode's year: in the format, then as the locale writes
|
|
613
|
+
* it with a month name or in numbers, with the English order too, then as `--MM-DD` or a full ISO
|
|
614
|
+
* date, whose year is dropped.
|
|
615
|
+
*/
|
|
616
|
+
function parseMonthDayText(text, format, locale = 'en-US') {
|
|
617
|
+
const formats = [format, monthDayFormat(locale), monthDayFormat(locale, 'numeric'), 'MMMM D', 'D MMMM', 'MMM D', 'D MMM'];
|
|
618
|
+
for (const candidate of formats) {
|
|
619
|
+
const read = parseWith(text, candidate, locale, MONTH_DAY_YEAR);
|
|
620
|
+
if (read)
|
|
621
|
+
return { date: inMonthDayYear(read.date), time: null };
|
|
622
|
+
}
|
|
623
|
+
const date = parseMonthDay(text);
|
|
624
|
+
return date ? { date, time: null } : null;
|
|
625
|
+
}
|
|
526
626
|
/**
|
|
527
627
|
* Parses a typed range: two dates (with times, optionally) joined by a dash or `to` with spaces
|
|
528
628
|
* around it, as `2026-09-13 – 2026-10-13`, or by `/` when the format doesn't use slashes. The
|
|
@@ -577,6 +677,8 @@ function clockFor(locale) {
|
|
|
577
677
|
const pdsDatepickerCss = () => `:host{--pds-datepicker-day-size:var(--pine-dimension-500);--pds-datepicker-presets-width:200px;display:inline-block}:host([full-width]){display:block}pds-input{display:block}:host(.pds-datepicker--time) pds-input{min-width:220px}:host(.pds-datepicker--range) pds-input{min-width:260px}:host(.pds-datepicker--range.pds-datepicker--time) pds-input{min-width:min(380px, 100%)}.pds-datepicker__trigger,.pds-datepicker__clear{-ms-flex-align:center;align-items:center;background:transparent;border:0;border-radius:var(--pine-border-radius-sm);color:var(--pine-color-text-secondary);cursor:pointer;display:-ms-inline-flexbox;display:inline-flex;-ms-flex-pack:center;justify-content:center;padding:var(--pine-dimension-2xs)}.pds-datepicker__trigger:hover:not(:disabled),.pds-datepicker__clear:hover:not(:disabled){background:var(--pine-color-background-muted);color:var(--pine-color-text-strong)}.pds-datepicker__trigger:focus-visible,.pds-datepicker__clear:focus-visible{outline:var(--pine-outline-focus)}.pds-datepicker__trigger:disabled,.pds-datepicker__clear:disabled{color:var(--pine-color-text-disabled);cursor:not-allowed}.pds-datepicker__clear{-webkit-margin-end:var(--pine-dimension-2xs);margin-inline-end:var(--pine-dimension-2xs)}.pds-datepicker__nav{-ms-flex-align:center;align-items:center;background:var(--pine-color-secondary);border:var(--pine-border);border-radius:var(--pine-border-radius-full);-webkit-box-sizing:border-box;box-sizing:border-box;color:var(--pine-color-text-secondary);cursor:pointer;display:-ms-inline-flexbox;display:inline-flex;height:var(--pine-dimension-400);-ms-flex-pack:center;justify-content:center;padding:0;width:var(--pine-dimension-400)}.pds-datepicker__nav:hover:not(:disabled){background:var(--pine-color-secondary-hover);border-color:var(--pine-color-border-hover)}.pds-datepicker__nav:focus-visible{outline:var(--pine-outline-focus);outline-offset:var(--pine-border-width)}.pds-datepicker__nav:disabled{border-color:var(--pine-color-border-disabled);color:var(--pine-color-text-secondary-disabled);cursor:not-allowed}.pds-datepicker__nav:dir(rtl) pds-icon{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.pds-datepicker__dialog:focus{outline:none}.pds-datepicker__dialog{background:var(--pine-color-background-overlay);border-radius:var(--pine-dimension-150);-webkit-box-shadow:var(--pine-box-shadow-200);box-shadow:var(--pine-box-shadow-200);-webkit-box-sizing:border-box;box-sizing:border-box;left:0;max-height:calc(100vh - var(--pine-dimension-sm));max-height:calc(100dvh - var(--pine-dimension-sm));max-width:calc(100vw - var(--pine-dimension-sm));overflow:auto;-ms-scroll-chaining:none;overscroll-behavior:contain;padding:var(--pine-dimension-sm);pointer-events:auto;position:fixed;top:0;visibility:hidden;z-index:var(--pine-z-index-priority)}:host-context([data-theme=dark]) .pds-datepicker__dialog{border:var(--pine-border)}.pds-datepicker__header{-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between;-webkit-margin-after:var(--pine-dimension-sm);margin-block-end:var(--pine-dimension-sm)}.pds-datepicker__nav-spacer{-ms-flex:none;flex:none;width:var(--pine-dimension-400)}.pds-datepicker__heading{color:var(--pine-color-text);font:var(--pine-typography-heading-5);font-weight:var(--pine-font-weight-semi-bold)}.pds-datepicker__grid{border-collapse:collapse;border-spacing:0}.pds-datepicker__weekday{color:var(--pine-color-text-muted);font:var(--pine-typography-body-sm);height:var(--pds-datepicker-day-size);padding:0;text-align:center;width:var(--pds-datepicker-day-size)}.pds-datepicker__day{border-radius:var(--pine-border-radius-full);color:var(--pine-color-text-secondary);cursor:pointer;font:var(--pine-typography-body);height:var(--pds-datepicker-day-size);padding:0;position:relative;text-align:center;-webkit-transition:background var(--pine-motion-duration-fast) var(--pine-motion-easing-in);transition:background var(--pine-motion-duration-fast) var(--pine-motion-easing-in);width:var(--pds-datepicker-day-size)}.pds-datepicker__day:hover{background:var(--pine-color-background-muted)}.pds-datepicker__day:focus-visible{outline:var(--pine-outline-focus);outline-offset:calc(var(--pine-border-width-thick) * -1)}.pds-datepicker__day--empty{cursor:default;pointer-events:none}.pds-datepicker__day--today::after{background:var(--pine-color-primary);border-radius:var(--pine-border-radius-full);content:"";height:var(--pine-dimension-050);inset-block-end:var(--pine-dimension-050);inset-inline-start:50%;position:absolute;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:var(--pine-dimension-050)}.pds-datepicker__day--selected,.pds-datepicker__day--selected:hover{background:var(--pine-color-primary);color:var(--pine-color-text-primary)}.pds-datepicker__day--selected:focus-visible{outline-offset:var(--pine-border-width)}.pds-datepicker__day--range-start{border-end-end-radius:0;border-start-end-radius:0}.pds-datepicker__day--range-end{border-end-start-radius:0;border-start-start-radius:0}.pds-datepicker__day--range-start.pds-datepicker__day--row-end{border-end-end-radius:var(--pine-border-radius-full);border-start-end-radius:var(--pine-border-radius-full)}.pds-datepicker__day--range-end.pds-datepicker__day--row-start{border-end-start-radius:var(--pine-border-radius-full);border-start-start-radius:var(--pine-border-radius-full)}.pds-datepicker__day--selected.pds-datepicker__day--today::after{background:var(--pine-color-text-primary)}.pds-datepicker__day--in-range{border-radius:0}.pds-datepicker__day--in-range,.pds-datepicker__day--in-range:hover{background:var(--pine-color-background-muted);color:var(--pine-color-text-secondary)}.pds-datepicker__day--row-start{border-end-start-radius:var(--pine-border-radius-full);border-start-start-radius:var(--pine-border-radius-full)}.pds-datepicker__day--row-end{border-end-end-radius:var(--pine-border-radius-full);border-start-end-radius:var(--pine-border-radius-full)}.pds-datepicker__day--disabled,.pds-datepicker__day--disabled:hover{background:transparent;color:var(--pine-color-text-disabled);cursor:not-allowed;text-decoration:line-through}.pds-datepicker__day--in-range:hover{background:var(--pine-color-background-inset)}.pds-datepicker__dialog--range{display:-ms-flexbox;display:flex;padding:0}.pds-datepicker__panel{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0}.pds-datepicker__dialog--range .pds-datepicker__panel{padding:var(--pine-dimension-sm)}.pds-datepicker__months{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;gap:var(--pine-dimension-lg)}.pds-datepicker__presets-column{-webkit-border-end:var(--pine-border-width-thin) solid var(--pine-color-border-subtle);border-inline-end:var(--pine-border-width-thin) solid var(--pine-color-border-subtle);-ms-flex:none;flex:none;position:relative;width:var(--pds-datepicker-presets-width)}.pds-datepicker__presets{-webkit-box-sizing:border-box;box-sizing:border-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;gap:var(--pine-dimension-2xs);inset:0;list-style:none;margin:0;overflow-y:auto;padding:var(--pine-dimension-150);position:absolute}.pds-datepicker__preset{-ms-flex-align:center;align-items:center;background:transparent;border:0;border-radius:var(--pine-border-radius-md);color:var(--pine-color-text-secondary);cursor:pointer;display:-ms-flexbox;display:flex;font:var(--pine-typography-body);gap:var(--pine-dimension-xs);-ms-flex-pack:justify;justify-content:space-between;padding:var(--pine-dimension-xs) var(--pine-dimension-sm);text-align:start;-webkit-transition:background var(--pine-motion-duration-fast) var(--pine-motion-easing-in);transition:background var(--pine-motion-duration-fast) var(--pine-motion-easing-in);width:100%}.pds-datepicker__preset:hover:not(:disabled){background:var(--pine-color-background-muted)}.pds-datepicker__preset:focus-visible{outline:var(--pine-outline-focus)}.pds-datepicker__preset:disabled{color:var(--pine-color-text-disabled);cursor:not-allowed}.pds-datepicker__preset--selected{background:var(--pine-color-background-muted);color:var(--pine-color-text-active)}.pds-datepicker__footer{-ms-flex-align:center;align-items:center;background:var(--pine-color-background-overlay);-webkit-border-before:var(--pine-border-width-thin) solid var(--pine-color-border-subtle);border-block-start:var(--pine-border-width-thin) solid var(--pine-color-border-subtle);display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;gap:var(--pine-dimension-xs);inset-block-end:calc(var(--pine-dimension-sm) * -1);-ms-flex-pack:end;justify-content:flex-end;margin-block:var(--pine-dimension-sm) calc(var(--pine-dimension-sm) * -1);padding-block:var(--pine-dimension-sm);position:-webkit-sticky;position:sticky}.pds-datepicker__selection{color:var(--pine-color-text-muted);font:var(--pine-typography-body-sm-medium);-webkit-margin-end:auto;margin-inline-end:auto}.pds-datepicker__dropdowns{display:-ms-flexbox;display:flex;gap:var(--pine-dimension-2xs)}.pds-datepicker__select-wrap{-ms-flex-align:center;align-items:center;display:-ms-inline-flexbox;display:inline-flex;position:relative}.pds-datepicker__heading-select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;border-radius:var(--pine-border-radius-md);color:var(--pine-color-text);cursor:pointer;field-sizing:content;font:var(--pine-typography-heading-5);font-weight:var(--pine-font-weight-semi-bold);padding-block:var(--pine-dimension-2xs);padding-inline:var(--pine-dimension-xs) calc(var(--pine-dimension-xs) + var(--pine-dimension-sm))}.pds-datepicker__heading-select:hover{background:var(--pine-color-background-muted)}.pds-datepicker__heading-select:focus-visible{outline:var(--pine-outline-focus)}.pds-datepicker__select-caret{color:var(--pine-color-text-secondary);inset-inline-end:var(--pine-dimension-xs);pointer-events:none;position:absolute}.pds-datepicker__time{-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;gap:var(--pine-dimension-xs);-ms-flex-pack:justify;justify-content:space-between;-webkit-margin-before:var(--pine-dimension-sm);margin-block-start:var(--pine-dimension-sm);padding-inline:var(--pine-dimension-xs)}.pds-datepicker__time-label{color:var(--pine-color-text-label);font:var(--pine-typography-body-medium)}.pds-datepicker__time-field{-ms-flex-align:center;align-items:center;background:var(--pine-color-background-container);border:var(--pine-border);border-radius:var(--pine-border-radius-md);display:-ms-inline-flexbox;display:inline-flex;min-height:var(--pine-dimension-400);padding-inline:var(--pine-dimension-2xs)}.pds-datepicker__time-field:hover{border-color:var(--pine-color-border-hover)}.pds-datepicker__time-field:focus-within{outline:var(--pine-outline-focus);outline-offset:var(--pine-border-width)}.pds-datepicker__time-select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;border-radius:var(--pine-border-radius-sm);color:var(--pine-color-text-strong);cursor:pointer;font:var(--pine-typography-body);font-variant-numeric:tabular-nums;padding:var(--pine-dimension-2xs);text-align:center}.pds-datepicker__time-select:focus-visible{background:var(--pine-color-background-muted);outline:none}.pds-datepicker__time-select--period{-webkit-margin-start:var(--pine-dimension-2xs);margin-inline-start:var(--pine-dimension-2xs)}.pds-datepicker__time-separator{color:var(--pine-color-text-secondary)}:host-context([data-theme=dark]) select{color-scheme:dark}.pds-datepicker__action{background:var(--pine-color-secondary);border:var(--pine-border);border-radius:var(--pine-border-radius-full);color:var(--pine-color-text-secondary);cursor:pointer;font:var(--pine-typography-body-medium);padding:calc(var(--pine-dimension-xs) - var(--pine-border-width)) var(--pine-dimension-sm)}.pds-datepicker__action:hover:not(:disabled){background:var(--pine-color-secondary-hover);border-color:var(--pine-color-border-hover)}.pds-datepicker__action:focus-visible{outline:var(--pine-outline-focus);outline-offset:var(--pine-border-width)}.pds-datepicker__action:disabled{background:var(--pine-color-secondary-disabled);border-color:var(--pine-color-border-disabled);color:var(--pine-color-text-secondary-disabled);cursor:not-allowed}.pds-datepicker__action--primary{background:var(--pine-color-primary);border-color:var(--pine-color-primary);color:var(--pine-color-text-primary)}.pds-datepicker__action--primary:hover:not(:disabled){background:var(--pine-color-primary-hover);border-color:var(--pine-color-primary-hover)}.pds-datepicker__action--primary:disabled{background:var(--pine-color-primary-disabled);border-color:var(--pine-color-primary-disabled);color:var(--pine-color-text-primary-disabled)}@media (max-width: 600px){.pds-datepicker__dialog--range{-ms-flex-direction:column;flex-direction:column}.pds-datepicker__presets-column{-webkit-border-after:var(--pine-border-width-thin) solid var(--pine-color-border-subtle);border-block-end:var(--pine-border-width-thin) solid var(--pine-color-border-subtle);-webkit-border-end:0;border-inline-end:0;width:auto}.pds-datepicker__presets{-ms-flex-direction:row;flex-direction:row;overflow-x:auto;position:static}.pds-datepicker__preset{white-space:nowrap}.pds-datepicker__selection{-ms-flex-preferred-size:100%;flex-basis:100%}}.pds-datepicker__trigger,.pds-datepicker__clear,.pds-datepicker__nav,.pds-datepicker__weekday,.pds-datepicker__day,.pds-datepicker__preset,.pds-datepicker__action{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media (forced-colors: active){.pds-datepicker__day--selected,.pds-datepicker__day--selected:hover{background:Highlight;color:HighlightText;forced-color-adjust:none}.pds-datepicker__day--in-range,.pds-datepicker__day--in-range:hover{border-block:var(--pine-border-width-thin) solid Highlight}.pds-datepicker__day--today::after{background:CanvasText;forced-color-adjust:none}.pds-datepicker__day--selected.pds-datepicker__day--today::after{background:HighlightText}.pds-datepicker__day--disabled{color:GrayText}}.visually-hidden{border:0;clip:rect(0 0 0 0);-webkit-clip-path:polygon(0 0, 0 0, 0 0);clip-path:polygon(0 0, 0 0, 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}`;
|
|
578
678
|
|
|
579
679
|
const FULL_DATE = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
|
|
680
|
+
/** A day in month-day mode, which has no year and so no weekday. */
|
|
681
|
+
const MONTH_AND_DAY = { month: 'long', day: 'numeric' };
|
|
580
682
|
const DEFAULT_START_TIME = '00:00';
|
|
581
683
|
const DEFAULT_END_TIME = '23:59';
|
|
582
684
|
/** Ids for datepickers without a `component-id`, so their calendars' ids stay unique. */
|
|
@@ -655,8 +757,10 @@ const PdsDatepicker = class {
|
|
|
655
757
|
* in the locale), `MM` or `M` (month), `DD` or `D` (day), and with the time picker `HH` or `H`
|
|
656
758
|
* (24-hour), `hh` or `h` (12-hour), `mm` and `A` (AM/PM, in the locale), as `MM/DD/YYYY`,
|
|
657
759
|
* `MMM D, YYYY` or `D.M.YYYY h:mm A`. Put literal text in brackets, as `[Due] D/M/YYYY`. The value
|
|
658
|
-
* and the change event stay ISO (`YYYY-MM-DD`), whatever the format.
|
|
659
|
-
* accepted too.
|
|
760
|
+
* and the change event stay ISO (`YYYY-MM-DD`; `--MM-DD` with `month-day`), whatever the format.
|
|
761
|
+
* Typed ISO dates are always accepted too. With `month-day`, a format without `YYYY` is taken as a
|
|
762
|
+
* month-and-day format and used as given, rather than reordered for the locale; leave it unset to
|
|
763
|
+
* follow the locale instead.
|
|
660
764
|
*/
|
|
661
765
|
this.format = 'YYYY-MM-DD';
|
|
662
766
|
/**
|
|
@@ -689,6 +793,15 @@ const PdsDatepicker = class {
|
|
|
689
793
|
* Accessible label for the month dropdown, when `show-dropdowns` is set.
|
|
690
794
|
*/
|
|
691
795
|
this.monthLabel = 'Month';
|
|
796
|
+
/**
|
|
797
|
+
* Picks a month and day with no year, for a date that comes round every year, such as a birthday.
|
|
798
|
+
* The value is `--MM-DD` (ISO 8601 without a year), the field shows the date as the locale writes
|
|
799
|
+
* a month and day, and the calendar shows each month without a year or weekdays, with February
|
|
800
|
+
* always having its 29th. Turns off `range`, `time-picker`, `show-dropdowns`, `min` and `max`, and
|
|
801
|
+
* has no effect with `auto-apply`, `presets`, `first-day-of-week`, `min-date-message` or
|
|
802
|
+
* `max-date-message`, which all depend on them.
|
|
803
|
+
*/
|
|
804
|
+
this.monthDay = false;
|
|
692
805
|
/**
|
|
693
806
|
* Accessible label for the button that shows the next month.
|
|
694
807
|
*/
|
|
@@ -737,7 +850,9 @@ const PdsDatepicker = class {
|
|
|
737
850
|
this.triggerLabel = 'Choose date';
|
|
738
851
|
/**
|
|
739
852
|
* The selected date as `YYYY-MM-DD`, or in range mode the range as `YYYY-MM-DD/YYYY-MM-DD`; with
|
|
740
|
-
* the time picker, each date as `YYYY-MM-DDTHH:mm
|
|
853
|
+
* the time picker, each date as `YYYY-MM-DDTHH:mm`; with `month-day`, the month and day with no
|
|
854
|
+
* year, as `--MM-DD` (a full date is read too, as that month and day, and rewritten to it). An
|
|
855
|
+
* empty string when nothing is selected.
|
|
741
856
|
*/
|
|
742
857
|
this.value = '';
|
|
743
858
|
/**
|
|
@@ -928,17 +1043,17 @@ const PdsDatepicker = class {
|
|
|
928
1043
|
// The times before the typed text changed them, for a pick in the calendar to go back to.
|
|
929
1044
|
(_b = this.timesBeforeTyping) !== null && _b !== void 0 ? _b : (this.timesBeforeTyping = [startTime, endTime]);
|
|
930
1045
|
this.loadDraft(parts);
|
|
931
|
-
if (this.
|
|
1046
|
+
if (this.hasTime) {
|
|
932
1047
|
// A time picked in the calendar survives typing that doesn't change the time: text with no
|
|
933
1048
|
// time, as a date part-way typed, or with the time as committed.
|
|
934
1049
|
const format = this.resolvedFormat;
|
|
935
1050
|
const locale = this.resolvedLocale;
|
|
936
|
-
const typed = this.
|
|
1051
|
+
const typed = this.isRange ? parseRangeText(this.text, format, locale) : [parseFieldText(this.text, format, locale)];
|
|
937
1052
|
const committed = this.parsedValue();
|
|
938
1053
|
const keeps = (i) => { var _a, _b, _c; return !((_a = typed === null || typed === void 0 ? void 0 : typed[i]) === null || _a === void 0 ? void 0 : _a.time) || ((_b = typed[i]) === null || _b === void 0 ? void 0 : _b.time) === ((_c = committed === null || committed === void 0 ? void 0 : committed[i]) === null || _c === void 0 ? void 0 : _c.time); };
|
|
939
1054
|
if (keeps(0))
|
|
940
1055
|
this.startTime = startTime;
|
|
941
|
-
if (this.
|
|
1056
|
+
if (this.isRange && keeps(1))
|
|
942
1057
|
this.endTime = endTime;
|
|
943
1058
|
}
|
|
944
1059
|
this.showDate(parts[parts.length - 1].date);
|
|
@@ -1047,7 +1162,8 @@ const PdsDatepicker = class {
|
|
|
1047
1162
|
if (!active)
|
|
1048
1163
|
return;
|
|
1049
1164
|
const rtl = getComputedStyle(this.el).direction === 'rtl';
|
|
1050
|
-
|
|
1165
|
+
// Month-day mode's rows start on the 1st, not on a weekday.
|
|
1166
|
+
const column = this.monthDay ? (active.getUTCDate() - 1) % 7 : weekdayColumn(active, this.weekStart);
|
|
1051
1167
|
let next = null;
|
|
1052
1168
|
switch (event.key) {
|
|
1053
1169
|
case 'ArrowLeft':
|
|
@@ -1065,14 +1181,21 @@ const PdsDatepicker = class {
|
|
|
1065
1181
|
case 'Home':
|
|
1066
1182
|
next = addDays(active, -column);
|
|
1067
1183
|
break;
|
|
1068
|
-
case 'End':
|
|
1069
|
-
next
|
|
1184
|
+
case 'End': {
|
|
1185
|
+
// Month-day mode's rows don't carry into the next month, so End stops at the month's last day.
|
|
1186
|
+
const lastColumn = this.monthDay
|
|
1187
|
+
? Math.min(6, daysInMonth(active.getUTCFullYear(), active.getUTCMonth()) - active.getUTCDate() + column)
|
|
1188
|
+
: 6;
|
|
1189
|
+
next = addDays(active, lastColumn - column);
|
|
1070
1190
|
break;
|
|
1191
|
+
}
|
|
1071
1192
|
case 'PageUp':
|
|
1072
|
-
|
|
1193
|
+
// Month-day mode has no year to jump by, so Shift+PageUp stays put rather than passing
|
|
1194
|
+
// through a real, possibly non-leap year and clamping Feb 29 to the 28th.
|
|
1195
|
+
next = event.shiftKey && this.monthDay ? active : addMonths(active, event.shiftKey ? -12 : -1);
|
|
1073
1196
|
break;
|
|
1074
1197
|
case 'PageDown':
|
|
1075
|
-
next = addMonths(active, event.shiftKey ? 12 : 1);
|
|
1198
|
+
next = event.shiftKey && this.monthDay ? active : addMonths(active, event.shiftKey ? 12 : 1);
|
|
1076
1199
|
break;
|
|
1077
1200
|
case 'Enter':
|
|
1078
1201
|
case ' ':
|
|
@@ -1085,9 +1208,9 @@ const PdsDatepicker = class {
|
|
|
1085
1208
|
return;
|
|
1086
1209
|
}
|
|
1087
1210
|
event.preventDefault();
|
|
1088
|
-
const target = clampDate(next, this.minDate, this.maxDate);
|
|
1211
|
+
const target = clampDate(this.inView(next), this.minDate, this.maxDate);
|
|
1089
1212
|
this.showDate(target);
|
|
1090
|
-
if (this.
|
|
1213
|
+
if (this.isRange)
|
|
1091
1214
|
this.previewDate = toIsoDate(target);
|
|
1092
1215
|
this.focusActiveDay = true;
|
|
1093
1216
|
};
|
|
@@ -1098,7 +1221,7 @@ const PdsDatepicker = class {
|
|
|
1098
1221
|
this.pickDate(date);
|
|
1099
1222
|
};
|
|
1100
1223
|
this.onGridPointerOver = (event) => {
|
|
1101
|
-
if (!this.
|
|
1224
|
+
if (!this.isRange || !this.draftStart || this.draftEnd)
|
|
1102
1225
|
return;
|
|
1103
1226
|
const date = this.dayFromEvent(event);
|
|
1104
1227
|
const iso = date && toIsoDate(date);
|
|
@@ -1147,19 +1270,28 @@ const PdsDatepicker = class {
|
|
|
1147
1270
|
var _a;
|
|
1148
1271
|
this.text = this.fieldText();
|
|
1149
1272
|
this.textError = '';
|
|
1273
|
+
const parts = this.parsedValue();
|
|
1274
|
+
// A full date reads as its month and day; keep what's submitted in step with what's shown,
|
|
1275
|
+
// rather than passing through whichever spelling was set until the next pick rewrites it.
|
|
1276
|
+
if (this.monthDay && parts) {
|
|
1277
|
+
const normalized = this.dayValue(parts[0].date);
|
|
1278
|
+
if (this.value !== normalized)
|
|
1279
|
+
this.value = normalized;
|
|
1280
|
+
}
|
|
1150
1281
|
this.updateFormValue();
|
|
1151
1282
|
if (!this.isOpen)
|
|
1152
1283
|
return;
|
|
1153
|
-
const parts = this.parsedValue();
|
|
1154
1284
|
this.loadDraft(parts);
|
|
1155
|
-
const day = (_a = (parts && parts[parts.length - 1].date)) !== null && _a !== void 0 ? _a :
|
|
1285
|
+
const day = (_a = (parts && parts[parts.length - 1].date)) !== null && _a !== void 0 ? _a : this.now;
|
|
1156
1286
|
this.showDate(clampDate(day, this.minDate, this.maxDate), true);
|
|
1157
1287
|
}
|
|
1158
1288
|
requiredChanged() {
|
|
1159
1289
|
this.updateFormValue();
|
|
1160
1290
|
}
|
|
1161
1291
|
componentWillLoad() {
|
|
1162
|
-
|
|
1292
|
+
// Through the same path a later value/context change takes, so an initial full date in
|
|
1293
|
+
// month-day mode submits normalized too, not only after the first pick rewrites it.
|
|
1294
|
+
this.refreshFromValue();
|
|
1163
1295
|
}
|
|
1164
1296
|
connectedCallback() {
|
|
1165
1297
|
if (this.el.attachInternals && !this.internals) {
|
|
@@ -1209,10 +1341,30 @@ const PdsDatepicker = class {
|
|
|
1209
1341
|
return localeFirstDayOfWeek(this.resolvedLocale);
|
|
1210
1342
|
}
|
|
1211
1343
|
get minDate() {
|
|
1212
|
-
return parseIsoDate(this.min);
|
|
1344
|
+
return this.monthDay ? null : parseIsoDate(this.min);
|
|
1213
1345
|
}
|
|
1214
1346
|
get maxDate() {
|
|
1215
|
-
return parseIsoDate(this.max);
|
|
1347
|
+
return this.monthDay ? null : parseIsoDate(this.max);
|
|
1348
|
+
}
|
|
1349
|
+
/** Range mode, which month-day mode turns off. */
|
|
1350
|
+
get isRange() {
|
|
1351
|
+
return this.range && !this.monthDay;
|
|
1352
|
+
}
|
|
1353
|
+
/** The time picker, which month-day mode turns off. */
|
|
1354
|
+
get hasTime() {
|
|
1355
|
+
return this.timePicker && !this.monthDay;
|
|
1356
|
+
}
|
|
1357
|
+
/** Today, or in month-day mode today's month and day. */
|
|
1358
|
+
get now() {
|
|
1359
|
+
return this.monthDay ? inMonthDayYear(today()) : today();
|
|
1360
|
+
}
|
|
1361
|
+
/** In month-day mode, a date moved to the mode's year, as when the calendar wraps past December. */
|
|
1362
|
+
inView(date) {
|
|
1363
|
+
return this.monthDay ? inMonthDayYear(date) : date;
|
|
1364
|
+
}
|
|
1365
|
+
/** One day as the value holds it: `YYYY-MM-DD`, or `--MM-DD` in month-day mode. */
|
|
1366
|
+
dayValue(date) {
|
|
1367
|
+
return this.monthDay ? toMonthDay(date) : toIsoDate(date);
|
|
1216
1368
|
}
|
|
1217
1369
|
get isInteractive() {
|
|
1218
1370
|
return !this.disabled && !this.readonly;
|
|
@@ -1220,22 +1372,22 @@ const PdsDatepicker = class {
|
|
|
1220
1372
|
/** How many months the calendar shows side by side. */
|
|
1221
1373
|
get monthCount() {
|
|
1222
1374
|
const narrow = typeof window !== 'undefined' && window.innerWidth < TWO_MONTHS_MIN_WIDTH;
|
|
1223
|
-
return this.
|
|
1375
|
+
return this.isRange && !narrow ? 2 : 1;
|
|
1224
1376
|
}
|
|
1225
1377
|
/** Whether picks apply at once, without the footer: `auto-apply` in range mode, without times. */
|
|
1226
1378
|
get appliesOnPick() {
|
|
1227
|
-
return this.
|
|
1379
|
+
return this.isRange && this.autoApply && !this.hasTime;
|
|
1228
1380
|
}
|
|
1229
1381
|
/** Whether picks wait for Apply: for a range, unless it auto-applies, and for a date with a time. */
|
|
1230
1382
|
get hasFooter() {
|
|
1231
|
-
return (this.
|
|
1383
|
+
return (this.isRange || this.hasTime) && !this.appliesOnPick;
|
|
1232
1384
|
}
|
|
1233
1385
|
get resolvedPresets() {
|
|
1234
|
-
return this.
|
|
1386
|
+
return this.isRange ? resolvePresets(this.presets, today()) : [];
|
|
1235
1387
|
}
|
|
1236
1388
|
/** The committed value, parsed: one date, or a range's start and end. */
|
|
1237
1389
|
parsedValue(value = this.value) {
|
|
1238
|
-
if (this.
|
|
1390
|
+
if (this.isRange) {
|
|
1239
1391
|
const [start, end, extra] = (value !== null && value !== void 0 ? value : '').split('/');
|
|
1240
1392
|
const startValue = parseDateTime(start);
|
|
1241
1393
|
const endValue = parseDateTime(end);
|
|
@@ -1243,12 +1395,22 @@ const PdsDatepicker = class {
|
|
|
1243
1395
|
? [startValue, endValue]
|
|
1244
1396
|
: null;
|
|
1245
1397
|
}
|
|
1398
|
+
if (this.monthDay) {
|
|
1399
|
+
const date = parseMonthDay(value);
|
|
1400
|
+
return date ? [{ date, time: null }] : null;
|
|
1401
|
+
}
|
|
1246
1402
|
const single = parseDateTime(value);
|
|
1247
1403
|
return single ? [single] : null;
|
|
1248
1404
|
}
|
|
1249
1405
|
/** The format the field shows and reads, with a time when the time picker is on. */
|
|
1250
1406
|
get resolvedFormat() {
|
|
1251
|
-
|
|
1407
|
+
var _a, _b;
|
|
1408
|
+
if (this.monthDay) {
|
|
1409
|
+
// A format with a year is the default, or one for full dates: use the locale's month and day.
|
|
1410
|
+
const format = (_b = (_a = this.format) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
|
|
1411
|
+
return format !== '' && !/YYYY/.test(format.replace(/\[[^\]]*\]/g, '')) ? format : monthDayFormat(this.resolvedLocale);
|
|
1412
|
+
}
|
|
1413
|
+
return fieldFormat(this.format, this.hasTime, this.hasTime && clockFor(this.resolvedLocale).hour12);
|
|
1252
1414
|
}
|
|
1253
1415
|
/** What the field shows for the committed value. */
|
|
1254
1416
|
fieldText() {
|
|
@@ -1264,20 +1426,24 @@ const PdsDatepicker = class {
|
|
|
1264
1426
|
/** Drops a time the time picker doesn't show, or adds the default one it does. */
|
|
1265
1427
|
withTimePicker(value, fallback = DEFAULT_START_TIME) {
|
|
1266
1428
|
var _a;
|
|
1267
|
-
return { date: value.date, time: this.
|
|
1429
|
+
return { date: value.date, time: this.hasTime ? ((_a = value.time) !== null && _a !== void 0 ? _a : fallback) : null };
|
|
1268
1430
|
}
|
|
1269
1431
|
get fieldPlaceholder() {
|
|
1270
1432
|
if (this.placeholder)
|
|
1271
1433
|
return this.placeholder;
|
|
1434
|
+
// Month-day tokens, as `MMMM D`, don't read as a hint; an example date does.
|
|
1435
|
+
if (this.monthDay) {
|
|
1436
|
+
return formatWith({ date: parseMonthDay('--12-31'), time: null }, this.resolvedFormat, this.resolvedLocale);
|
|
1437
|
+
}
|
|
1272
1438
|
const one = this.resolvedFormat;
|
|
1273
|
-
return this.
|
|
1439
|
+
return this.isRange ? `${one}${RANGE_SEPARATOR}${one}` : one;
|
|
1274
1440
|
}
|
|
1275
1441
|
/** Loads the committed value into the draft the calendar edits. */
|
|
1276
1442
|
loadDraft(parts) {
|
|
1277
1443
|
var _a, _b;
|
|
1278
1444
|
const [start, end] = parts !== null && parts !== void 0 ? parts : [];
|
|
1279
1445
|
this.draftStart = start ? toIsoDate(start.date) : '';
|
|
1280
|
-
this.draftEnd = this.
|
|
1446
|
+
this.draftEnd = this.isRange && end ? toIsoDate(end.date) : '';
|
|
1281
1447
|
this.startTime = (_a = start === null || start === void 0 ? void 0 : start.time) !== null && _a !== void 0 ? _a : DEFAULT_START_TIME;
|
|
1282
1448
|
this.endTime = (_b = end === null || end === void 0 ? void 0 : end.time) !== null && _b !== void 0 ? _b : DEFAULT_END_TIME;
|
|
1283
1449
|
this.previewDate = '';
|
|
@@ -1293,7 +1459,7 @@ const PdsDatepicker = class {
|
|
|
1293
1459
|
const parts = this.parsedValue();
|
|
1294
1460
|
this.loadDraft(parts);
|
|
1295
1461
|
this.valueAtOpen = (_a = this.value) !== null && _a !== void 0 ? _a : '';
|
|
1296
|
-
const start = (_b = (parts && parts[parts.length - 1].date)) !== null && _b !== void 0 ? _b :
|
|
1462
|
+
const start = (_b = (parts && parts[parts.length - 1].date)) !== null && _b !== void 0 ? _b : this.now;
|
|
1297
1463
|
this.showDate(clampDate(start, this.minDate, this.maxDate), true);
|
|
1298
1464
|
this.focusActiveDay = focusDay;
|
|
1299
1465
|
this.isOpen = true;
|
|
@@ -1315,7 +1481,8 @@ const PdsDatepicker = class {
|
|
|
1315
1481
|
* Makes a day the active one and brings it into view. With two months, a day already on view
|
|
1316
1482
|
* doesn't move the view; `reset` lines the view up so the day is in the last month shown.
|
|
1317
1483
|
*/
|
|
1318
|
-
showDate(
|
|
1484
|
+
showDate(day, reset = false) {
|
|
1485
|
+
const date = this.inView(day);
|
|
1319
1486
|
const first = firstOfMonth(date);
|
|
1320
1487
|
const view = parseIsoDate(this.viewMonth);
|
|
1321
1488
|
const lastView = view && addMonths(view, this.monthCount - 1);
|
|
@@ -1333,15 +1500,15 @@ const PdsDatepicker = class {
|
|
|
1333
1500
|
// A pick in the calendar takes over from typed text, so dismissing afterwards commits nothing,
|
|
1334
1501
|
// as when nothing was typed.
|
|
1335
1502
|
this.dropTypedText();
|
|
1336
|
-
if (this.
|
|
1503
|
+
if (this.isRange) {
|
|
1337
1504
|
this.pickRangeDay(date);
|
|
1338
1505
|
}
|
|
1339
|
-
else if (this.
|
|
1506
|
+
else if (this.hasTime) {
|
|
1340
1507
|
this.draftStart = toIsoDate(date);
|
|
1341
1508
|
this.activeDate = this.draftStart;
|
|
1342
1509
|
}
|
|
1343
1510
|
else {
|
|
1344
|
-
this.commitValue(
|
|
1511
|
+
this.commitValue(this.dayValue(date));
|
|
1345
1512
|
this.closeCalendar(true);
|
|
1346
1513
|
}
|
|
1347
1514
|
}
|
|
@@ -1385,7 +1552,11 @@ const PdsDatepicker = class {
|
|
|
1385
1552
|
}
|
|
1386
1553
|
/** Commits one date, or a range with its start, end and matching preset. */
|
|
1387
1554
|
commitParts(parts) {
|
|
1388
|
-
if (
|
|
1555
|
+
if (this.monthDay) {
|
|
1556
|
+
this.commitValue(toMonthDay(parts[0].date));
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
if (!this.isRange) {
|
|
1389
1560
|
this.commitValue(toValueString(parts[0]));
|
|
1390
1561
|
return;
|
|
1391
1562
|
}
|
|
@@ -1400,7 +1571,7 @@ const PdsDatepicker = class {
|
|
|
1400
1571
|
if (!startDate)
|
|
1401
1572
|
return null;
|
|
1402
1573
|
const start = this.withTimePicker({ date: startDate, time: this.startTime });
|
|
1403
|
-
if (!this.
|
|
1574
|
+
if (!this.isRange)
|
|
1404
1575
|
return [start];
|
|
1405
1576
|
const end = this.withTimePicker({ date: (_a = parseIsoDate(this.draftEnd)) !== null && _a !== void 0 ? _a : startDate, time: this.endTime });
|
|
1406
1577
|
// An end time before the start time on the same day ends at the start.
|
|
@@ -1526,7 +1697,11 @@ const PdsDatepicker = class {
|
|
|
1526
1697
|
readText(text) {
|
|
1527
1698
|
const format = this.resolvedFormat;
|
|
1528
1699
|
const locale = this.resolvedLocale;
|
|
1529
|
-
const parts = this.
|
|
1700
|
+
const parts = this.monthDay
|
|
1701
|
+
? [parseMonthDayText(text, format, locale)]
|
|
1702
|
+
: this.isRange
|
|
1703
|
+
? parseRangeText(text, format, locale)
|
|
1704
|
+
: [parseFieldText(text, format, locale)];
|
|
1530
1705
|
if (!parts || parts.some((part) => !part))
|
|
1531
1706
|
return null;
|
|
1532
1707
|
return parts.map((part, i) => this.withTimePicker(part, i === 1 ? DEFAULT_END_TIME : DEFAULT_START_TIME));
|
|
@@ -1570,17 +1745,17 @@ const PdsDatepicker = class {
|
|
|
1570
1745
|
/** Whether the month before (`-1`) or after (`1`) the ones on view has a day in range. */
|
|
1571
1746
|
canShowMonth(direction) {
|
|
1572
1747
|
var _a;
|
|
1573
|
-
const view = (_a = parseIsoDate(this.viewMonth)) !== null && _a !== void 0 ? _a :
|
|
1748
|
+
const view = (_a = parseIsoDate(this.viewMonth)) !== null && _a !== void 0 ? _a : this.now;
|
|
1574
1749
|
if (direction < 0)
|
|
1575
1750
|
return !this.minDate || addDays(view, -1) >= this.minDate;
|
|
1576
1751
|
return !this.maxDate || addMonths(view, this.monthCount) <= this.maxDate;
|
|
1577
1752
|
}
|
|
1578
1753
|
showMonth(event, direction) {
|
|
1579
1754
|
var _a, _b;
|
|
1580
|
-
const view = (_a = parseIsoDate(this.viewMonth)) !== null && _a !== void 0 ? _a :
|
|
1581
|
-
const active = (_b = parseIsoDate(this.activeDate)) !== null && _b !== void 0 ? _b :
|
|
1582
|
-
this.viewMonth = toIsoDate(addMonths(view, direction));
|
|
1583
|
-
this.activeDate = toIsoDate(clampDate(addMonths(active, direction), this.minDate, this.maxDate));
|
|
1755
|
+
const view = (_a = parseIsoDate(this.viewMonth)) !== null && _a !== void 0 ? _a : this.now;
|
|
1756
|
+
const active = (_b = parseIsoDate(this.activeDate)) !== null && _b !== void 0 ? _b : this.now;
|
|
1757
|
+
this.viewMonth = toIsoDate(this.inView(addMonths(view, direction)));
|
|
1758
|
+
this.activeDate = toIsoDate(clampDate(this.inView(addMonths(active, direction)), this.minDate, this.maxDate));
|
|
1584
1759
|
if (this.canShowMonth(direction)) {
|
|
1585
1760
|
// Safari doesn't focus a clicked button; without this, focus falls to the page and Escape and
|
|
1586
1761
|
// the Tab trap stop working.
|
|
@@ -1702,7 +1877,7 @@ const PdsDatepicker = class {
|
|
|
1702
1877
|
const parts = this.parsedValue();
|
|
1703
1878
|
if (!parts)
|
|
1704
1879
|
return this.triggerLabel;
|
|
1705
|
-
const dates = parts.map((part) => formatDate(part.date, locale, FULL_DATE));
|
|
1880
|
+
const dates = parts.map((part) => formatDate(part.date, locale, this.monthDay ? MONTH_AND_DAY : FULL_DATE));
|
|
1706
1881
|
return `${this.triggerLabel}, ${dates.join(RANGE_SEPARATOR)}`;
|
|
1707
1882
|
}
|
|
1708
1883
|
/** The range to highlight: the chosen one, or the start to the previewed day while picking the end. */
|
|
@@ -1716,15 +1891,15 @@ const PdsDatepicker = class {
|
|
|
1716
1891
|
}
|
|
1717
1892
|
/** The day shown as selected: the draft, which opens as the committed day and follows typing. */
|
|
1718
1893
|
selectedDay() {
|
|
1719
|
-
if (this.
|
|
1894
|
+
if (this.isRange)
|
|
1720
1895
|
return null;
|
|
1721
1896
|
return parseIsoDate(this.draftStart);
|
|
1722
1897
|
}
|
|
1723
1898
|
renderMonthHeading(view, index$2, locale, headingId) {
|
|
1724
|
-
const heading = formatDate(view, locale, { month: 'long', year: 'numeric' });
|
|
1899
|
+
const heading = formatDate(view, locale, this.monthDay ? { month: 'long' } : { month: 'long', year: 'numeric' });
|
|
1725
1900
|
// Only the first month announces itself as it changes; two months move together.
|
|
1726
1901
|
const live = index$2 === 0 ? 'polite' : null;
|
|
1727
|
-
if (!this.showDropdowns) {
|
|
1902
|
+
if (!this.showDropdowns || this.monthDay) {
|
|
1728
1903
|
return (index.h("div", { class: "pds-datepicker__heading", id: headingId, "aria-live": live }, heading));
|
|
1729
1904
|
}
|
|
1730
1905
|
const year = view.getUTCFullYear();
|
|
@@ -1750,14 +1925,14 @@ const PdsDatepicker = class {
|
|
|
1750
1925
|
const min = this.minDate;
|
|
1751
1926
|
const max = this.maxDate;
|
|
1752
1927
|
const selected = this.selectedDay();
|
|
1753
|
-
const [rangeStart, rangeEnd] = this.
|
|
1754
|
-
const now =
|
|
1755
|
-
const fullDate = dateFormatter(locale, FULL_DATE);
|
|
1928
|
+
const [rangeStart, rangeEnd] = this.isRange ? this.highlightedRange() : [null, null];
|
|
1929
|
+
const now = this.now;
|
|
1930
|
+
const fullDate = dateFormatter(locale, this.monthDay ? MONTH_AND_DAY : FULL_DATE);
|
|
1756
1931
|
const dayNumber = dateFormatter(locale, { day: 'numeric' });
|
|
1757
1932
|
const headingId = `${this.baseId}-calendar-heading-${index$2}`;
|
|
1758
1933
|
const isFirst = index$2 === 0;
|
|
1759
1934
|
const isLast = index$2 === this.monthCount - 1;
|
|
1760
|
-
return (index.h("div", { class: "pds-datepicker__month" }, index.h("div", { class: "pds-datepicker__header" }, isFirst ? (index.h("button", { type: "button", class: "pds-datepicker__nav pds-datepicker__nav--previous", "aria-label": this.previousMonthLabel, disabled: !this.canShowMonth(-1), onClick: (event) => this.showMonth(event, -1) }, index.h("pds-icon", { icon: index$1.caretLeft, "aria-hidden": "true" }))) : (index.h("span", { class: "pds-datepicker__nav-spacer" })), this.renderMonthHeading(view, index$2, locale, headingId), isLast ? (index.h("button", { type: "button", class: "pds-datepicker__nav pds-datepicker__nav--next", "aria-label": this.nextMonthLabel, disabled: !this.canShowMonth(1), onClick: (event) => this.showMonth(event, 1) }, index.h("pds-icon", { icon: index$1.caretRight, "aria-hidden": "true" }))) : (index.h("span", { class: "pds-datepicker__nav-spacer" }))), index.h("table", { class: "pds-datepicker__grid", role: "grid", "aria-labelledby": headingId, "aria-multiselectable": this.
|
|
1935
|
+
return (index.h("div", { class: "pds-datepicker__month" }, index.h("div", { class: "pds-datepicker__header" }, isFirst ? (index.h("button", { type: "button", class: "pds-datepicker__nav pds-datepicker__nav--previous", "aria-label": this.previousMonthLabel, disabled: !this.canShowMonth(-1), onClick: (event) => this.showMonth(event, -1) }, index.h("pds-icon", { icon: index$1.caretLeft, "aria-hidden": "true" }))) : (index.h("span", { class: "pds-datepicker__nav-spacer" })), this.renderMonthHeading(view, index$2, locale, headingId), isLast ? (index.h("button", { type: "button", class: "pds-datepicker__nav pds-datepicker__nav--next", "aria-label": this.nextMonthLabel, disabled: !this.canShowMonth(1), onClick: (event) => this.showMonth(event, 1) }, index.h("pds-icon", { icon: index$1.caretRight, "aria-hidden": "true" }))) : (index.h("span", { class: "pds-datepicker__nav-spacer" }))), index.h("table", { class: "pds-datepicker__grid", role: "grid", "aria-labelledby": headingId, "aria-multiselectable": this.isRange ? 'true' : null, onKeyDown: this.onGridKeyDown, onClick: this.onGridClick, onPointerOver: this.onGridPointerOver }, !this.monthDay && (index.h("thead", null, index.h("tr", null, weekdayNames(locale, this.weekStart).map((name) => (index.h("th", { scope: "col", class: "pds-datepicker__weekday" }, index.h("span", { "aria-hidden": "true" }, name.short), index.h("span", { class: "visually-hidden" }, name.long))))))), index.h("tbody", null, (this.monthDay ? monthDayRows(month) : monthWeeks(year, month, this.weekStart)).map((week) => (index.h("tr", null, week.map((date, column) => {
|
|
1761
1936
|
if (!date)
|
|
1762
1937
|
return index.h("td", { class: "pds-datepicker__day pds-datepicker__day--empty" });
|
|
1763
1938
|
const iso = toIsoDate(date);
|
|
@@ -1779,14 +1954,14 @@ const PdsDatepicker = class {
|
|
|
1779
1954
|
'pds-datepicker__day--today': isToday,
|
|
1780
1955
|
'pds-datepicker__day--disabled': unavailable,
|
|
1781
1956
|
}, "data-date": iso, tabindex: iso === this.activeDate ? '0' : '-1', "aria-label": fullDate.format(date), "aria-selected": isSelected ? 'true' : null, "aria-current": isToday ? 'date' : null, "aria-disabled": unavailable ? 'true' : null }, dayNumberText(dayNumber, date)));
|
|
1782
|
-
})))))), this.
|
|
1957
|
+
})))))), this.hasTime && this.timesUnderMonth(index$2).map((which) => this.renderTime(which, locale))));
|
|
1783
1958
|
}
|
|
1784
1959
|
/**
|
|
1785
1960
|
* The time pickers under a month: the start time under the first and the end time under the
|
|
1786
1961
|
* second, or both under the one month a narrow range shows.
|
|
1787
1962
|
*/
|
|
1788
1963
|
timesUnderMonth(index) {
|
|
1789
|
-
if (!this.
|
|
1964
|
+
if (!this.isRange)
|
|
1790
1965
|
return ['start'];
|
|
1791
1966
|
if (this.monthCount === 1)
|
|
1792
1967
|
return ['start', 'end'];
|
|
@@ -1811,7 +1986,7 @@ const PdsDatepicker = class {
|
|
|
1811
1986
|
if (this.timesBeforeTyping)
|
|
1812
1987
|
this.timesBeforeTyping[which === 'start' ? 0 : 1] = time;
|
|
1813
1988
|
};
|
|
1814
|
-
const groupLabel = !this.
|
|
1989
|
+
const groupLabel = !this.isRange ? this.timeLabel : which === 'start' ? this.startTimeLabel : this.endTimeLabel;
|
|
1815
1990
|
const isPm = hours >= 12;
|
|
1816
1991
|
const hourOptions = clock.hour12 ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 24 }, (_, i) => i);
|
|
1817
1992
|
const shownHour = clock.hour12 ? hours % 12 || 12 : hours;
|
|
@@ -1852,7 +2027,7 @@ const PdsDatepicker = class {
|
|
|
1852
2027
|
return '';
|
|
1853
2028
|
const format = this.resolvedFormat;
|
|
1854
2029
|
const locale = this.resolvedLocale;
|
|
1855
|
-
if (this.
|
|
2030
|
+
if (this.isRange && !this.draftEnd)
|
|
1856
2031
|
return `${toFieldText(parts[0], format, locale)}${RANGE_SEPARATOR}`;
|
|
1857
2032
|
return parts.map((part) => toFieldText(part, format, locale)).join(RANGE_SEPARATOR);
|
|
1858
2033
|
}
|
|
@@ -1863,25 +2038,25 @@ const PdsDatepicker = class {
|
|
|
1863
2038
|
}
|
|
1864
2039
|
renderCalendar(locale) {
|
|
1865
2040
|
var _a;
|
|
1866
|
-
const view = (_a = parseIsoDate(this.viewMonth)) !== null && _a !== void 0 ? _a :
|
|
2041
|
+
const view = (_a = parseIsoDate(this.viewMonth)) !== null && _a !== void 0 ? _a : this.now;
|
|
1867
2042
|
const months = Array.from({ length: this.monthCount }, (_, i) => addMonths(view, i));
|
|
1868
|
-
return (index.h("div", { class: { 'pds-datepicker__dialog': true, 'pds-datepicker__dialog--range': this.
|
|
2043
|
+
return (index.h("div", { class: { 'pds-datepicker__dialog': true, 'pds-datepicker__dialog--range': this.isRange }, id: `${this.baseId}-calendar`, role: "dialog",
|
|
1869
2044
|
// A click on a part that can't take focus, as the month heading, focuses the dialog
|
|
1870
2045
|
// rather than dropping focus to the page, which would report blur mid-selection.
|
|
1871
|
-
tabindex: "-1", "aria-labelledby": this.
|
|
2046
|
+
tabindex: "-1", "aria-labelledby": this.isRange ? null : `${this.baseId}-calendar-heading-0`, "aria-label": this.isRange ? this.label || this.triggerLabel : null, part: "calendar", ref: (el) => (this.dialogEl = el), onKeyDown: this.onDialogKeyDown, onFocusin: this.onCalendarFocusIn, onFocusout: this.onCalendarFocusOut }, this.isRange && this.renderPresets(), index.h("div", { class: "pds-datepicker__panel" }, index.h("div", { class: "pds-datepicker__months" }, months.map((month, index) => this.renderMonth(month, index, locale))), this.hasFooter && this.renderFooter(), this.appliesOnPick && (
|
|
1872
2047
|
// With no footer, still announce the start once it's picked, so it's clear an end comes next.
|
|
1873
2048
|
index.h("span", { class: "visually-hidden", "aria-live": "polite" }, this.selectionText())))));
|
|
1874
2049
|
}
|
|
1875
2050
|
render() {
|
|
1876
2051
|
const locale = this.resolvedLocale;
|
|
1877
|
-
return (index.h(index.Host, { key: '
|
|
2052
|
+
return (index.h(index.Host, { key: '7c5388369fe78becfd26b0807f1c6cb54c7934a1', class: { 'pds-datepicker--range': this.isRange, 'pds-datepicker--time': this.hasTime }, "full-width": this.fullWidth ? 'true' : null, onFocusin: this.onHostFocusIn, onFocusout: this.onHostFocusOut }, index.h("pds-input", { key: 'fc565c99359715415886ae63ef1bcbf8671569c9', ref: (el) => (this.inputEl = el), componentId: this.baseId, label: this.label, hideLabel: this.hideLabel, helperMessage: this.helperMessage, errorMessage: this.errorMessage, invalid: this.invalid || !!this.textError || !!this.valueError(), disabled: this.disabled, readonly: this.readonly, required: this.required, placeholder: this.fieldPlaceholder, fullWidth: this.fullWidth, autocomplete: "off", value: this.text, onClick: this.onFieldClick, onPdsInput: this.onTextInput, onPdsChange: this.onTextChange, onPdsKeyDown: this.onTextKeyDown, onPdsFocus: this.stopInnerEvent, onPdsBlur: this.stopInnerEvent }, this.clearable && !!this.value && this.isInteractive && (index.h("button", {
|
|
1878
2053
|
// Keyed, as is the calendar button: when Clear appears after a pick, the focused
|
|
1879
2054
|
// calendar button must stay that button rather than be reused as Clear.
|
|
1880
2055
|
key: "clear", slot: "suffix", type: "button", class: "pds-datepicker__clear", ref: (el) => (this.clearEl = el), "aria-label": this.label ? `${this.clearLabel} ${this.label}` : this.clearLabel,
|
|
1881
2056
|
// Out of the tab order, as a native search field's clear button is: Tab from a field
|
|
1882
2057
|
// just emptied would land on it as the change removes it, dropping focus to the page.
|
|
1883
2058
|
tabindex: "-1", onMouseDown: this.onClearMouseDown, onClick: this.onClearClick
|
|
1884
|
-
}, index.h("pds-icon", { key: '
|
|
2059
|
+
}, index.h("pds-icon", { key: '6a7da726d86654d3598f84b8ea7a8ed244a8e5da', icon: index$1.remove, "aria-hidden": "true" }))), index.h("button", { key: "trigger", slot: "suffix", type: "button", class: "pds-datepicker__trigger", ref: (el) => (this.triggerEl = el), "aria-label": this.triggerAccessibleName(locale), "aria-haspopup": "dialog", "aria-expanded": this.isOpen ? 'true' : 'false', disabled: !this.isInteractive, onClick: this.onTriggerClick, onKeyDown: this.onTriggerKeyDown }, index.h("pds-icon", { key: '92af87e66b7bb2e95861b873077a7bfb0268c2e0', icon: index$1.calendarSimple, "aria-hidden": "true" }))), this.isOpen && this.renderCalendar(locale)));
|
|
1885
2060
|
}
|
|
1886
2061
|
static get formAssociated() { return true; }
|
|
1887
2062
|
get el() { return index.getElement(this); }
|
|
@@ -1901,6 +2076,9 @@ const PdsDatepicker = class {
|
|
|
1901
2076
|
"range": [{
|
|
1902
2077
|
"contextChanged": 0
|
|
1903
2078
|
}],
|
|
2079
|
+
"monthDay": [{
|
|
2080
|
+
"contextChanged": 0
|
|
2081
|
+
}],
|
|
1904
2082
|
"min": [{
|
|
1905
2083
|
"contextChanged": 0
|
|
1906
2084
|
}],
|