@skra/ui 0.2.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 +201 -0
- package/NOTICE +7 -0
- package/README.md +39 -0
- package/consumer-guide/README.md +84 -0
- package/consumer-guide/accessible-names.md +80 -0
- package/consumer-guide/forms-and-binding.md +149 -0
- package/consumer-guide/layout.md +86 -0
- package/consumer-guide/overlays.md +100 -0
- package/consumer-guide/setup-and-gotchas.md +103 -0
- package/consumer-guide/theming-and-encapsulation.md +115 -0
- package/consumer-guide/wrapping-patterns.md +90 -0
- package/fesm2022/skra-ui-accordion.mjs +333 -0
- package/fesm2022/skra-ui-accordion.mjs.map +1 -0
- package/fesm2022/skra-ui-alert.mjs +110 -0
- package/fesm2022/skra-ui-alert.mjs.map +1 -0
- package/fesm2022/skra-ui-avatar.mjs +83 -0
- package/fesm2022/skra-ui-avatar.mjs.map +1 -0
- package/fesm2022/skra-ui-badge.mjs +91 -0
- package/fesm2022/skra-ui-badge.mjs.map +1 -0
- package/fesm2022/skra-ui-button.mjs +67 -0
- package/fesm2022/skra-ui-button.mjs.map +1 -0
- package/fesm2022/skra-ui-checkbox.mjs +98 -0
- package/fesm2022/skra-ui-checkbox.mjs.map +1 -0
- package/fesm2022/skra-ui-chip.mjs +82 -0
- package/fesm2022/skra-ui-chip.mjs.map +1 -0
- package/fesm2022/skra-ui-combobox.mjs +335 -0
- package/fesm2022/skra-ui-combobox.mjs.map +1 -0
- package/fesm2022/skra-ui-core.mjs +529 -0
- package/fesm2022/skra-ui-core.mjs.map +1 -0
- package/fesm2022/skra-ui-counter.mjs +53 -0
- package/fesm2022/skra-ui-counter.mjs.map +1 -0
- package/fesm2022/skra-ui-datepicker.mjs +867 -0
- package/fesm2022/skra-ui-datepicker.mjs.map +1 -0
- package/fesm2022/skra-ui-dialog.mjs +133 -0
- package/fesm2022/skra-ui-dialog.mjs.map +1 -0
- package/fesm2022/skra-ui-field.mjs +293 -0
- package/fesm2022/skra-ui-field.mjs.map +1 -0
- package/fesm2022/skra-ui-icon.mjs +54 -0
- package/fesm2022/skra-ui-icon.mjs.map +1 -0
- package/fesm2022/skra-ui-input.mjs +42 -0
- package/fesm2022/skra-ui-input.mjs.map +1 -0
- package/fesm2022/skra-ui-kbd.mjs +44 -0
- package/fesm2022/skra-ui-kbd.mjs.map +1 -0
- package/fesm2022/skra-ui-layout.mjs +119 -0
- package/fesm2022/skra-ui-layout.mjs.map +1 -0
- package/fesm2022/skra-ui-link.mjs +29 -0
- package/fesm2022/skra-ui-link.mjs.map +1 -0
- package/fesm2022/skra-ui-loading-indicator.mjs +24 -0
- package/fesm2022/skra-ui-loading-indicator.mjs.map +1 -0
- package/fesm2022/skra-ui-menu.mjs +334 -0
- package/fesm2022/skra-ui-menu.mjs.map +1 -0
- package/fesm2022/skra-ui-multi-select.mjs +179 -0
- package/fesm2022/skra-ui-multi-select.mjs.map +1 -0
- package/fesm2022/skra-ui-native-select.mjs +123 -0
- package/fesm2022/skra-ui-native-select.mjs.map +1 -0
- package/fesm2022/skra-ui-popover.mjs +158 -0
- package/fesm2022/skra-ui-popover.mjs.map +1 -0
- package/fesm2022/skra-ui-progress.mjs +57 -0
- package/fesm2022/skra-ui-progress.mjs.map +1 -0
- package/fesm2022/skra-ui-radio.mjs +92 -0
- package/fesm2022/skra-ui-radio.mjs.map +1 -0
- package/fesm2022/skra-ui-rating-display.mjs +81 -0
- package/fesm2022/skra-ui-rating-display.mjs.map +1 -0
- package/fesm2022/skra-ui-select.mjs +151 -0
- package/fesm2022/skra-ui-select.mjs.map +1 -0
- package/fesm2022/skra-ui-skeleton.mjs +65 -0
- package/fesm2022/skra-ui-skeleton.mjs.map +1 -0
- package/fesm2022/skra-ui-star-rating.mjs +110 -0
- package/fesm2022/skra-ui-star-rating.mjs.map +1 -0
- package/fesm2022/skra-ui-status-dot.mjs +61 -0
- package/fesm2022/skra-ui-status-dot.mjs.map +1 -0
- package/fesm2022/skra-ui-switch.mjs +94 -0
- package/fesm2022/skra-ui-switch.mjs.map +1 -0
- package/fesm2022/skra-ui-tabs.mjs +356 -0
- package/fesm2022/skra-ui-tabs.mjs.map +1 -0
- package/fesm2022/skra-ui-textarea.mjs +81 -0
- package/fesm2022/skra-ui-textarea.mjs.map +1 -0
- package/fesm2022/skra-ui-toast.mjs +208 -0
- package/fesm2022/skra-ui-toast.mjs.map +1 -0
- package/fesm2022/skra-ui-tooltip.mjs +252 -0
- package/fesm2022/skra-ui-tooltip.mjs.map +1 -0
- package/fesm2022/skra-ui-typography.mjs +35 -0
- package/fesm2022/skra-ui-typography.mjs.map +1 -0
- package/fesm2022/skra-ui.mjs +4 -0
- package/fesm2022/skra-ui.mjs.map +1 -0
- package/package.json +175 -0
- package/types/skra-ui-accordion.d.ts +196 -0
- package/types/skra-ui-accordion.d.ts.map +1 -0
- package/types/skra-ui-alert.d.ts +81 -0
- package/types/skra-ui-alert.d.ts.map +1 -0
- package/types/skra-ui-avatar.d.ts +71 -0
- package/types/skra-ui-avatar.d.ts.map +1 -0
- package/types/skra-ui-badge.d.ts +73 -0
- package/types/skra-ui-badge.d.ts.map +1 -0
- package/types/skra-ui-button.d.ts +45 -0
- package/types/skra-ui-button.d.ts.map +1 -0
- package/types/skra-ui-checkbox.d.ts +52 -0
- package/types/skra-ui-checkbox.d.ts.map +1 -0
- package/types/skra-ui-chip.d.ts +60 -0
- package/types/skra-ui-chip.d.ts.map +1 -0
- package/types/skra-ui-combobox.d.ts +95 -0
- package/types/skra-ui-combobox.d.ts.map +1 -0
- package/types/skra-ui-core.d.ts +314 -0
- package/types/skra-ui-core.d.ts.map +1 -0
- package/types/skra-ui-counter.d.ts +33 -0
- package/types/skra-ui-counter.d.ts.map +1 -0
- package/types/skra-ui-datepicker.d.ts +164 -0
- package/types/skra-ui-datepicker.d.ts.map +1 -0
- package/types/skra-ui-dialog.d.ts +59 -0
- package/types/skra-ui-dialog.d.ts.map +1 -0
- package/types/skra-ui-field.d.ts +181 -0
- package/types/skra-ui-field.d.ts.map +1 -0
- package/types/skra-ui-icon.d.ts +38 -0
- package/types/skra-ui-icon.d.ts.map +1 -0
- package/types/skra-ui-input.d.ts +22 -0
- package/types/skra-ui-input.d.ts.map +1 -0
- package/types/skra-ui-kbd.d.ts +33 -0
- package/types/skra-ui-kbd.d.ts.map +1 -0
- package/types/skra-ui-layout.d.ts +86 -0
- package/types/skra-ui-layout.d.ts.map +1 -0
- package/types/skra-ui-link.d.ts +20 -0
- package/types/skra-ui-link.d.ts.map +1 -0
- package/types/skra-ui-loading-indicator.d.ts +9 -0
- package/types/skra-ui-loading-indicator.d.ts.map +1 -0
- package/types/skra-ui-menu.d.ts +116 -0
- package/types/skra-ui-menu.d.ts.map +1 -0
- package/types/skra-ui-multi-select.d.ts +47 -0
- package/types/skra-ui-multi-select.d.ts.map +1 -0
- package/types/skra-ui-native-select.d.ts +74 -0
- package/types/skra-ui-native-select.d.ts.map +1 -0
- package/types/skra-ui-popover.d.ts +57 -0
- package/types/skra-ui-popover.d.ts.map +1 -0
- package/types/skra-ui-progress.d.ts +41 -0
- package/types/skra-ui-progress.d.ts.map +1 -0
- package/types/skra-ui-radio.d.ts +54 -0
- package/types/skra-ui-radio.d.ts.map +1 -0
- package/types/skra-ui-rating-display.d.ts +56 -0
- package/types/skra-ui-rating-display.d.ts.map +1 -0
- package/types/skra-ui-select.d.ts +43 -0
- package/types/skra-ui-select.d.ts.map +1 -0
- package/types/skra-ui-skeleton.d.ts +42 -0
- package/types/skra-ui-skeleton.d.ts.map +1 -0
- package/types/skra-ui-star-rating.d.ts +71 -0
- package/types/skra-ui-star-rating.d.ts.map +1 -0
- package/types/skra-ui-status-dot.d.ts +62 -0
- package/types/skra-ui-status-dot.d.ts.map +1 -0
- package/types/skra-ui-switch.d.ts +54 -0
- package/types/skra-ui-switch.d.ts.map +1 -0
- package/types/skra-ui-tabs.d.ts +191 -0
- package/types/skra-ui-tabs.d.ts.map +1 -0
- package/types/skra-ui-textarea.d.ts +55 -0
- package/types/skra-ui-textarea.d.ts.map +1 -0
- package/types/skra-ui-toast.d.ts +140 -0
- package/types/skra-ui-toast.d.ts.map +1 -0
- package/types/skra-ui-tooltip.d.ts +90 -0
- package/types/skra-ui-tooltip.d.ts.map +1 -0
- package/types/skra-ui-typography.d.ts +17 -0
- package/types/skra-ui-typography.d.ts.map +1 -0
- package/types/skra-ui.d.ts +3 -0
- package/types/skra-ui.d.ts.map +1 -0
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, booleanAttribute, output, viewChild, inject, Injector, signal, computed, DestroyRef, afterNextRender, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { SkraFieldContext } from '@skra/ui/field';
|
|
4
|
+
import { _attachPositioner } from '@skra/ui/core';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* calendar-math — the ONE sanctioned site for DatePicker date arithmetic (ADR 0050 §3/§6).
|
|
8
|
+
*
|
|
9
|
+
* Zoneless calendar-date discipline (the timezone off-by-one is the bug class this exists to prevent):
|
|
10
|
+
* - The value is an ISO 8601 date-only string (`YYYY-MM-DD`) — NEVER materialized as a `Date`.
|
|
11
|
+
* - `Date` is used ONLY as a UTC calendar calculator — built via `Date.UTC(...)`, read via `getUTC*`.
|
|
12
|
+
* - `new Date(isoString)` is FORBIDDEN (date-only parses as UTC midnight → local accessors shift it).
|
|
13
|
+
* - "today" uses LOCAL accessors — "today" is inherently local-civil.
|
|
14
|
+
* - Month length + leap years are delegated to the platform engine, NEVER a hand-written leap rule.
|
|
15
|
+
*
|
|
16
|
+
* Months are **1-based** here (1=Jan … 12=Dec) to mirror the ISO string; the single 0-based conversion
|
|
17
|
+
* happens at the `Date.UTC` call sites. Weekdays are **ISO** (1=Mon … 7=Sun) to match `firstDayOfWeek`.
|
|
18
|
+
*
|
|
19
|
+
* This module is the ADR 0050 §6 swap-point: a future `Temporal.PlainDate` migration touches only here.
|
|
20
|
+
*/
|
|
21
|
+
const ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
22
|
+
/** Zero-pad to a fixed width (no locale, no `Intl` — pure formatting). */
|
|
23
|
+
function pad(n, width) {
|
|
24
|
+
return String(n).padStart(width, '0');
|
|
25
|
+
}
|
|
26
|
+
/** Days in month `m` (1-based) of year `y` — delegated to the engine (leap years handled for free). */
|
|
27
|
+
function daysInMonth(y, m) {
|
|
28
|
+
// Date.UTC month is 0-based; passing the 1-based `m` selects the NEXT month, day 0 = last of `m`.
|
|
29
|
+
return new Date(Date.UTC(y, m, 0)).getUTCDate();
|
|
30
|
+
}
|
|
31
|
+
/** ISO weekday (1=Mon … 7=Sun) of an arbitrary {y,m,d}, via the UTC calculator. */
|
|
32
|
+
function isoWeekday(y, m, d) {
|
|
33
|
+
const jsDay = new Date(Date.UTC(y, m - 1, d)).getUTCDay(); // 0=Sun … 6=Sat
|
|
34
|
+
return ((jsDay + 6) % 7) + 1; // → 1=Mon … 7=Sun
|
|
35
|
+
}
|
|
36
|
+
/** Strict `YYYY-MM-DD` parse with real calendar validation. Returns `null` for anything invalid. */
|
|
37
|
+
function parseIso(iso) {
|
|
38
|
+
const match = ISO_DATE.exec(iso);
|
|
39
|
+
if (!match)
|
|
40
|
+
return null;
|
|
41
|
+
const y = Number(match[1]);
|
|
42
|
+
const m = Number(match[2]);
|
|
43
|
+
const d = Number(match[3]);
|
|
44
|
+
if (m < 1 || m > 12)
|
|
45
|
+
return null;
|
|
46
|
+
if (d < 1 || d > daysInMonth(y, m))
|
|
47
|
+
return null; // rejects 2026-02-30, 2026-04-31, etc.
|
|
48
|
+
return { y, m, d };
|
|
49
|
+
}
|
|
50
|
+
/** `true` iff `iso` is a strictly valid `YYYY-MM-DD` calendar date. */
|
|
51
|
+
function isValidIso(iso) {
|
|
52
|
+
return parseIso(iso) !== null;
|
|
53
|
+
}
|
|
54
|
+
/** Serialize a calendar date to `YYYY-MM-DD`. */
|
|
55
|
+
function toIso({ y, m, d }) {
|
|
56
|
+
return `${pad(y, 4)}-${pad(m, 2)}-${pad(d, 2)}`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build a UTC `Date` from an ISO string — the ONLY sanctioned `Date`-from-value site (used by
|
|
60
|
+
* `calendar-intl` to feed `Intl.DateTimeFormat`, which MUST also pass `timeZone:'UTC'`). Returns the
|
|
61
|
+
* value as a UTC instant; callers MUST read it back via UTC accessors / a UTC-zoned formatter ONLY.
|
|
62
|
+
*/
|
|
63
|
+
function toUtcDate(iso) {
|
|
64
|
+
const date = parseIso(iso);
|
|
65
|
+
if (!date)
|
|
66
|
+
return null;
|
|
67
|
+
return new Date(Date.UTC(date.y, date.m - 1, date.d));
|
|
68
|
+
}
|
|
69
|
+
/** Today's LOCAL calendar date as ISO (local accessors — "today" is local-civil, ADR 0050 §3). */
|
|
70
|
+
function today() {
|
|
71
|
+
const now = new Date();
|
|
72
|
+
return toIso({
|
|
73
|
+
y: now.getFullYear(),
|
|
74
|
+
m: now.getMonth() + 1,
|
|
75
|
+
d: now.getDate(),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/** Add `n` days (may be negative); crosses month/year boundaries via the UTC engine. */
|
|
79
|
+
function addDays(iso, n) {
|
|
80
|
+
const date = parseIso(iso);
|
|
81
|
+
if (!date)
|
|
82
|
+
return iso;
|
|
83
|
+
const dt = new Date(Date.UTC(date.y, date.m - 1, date.d));
|
|
84
|
+
dt.setUTCDate(dt.getUTCDate() + n);
|
|
85
|
+
return toIso({
|
|
86
|
+
y: dt.getUTCFullYear(),
|
|
87
|
+
m: dt.getUTCMonth() + 1,
|
|
88
|
+
d: dt.getUTCDate(),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Add `n` months (may be negative), preserving day-of-month CLAMPED to the target month length
|
|
93
|
+
* (ADR 0050 §4): Jan 31 + 1 month → Feb 28/29, never Mar 3.
|
|
94
|
+
*/
|
|
95
|
+
function addMonthsClamped(iso, n) {
|
|
96
|
+
const date = parseIso(iso);
|
|
97
|
+
if (!date)
|
|
98
|
+
return iso;
|
|
99
|
+
const total = date.y * 12 + (date.m - 1) + n;
|
|
100
|
+
const y = Math.floor(total / 12);
|
|
101
|
+
const m = (((total % 12) + 12) % 12) + 1; // 1-based, safe for negative totals
|
|
102
|
+
const d = Math.min(date.d, daysInMonth(y, m));
|
|
103
|
+
return toIso({ y, m, d });
|
|
104
|
+
}
|
|
105
|
+
/** Add `n` years, clamped (Feb 29 + 1 year → Feb 28). Delegates to month arithmetic. */
|
|
106
|
+
function addYearsClamped(iso, n) {
|
|
107
|
+
return addMonthsClamped(iso, n * 12);
|
|
108
|
+
}
|
|
109
|
+
/** Lexicographic comparison — valid because same-format ISO 8601 date-only strings are byte-sortable. */
|
|
110
|
+
function compareIso(a, b) {
|
|
111
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
112
|
+
}
|
|
113
|
+
/** Inclusive range test. `''` bound = unbounded on that side (ADR 0050 §2, lexicographic). */
|
|
114
|
+
function isInRange(iso, min, max) {
|
|
115
|
+
if (min !== '' && compareIso(iso, min) < 0)
|
|
116
|
+
return false;
|
|
117
|
+
if (max !== '' && compareIso(iso, max) > 0)
|
|
118
|
+
return false;
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
/** Clamp `iso` into `[min,max]` (each `''` = unbounded). Used to seed the displayed month / focus. */
|
|
122
|
+
function clampIso(iso, min, max) {
|
|
123
|
+
if (min !== '' && compareIso(iso, min) < 0)
|
|
124
|
+
return min;
|
|
125
|
+
if (max !== '' && compareIso(iso, max) > 0)
|
|
126
|
+
return max;
|
|
127
|
+
return iso;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Build a month grid: weeks of 7 cells, each a day ISO string or `null` (leading/trailing padding —
|
|
131
|
+
* adjacent-month days are NOT rendered in MVP). `firstDayOfWeek` is ISO (1=Mon … 7=Sun).
|
|
132
|
+
* Pure + deterministic; the component decorates cells with selected/today/disabled/focused state.
|
|
133
|
+
*/
|
|
134
|
+
function monthGrid(y, m, firstDayOfWeek) {
|
|
135
|
+
const firstWeekday = isoWeekday(y, m, 1); // ISO weekday of the 1st
|
|
136
|
+
const leadingBlanks = (firstWeekday - firstDayOfWeek + 7) % 7;
|
|
137
|
+
const dim = daysInMonth(y, m);
|
|
138
|
+
const cells = [];
|
|
139
|
+
for (let i = 0; i < leadingBlanks; i++)
|
|
140
|
+
cells.push(null);
|
|
141
|
+
for (let d = 1; d <= dim; d++)
|
|
142
|
+
cells.push(toIso({ y, m, d }));
|
|
143
|
+
while (cells.length % 7 !== 0)
|
|
144
|
+
cells.push(null); // trailing padding
|
|
145
|
+
const weeks = [];
|
|
146
|
+
for (let i = 0; i < cells.length; i += 7)
|
|
147
|
+
weeks.push(cells.slice(i, i + 7));
|
|
148
|
+
return weeks;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const NONE = { type: 'none' };
|
|
152
|
+
/**
|
|
153
|
+
* SkraCalendarKeyboard — the WAI-ARIA APG **Date Picker Dialog** grid keyboard model (2-D), confined to
|
|
154
|
+
* ONE file. The THIRD per-pattern keyboard controller and the FIRST that does NOT wrap a CDK key manager:
|
|
155
|
+
* neither `FocusKeyManager` (Menu) nor `ActiveDescendantKeyManager` (Select/Combobox) models a 2-D grid
|
|
156
|
+
* whose focusable cell set is recomputed on month change (ADR 0044 §7 note, 2026-06-14). Date arithmetic
|
|
157
|
+
* is delegated to `calendar-math` (the ADR 0050 single math site); this controller only maps keys to
|
|
158
|
+
* intents. Roving REAL focus (NOT `aria-activedescendant`) — the divergence from Select/Combobox.
|
|
159
|
+
*
|
|
160
|
+
* Navigation is unclamped (a user may move onto a disabled out-of-range day — APG-discoverable); only
|
|
161
|
+
* SELECTION is range-guarded, and that guard lives in the component (one place, shared with pointer click).
|
|
162
|
+
*/
|
|
163
|
+
class SkraCalendarKeyboard {
|
|
164
|
+
resolve(event, ctx) {
|
|
165
|
+
const { focusedIso, firstDayOfWeek } = ctx;
|
|
166
|
+
switch (event.key) {
|
|
167
|
+
case 'ArrowLeft':
|
|
168
|
+
return this.toFocus(focusedIso, addDays(focusedIso, -1));
|
|
169
|
+
case 'ArrowRight':
|
|
170
|
+
return this.toFocus(focusedIso, addDays(focusedIso, 1));
|
|
171
|
+
case 'ArrowUp':
|
|
172
|
+
return this.toFocus(focusedIso, addDays(focusedIso, -7));
|
|
173
|
+
case 'ArrowDown':
|
|
174
|
+
return this.toFocus(focusedIso, addDays(focusedIso, 7));
|
|
175
|
+
case 'Home':
|
|
176
|
+
return this.toFocus(focusedIso, this.weekStart(focusedIso, firstDayOfWeek));
|
|
177
|
+
case 'End':
|
|
178
|
+
return this.toFocus(focusedIso, addDays(this.weekStart(focusedIso, firstDayOfWeek), 6));
|
|
179
|
+
case 'PageUp':
|
|
180
|
+
return this.toFocus(focusedIso, event.shiftKey
|
|
181
|
+
? addYearsClamped(focusedIso, -1)
|
|
182
|
+
: addMonthsClamped(focusedIso, -1));
|
|
183
|
+
case 'PageDown':
|
|
184
|
+
return this.toFocus(focusedIso, event.shiftKey
|
|
185
|
+
? addYearsClamped(focusedIso, 1)
|
|
186
|
+
: addMonthsClamped(focusedIso, 1));
|
|
187
|
+
case 'Enter':
|
|
188
|
+
case ' ':
|
|
189
|
+
case 'Spacebar': // legacy key name
|
|
190
|
+
return { type: 'select' };
|
|
191
|
+
case 'Escape':
|
|
192
|
+
return { type: 'close' };
|
|
193
|
+
default:
|
|
194
|
+
return NONE;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
toFocus(fromIso, toIso) {
|
|
198
|
+
return {
|
|
199
|
+
type: 'focus',
|
|
200
|
+
iso: toIso,
|
|
201
|
+
monthChanged: this.monthChanged(fromIso, toIso),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
monthChanged(a, b) {
|
|
205
|
+
const pa = parseIso(a);
|
|
206
|
+
const pb = parseIso(b);
|
|
207
|
+
return !pa || !pb || pa.y !== pb.y || pa.m !== pb.m;
|
|
208
|
+
}
|
|
209
|
+
weekStart(iso, firstDayOfWeek) {
|
|
210
|
+
const p = parseIso(iso);
|
|
211
|
+
if (!p)
|
|
212
|
+
return iso;
|
|
213
|
+
const offset = (isoWeekday(p.y, p.m, p.d) - firstDayOfWeek + 7) % 7;
|
|
214
|
+
return addDays(iso, -offset);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* calendar-intl — DatePicker localization via the platform `Intl` (ADR 0050 §5). NO locale string
|
|
220
|
+
* catalog is bundled (atoms-agnostic); everything language/digit-related is derived from `Intl`, keyed
|
|
221
|
+
* by the consumer `locale`. Every `Date` fed to a formatter comes from `calendar-math.toUtcDate` AND the
|
|
222
|
+
* formatter MUST set `timeZone: 'UTC'` (else the displayed date shifts off the zoneless value).
|
|
223
|
+
*/
|
|
224
|
+
const UTC = 'UTC';
|
|
225
|
+
/** Resolve the host's default BCP-47 locale tag, SSR-safe (no `navigator` in Node). */
|
|
226
|
+
function hostLocale() {
|
|
227
|
+
return typeof navigator !== 'undefined' ? navigator.language : undefined;
|
|
228
|
+
}
|
|
229
|
+
/** Trigger display of the selected date (`dateStyle: 'medium'`). `''`/invalid → `''`. */
|
|
230
|
+
function formatValue(iso, locale) {
|
|
231
|
+
const dt = toUtcDate(iso);
|
|
232
|
+
if (!dt)
|
|
233
|
+
return '';
|
|
234
|
+
return new Intl.DateTimeFormat(locale, {
|
|
235
|
+
dateStyle: 'medium',
|
|
236
|
+
timeZone: UTC,
|
|
237
|
+
}).format(dt);
|
|
238
|
+
}
|
|
239
|
+
/** Header / nav-button label for a month: localized `month long + year` (e.g. "czerwiec 2026"). */
|
|
240
|
+
function formatMonthYear(y, m, locale) {
|
|
241
|
+
const dt = toUtcDate(`${String(y).padStart(4, '0')}-${String(m).padStart(2, '0')}-01`);
|
|
242
|
+
if (!dt)
|
|
243
|
+
return '';
|
|
244
|
+
return new Intl.DateTimeFormat(locale, {
|
|
245
|
+
month: 'long',
|
|
246
|
+
year: 'numeric',
|
|
247
|
+
timeZone: UTC,
|
|
248
|
+
}).format(dt);
|
|
249
|
+
}
|
|
250
|
+
/** Full localized date for a day cell's `[skra-sr-only]` accessible-name extension (ADR 033 Path 2). */
|
|
251
|
+
function formatDayName(iso, locale) {
|
|
252
|
+
const dt = toUtcDate(iso);
|
|
253
|
+
if (!dt)
|
|
254
|
+
return '';
|
|
255
|
+
return new Intl.DateTimeFormat(locale, {
|
|
256
|
+
dateStyle: 'long',
|
|
257
|
+
timeZone: UTC,
|
|
258
|
+
}).format(dt);
|
|
259
|
+
}
|
|
260
|
+
/** Visible day number, localized digits (e.g. Arabic-Indic `٥`) — never a raw `toString()`. */
|
|
261
|
+
function formatDayNumber(day, locale) {
|
|
262
|
+
return new Intl.NumberFormat(locale).format(day);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Localized weekday column headers, 7 entries starting at `firstDayOfWeek` (ISO 1=Mon … 7=Sun).
|
|
266
|
+
* Reference week: 2024-01-01 is a Monday, so day-of-month `d` (1–7) maps directly to ISO weekday `d`.
|
|
267
|
+
*/
|
|
268
|
+
function weekdayHeaders(firstDayOfWeek, locale) {
|
|
269
|
+
const shortFmt = new Intl.DateTimeFormat(locale, {
|
|
270
|
+
weekday: 'short',
|
|
271
|
+
timeZone: UTC,
|
|
272
|
+
});
|
|
273
|
+
const longFmt = new Intl.DateTimeFormat(locale, {
|
|
274
|
+
weekday: 'long',
|
|
275
|
+
timeZone: UTC,
|
|
276
|
+
});
|
|
277
|
+
const headers = [];
|
|
278
|
+
for (let i = 0; i < 7; i++) {
|
|
279
|
+
const dow = ((firstDayOfWeek - 1 + i) % 7) + 1; // 1 … 7
|
|
280
|
+
const dt = toUtcDate(`2024-01-0${dow}`); // dow 1–7 → '2024-01-01' … '2024-01-07'
|
|
281
|
+
headers.push({
|
|
282
|
+
key: dow,
|
|
283
|
+
short: shortFmt.format(dt),
|
|
284
|
+
long: longFmt.format(dt),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
return headers;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Resolve the first day of the week (ISO 1=Mon … 7=Sun): explicit `override` → `Intl` `getWeekInfo` →
|
|
291
|
+
* **Monday fallback**. The override is the MVP safety valve — `Intl.Locale.getWeekInfo()` support is
|
|
292
|
+
* spotty (older Firefox/Safari), so the detection path genuinely fails in mainstream browsers and the
|
|
293
|
+
* Monday fallback is wrong for en-US (Sun) / much of the Middle East (Sat) (ADR 0050 §5).
|
|
294
|
+
*/
|
|
295
|
+
function resolveFirstDayOfWeek(override, locale) {
|
|
296
|
+
if (typeof override === 'number' && override >= 1 && override <= 7)
|
|
297
|
+
return override;
|
|
298
|
+
try {
|
|
299
|
+
const loc = new Intl.Locale(locale ?? hostLocale() ?? 'en');
|
|
300
|
+
const info = loc.getWeekInfo?.() ?? loc.weekInfo; // method (modern) or property (older engines)
|
|
301
|
+
const firstDay = info?.firstDay;
|
|
302
|
+
if (typeof firstDay === 'number' && firstDay >= 1 && firstDay <= 7)
|
|
303
|
+
return firstDay;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
/* unsupported / malformed locale → fall through to the documented fallback */
|
|
307
|
+
}
|
|
308
|
+
return 1; // Monday (ISO-8601 week start)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
let nextDatePickerId = 0;
|
|
312
|
+
/**
|
|
313
|
+
* SkraDatePicker — pick a single **calendar date** from an APG Date Picker grid in the native top layer.
|
|
314
|
+
* Second Tier-3 component. A value-display **trigger** (`<button>`) opens a non-modal top-layer **calendar
|
|
315
|
+
* panel** (Popover API, `popover="auto"`, ADR 0044). The value is an **ISO 8601 date-only string**
|
|
316
|
+
* (`YYYY-MM-DD`), consumer-owned (ADR 0050/037 — NO `@angular/forms`); all date math is the zoneless
|
|
317
|
+
* UTC-calculator discipline in `calendar-math` (no date library). Localization (month/weekday names,
|
|
318
|
+
* first-day-of-week, digits) is `Intl`-derived in `calendar-intl`; the 2-D grid keyboard is the
|
|
319
|
+
* hand-rolled `SkraCalendarKeyboard` (ADR 0044 §7 — roving REAL focus, not aria-activedescendant).
|
|
320
|
+
*
|
|
321
|
+
* No projected content: the calendar days are GENERATED (the structural divergence from select/combobox —
|
|
322
|
+
* there is no `skra-option` reuse). The only focusable element in the open panel is the roving day cell;
|
|
323
|
+
* the month-nav buttons are pointer affordances (`tabindex="-1"`, driven by PageUp/PageDown on keyboard),
|
|
324
|
+
* and Tab dismisses — so no focus-containment machinery is needed (spec Q-1).
|
|
325
|
+
*
|
|
326
|
+
* Full contract: adr/specs/component-datepicker-spec.md.
|
|
327
|
+
*/
|
|
328
|
+
class SkraDatePicker {
|
|
329
|
+
/** Consumer-owned value: ISO 8601 date-only (`YYYY-MM-DD`); `''` = unset. NEVER a Date (ADR 0050/037). */
|
|
330
|
+
value = input('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
331
|
+
/** Inclusive lower bound (ISO); `''` = unbounded (ADR 0050 §2). */
|
|
332
|
+
min = input('', ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
|
|
333
|
+
/** Inclusive upper bound (ISO); `''` = unbounded. */
|
|
334
|
+
max = input('', ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
|
|
335
|
+
/** `Intl` locale (BCP-47); `undefined` → host default (ADR 0050 §5). */
|
|
336
|
+
locale = input(undefined, ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
337
|
+
/** First day of week, ISO 1=Mon … 7=Sun; overrides `Intl.getWeekInfo` (the MVP safety valve, ADR 0050 §5). */
|
|
338
|
+
firstDayOfWeek = input(undefined, ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : /* istanbul ignore next */ []));
|
|
339
|
+
/** Value-empty hint in the trigger (NOT the accessible name — ADR 033). */
|
|
340
|
+
placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
341
|
+
/** Placement intent; flip/shift adjust at runtime. */
|
|
342
|
+
placement = input('bottom', ...(ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []));
|
|
343
|
+
/** Standalone availability source — `SkraFieldContext` wins when present. */
|
|
344
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
345
|
+
readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
346
|
+
/** Fires ONLY on user date selection — never on a programmatic `value()` change (ADR 037/0050). */
|
|
347
|
+
valueChange = output();
|
|
348
|
+
triggerRef = viewChild('trigger', ...(ngDevMode ? [{ debugName: "triggerRef" }] : /* istanbul ignore next */ []));
|
|
349
|
+
panelRef = viewChild('panel', ...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
|
|
350
|
+
context = inject(SkraFieldContext, { optional: true });
|
|
351
|
+
injector = inject(Injector);
|
|
352
|
+
keyboard = new SkraCalendarKeyboard();
|
|
353
|
+
panelId = `skra-datepicker-${nextDatePickerId}`;
|
|
354
|
+
monthYearId = `skra-datepicker-monthyear-${nextDatePickerId++}`;
|
|
355
|
+
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
|
|
356
|
+
/** Today's LOCAL date (ADR 0050 §3) — stable for the session. */
|
|
357
|
+
todayIso = today();
|
|
358
|
+
/** The roving-focus day; SINGLE source of truth for what the grid shows AND which cell is focusable. */
|
|
359
|
+
focusedIso = signal(this.todayIso, ...(ngDevMode ? [{ debugName: "focusedIso" }] : /* istanbul ignore next */ []));
|
|
360
|
+
positioner;
|
|
361
|
+
/** Set when a close should return focus to the trigger (keyboard/select), not on outside-click. */
|
|
362
|
+
returnFocus = false;
|
|
363
|
+
// ── Availability / validation (SkraFieldContext, ADR 013, with standalone fallbacks) ──
|
|
364
|
+
isDisabled = computed(() => this.context?.isDisabled() ?? this.disabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
365
|
+
isReadonly = computed(() => this.context?.isReadonly() ?? this.readonly(), ...(ngDevMode ? [{ debugName: "isReadonly" }] : /* istanbul ignore next */ []));
|
|
366
|
+
hasError = computed(() => this.context?.hasError() ?? false, ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
|
|
367
|
+
isRequired = computed(() => this.context?.isRequired() ?? false, ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
|
|
368
|
+
triggerId = computed(() => this.context?.fieldId() || null, ...(ngDevMode ? [{ debugName: "triggerId" }] : /* istanbul ignore next */ []));
|
|
369
|
+
describedBy = computed(() => this.context?.describedByIds() ?? null, ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
|
|
370
|
+
// ── Derived calendar state ──
|
|
371
|
+
/** The committed value, defensively normalized: invalid/out-of-range → `''` for display (ADR 0050 §1). */
|
|
372
|
+
selectedIso = computed(() => {
|
|
373
|
+
const v = this.value();
|
|
374
|
+
return isValidIso(v) && isInRange(v, this.min(), this.max()) ? v : '';
|
|
375
|
+
}, ...(ngDevMode ? [{ debugName: "selectedIso" }] : /* istanbul ignore next */ []));
|
|
376
|
+
formattedValue = computed(() => formatValue(this.selectedIso(), this.locale()), ...(ngDevMode ? [{ debugName: "formattedValue" }] : /* istanbul ignore next */ []));
|
|
377
|
+
firstDow = computed(() => resolveFirstDayOfWeek(this.firstDayOfWeek(), this.locale()), ...(ngDevMode ? [{ debugName: "firstDow" }] : /* istanbul ignore next */ []));
|
|
378
|
+
/** The month being shown — always the focused day's month (keeps the roving tabindex valid). */
|
|
379
|
+
displayedMonth = computed(() => {
|
|
380
|
+
const p = parseIso(this.focusedIso()) ?? parseIso(this.todayIso);
|
|
381
|
+
return { y: p.y, m: p.m };
|
|
382
|
+
}, ...(ngDevMode ? [{ debugName: "displayedMonth" }] : /* istanbul ignore next */ []));
|
|
383
|
+
monthYearLabel = computed(() => formatMonthYear(this.displayedMonth().y, this.displayedMonth().m, this.locale()), ...(ngDevMode ? [{ debugName: "monthYearLabel" }] : /* istanbul ignore next */ []));
|
|
384
|
+
prevMonthLabel = computed(() => this.monthOffsetLabel(-1), ...(ngDevMode ? [{ debugName: "prevMonthLabel" }] : /* istanbul ignore next */ []));
|
|
385
|
+
nextMonthLabel = computed(() => this.monthOffsetLabel(1), ...(ngDevMode ? [{ debugName: "nextMonthLabel" }] : /* istanbul ignore next */ []));
|
|
386
|
+
weekdayHeadersView = computed(() => weekdayHeaders(this.firstDow(), this.locale()), ...(ngDevMode ? [{ debugName: "weekdayHeadersView" }] : /* istanbul ignore next */ []));
|
|
387
|
+
weeks = computed(() => {
|
|
388
|
+
const { y, m } = this.displayedMonth();
|
|
389
|
+
const loc = this.locale();
|
|
390
|
+
const sel = this.selectedIso();
|
|
391
|
+
const foc = this.focusedIso();
|
|
392
|
+
const min = this.min();
|
|
393
|
+
const max = this.max();
|
|
394
|
+
return monthGrid(y, m, this.firstDow()).map((week, wi) => ({
|
|
395
|
+
key: wi,
|
|
396
|
+
days: week.map((iso, di) => iso === null
|
|
397
|
+
? this.emptyCell(`${wi}-${di}`)
|
|
398
|
+
: {
|
|
399
|
+
key: iso,
|
|
400
|
+
iso,
|
|
401
|
+
dayNum: formatDayNumber(parseIso(iso).d, loc),
|
|
402
|
+
srName: formatDayName(iso, loc),
|
|
403
|
+
today: iso === this.todayIso,
|
|
404
|
+
selected: iso === sel,
|
|
405
|
+
disabled: !isInRange(iso, min, max),
|
|
406
|
+
focused: iso === foc,
|
|
407
|
+
}),
|
|
408
|
+
}));
|
|
409
|
+
}, ...(ngDevMode ? [{ debugName: "weeks" }] : /* istanbul ignore next */ []));
|
|
410
|
+
constructor() {
|
|
411
|
+
inject(DestroyRef).onDestroy(() => this.positioner?.destroy());
|
|
412
|
+
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
|
413
|
+
// Dev-only sanity warnings (stripped in prod) — never thrown (ADR 0050 §1).
|
|
414
|
+
const warn = computed(() => {
|
|
415
|
+
const v = this.value();
|
|
416
|
+
if (v !== '' && !isValidIso(v))
|
|
417
|
+
return `skra-datepicker: value "${v}" is not a valid YYYY-MM-DD date; shown as unset.`;
|
|
418
|
+
if (this.min() !== '' &&
|
|
419
|
+
this.max() !== '' &&
|
|
420
|
+
compareIso(this.min(), this.max()) > 0)
|
|
421
|
+
return `skra-datepicker: min "${this.min()}" is after max "${this.max()}" — every day is disabled.`;
|
|
422
|
+
return null;
|
|
423
|
+
}, ...(ngDevMode ? [{ debugName: "warn" }] : /* istanbul ignore next */ []));
|
|
424
|
+
afterNextRender(() => {
|
|
425
|
+
const message = warn();
|
|
426
|
+
if (message)
|
|
427
|
+
console.warn(message);
|
|
428
|
+
}, { injector: this.injector });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// ── Trigger interaction ──
|
|
432
|
+
onTriggerClick(event) {
|
|
433
|
+
if (this.isReadonly())
|
|
434
|
+
event.preventDefault(); // block the native popovertarget open
|
|
435
|
+
}
|
|
436
|
+
onTriggerKeydown(event) {
|
|
437
|
+
if (this.isDisabled() || this.isReadonly() || this.isOpen())
|
|
438
|
+
return;
|
|
439
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
440
|
+
event.preventDefault();
|
|
441
|
+
this.open(); // Enter/Space/click open via the native popovertarget
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// ── Grid keyboard (APG Date Picker, 2-D) ──
|
|
445
|
+
onGridKeydown(event) {
|
|
446
|
+
if (event.key === 'Tab') {
|
|
447
|
+
// APG: Tab dismisses the calendar; focus returns to the trigger (deterministic, no containment).
|
|
448
|
+
event.preventDefault();
|
|
449
|
+
this.returnFocus = true;
|
|
450
|
+
this.close();
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
const intent = this.keyboard.resolve(event, {
|
|
454
|
+
focusedIso: this.focusedIso(),
|
|
455
|
+
firstDayOfWeek: this.firstDow(),
|
|
456
|
+
});
|
|
457
|
+
switch (intent.type) {
|
|
458
|
+
case 'focus':
|
|
459
|
+
event.preventDefault();
|
|
460
|
+
this.focusedIso.set(intent.iso);
|
|
461
|
+
this.scheduleFocus(); // re-focus after the grid re-stamps
|
|
462
|
+
break;
|
|
463
|
+
case 'select':
|
|
464
|
+
event.preventDefault();
|
|
465
|
+
this.selectDate(this.focusedIso());
|
|
466
|
+
break;
|
|
467
|
+
case 'close':
|
|
468
|
+
this.returnFocus = true;
|
|
469
|
+
this.close();
|
|
470
|
+
break;
|
|
471
|
+
case 'none':
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// ── Month navigation (pointer; focus stays on the nav button) ──
|
|
476
|
+
goToPreviousMonth() {
|
|
477
|
+
this.focusedIso.set(addMonthsClamped(this.focusedIso(), -1));
|
|
478
|
+
}
|
|
479
|
+
goToNextMonth() {
|
|
480
|
+
this.focusedIso.set(addMonthsClamped(this.focusedIso(), 1));
|
|
481
|
+
}
|
|
482
|
+
// ── Selection ──
|
|
483
|
+
selectDate(iso) {
|
|
484
|
+
if (!isInRange(iso, this.min(), this.max()))
|
|
485
|
+
return; // guard out-of-range (one place: keyboard + pointer)
|
|
486
|
+
this.returnFocus = true;
|
|
487
|
+
this.valueChange.emit(iso);
|
|
488
|
+
this.close();
|
|
489
|
+
}
|
|
490
|
+
// ── Panel open / close (native toggle from the Popover API) ──
|
|
491
|
+
onPanelToggle(event) {
|
|
492
|
+
const open = event.newState === 'open';
|
|
493
|
+
this.isOpen.set(open);
|
|
494
|
+
if (open)
|
|
495
|
+
this.onOpen();
|
|
496
|
+
else
|
|
497
|
+
this.onClose();
|
|
498
|
+
}
|
|
499
|
+
onOpen() {
|
|
500
|
+
this.focusedIso.set(this.seedFocus());
|
|
501
|
+
const anchor = this.triggerRef()?.nativeElement;
|
|
502
|
+
const panel = this.panelRef()?.nativeElement;
|
|
503
|
+
if (anchor && panel) {
|
|
504
|
+
this.positioner?.destroy();
|
|
505
|
+
this.positioner = _attachPositioner(anchor, panel, () => ({
|
|
506
|
+
placement: this.placement(),
|
|
507
|
+
offset: this.cssLenPx('--skra-comp-datepicker-panel-offset'),
|
|
508
|
+
viewportPadding: this.cssLenPx('--skra-comp-datepicker-panel-viewport-padding'),
|
|
509
|
+
}));
|
|
510
|
+
}
|
|
511
|
+
this.scheduleFocus();
|
|
512
|
+
}
|
|
513
|
+
onClose() {
|
|
514
|
+
this.positioner?.destroy();
|
|
515
|
+
this.positioner = undefined;
|
|
516
|
+
if (this.returnFocus) {
|
|
517
|
+
this.returnFocus = false;
|
|
518
|
+
this.triggerRef()?.nativeElement.focus();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
open() {
|
|
522
|
+
this.panelRef()?.nativeElement.showPopover?.();
|
|
523
|
+
}
|
|
524
|
+
close() {
|
|
525
|
+
this.panelRef()?.nativeElement.hidePopover?.();
|
|
526
|
+
}
|
|
527
|
+
/** Seed the focused day on open: the valid current value, else today, clamped into `[min,max]`. */
|
|
528
|
+
seedFocus() {
|
|
529
|
+
const v = this.value();
|
|
530
|
+
const base = isValidIso(v) ? v : this.todayIso;
|
|
531
|
+
return clampIso(base, this.min(), this.max());
|
|
532
|
+
}
|
|
533
|
+
monthOffsetLabel(n) {
|
|
534
|
+
const { y, m } = this.displayedMonth();
|
|
535
|
+
const ref = parseIso(addMonthsClamped(toIso({ y, m, d: 1 }), n));
|
|
536
|
+
return formatMonthYear(ref.y, ref.m, this.locale());
|
|
537
|
+
}
|
|
538
|
+
emptyCell(key) {
|
|
539
|
+
return {
|
|
540
|
+
key,
|
|
541
|
+
iso: null,
|
|
542
|
+
dayNum: '',
|
|
543
|
+
srName: '',
|
|
544
|
+
today: false,
|
|
545
|
+
selected: false,
|
|
546
|
+
disabled: true,
|
|
547
|
+
focused: false,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
scheduleFocus() {
|
|
551
|
+
afterNextRender(() => this.focusActiveDay(), { injector: this.injector });
|
|
552
|
+
}
|
|
553
|
+
focusActiveDay() {
|
|
554
|
+
this.panelRef()
|
|
555
|
+
?.nativeElement.querySelector('.skra-datepicker-day[tabindex="0"]')
|
|
556
|
+
?.focus();
|
|
557
|
+
}
|
|
558
|
+
/** Read a length comp token (px/rem) off the panel computed style for the positioner. */
|
|
559
|
+
cssLenPx(prop) {
|
|
560
|
+
const el = this.panelRef()?.nativeElement;
|
|
561
|
+
if (!el)
|
|
562
|
+
return 0;
|
|
563
|
+
const raw = getComputedStyle(el).getPropertyValue(prop).trim();
|
|
564
|
+
if (raw.endsWith('px'))
|
|
565
|
+
return parseFloat(raw);
|
|
566
|
+
if (raw.endsWith('rem')) {
|
|
567
|
+
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
|
|
568
|
+
return parseFloat(raw) * rootFontSize;
|
|
569
|
+
}
|
|
570
|
+
return parseFloat(raw) || 0;
|
|
571
|
+
}
|
|
572
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraDatePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
573
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: SkraDatePicker, isStandalone: true, selector: "skra-datepicker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { properties: { "attr.data-skra-disabled": "isDisabled() ? \"\" : null", "attr.data-skra-readonly": "isReadonly() ? \"\" : null", "attr.data-skra-validation": "hasError() ? \"invalid\" : null" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], exportAs: ["skraDatePicker"], ngImport: i0, template: `
|
|
574
|
+
<button
|
|
575
|
+
#trigger
|
|
576
|
+
class="skra-datepicker-trigger"
|
|
577
|
+
type="button"
|
|
578
|
+
role="combobox"
|
|
579
|
+
aria-haspopup="dialog"
|
|
580
|
+
[attr.id]="triggerId()"
|
|
581
|
+
[attr.popovertarget]="panelId"
|
|
582
|
+
[attr.aria-controls]="panelId"
|
|
583
|
+
[attr.aria-expanded]="isOpen()"
|
|
584
|
+
[attr.aria-describedby]="describedBy()"
|
|
585
|
+
[attr.aria-invalid]="hasError() ? 'true' : null"
|
|
586
|
+
[attr.aria-required]="isRequired() ? 'true' : null"
|
|
587
|
+
[attr.aria-readonly]="isReadonly() ? 'true' : null"
|
|
588
|
+
[disabled]="isDisabled()"
|
|
589
|
+
(click)="onTriggerClick($event)"
|
|
590
|
+
(keydown)="onTriggerKeydown($event)"
|
|
591
|
+
>
|
|
592
|
+
@if (formattedValue(); as label) {
|
|
593
|
+
<span class="skra-datepicker-value">{{ label }}</span>
|
|
594
|
+
} @else {
|
|
595
|
+
<span class="skra-datepicker-placeholder">{{ placeholder() }}</span>
|
|
596
|
+
}
|
|
597
|
+
<svg
|
|
598
|
+
class="skra-datepicker-icon"
|
|
599
|
+
aria-hidden="true"
|
|
600
|
+
viewBox="0 0 24 24"
|
|
601
|
+
fill="none"
|
|
602
|
+
stroke="currentColor"
|
|
603
|
+
stroke-width="2"
|
|
604
|
+
>
|
|
605
|
+
<rect x="3" y="4" width="18" height="17" rx="2" />
|
|
606
|
+
<path d="M3 9h18M8 2v4M16 2v4" />
|
|
607
|
+
</svg>
|
|
608
|
+
</button>
|
|
609
|
+
|
|
610
|
+
<div
|
|
611
|
+
#panel
|
|
612
|
+
class="skra-datepicker-panel"
|
|
613
|
+
[id]="panelId"
|
|
614
|
+
popover="auto"
|
|
615
|
+
role="dialog"
|
|
616
|
+
aria-modal="false"
|
|
617
|
+
[attr.aria-labelledby]="monthYearId"
|
|
618
|
+
(toggle)="onPanelToggle($event)"
|
|
619
|
+
>
|
|
620
|
+
<div class="skra-datepicker-header">
|
|
621
|
+
<button
|
|
622
|
+
type="button"
|
|
623
|
+
class="skra-datepicker-nav"
|
|
624
|
+
tabindex="-1"
|
|
625
|
+
[attr.aria-label]="prevMonthLabel()"
|
|
626
|
+
(click)="goToPreviousMonth()"
|
|
627
|
+
>
|
|
628
|
+
<svg
|
|
629
|
+
viewBox="0 0 24 24"
|
|
630
|
+
fill="none"
|
|
631
|
+
stroke="currentColor"
|
|
632
|
+
stroke-width="2"
|
|
633
|
+
aria-hidden="true"
|
|
634
|
+
>
|
|
635
|
+
<path d="M15 6l-6 6 6 6" />
|
|
636
|
+
</svg>
|
|
637
|
+
</button>
|
|
638
|
+
<span
|
|
639
|
+
class="skra-datepicker-monthyear"
|
|
640
|
+
[id]="monthYearId"
|
|
641
|
+
aria-live="polite"
|
|
642
|
+
>{{ monthYearLabel() }}</span
|
|
643
|
+
>
|
|
644
|
+
<button
|
|
645
|
+
type="button"
|
|
646
|
+
class="skra-datepicker-nav"
|
|
647
|
+
tabindex="-1"
|
|
648
|
+
[attr.aria-label]="nextMonthLabel()"
|
|
649
|
+
(click)="goToNextMonth()"
|
|
650
|
+
>
|
|
651
|
+
<svg
|
|
652
|
+
viewBox="0 0 24 24"
|
|
653
|
+
fill="none"
|
|
654
|
+
stroke="currentColor"
|
|
655
|
+
stroke-width="2"
|
|
656
|
+
aria-hidden="true"
|
|
657
|
+
>
|
|
658
|
+
<path d="M9 6l6 6-6 6" />
|
|
659
|
+
</svg>
|
|
660
|
+
</button>
|
|
661
|
+
</div>
|
|
662
|
+
|
|
663
|
+
<table
|
|
664
|
+
class="skra-datepicker-grid"
|
|
665
|
+
role="grid"
|
|
666
|
+
[attr.aria-labelledby]="monthYearId"
|
|
667
|
+
>
|
|
668
|
+
<thead>
|
|
669
|
+
<tr>
|
|
670
|
+
@for (wd of weekdayHeadersView(); track wd.key) {
|
|
671
|
+
<th scope="col" class="skra-datepicker-weekday">
|
|
672
|
+
<abbr [title]="wd.long">{{ wd.short }}</abbr>
|
|
673
|
+
</th>
|
|
674
|
+
}
|
|
675
|
+
</tr>
|
|
676
|
+
</thead>
|
|
677
|
+
<tbody>
|
|
678
|
+
@for (week of weeks(); track week.key) {
|
|
679
|
+
<tr>
|
|
680
|
+
@for (cell of week.days; track cell.key) {
|
|
681
|
+
@if (cell.iso) {
|
|
682
|
+
<td
|
|
683
|
+
role="gridcell"
|
|
684
|
+
[attr.aria-selected]="cell.selected ? 'true' : null"
|
|
685
|
+
>
|
|
686
|
+
<button
|
|
687
|
+
type="button"
|
|
688
|
+
class="skra-datepicker-day"
|
|
689
|
+
[attr.tabindex]="cell.focused ? 0 : -1"
|
|
690
|
+
[attr.aria-current]="cell.today ? 'date' : null"
|
|
691
|
+
[attr.aria-disabled]="cell.disabled ? 'true' : null"
|
|
692
|
+
[attr.data-skra-selected]="cell.selected ? '' : null"
|
|
693
|
+
[attr.data-skra-today]="cell.today ? '' : null"
|
|
694
|
+
(click)="selectDate(cell.iso)"
|
|
695
|
+
(keydown)="onGridKeydown($event)"
|
|
696
|
+
>
|
|
697
|
+
{{ cell.dayNum
|
|
698
|
+
}}<span class="skra-datepicker-day-sr" skra-sr-only>{{
|
|
699
|
+
cell.srName
|
|
700
|
+
}}</span>
|
|
701
|
+
</button>
|
|
702
|
+
</td>
|
|
703
|
+
} @else {
|
|
704
|
+
<td class="skra-datepicker-day-empty" aria-hidden="true"></td>
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
</tr>
|
|
708
|
+
}
|
|
709
|
+
</tbody>
|
|
710
|
+
</table>
|
|
711
|
+
</div>
|
|
712
|
+
`, isInline: true, styles: ["@charset \"UTF-8\";:host{display:inline-block;box-sizing:border-box;margin:0}.skra-datepicker-trigger{display:flex;align-items:center;gap:var(--skra-comp-datepicker-gap);inline-size:100%;min-block-size:var(--skra-comp-datepicker-min-block-size);box-sizing:border-box;margin:0;padding-block:var(--skra-comp-datepicker-padding-block);padding-inline:var(--skra-comp-datepicker-padding-inline);text-align:start;cursor:pointer;background:var(--skra-comp-datepicker-background);color:var(--skra-comp-datepicker-foreground);border-color:var(--skra-comp-datepicker-border-color);border-width:var(--skra-comp-datepicker-border-width);border-style:var(--skra-comp-datepicker-border-style);border-radius:var(--skra-comp-datepicker-radius);box-shadow:var(--skra-comp-datepicker-shadow);font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing);transition:background-color var(--skra-comp-datepicker-motion-duration) var(--skra-comp-datepicker-motion-easing),border-color var(--skra-comp-datepicker-motion-duration) var(--skra-comp-datepicker-motion-easing)}.skra-datepicker-trigger:focus-visible{outline:var(--skra-comp-datepicker-focus-ring-width) solid var(--skra-comp-datepicker-focus-ring-color);outline-offset:.125rem}.skra-datepicker-trigger:disabled{background:var(--skra-comp-datepicker-background-disabled);color:var(--skra-comp-datepicker-foreground-disabled);cursor:not-allowed}.skra-datepicker-trigger[aria-readonly=true]{background:var(--skra-comp-datepicker-background-readonly);cursor:default}.skra-datepicker-trigger[aria-invalid=true]{border-color:var(--skra-comp-datepicker-border-color-validation-invalid)}.skra-datepicker-value{flex:1 1 auto;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.skra-datepicker-placeholder{flex:1 1 auto;color:var(--skra-comp-datepicker-foreground-disabled)}.skra-datepicker-icon{flex:none;inline-size:var(--skra-comp-datepicker-icon-size);block-size:var(--skra-comp-datepicker-icon-size);color:var(--skra-comp-datepicker-icon-color)}.skra-datepicker-panel{position:fixed;margin:0;inset:auto;box-sizing:border-box;inline-size:max-content;padding:var(--skra-comp-datepicker-panel-padding);background:var(--skra-comp-datepicker-panel-background);color:var(--skra-comp-datepicker-panel-foreground);border:var(--skra-comp-datepicker-border-width) solid var(--skra-comp-datepicker-panel-border-color);border-radius:var(--skra-comp-datepicker-panel-radius);box-shadow:var(--skra-comp-datepicker-panel-shadow);opacity:0;transition:opacity var(--skra-comp-datepicker-motion-duration) var(--skra-comp-datepicker-motion-easing),display var(--skra-comp-datepicker-motion-duration) allow-discrete,overlay var(--skra-comp-datepicker-motion-duration) allow-discrete}.skra-datepicker-panel:popover-open{display:block;opacity:1}@starting-style{.skra-datepicker-panel:popover-open{opacity:0}}.skra-datepicker-header{display:flex;align-items:center;justify-content:space-between;gap:var(--skra-comp-datepicker-gap);margin-block-end:var(--skra-comp-datepicker-panel-padding)}.skra-datepicker-monthyear{flex:1 1 auto;text-align:center;color:var(--skra-comp-datepicker-header-foreground);font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing)}.skra-datepicker-nav{flex:none;display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-datepicker-nav-icon-size);block-size:var(--skra-comp-datepicker-nav-icon-size);margin:0;padding:0;background:transparent;border:0;color:var(--skra-comp-datepicker-nav-color);cursor:pointer}.skra-datepicker-nav svg{inline-size:100%;block-size:100%}.skra-datepicker-nav:hover{color:var(--skra-comp-datepicker-nav-color-hover)}.skra-datepicker-nav:focus-visible{outline:var(--skra-comp-datepicker-focus-ring-width) solid var(--skra-comp-datepicker-focus-ring-color);outline-offset:.125rem}.skra-datepicker-grid{border-collapse:collapse}.skra-datepicker-weekday{inline-size:var(--skra-comp-datepicker-day-size);padding:0;color:var(--skra-comp-datepicker-weekday-foreground);font-family:var(--skra-sys-typography-role-body-small-font-family);font-size:var(--skra-sys-typography-role-body-small-font-size);font-weight:var(--skra-sys-typography-role-body-small-font-weight);line-height:var(--skra-sys-typography-role-body-small-line-height);letter-spacing:var(--skra-sys-typography-role-body-small-letter-spacing)}.skra-datepicker-weekday abbr{text-decoration:none}.skra-datepicker-day-empty{padding:0}.skra-datepicker-day{display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-datepicker-day-size);block-size:var(--skra-comp-datepicker-day-size);margin:0;padding:0;background:transparent;border:0;border-radius:var(--skra-comp-datepicker-day-radius);color:var(--skra-comp-datepicker-day-foreground);cursor:pointer;font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing);transition:background-color var(--skra-comp-datepicker-motion-duration) var(--skra-comp-datepicker-motion-easing)}.skra-datepicker-day:hover:not([aria-disabled=true]){background:var(--skra-comp-datepicker-day-background-hover)}.skra-datepicker-day:focus-visible{outline:var(--skra-comp-datepicker-focus-ring-width) solid var(--skra-comp-datepicker-focus-ring-color);outline-offset:-.0625rem}.skra-datepicker-day[data-skra-today]{box-shadow:inset 0 0 0 .125rem var(--skra-comp-datepicker-day-today-indicator-color)}.skra-datepicker-day[data-skra-selected]{background:var(--skra-comp-datepicker-day-background-selected);color:var(--skra-comp-datepicker-day-foreground-selected)}.skra-datepicker-day[aria-disabled=true]{color:var(--skra-comp-datepicker-day-foreground-disabled);cursor:default}@media(prefers-reduced-motion:reduce){.skra-datepicker-trigger,.skra-datepicker-panel,.skra-datepicker-day{transition-duration:0ms}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
713
|
+
}
|
|
714
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraDatePicker, decorators: [{
|
|
715
|
+
type: Component,
|
|
716
|
+
args: [{ selector: 'skra-datepicker', standalone: true, exportAs: 'skraDatePicker', template: `
|
|
717
|
+
<button
|
|
718
|
+
#trigger
|
|
719
|
+
class="skra-datepicker-trigger"
|
|
720
|
+
type="button"
|
|
721
|
+
role="combobox"
|
|
722
|
+
aria-haspopup="dialog"
|
|
723
|
+
[attr.id]="triggerId()"
|
|
724
|
+
[attr.popovertarget]="panelId"
|
|
725
|
+
[attr.aria-controls]="panelId"
|
|
726
|
+
[attr.aria-expanded]="isOpen()"
|
|
727
|
+
[attr.aria-describedby]="describedBy()"
|
|
728
|
+
[attr.aria-invalid]="hasError() ? 'true' : null"
|
|
729
|
+
[attr.aria-required]="isRequired() ? 'true' : null"
|
|
730
|
+
[attr.aria-readonly]="isReadonly() ? 'true' : null"
|
|
731
|
+
[disabled]="isDisabled()"
|
|
732
|
+
(click)="onTriggerClick($event)"
|
|
733
|
+
(keydown)="onTriggerKeydown($event)"
|
|
734
|
+
>
|
|
735
|
+
@if (formattedValue(); as label) {
|
|
736
|
+
<span class="skra-datepicker-value">{{ label }}</span>
|
|
737
|
+
} @else {
|
|
738
|
+
<span class="skra-datepicker-placeholder">{{ placeholder() }}</span>
|
|
739
|
+
}
|
|
740
|
+
<svg
|
|
741
|
+
class="skra-datepicker-icon"
|
|
742
|
+
aria-hidden="true"
|
|
743
|
+
viewBox="0 0 24 24"
|
|
744
|
+
fill="none"
|
|
745
|
+
stroke="currentColor"
|
|
746
|
+
stroke-width="2"
|
|
747
|
+
>
|
|
748
|
+
<rect x="3" y="4" width="18" height="17" rx="2" />
|
|
749
|
+
<path d="M3 9h18M8 2v4M16 2v4" />
|
|
750
|
+
</svg>
|
|
751
|
+
</button>
|
|
752
|
+
|
|
753
|
+
<div
|
|
754
|
+
#panel
|
|
755
|
+
class="skra-datepicker-panel"
|
|
756
|
+
[id]="panelId"
|
|
757
|
+
popover="auto"
|
|
758
|
+
role="dialog"
|
|
759
|
+
aria-modal="false"
|
|
760
|
+
[attr.aria-labelledby]="monthYearId"
|
|
761
|
+
(toggle)="onPanelToggle($event)"
|
|
762
|
+
>
|
|
763
|
+
<div class="skra-datepicker-header">
|
|
764
|
+
<button
|
|
765
|
+
type="button"
|
|
766
|
+
class="skra-datepicker-nav"
|
|
767
|
+
tabindex="-1"
|
|
768
|
+
[attr.aria-label]="prevMonthLabel()"
|
|
769
|
+
(click)="goToPreviousMonth()"
|
|
770
|
+
>
|
|
771
|
+
<svg
|
|
772
|
+
viewBox="0 0 24 24"
|
|
773
|
+
fill="none"
|
|
774
|
+
stroke="currentColor"
|
|
775
|
+
stroke-width="2"
|
|
776
|
+
aria-hidden="true"
|
|
777
|
+
>
|
|
778
|
+
<path d="M15 6l-6 6 6 6" />
|
|
779
|
+
</svg>
|
|
780
|
+
</button>
|
|
781
|
+
<span
|
|
782
|
+
class="skra-datepicker-monthyear"
|
|
783
|
+
[id]="monthYearId"
|
|
784
|
+
aria-live="polite"
|
|
785
|
+
>{{ monthYearLabel() }}</span
|
|
786
|
+
>
|
|
787
|
+
<button
|
|
788
|
+
type="button"
|
|
789
|
+
class="skra-datepicker-nav"
|
|
790
|
+
tabindex="-1"
|
|
791
|
+
[attr.aria-label]="nextMonthLabel()"
|
|
792
|
+
(click)="goToNextMonth()"
|
|
793
|
+
>
|
|
794
|
+
<svg
|
|
795
|
+
viewBox="0 0 24 24"
|
|
796
|
+
fill="none"
|
|
797
|
+
stroke="currentColor"
|
|
798
|
+
stroke-width="2"
|
|
799
|
+
aria-hidden="true"
|
|
800
|
+
>
|
|
801
|
+
<path d="M9 6l6 6-6 6" />
|
|
802
|
+
</svg>
|
|
803
|
+
</button>
|
|
804
|
+
</div>
|
|
805
|
+
|
|
806
|
+
<table
|
|
807
|
+
class="skra-datepicker-grid"
|
|
808
|
+
role="grid"
|
|
809
|
+
[attr.aria-labelledby]="monthYearId"
|
|
810
|
+
>
|
|
811
|
+
<thead>
|
|
812
|
+
<tr>
|
|
813
|
+
@for (wd of weekdayHeadersView(); track wd.key) {
|
|
814
|
+
<th scope="col" class="skra-datepicker-weekday">
|
|
815
|
+
<abbr [title]="wd.long">{{ wd.short }}</abbr>
|
|
816
|
+
</th>
|
|
817
|
+
}
|
|
818
|
+
</tr>
|
|
819
|
+
</thead>
|
|
820
|
+
<tbody>
|
|
821
|
+
@for (week of weeks(); track week.key) {
|
|
822
|
+
<tr>
|
|
823
|
+
@for (cell of week.days; track cell.key) {
|
|
824
|
+
@if (cell.iso) {
|
|
825
|
+
<td
|
|
826
|
+
role="gridcell"
|
|
827
|
+
[attr.aria-selected]="cell.selected ? 'true' : null"
|
|
828
|
+
>
|
|
829
|
+
<button
|
|
830
|
+
type="button"
|
|
831
|
+
class="skra-datepicker-day"
|
|
832
|
+
[attr.tabindex]="cell.focused ? 0 : -1"
|
|
833
|
+
[attr.aria-current]="cell.today ? 'date' : null"
|
|
834
|
+
[attr.aria-disabled]="cell.disabled ? 'true' : null"
|
|
835
|
+
[attr.data-skra-selected]="cell.selected ? '' : null"
|
|
836
|
+
[attr.data-skra-today]="cell.today ? '' : null"
|
|
837
|
+
(click)="selectDate(cell.iso)"
|
|
838
|
+
(keydown)="onGridKeydown($event)"
|
|
839
|
+
>
|
|
840
|
+
{{ cell.dayNum
|
|
841
|
+
}}<span class="skra-datepicker-day-sr" skra-sr-only>{{
|
|
842
|
+
cell.srName
|
|
843
|
+
}}</span>
|
|
844
|
+
</button>
|
|
845
|
+
</td>
|
|
846
|
+
} @else {
|
|
847
|
+
<td class="skra-datepicker-day-empty" aria-hidden="true"></td>
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
</tr>
|
|
851
|
+
}
|
|
852
|
+
</tbody>
|
|
853
|
+
</table>
|
|
854
|
+
</div>
|
|
855
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
|
|
856
|
+
'[attr.data-skra-disabled]': 'isDisabled() ? "" : null',
|
|
857
|
+
'[attr.data-skra-readonly]': 'isReadonly() ? "" : null',
|
|
858
|
+
'[attr.data-skra-validation]': 'hasError() ? "invalid" : null',
|
|
859
|
+
}, styles: ["@charset \"UTF-8\";:host{display:inline-block;box-sizing:border-box;margin:0}.skra-datepicker-trigger{display:flex;align-items:center;gap:var(--skra-comp-datepicker-gap);inline-size:100%;min-block-size:var(--skra-comp-datepicker-min-block-size);box-sizing:border-box;margin:0;padding-block:var(--skra-comp-datepicker-padding-block);padding-inline:var(--skra-comp-datepicker-padding-inline);text-align:start;cursor:pointer;background:var(--skra-comp-datepicker-background);color:var(--skra-comp-datepicker-foreground);border-color:var(--skra-comp-datepicker-border-color);border-width:var(--skra-comp-datepicker-border-width);border-style:var(--skra-comp-datepicker-border-style);border-radius:var(--skra-comp-datepicker-radius);box-shadow:var(--skra-comp-datepicker-shadow);font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing);transition:background-color var(--skra-comp-datepicker-motion-duration) var(--skra-comp-datepicker-motion-easing),border-color var(--skra-comp-datepicker-motion-duration) var(--skra-comp-datepicker-motion-easing)}.skra-datepicker-trigger:focus-visible{outline:var(--skra-comp-datepicker-focus-ring-width) solid var(--skra-comp-datepicker-focus-ring-color);outline-offset:.125rem}.skra-datepicker-trigger:disabled{background:var(--skra-comp-datepicker-background-disabled);color:var(--skra-comp-datepicker-foreground-disabled);cursor:not-allowed}.skra-datepicker-trigger[aria-readonly=true]{background:var(--skra-comp-datepicker-background-readonly);cursor:default}.skra-datepicker-trigger[aria-invalid=true]{border-color:var(--skra-comp-datepicker-border-color-validation-invalid)}.skra-datepicker-value{flex:1 1 auto;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.skra-datepicker-placeholder{flex:1 1 auto;color:var(--skra-comp-datepicker-foreground-disabled)}.skra-datepicker-icon{flex:none;inline-size:var(--skra-comp-datepicker-icon-size);block-size:var(--skra-comp-datepicker-icon-size);color:var(--skra-comp-datepicker-icon-color)}.skra-datepicker-panel{position:fixed;margin:0;inset:auto;box-sizing:border-box;inline-size:max-content;padding:var(--skra-comp-datepicker-panel-padding);background:var(--skra-comp-datepicker-panel-background);color:var(--skra-comp-datepicker-panel-foreground);border:var(--skra-comp-datepicker-border-width) solid var(--skra-comp-datepicker-panel-border-color);border-radius:var(--skra-comp-datepicker-panel-radius);box-shadow:var(--skra-comp-datepicker-panel-shadow);opacity:0;transition:opacity var(--skra-comp-datepicker-motion-duration) var(--skra-comp-datepicker-motion-easing),display var(--skra-comp-datepicker-motion-duration) allow-discrete,overlay var(--skra-comp-datepicker-motion-duration) allow-discrete}.skra-datepicker-panel:popover-open{display:block;opacity:1}@starting-style{.skra-datepicker-panel:popover-open{opacity:0}}.skra-datepicker-header{display:flex;align-items:center;justify-content:space-between;gap:var(--skra-comp-datepicker-gap);margin-block-end:var(--skra-comp-datepicker-panel-padding)}.skra-datepicker-monthyear{flex:1 1 auto;text-align:center;color:var(--skra-comp-datepicker-header-foreground);font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing)}.skra-datepicker-nav{flex:none;display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-datepicker-nav-icon-size);block-size:var(--skra-comp-datepicker-nav-icon-size);margin:0;padding:0;background:transparent;border:0;color:var(--skra-comp-datepicker-nav-color);cursor:pointer}.skra-datepicker-nav svg{inline-size:100%;block-size:100%}.skra-datepicker-nav:hover{color:var(--skra-comp-datepicker-nav-color-hover)}.skra-datepicker-nav:focus-visible{outline:var(--skra-comp-datepicker-focus-ring-width) solid var(--skra-comp-datepicker-focus-ring-color);outline-offset:.125rem}.skra-datepicker-grid{border-collapse:collapse}.skra-datepicker-weekday{inline-size:var(--skra-comp-datepicker-day-size);padding:0;color:var(--skra-comp-datepicker-weekday-foreground);font-family:var(--skra-sys-typography-role-body-small-font-family);font-size:var(--skra-sys-typography-role-body-small-font-size);font-weight:var(--skra-sys-typography-role-body-small-font-weight);line-height:var(--skra-sys-typography-role-body-small-line-height);letter-spacing:var(--skra-sys-typography-role-body-small-letter-spacing)}.skra-datepicker-weekday abbr{text-decoration:none}.skra-datepicker-day-empty{padding:0}.skra-datepicker-day{display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-datepicker-day-size);block-size:var(--skra-comp-datepicker-day-size);margin:0;padding:0;background:transparent;border:0;border-radius:var(--skra-comp-datepicker-day-radius);color:var(--skra-comp-datepicker-day-foreground);cursor:pointer;font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing);transition:background-color var(--skra-comp-datepicker-motion-duration) var(--skra-comp-datepicker-motion-easing)}.skra-datepicker-day:hover:not([aria-disabled=true]){background:var(--skra-comp-datepicker-day-background-hover)}.skra-datepicker-day:focus-visible{outline:var(--skra-comp-datepicker-focus-ring-width) solid var(--skra-comp-datepicker-focus-ring-color);outline-offset:-.0625rem}.skra-datepicker-day[data-skra-today]{box-shadow:inset 0 0 0 .125rem var(--skra-comp-datepicker-day-today-indicator-color)}.skra-datepicker-day[data-skra-selected]{background:var(--skra-comp-datepicker-day-background-selected);color:var(--skra-comp-datepicker-day-foreground-selected)}.skra-datepicker-day[aria-disabled=true]{color:var(--skra-comp-datepicker-day-foreground-disabled);cursor:default}@media(prefers-reduced-motion:reduce){.skra-datepicker-trigger,.skra-datepicker-panel,.skra-datepicker-day{transition-duration:0ms}}\n"] }]
|
|
860
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], panelRef: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }] } });
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Generated bundle index. Do not edit.
|
|
864
|
+
*/
|
|
865
|
+
|
|
866
|
+
export { SkraCalendarKeyboard, SkraDatePicker };
|
|
867
|
+
//# sourceMappingURL=skra-ui-datepicker.mjs.map
|