@skra/ui 0.4.0 → 0.5.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/fesm2022/skra-ui-accordion.mjs +2 -2
- package/fesm2022/skra-ui-accordion.mjs.map +1 -1
- package/fesm2022/skra-ui-alert.mjs +2 -2
- package/fesm2022/skra-ui-alert.mjs.map +1 -1
- package/fesm2022/skra-ui-avatar.mjs.map +1 -1
- package/fesm2022/skra-ui-badge.mjs.map +1 -1
- package/fesm2022/skra-ui-button.mjs +2 -2
- package/fesm2022/skra-ui-button.mjs.map +1 -1
- package/fesm2022/skra-ui-checkbox.mjs +2 -2
- package/fesm2022/skra-ui-checkbox.mjs.map +1 -1
- package/fesm2022/skra-ui-chip.mjs +2 -2
- package/fesm2022/skra-ui-chip.mjs.map +1 -1
- package/fesm2022/skra-ui-combobox.mjs +120 -120
- package/fesm2022/skra-ui-combobox.mjs.map +1 -1
- package/fesm2022/skra-ui-core.mjs +36 -36
- package/fesm2022/skra-ui-core.mjs.map +1 -1
- package/fesm2022/skra-ui-counter.mjs.map +1 -1
- package/fesm2022/skra-ui-datepicker.mjs +605 -190
- package/fesm2022/skra-ui-datepicker.mjs.map +1 -1
- package/fesm2022/skra-ui-dialog.mjs.map +1 -1
- package/fesm2022/skra-ui-field.mjs.map +1 -1
- package/fesm2022/skra-ui-icon.mjs.map +1 -1
- package/fesm2022/skra-ui-input.mjs +2 -2
- package/fesm2022/skra-ui-input.mjs.map +1 -1
- package/fesm2022/skra-ui-kbd.mjs.map +1 -1
- package/fesm2022/skra-ui-layout.mjs.map +1 -1
- package/fesm2022/skra-ui-link.mjs +2 -2
- package/fesm2022/skra-ui-link.mjs.map +1 -1
- package/fesm2022/skra-ui-loading-indicator.mjs.map +1 -1
- package/fesm2022/skra-ui-menu.mjs +2 -2
- package/fesm2022/skra-ui-menu.mjs.map +1 -1
- package/fesm2022/skra-ui-multi-select.mjs +94 -94
- package/fesm2022/skra-ui-multi-select.mjs.map +1 -1
- package/fesm2022/skra-ui-native-select.mjs +2 -2
- package/fesm2022/skra-ui-native-select.mjs.map +1 -1
- package/fesm2022/skra-ui-popover.mjs.map +1 -1
- package/fesm2022/skra-ui-progress.mjs.map +1 -1
- package/fesm2022/skra-ui-radio.mjs +2 -2
- package/fesm2022/skra-ui-radio.mjs.map +1 -1
- package/fesm2022/skra-ui-rating-display.mjs.map +1 -1
- package/fesm2022/skra-ui-select.mjs +92 -92
- package/fesm2022/skra-ui-select.mjs.map +1 -1
- package/fesm2022/skra-ui-skeleton.mjs +8 -8
- package/fesm2022/skra-ui-skeleton.mjs.map +1 -1
- package/fesm2022/skra-ui-star-rating.mjs +2 -2
- package/fesm2022/skra-ui-star-rating.mjs.map +1 -1
- package/fesm2022/skra-ui-status-dot.mjs.map +1 -1
- package/fesm2022/skra-ui-switch.mjs +2 -2
- package/fesm2022/skra-ui-switch.mjs.map +1 -1
- package/fesm2022/skra-ui-tabs.mjs +2 -2
- package/fesm2022/skra-ui-tabs.mjs.map +1 -1
- package/fesm2022/skra-ui-textarea.mjs +2 -2
- package/fesm2022/skra-ui-textarea.mjs.map +1 -1
- package/fesm2022/skra-ui-toast.mjs +2 -2
- package/fesm2022/skra-ui-toast.mjs.map +1 -1
- package/fesm2022/skra-ui-tooltip.mjs.map +1 -1
- package/fesm2022/skra-ui-typography.mjs.map +1 -1
- package/package.json +3 -3
- package/types/skra-ui-datepicker.d.ts +146 -46
- package/types/skra-ui-datepicker.d.ts.map +1 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
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';
|
|
2
|
+
import { input, booleanAttribute, output, viewChild, inject, Injector, signal, linkedSignal, computed, DestroyRef, effect, untracked, afterNextRender, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { SkraFieldContext } from '@skra/ui/field';
|
|
4
4
|
import { _attachPositioner } from '@skra/ui/core';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* calendar-math — the ONE sanctioned site for DatePicker date arithmetic (ADR 0050 §3/§6)
|
|
7
|
+
* calendar-math — the ONE sanctioned site for DatePicker date arithmetic (ADR 0050 §3/§6) AND for every
|
|
8
|
+
* precision-aware key operation (ADR 0057 R3 — no parallel helper).
|
|
8
9
|
*
|
|
9
10
|
* Zoneless calendar-date discipline (the timezone off-by-one is the bug class this exists to prevent):
|
|
10
11
|
* - The value is an ISO 8601 date-only string (`YYYY-MM-DD`) — NEVER materialized as a `Date`.
|
|
@@ -147,43 +148,178 @@ function monthGrid(y, m, firstDayOfWeek) {
|
|
|
147
148
|
weeks.push(cells.slice(i, i + 7));
|
|
148
149
|
return weeks;
|
|
149
150
|
}
|
|
151
|
+
const ISO_MONTH = /^(\d{4})-(\d{2})$/;
|
|
152
|
+
/** Strict `YYYY-MM` parse. Returns `null` for anything invalid. */
|
|
153
|
+
function parseIsoMonth(iso) {
|
|
154
|
+
const match = ISO_MONTH.exec(iso);
|
|
155
|
+
if (!match)
|
|
156
|
+
return null;
|
|
157
|
+
const y = Number(match[1]);
|
|
158
|
+
const m = Number(match[2]);
|
|
159
|
+
if (m < 1 || m > 12)
|
|
160
|
+
return null;
|
|
161
|
+
return { y, m };
|
|
162
|
+
}
|
|
163
|
+
/** Serialize a calendar month to `YYYY-MM`. */
|
|
164
|
+
function toIsoMonth({ y, m }) {
|
|
165
|
+
return `${pad(y, 4)}-${pad(m, 2)}`;
|
|
166
|
+
}
|
|
167
|
+
/** `true` iff `iso` is a strictly valid `YYYY-MM` month key. */
|
|
168
|
+
function isValidIsoMonth(iso) {
|
|
169
|
+
return parseIsoMonth(iso) !== null;
|
|
170
|
+
}
|
|
171
|
+
/** `true` iff `iso` is strictly valid AT `precision` (ADR 0057 R3 — format follows precision). */
|
|
172
|
+
function isValidAt(iso, precision) {
|
|
173
|
+
return precision === 'month' ? isValidIsoMonth(iso) : isValidIso(iso);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Re-key an ISO key to `precision`, or `''` when it cannot be expressed there.
|
|
177
|
+
* Truncating down is a slice (`2026-06-14` → `2026-06`); widening up anchors to the first of the
|
|
178
|
+
* month (`2026-06` → `2026-06-01`), which is the only choice that keeps the key valid.
|
|
179
|
+
*/
|
|
180
|
+
function reKey(key, precision) {
|
|
181
|
+
if (key === '')
|
|
182
|
+
return '';
|
|
183
|
+
if (precision === 'month') {
|
|
184
|
+
if (isValidIsoMonth(key))
|
|
185
|
+
return key;
|
|
186
|
+
return isValidIso(key) ? key.slice(0, 7) : '';
|
|
187
|
+
}
|
|
188
|
+
if (isValidIso(key))
|
|
189
|
+
return key;
|
|
190
|
+
return isValidIsoMonth(key) ? `${key}-01` : '';
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Normalize a `min`/`max` bound to `precision`, or `''` (= unbounded) when it cannot be expressed there.
|
|
194
|
+
*
|
|
195
|
+
* ADR 0057 R4: in month precision a DAY bound's day component is deliberately ignored — `min="2026-03-15"`
|
|
196
|
+
* truncates to `2026-03`, so **March 2026 stays selectable**. The value is the month, not a day inside it,
|
|
197
|
+
* so a partially-in-range month is in range. In day precision a MONTH bound is rejected rather than
|
|
198
|
+
* guessed: widening it would mean `min` anchors to the 1st but `max` would have to anchor to the last day,
|
|
199
|
+
* and inventing that asymmetry silently is worse than treating the bound as unset.
|
|
200
|
+
*/
|
|
201
|
+
function boundAt(bound, precision) {
|
|
202
|
+
if (bound === '')
|
|
203
|
+
return '';
|
|
204
|
+
if (precision === 'month')
|
|
205
|
+
return reKey(bound, 'month');
|
|
206
|
+
return isValidIso(bound) ? bound : '';
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Inclusive range test AT `precision` — the candidate and BOTH bounds are truncated to the same
|
|
210
|
+
* precision before the lexicographic compare (ADR 0057 R4).
|
|
211
|
+
*
|
|
212
|
+
* Comparing mixed-precision keys byte-wise is the trap this exists to close: `'2026-03' < '2026-03-15'`
|
|
213
|
+
* is lexicographically true, so an un-truncated compare would silently disable a month that is in range.
|
|
214
|
+
*/
|
|
215
|
+
function isInRangeAt(key, min, max, precision) {
|
|
216
|
+
const k = reKey(key, precision);
|
|
217
|
+
if (k === '')
|
|
218
|
+
return false;
|
|
219
|
+
return isInRange(k, boundAt(min, precision), boundAt(max, precision));
|
|
220
|
+
}
|
|
221
|
+
/** Clamp `key` into `[min,max]` AT `precision` (each `''` = unbounded). Seeds the displayed period. */
|
|
222
|
+
function clampAt(key, min, max, precision) {
|
|
223
|
+
const k = reKey(key, precision);
|
|
224
|
+
if (k === '')
|
|
225
|
+
return k;
|
|
226
|
+
return clampIso(k, boundAt(min, precision), boundAt(max, precision));
|
|
227
|
+
}
|
|
228
|
+
/** Add `n` months (may be negative) to a `YYYY-MM` key. No day component, so nothing to clamp. */
|
|
229
|
+
function addMonthsToKey(key, n) {
|
|
230
|
+
const month = parseIsoMonth(key);
|
|
231
|
+
if (!month)
|
|
232
|
+
return key;
|
|
233
|
+
const total = month.y * 12 + (month.m - 1) + n;
|
|
234
|
+
const y = Math.floor(total / 12);
|
|
235
|
+
const m = (((total % 12) + 12) % 12) + 1; // 1-based, safe for negative totals
|
|
236
|
+
return toIsoMonth({ y, m });
|
|
237
|
+
}
|
|
238
|
+
/** Add `n` years to a `YYYY-MM` key. Delegates to month arithmetic. */
|
|
239
|
+
function addYearsToKey(key, n) {
|
|
240
|
+
return addMonthsToKey(key, n * 12);
|
|
241
|
+
}
|
|
242
|
+
/** First month of the calendar quarter containing `key` (the month grid's ROW start — ADR 0057 §5). */
|
|
243
|
+
function quarterStartKey(key) {
|
|
244
|
+
const month = parseIsoMonth(key);
|
|
245
|
+
if (!month)
|
|
246
|
+
return key;
|
|
247
|
+
return toIsoMonth({ y: month.y, m: Math.floor((month.m - 1) / 3) * 3 + 1 });
|
|
248
|
+
}
|
|
249
|
+
/** Last month of the calendar quarter containing `key` (the month grid's ROW end). */
|
|
250
|
+
function quarterEndKey(key) {
|
|
251
|
+
return addMonthsToKey(quarterStartKey(key), 2);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Build a month grid for year `y`: the 12 months as `YYYY-MM` keys in **4 rows × 3 columns**.
|
|
255
|
+
* Rows are quarter-aligned, which is what makes Arrow Up/Down (∓3 months) a meaningful step, and
|
|
256
|
+
* 3 wide columns fit full localized month names at the panel width the 7-column day grid already sets.
|
|
257
|
+
* Unlike `monthGrid` there is no padding — a year always holds exactly 12 months.
|
|
258
|
+
*/
|
|
259
|
+
function yearGrid(y) {
|
|
260
|
+
const rows = [];
|
|
261
|
+
for (let q = 0; q < 4; q++) {
|
|
262
|
+
const row = [];
|
|
263
|
+
for (let i = 1; i <= 3; i++)
|
|
264
|
+
row.push(toIsoMonth({ y, m: q * 3 + i }));
|
|
265
|
+
rows.push(row);
|
|
266
|
+
}
|
|
267
|
+
return rows;
|
|
268
|
+
}
|
|
269
|
+
/** Today's LOCAL calendar key at `precision` (local accessors — "today" is local-civil, ADR 0050 §3). */
|
|
270
|
+
function todayAt(precision) {
|
|
271
|
+
return reKey(today(), precision);
|
|
272
|
+
}
|
|
150
273
|
|
|
151
274
|
const NONE = { type: 'none' };
|
|
275
|
+
/** Grid width per view — also the Arrow Up/Down step (one row). */
|
|
276
|
+
const COLUMNS = { day: 7, month: 3 };
|
|
152
277
|
/**
|
|
153
278
|
* SkraCalendarKeyboard — the WAI-ARIA APG **Date Picker Dialog** grid keyboard model (2-D), confined to
|
|
154
279
|
* ONE file. The THIRD per-pattern keyboard controller and the FIRST that does NOT wrap a CDK key manager:
|
|
155
280
|
* neither `FocusKeyManager` (Menu) nor `ActiveDescendantKeyManager` (Select/Combobox) models a 2-D grid
|
|
156
|
-
* whose focusable cell set is recomputed on
|
|
281
|
+
* whose focusable cell set is recomputed on period change (ADR 0044 §7 note, 2026-06-14). Date arithmetic
|
|
157
282
|
* is delegated to `calendar-math` (the ADR 0050 single math site); this controller only maps keys to
|
|
158
283
|
* intents. Roving REAL focus (NOT `aria-activedescendant`) — the divergence from Select/Combobox.
|
|
159
284
|
*
|
|
160
|
-
*
|
|
285
|
+
* **One model, two unit sets (ADR 0057 R8).** The `day` and `month` views share every key, every intent and
|
|
286
|
+
* the whole `switch` below; they differ ONLY in what a step, a row and a page mean. That parameterization is
|
|
287
|
+
* the falsifiable claim behind treating precision as a runtime axis rather than a second component — if this
|
|
288
|
+
* ever needs a second `resolve`, the classification was wrong and the feature splits.
|
|
289
|
+
*
|
|
290
|
+
* | Key | `day` view | `month` view |
|
|
291
|
+
* | --------------------- | ----------------------------- | ----------------------------- |
|
|
292
|
+
* | Left / Right | ∓1 day | ∓1 month |
|
|
293
|
+
* | Up / Down | ∓7 days (one week row) | ∓3 months (one quarter row) |
|
|
294
|
+
* | Home / End | first / last day of the week | first / last month of the quarter |
|
|
295
|
+
* | PageUp / PageDown | ∓1 month (clamped) | ∓1 year |
|
|
296
|
+
* | Shift+PageUp / -Down | ∓1 year (clamped) | ∓10 years |
|
|
297
|
+
*
|
|
298
|
+
* Home/End stay ROW-scoped in both views — APG grid semantics, and one rule beats two.
|
|
299
|
+
*
|
|
300
|
+
* Navigation is unclamped (a user may move onto a disabled out-of-range cell — APG-discoverable); only
|
|
161
301
|
* SELECTION is range-guarded, and that guard lives in the component (one place, shared with pointer click).
|
|
162
302
|
*/
|
|
163
303
|
class SkraCalendarKeyboard {
|
|
164
304
|
resolve(event, ctx) {
|
|
165
|
-
const
|
|
305
|
+
const columns = COLUMNS[ctx.view];
|
|
166
306
|
switch (event.key) {
|
|
167
307
|
case 'ArrowLeft':
|
|
168
|
-
return this.toFocus(
|
|
308
|
+
return this.toFocus(this.step(ctx, -1));
|
|
169
309
|
case 'ArrowRight':
|
|
170
|
-
return this.toFocus(
|
|
310
|
+
return this.toFocus(this.step(ctx, 1));
|
|
171
311
|
case 'ArrowUp':
|
|
172
|
-
return this.toFocus(
|
|
312
|
+
return this.toFocus(this.step(ctx, -columns));
|
|
173
313
|
case 'ArrowDown':
|
|
174
|
-
return this.toFocus(
|
|
314
|
+
return this.toFocus(this.step(ctx, columns));
|
|
175
315
|
case 'Home':
|
|
176
|
-
return this.toFocus(
|
|
316
|
+
return this.toFocus(this.rowStart(ctx));
|
|
177
317
|
case 'End':
|
|
178
|
-
return this.toFocus(
|
|
318
|
+
return this.toFocus(this.rowEnd(ctx));
|
|
179
319
|
case 'PageUp':
|
|
180
|
-
return this.toFocus(
|
|
181
|
-
? addYearsClamped(focusedIso, -1)
|
|
182
|
-
: addMonthsClamped(focusedIso, -1));
|
|
320
|
+
return this.toFocus(this.page(ctx, event.shiftKey, -1));
|
|
183
321
|
case 'PageDown':
|
|
184
|
-
return this.toFocus(
|
|
185
|
-
? addYearsClamped(focusedIso, 1)
|
|
186
|
-
: addMonthsClamped(focusedIso, 1));
|
|
322
|
+
return this.toFocus(this.page(ctx, event.shiftKey, 1));
|
|
187
323
|
case 'Enter':
|
|
188
324
|
case ' ':
|
|
189
325
|
case 'Spacebar': // legacy key name
|
|
@@ -194,17 +330,34 @@ class SkraCalendarKeyboard {
|
|
|
194
330
|
return NONE;
|
|
195
331
|
}
|
|
196
332
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
333
|
+
/** One cell = one day / one month. */
|
|
334
|
+
step(ctx, n) {
|
|
335
|
+
return ctx.view === 'month'
|
|
336
|
+
? addMonthsToKey(ctx.focusedIso, n)
|
|
337
|
+
: addDays(ctx.focusedIso, n);
|
|
338
|
+
}
|
|
339
|
+
/** Row start: the week (day view) / the calendar quarter (month view). */
|
|
340
|
+
rowStart(ctx) {
|
|
341
|
+
return ctx.view === 'month'
|
|
342
|
+
? quarterStartKey(ctx.focusedIso)
|
|
343
|
+
: this.weekStart(ctx.focusedIso, ctx.firstDayOfWeek);
|
|
344
|
+
}
|
|
345
|
+
/** Row end: last day of the week / last month of the quarter. */
|
|
346
|
+
rowEnd(ctx) {
|
|
347
|
+
return ctx.view === 'month'
|
|
348
|
+
? quarterEndKey(ctx.focusedIso)
|
|
349
|
+
: addDays(this.weekStart(ctx.focusedIso, ctx.firstDayOfWeek), 6);
|
|
203
350
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
351
|
+
/** Page = the next container up; Shift = the one above that. Day math stays clamped (ADR 0050 §4). */
|
|
352
|
+
page(ctx, shiftKey, direction) {
|
|
353
|
+
if (ctx.view === 'month')
|
|
354
|
+
return addYearsToKey(ctx.focusedIso, direction * (shiftKey ? 10 : 1));
|
|
355
|
+
return shiftKey
|
|
356
|
+
? addYearsClamped(ctx.focusedIso, direction)
|
|
357
|
+
: addMonthsClamped(ctx.focusedIso, direction);
|
|
358
|
+
}
|
|
359
|
+
toFocus(toIso) {
|
|
360
|
+
return { type: 'focus', iso: toIso };
|
|
208
361
|
}
|
|
209
362
|
weekStart(iso, firstDayOfWeek) {
|
|
210
363
|
const p = parseIso(iso);
|
|
@@ -222,11 +375,28 @@ class SkraCalendarKeyboard {
|
|
|
222
375
|
* formatter MUST set `timeZone: 'UTC'` (else the displayed date shifts off the zoneless value).
|
|
223
376
|
*/
|
|
224
377
|
const UTC = 'UTC';
|
|
378
|
+
/**
|
|
379
|
+
* The grid, `monthGrid()` and `formatDayNumber()` are all GREGORIAN — non-Gregorian calendars re-shape the
|
|
380
|
+
* grid, not just its language, and are out of scope (ADR 0050 Q-8). Every formatter here MUST therefore
|
|
381
|
+
* pin the calendar: left to its default, `Intl` follows the LOCALE's, and the widget then labels a
|
|
382
|
+
* Gregorian grid with another calendar's months and years. Measured before this was pinned: `fa-IR`
|
|
383
|
+
* showed `۱۴۰۴` in the panel header and `۱۴۰۵` in the trigger for ONE value, and a day cell displaying
|
|
384
|
+
* `۱۴` named `خرداد ۱۴۰۵` — which is 2026-06-04, not 2026-06-14. Localizing the LANGUAGE is the job;
|
|
385
|
+
* re-basing the CALENDAR is a different feature (ADR 0050 §6 adapter seam).
|
|
386
|
+
*/
|
|
387
|
+
const GREGORY = 'gregory';
|
|
388
|
+
/** Zero-pad a year / month for building the ISO key a formatter is fed. No locale involved. */
|
|
389
|
+
function pad4(n) {
|
|
390
|
+
return String(n).padStart(4, '0');
|
|
391
|
+
}
|
|
392
|
+
function pad2(n) {
|
|
393
|
+
return String(n).padStart(2, '0');
|
|
394
|
+
}
|
|
225
395
|
/** Resolve the host's default BCP-47 locale tag, SSR-safe (no `navigator` in Node). */
|
|
226
396
|
function hostLocale() {
|
|
227
397
|
return typeof navigator !== 'undefined' ? navigator.language : undefined;
|
|
228
398
|
}
|
|
229
|
-
/** Trigger display of the selected date (`dateStyle: 'medium'`). `''`/invalid → `''`. */
|
|
399
|
+
/** Trigger display of the selected date, `day` precision (`dateStyle: 'medium'`). `''`/invalid → `''`. */
|
|
230
400
|
function formatValue(iso, locale) {
|
|
231
401
|
const dt = toUtcDate(iso);
|
|
232
402
|
if (!dt)
|
|
@@ -234,27 +404,93 @@ function formatValue(iso, locale) {
|
|
|
234
404
|
return new Intl.DateTimeFormat(locale, {
|
|
235
405
|
dateStyle: 'medium',
|
|
236
406
|
timeZone: UTC,
|
|
407
|
+
calendar: GREGORY,
|
|
237
408
|
}).format(dt);
|
|
238
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Trigger display of the selected month, `month` precision (ADR 0045 — the trigger shows the VALUE).
|
|
412
|
+
* Takes a `YYYY-MM` key; `''`/invalid → `''`.
|
|
413
|
+
*/
|
|
414
|
+
function formatMonthValue(key, locale) {
|
|
415
|
+
const match = /^(\d{4})-(\d{2})$/.exec(key);
|
|
416
|
+
if (!match)
|
|
417
|
+
return '';
|
|
418
|
+
return formatMonthYear(Number(match[1]), Number(match[2]), locale);
|
|
419
|
+
}
|
|
239
420
|
/** Header / nav-button label for a month: localized `month long + year` (e.g. "czerwiec 2026"). */
|
|
240
421
|
function formatMonthYear(y, m, locale) {
|
|
241
|
-
const dt = toUtcDate(`${
|
|
422
|
+
const dt = toUtcDate(`${pad4(y)}-${pad2(m)}-01`);
|
|
242
423
|
if (!dt)
|
|
243
424
|
return '';
|
|
244
425
|
return new Intl.DateTimeFormat(locale, {
|
|
245
426
|
month: 'long',
|
|
246
427
|
year: 'numeric',
|
|
247
428
|
timeZone: UTC,
|
|
429
|
+
calendar: GREGORY,
|
|
248
430
|
}).format(dt);
|
|
249
431
|
}
|
|
250
|
-
/**
|
|
251
|
-
|
|
432
|
+
/**
|
|
433
|
+
* The day cell's `[skra-sr-only]` accessible-name **extension** — the localized month and year, with the
|
|
434
|
+
* day number REMOVED (ADR 0057 R6).
|
|
435
|
+
*
|
|
436
|
+
* The visible text of the cell is already the day number, and `[skra-sr-only]` keeps its content in
|
|
437
|
+
* `textContent` (that is the whole point of the clip technique), so an extension formatted with
|
|
438
|
+
* `dateStyle: 'long'` restates it: the name computes to `1414 czerwca 2026` in `pl-PL` and
|
|
439
|
+
* `14June 14, 2026` in `en-US`. Path 2 extends the visible text; it never repeats it.
|
|
440
|
+
*
|
|
441
|
+
* The day part is dropped via `formatToParts` so the remaining parts keep the LOCALE's own order, together
|
|
442
|
+
* with the literal that separates it from its neighbour (the following one, or the preceding one when the
|
|
443
|
+
* day is last) so no stray `, ` or `月` is left behind. Accepted trade-off (ADR 0057 R6): in year-first
|
|
444
|
+
* locales the assembled name reads `14 2026年6月` rather than `2026年6月14日` — the visible text is
|
|
445
|
+
* positionally first in the DOM and WCAG 2.5.3 requires it to be contained in the name, so perfect
|
|
446
|
+
* grammatical order is not reachable without reordering the DOM. Containment and non-duplication are the
|
|
447
|
+
* properties that matter.
|
|
448
|
+
*/
|
|
449
|
+
function formatDayNameExtension(iso, locale) {
|
|
252
450
|
const dt = toUtcDate(iso);
|
|
253
451
|
if (!dt)
|
|
254
452
|
return '';
|
|
255
|
-
|
|
453
|
+
const parts = new Intl.DateTimeFormat(locale, {
|
|
256
454
|
dateStyle: 'long',
|
|
257
455
|
timeZone: UTC,
|
|
456
|
+
calendar: GREGORY,
|
|
457
|
+
}).formatToParts(dt);
|
|
458
|
+
const dayIndex = parts.findIndex((part) => part.type === 'day');
|
|
459
|
+
if (dayIndex === -1)
|
|
460
|
+
return parts
|
|
461
|
+
.map((part) => part.value)
|
|
462
|
+
.join('')
|
|
463
|
+
.trim();
|
|
464
|
+
const separatorIndex = parts[dayIndex + 1]?.type === 'literal' ? dayIndex + 1 : dayIndex - 1;
|
|
465
|
+
return parts
|
|
466
|
+
.filter((_, index) => index !== dayIndex && index !== separatorIndex)
|
|
467
|
+
.map((part) => part.value)
|
|
468
|
+
.join('')
|
|
469
|
+
.trim();
|
|
470
|
+
}
|
|
471
|
+
/** Visible text of a month cell: the localized full month name (e.g. "czerwiec"), no year. */
|
|
472
|
+
function formatMonthCell(y, m, locale) {
|
|
473
|
+
const dt = toUtcDate(`${pad4(y)}-${pad2(m)}-01`);
|
|
474
|
+
if (!dt)
|
|
475
|
+
return '';
|
|
476
|
+
return new Intl.DateTimeFormat(locale, {
|
|
477
|
+
month: 'long',
|
|
478
|
+
timeZone: UTC,
|
|
479
|
+
calendar: GREGORY,
|
|
480
|
+
}).format(dt);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* The month cell's `[skra-sr-only]` extension — the localized year (ADR 0057 R6). The visible text is
|
|
484
|
+
* already the month name, so the extension carries the year and nothing else.
|
|
485
|
+
*/
|
|
486
|
+
function formatYear(y, locale) {
|
|
487
|
+
const dt = toUtcDate(`${pad4(y)}-01-01`);
|
|
488
|
+
if (!dt)
|
|
489
|
+
return '';
|
|
490
|
+
return new Intl.DateTimeFormat(locale, {
|
|
491
|
+
year: 'numeric',
|
|
492
|
+
timeZone: UTC,
|
|
493
|
+
calendar: GREGORY,
|
|
258
494
|
}).format(dt);
|
|
259
495
|
}
|
|
260
496
|
/** Visible day number, localized digits (e.g. Arabic-Indic `٥`) — never a raw `toString()`. */
|
|
@@ -269,10 +505,12 @@ function weekdayHeaders(firstDayOfWeek, locale) {
|
|
|
269
505
|
const shortFmt = new Intl.DateTimeFormat(locale, {
|
|
270
506
|
weekday: 'short',
|
|
271
507
|
timeZone: UTC,
|
|
508
|
+
calendar: GREGORY,
|
|
272
509
|
});
|
|
273
510
|
const longFmt = new Intl.DateTimeFormat(locale, {
|
|
274
511
|
weekday: 'long',
|
|
275
512
|
timeZone: UTC,
|
|
513
|
+
calendar: GREGORY,
|
|
276
514
|
});
|
|
277
515
|
const headers = [];
|
|
278
516
|
for (let i = 0; i < 7; i++) {
|
|
@@ -310,26 +548,44 @@ function resolveFirstDayOfWeek(override, locale) {
|
|
|
310
548
|
|
|
311
549
|
let nextDatePickerId = 0;
|
|
312
550
|
/**
|
|
313
|
-
* SkraDatePicker — pick a
|
|
314
|
-
* Second Tier-3 component. A value-display **trigger** (`<button>`) opens a non-modal
|
|
315
|
-
* panel** (Popover API, `popover="auto"`, ADR 0044). The value is an **ISO 8601
|
|
316
|
-
* (`YYYY-MM-DD`), consumer-owned (ADR 0050/037 — NO
|
|
317
|
-
* UTC-calculator discipline in `calendar-math` (no date
|
|
318
|
-
* first-day-of-week, digits) is `Intl`-derived in
|
|
319
|
-
* hand-rolled `SkraCalendarKeyboard` (ADR 0044 §7 — roving
|
|
551
|
+
* SkraDatePicker — pick a **calendar date** or a **calendar month** from an APG Date Picker grid in the
|
|
552
|
+
* native top layer. Second Tier-3 component. A value-display **trigger** (`<button>`) opens a non-modal
|
|
553
|
+
* top-layer **calendar panel** (Popover API, `popover="auto"`, ADR 0044). The value is an **ISO 8601 string**
|
|
554
|
+
* truncated to the active `precision` (`YYYY-MM-DD` / `YYYY-MM`), consumer-owned (ADR 0050/0057/037 — NO
|
|
555
|
+
* `@angular/forms`); all date math is the zoneless UTC-calculator discipline in `calendar-math` (no date
|
|
556
|
+
* library). Localization (month/weekday names, first-day-of-week, digits) is `Intl`-derived in
|
|
557
|
+
* `calendar-intl`; the 2-D grid keyboard is the hand-rolled `SkraCalendarKeyboard` (ADR 0044 §7 — roving
|
|
558
|
+
* REAL focus, not aria-activedescendant).
|
|
559
|
+
*
|
|
560
|
+
* **Precision is a runtime axis, not a second component (ADR 0057).** `precision="month"` swaps the 7×N day
|
|
561
|
+
* grid for a 3×4 quarter-aligned month grid inside the SAME trigger, panel, positioner, field DI wiring,
|
|
562
|
+
* dismissal model, selection model and keyboard controller. The value channel does not widen — `YYYY-MM` is
|
|
563
|
+
* a truncation of `YYYY-MM-DD`, both `string` — which is exactly why this is an input and cardinality
|
|
564
|
+
* (ADR 0046) is not. `week` / `time` / `datetime-local` are excluded by rule (ADR 0057 R5): they need a
|
|
565
|
+
* second calendar coordinate system or are not grids at all, and stay the native `skraInput` path.
|
|
320
566
|
*
|
|
321
|
-
* No projected content: the
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
567
|
+
* No projected content: the cells are GENERATED (the structural divergence from select/combobox — there is
|
|
568
|
+
* no `skra-option` reuse). The only focusable element in the open panel is the roving cell; the period-nav
|
|
569
|
+
* buttons are pointer affordances (`tabindex="-1"`, driven by PageUp/PageDown on keyboard), and Tab
|
|
570
|
+
* dismisses — so no focus-containment machinery is needed (spec Q-1).
|
|
325
571
|
*
|
|
326
572
|
* Full contract: adr/specs/component-datepicker-spec.md.
|
|
327
573
|
*/
|
|
328
574
|
class SkraDatePicker {
|
|
329
|
-
/**
|
|
575
|
+
/**
|
|
576
|
+
* Consumer-owned value: an ISO 8601 key truncated to `precision` — `YYYY-MM-DD` (`day`) or `YYYY-MM`
|
|
577
|
+
* (`month`); `''` = unset. NEVER a Date (ADR 0050/0057/037).
|
|
578
|
+
*/
|
|
330
579
|
value = input('', /* @ts-ignore */
|
|
331
580
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
332
|
-
/**
|
|
581
|
+
/**
|
|
582
|
+
* Value granularity (ADR 0057 R1). `'day'` (default) emits `YYYY-MM-DD` over a 7×N day grid; `'month'`
|
|
583
|
+
* emits `YYYY-MM` over a 3×4 quarter-aligned month grid. NOT a look input, NOT a cardinality flag, and
|
|
584
|
+
* NOT reflected to a `data-skra-*` attribute — precision is configuration, not state.
|
|
585
|
+
*/
|
|
586
|
+
precision = input('day', /* @ts-ignore */
|
|
587
|
+
...(ngDevMode ? [{ debugName: "precision" }] : /* istanbul ignore next */ []));
|
|
588
|
+
/** Inclusive lower bound (ISO); `''` = unbounded (ADR 0050 §2, truncated to `precision` per ADR 0057 R4). */
|
|
333
589
|
min = input('', /* @ts-ignore */
|
|
334
590
|
...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
|
|
335
591
|
/** Inclusive upper bound (ISO); `''` = unbounded. */
|
|
@@ -350,7 +606,7 @@ class SkraDatePicker {
|
|
|
350
606
|
/** Standalone availability source — `SkraFieldContext` wins when present. */
|
|
351
607
|
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
352
608
|
readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
353
|
-
/** Fires ONLY on user
|
|
609
|
+
/** Fires ONLY on user selection — never on a programmatic `value()`/`precision()` change (ADR 037/0057 R3). */
|
|
354
610
|
valueChange = output();
|
|
355
611
|
triggerRef = viewChild('trigger', /* @ts-ignore */
|
|
356
612
|
...(ngDevMode ? [{ debugName: "triggerRef" }] : /* istanbul ignore next */ []));
|
|
@@ -360,14 +616,35 @@ class SkraDatePicker {
|
|
|
360
616
|
injector = inject(Injector);
|
|
361
617
|
keyboard = new SkraCalendarKeyboard();
|
|
362
618
|
panelId = `skra-datepicker-${nextDatePickerId}`;
|
|
363
|
-
|
|
619
|
+
periodId = `skra-datepicker-period-${nextDatePickerId++}`;
|
|
364
620
|
isOpen = signal(false, /* @ts-ignore */
|
|
365
621
|
...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
|
|
622
|
+
/**
|
|
623
|
+
* Which grid is on screen. Seeded from `precision` and writable in between (ADR 0057 R9) so the deferred
|
|
624
|
+
* month/year quick-pick — stepping UP from the day grid to pick a month, then back down — is an additive
|
|
625
|
+
* change rather than a rewrite. In MVP nothing writes it, so `view() === precision()` always.
|
|
626
|
+
*
|
|
627
|
+
* NOTE for whoever lands quick-pick: it adds a second focusable element to the open panel and therefore
|
|
628
|
+
* RE-OPENS spec Q-1, which was resolved on the premise that the panel holds exactly one focusable stop
|
|
629
|
+
* and Tab dismisses. Re-resolve Q-1 first.
|
|
630
|
+
*/
|
|
631
|
+
view = linkedSignal(() => this.precision(), /* @ts-ignore */
|
|
632
|
+
...(ngDevMode ? [{ debugName: "view" }] : /* istanbul ignore next */ []));
|
|
366
633
|
/** Today's LOCAL date (ADR 0050 §3) — stable for the session. */
|
|
367
634
|
todayIso = today();
|
|
368
|
-
/** The roving-focus
|
|
369
|
-
|
|
370
|
-
...(ngDevMode ? [{ debugName: "
|
|
635
|
+
/** The roving-focus cell; SINGLE source of truth for what the grid shows AND which cell is focusable. */
|
|
636
|
+
focusedKey = signal(this.todayIso, /* @ts-ignore */
|
|
637
|
+
...(ngDevMode ? [{ debugName: "focusedKey" }] : /* istanbul ignore next */ []));
|
|
638
|
+
/**
|
|
639
|
+
* The focused key re-keyed to the active view. A `precision` flip while the panel is closed leaves a key
|
|
640
|
+
* at the previous granularity behind; re-keying defensively keeps every downstream `computed` total
|
|
641
|
+
* without an effect or a reset cascade. `onOpen` re-seeds properly from the value.
|
|
642
|
+
*/
|
|
643
|
+
focusedAt = computed(() => {
|
|
644
|
+
const view = this.view();
|
|
645
|
+
return reKey(this.focusedKey(), view) || todayAt(view);
|
|
646
|
+
}, /* @ts-ignore */
|
|
647
|
+
...(ngDevMode ? [{ debugName: "focusedAt" }] : /* istanbul ignore next */ []));
|
|
371
648
|
positioner;
|
|
372
649
|
/** Set when a close should return focus to the trigger (keyboard/select), not on outside-click. */
|
|
373
650
|
returnFocus = false;
|
|
@@ -385,76 +662,122 @@ class SkraDatePicker {
|
|
|
385
662
|
describedBy = computed(() => this.context?.describedByIds() ?? null, /* @ts-ignore */
|
|
386
663
|
...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
|
|
387
664
|
// ── Derived calendar state ──
|
|
388
|
-
/**
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
665
|
+
/**
|
|
666
|
+
* The committed value, defensively normalized: wrong-format-for-precision, invalid or out-of-range → `''`
|
|
667
|
+
* for display (ADR 0050 §1 as amended by ADR 0057 R3). Keyed on `precision` (what the value IS), not on
|
|
668
|
+
* `view` (what is on screen) — the two only diverge once quick-pick lands.
|
|
669
|
+
*/
|
|
670
|
+
selectedKey = computed(() => {
|
|
671
|
+
const value = this.value();
|
|
672
|
+
const precision = this.precision();
|
|
673
|
+
return isValidAt(value, precision) &&
|
|
674
|
+
isInRangeAt(value, this.min(), this.max(), precision)
|
|
675
|
+
? value
|
|
676
|
+
: '';
|
|
392
677
|
}, /* @ts-ignore */
|
|
393
|
-
...(ngDevMode ? [{ debugName: "
|
|
394
|
-
formattedValue = computed(() =>
|
|
678
|
+
...(ngDevMode ? [{ debugName: "selectedKey" }] : /* istanbul ignore next */ []));
|
|
679
|
+
formattedValue = computed(() => this.precision() === 'month'
|
|
680
|
+
? formatMonthValue(this.selectedKey(), this.locale())
|
|
681
|
+
: formatValue(this.selectedKey(), this.locale()), /* @ts-ignore */
|
|
395
682
|
...(ngDevMode ? [{ debugName: "formattedValue" }] : /* istanbul ignore next */ []));
|
|
396
683
|
firstDow = computed(() => resolveFirstDayOfWeek(this.firstDayOfWeek(), this.locale()), /* @ts-ignore */
|
|
397
684
|
...(ngDevMode ? [{ debugName: "firstDow" }] : /* istanbul ignore next */ []));
|
|
398
|
-
/**
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
685
|
+
/**
|
|
686
|
+
* The container the grid shows — the focused cell's MONTH in day view, its YEAR in month view (`m` is
|
|
687
|
+
* then meaningless). Always derived from the focused cell, which keeps the roving tabindex valid.
|
|
688
|
+
*/
|
|
689
|
+
displayed = computed(() => {
|
|
690
|
+
const key = this.focusedAt();
|
|
691
|
+
if (this.view() === 'month') {
|
|
692
|
+
const month = parseIsoMonth(key);
|
|
693
|
+
return { y: month?.y ?? parseIso(this.todayIso).y, m: 1 };
|
|
694
|
+
}
|
|
695
|
+
const date = parseIso(key) ?? parseIso(this.todayIso);
|
|
696
|
+
return { y: date.y, m: date.m };
|
|
402
697
|
}, /* @ts-ignore */
|
|
403
|
-
...(ngDevMode ? [{ debugName: "
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
698
|
+
...(ngDevMode ? [{ debugName: "displayed" }] : /* istanbul ignore next */ []));
|
|
699
|
+
periodLabel = computed(() => {
|
|
700
|
+
const { y, m } = this.displayed();
|
|
701
|
+
return this.view() === 'month'
|
|
702
|
+
? formatYear(y, this.locale())
|
|
703
|
+
: formatMonthYear(y, m, this.locale());
|
|
704
|
+
}, /* @ts-ignore */
|
|
705
|
+
...(ngDevMode ? [{ debugName: "periodLabel" }] : /* istanbul ignore next */ []));
|
|
706
|
+
prevPeriodLabel = computed(() => this.periodOffsetLabel(-1), /* @ts-ignore */
|
|
707
|
+
...(ngDevMode ? [{ debugName: "prevPeriodLabel" }] : /* istanbul ignore next */ []));
|
|
708
|
+
nextPeriodLabel = computed(() => this.periodOffsetLabel(1), /* @ts-ignore */
|
|
709
|
+
...(ngDevMode ? [{ debugName: "nextPeriodLabel" }] : /* istanbul ignore next */ []));
|
|
710
|
+
/**
|
|
711
|
+
* `aria-current` token for the cell holding today. ARIA 1.2 has no `month` value, and `date` is
|
|
712
|
+
* defined as the current DATE within a collection of dates — accurate for a day cell, a stretch for a
|
|
713
|
+
* month cell, where the generic `true` ("current item within a set") is the honest mapping.
|
|
714
|
+
*/
|
|
715
|
+
currentToken = computed(() => this.view() === 'month' ? 'true' : 'date', /* @ts-ignore */
|
|
716
|
+
...(ngDevMode ? [{ debugName: "currentToken" }] : /* istanbul ignore next */ []));
|
|
410
717
|
weekdayHeadersView = computed(() => weekdayHeaders(this.firstDow(), this.locale()), /* @ts-ignore */
|
|
411
718
|
...(ngDevMode ? [{ debugName: "weekdayHeadersView" }] : /* istanbul ignore next */ []));
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const sel = this.selectedIso();
|
|
416
|
-
const foc = this.focusedIso();
|
|
417
|
-
const min = this.min();
|
|
418
|
-
const max = this.max();
|
|
419
|
-
return monthGrid(y, m, this.firstDow()).map((week, wi) => ({
|
|
420
|
-
key: wi,
|
|
421
|
-
days: week.map((iso, di) => iso === null
|
|
422
|
-
? this.emptyCell(`${wi}-${di}`)
|
|
423
|
-
: {
|
|
424
|
-
key: iso,
|
|
425
|
-
iso,
|
|
426
|
-
dayNum: formatDayNumber(parseIso(iso).d, loc),
|
|
427
|
-
srName: formatDayName(iso, loc),
|
|
428
|
-
today: iso === this.todayIso,
|
|
429
|
-
selected: iso === sel,
|
|
430
|
-
disabled: !isInRange(iso, min, max),
|
|
431
|
-
focused: iso === foc,
|
|
432
|
-
}),
|
|
433
|
-
}));
|
|
434
|
-
}, /* @ts-ignore */
|
|
435
|
-
...(ngDevMode ? [{ debugName: "weeks" }] : /* istanbul ignore next */ []));
|
|
719
|
+
/** ONE grid renderer, parameterized by view — the ADR 0057 R8 claim, made structural. */
|
|
720
|
+
rows = computed(() => this.view() === 'month' ? this.buildMonthRows() : this.buildDayRows(), /* @ts-ignore */
|
|
721
|
+
...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
436
722
|
constructor() {
|
|
437
723
|
inject(DestroyRef).onDestroy(() => this.positioner?.destroy());
|
|
724
|
+
// A `precision` flip while the panel is OPEN re-stamps the whole grid: cells are tracked by their
|
|
725
|
+
// ISO key, and a day key never matches a month key, so `@for` destroys and rebuilds every cell
|
|
726
|
+
// button and DOM focus falls back to <body>. The roving tabindex survives, but nothing is focused,
|
|
727
|
+
// so the arrow keys would be dead until the user clicked. Re-seed and re-focus. (Found in review.)
|
|
728
|
+
effect(() => {
|
|
729
|
+
this.view(); // the only tracked read — this effect exists to react to a view swap
|
|
730
|
+
if (!untracked(() => this.isOpen()))
|
|
731
|
+
return;
|
|
732
|
+
untracked(() => {
|
|
733
|
+
this.focusedKey.set(this.seedFocus());
|
|
734
|
+
this.scheduleFocus();
|
|
735
|
+
});
|
|
736
|
+
});
|
|
438
737
|
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
|
439
|
-
// Dev-only sanity warnings (stripped in prod) — never thrown (ADR 0050 §1).
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
this.max() !== '' &&
|
|
446
|
-
compareIso(this.min(), this.max()) > 0)
|
|
447
|
-
return `skra-datepicker: min "${this.min()}" is after max "${this.max()}" — every day is disabled.`;
|
|
448
|
-
return null;
|
|
449
|
-
}, /* @ts-ignore */
|
|
450
|
-
...(ngDevMode ? [{ debugName: "warn" }] : /* istanbul ignore next */ []));
|
|
451
|
-
afterNextRender(() => {
|
|
452
|
-
const message = warn();
|
|
738
|
+
// Dev-only sanity warnings (stripped in prod) — never thrown (ADR 0050 §1). REACTIVE on purpose:
|
|
739
|
+
// ADR 0057 R3 contemplates a runtime `precision` flip, and a one-shot first-render check would go
|
|
740
|
+
// silent for exactly the mismatch that flip creates. (The `afterNextRender` version this replaces
|
|
741
|
+
// read a `computed` once and was reactive in appearance only — found in review.)
|
|
742
|
+
effect(() => {
|
|
743
|
+
const message = this.devWarning();
|
|
453
744
|
if (message)
|
|
454
745
|
console.warn(message);
|
|
455
|
-
}
|
|
746
|
+
});
|
|
456
747
|
}
|
|
457
748
|
}
|
|
749
|
+
/**
|
|
750
|
+
* The dev-mode diagnostic, or `null` when the inputs are coherent. Split out of the constructor so it
|
|
751
|
+
* is a plain reactive expression rather than a closure buried in a lifecycle hook.
|
|
752
|
+
*/
|
|
753
|
+
devWarning = computed(() => {
|
|
754
|
+
const value = this.value();
|
|
755
|
+
const precision = this.precision();
|
|
756
|
+
const rawMin = this.min();
|
|
757
|
+
const rawMax = this.max();
|
|
758
|
+
if (value !== '' && !isValidAt(value, precision))
|
|
759
|
+
return `skra-datepicker: value "${value}" is not a valid ${precision === 'month' ? 'YYYY-MM month' : 'YYYY-MM-DD date'} key for precision="${precision}"; shown as unset.`;
|
|
760
|
+
const min = boundAt(rawMin, precision);
|
|
761
|
+
const max = boundAt(rawMax, precision);
|
|
762
|
+
if (min !== '' && max !== '' && compareIso(min, max) > 0)
|
|
763
|
+
return `skra-datepicker: min "${rawMin}" is after max "${rawMax}" — every cell is disabled.`;
|
|
764
|
+
// A bound that cannot be expressed at the active precision is NOT an error, but it is never what
|
|
765
|
+
// the author meant, and silence is the failure mode: in month precision a day bound is truncated
|
|
766
|
+
// (ADR 0057 R4), and in day precision a month bound is dropped to unbounded rather than guessed.
|
|
767
|
+
// Report BOTH bounds — reporting only `min` hid the `max` case.
|
|
768
|
+
const ignored = [
|
|
769
|
+
['min', rawMin],
|
|
770
|
+
['max', rawMax],
|
|
771
|
+
].filter(([, raw]) => raw !== '' && boundAt(raw, precision) !== raw);
|
|
772
|
+
if (ignored.length > 0)
|
|
773
|
+
return ignored
|
|
774
|
+
.map(([name, raw]) => precision === 'month'
|
|
775
|
+
? `skra-datepicker: ${name}="${raw}" carries a day component that precision="month" ignores; compared as "${raw.slice(0, 7)}".`
|
|
776
|
+
: `skra-datepicker: ${name}="${raw}" is not a valid YYYY-MM-DD key for precision="day"; treated as unbounded. A month bound is NOT widened — "min" would anchor to the 1st but "max" would have to anchor to the last day, and inventing that asymmetry silently is worse.`)
|
|
777
|
+
.join(' ');
|
|
778
|
+
return null;
|
|
779
|
+
}, /* @ts-ignore */
|
|
780
|
+
...(ngDevMode ? [{ debugName: "devWarning" }] : /* istanbul ignore next */ []));
|
|
458
781
|
// ── Trigger interaction ──
|
|
459
782
|
onTriggerClick(event) {
|
|
460
783
|
if (this.isReadonly())
|
|
@@ -478,18 +801,19 @@ class SkraDatePicker {
|
|
|
478
801
|
return;
|
|
479
802
|
}
|
|
480
803
|
const intent = this.keyboard.resolve(event, {
|
|
481
|
-
focusedIso: this.
|
|
804
|
+
focusedIso: this.focusedAt(),
|
|
482
805
|
firstDayOfWeek: this.firstDow(),
|
|
806
|
+
view: this.view(),
|
|
483
807
|
});
|
|
484
808
|
switch (intent.type) {
|
|
485
809
|
case 'focus':
|
|
486
810
|
event.preventDefault();
|
|
487
|
-
this.
|
|
811
|
+
this.focusedKey.set(intent.iso);
|
|
488
812
|
this.scheduleFocus(); // re-focus after the grid re-stamps
|
|
489
813
|
break;
|
|
490
814
|
case 'select':
|
|
491
815
|
event.preventDefault();
|
|
492
|
-
this.
|
|
816
|
+
this.selectCell(this.focusedAt());
|
|
493
817
|
break;
|
|
494
818
|
case 'close':
|
|
495
819
|
this.returnFocus = true;
|
|
@@ -499,19 +823,39 @@ class SkraDatePicker {
|
|
|
499
823
|
break;
|
|
500
824
|
}
|
|
501
825
|
}
|
|
502
|
-
// ──
|
|
503
|
-
|
|
504
|
-
this.
|
|
826
|
+
// ── Period navigation (pointer; focus stays on the nav button) ──
|
|
827
|
+
goToPreviousPeriod() {
|
|
828
|
+
this.stepPeriod(-1);
|
|
829
|
+
}
|
|
830
|
+
goToNextPeriod() {
|
|
831
|
+
this.stepPeriod(1);
|
|
505
832
|
}
|
|
506
|
-
|
|
507
|
-
|
|
833
|
+
/** One period = one month (day view) / one year (month view). */
|
|
834
|
+
stepPeriod(offset) {
|
|
835
|
+
const key = this.focusedAt();
|
|
836
|
+
this.focusedKey.set(this.view() === 'month'
|
|
837
|
+
? addYearsToKey(key, offset)
|
|
838
|
+
: addMonthsClamped(key, offset));
|
|
508
839
|
}
|
|
509
840
|
// ── Selection ──
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
841
|
+
selectCell(iso) {
|
|
842
|
+
const precision = this.precision();
|
|
843
|
+
if (this.view() !== precision) {
|
|
844
|
+
// Reserved for the ADR 0057 R9 quick-pick. The moment a NAVIGATION view can differ from the
|
|
845
|
+
// terminal precision, a cell click in it is a step DOWN, not a commit — emitting here would
|
|
846
|
+
// silently write `2026-06-01` for a click on "June" under `precision="day"`. No such view exists
|
|
847
|
+
// yet, so this is a hard stop, not a behaviour: the step-down path must be filled in before a
|
|
848
|
+
// navigation view can ship. Cheap to add now, invisible to fix later. (Raised in review.)
|
|
849
|
+
if (typeof ngDevMode !== 'undefined' && ngDevMode)
|
|
850
|
+
console.warn(`skra-datepicker: a cell was activated in the "${this.view()}" view while precision is ` +
|
|
851
|
+
`"${precision}". Nothing was emitted — a navigation view needs the ADR 0057 R9 step-down path.`);
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
// Guard out-of-range in ONE place (keyboard + pointer), at the precision the value is emitted in.
|
|
855
|
+
if (!isInRangeAt(iso, this.min(), this.max(), precision))
|
|
856
|
+
return;
|
|
513
857
|
this.returnFocus = true;
|
|
514
|
-
this.valueChange.emit(iso);
|
|
858
|
+
this.valueChange.emit(reKey(iso, precision));
|
|
515
859
|
this.close();
|
|
516
860
|
}
|
|
517
861
|
// ── Panel open / close (native toggle from the Popover API) ──
|
|
@@ -524,7 +868,7 @@ class SkraDatePicker {
|
|
|
524
868
|
this.onClose();
|
|
525
869
|
}
|
|
526
870
|
onOpen() {
|
|
527
|
-
this.
|
|
871
|
+
this.focusedKey.set(this.seedFocus());
|
|
528
872
|
const anchor = this.triggerRef()?.nativeElement;
|
|
529
873
|
const panel = this.panelRef()?.nativeElement;
|
|
530
874
|
if (anchor && panel) {
|
|
@@ -551,23 +895,82 @@ class SkraDatePicker {
|
|
|
551
895
|
close() {
|
|
552
896
|
this.panelRef()?.nativeElement.hidePopover?.();
|
|
553
897
|
}
|
|
554
|
-
/** Seed the focused
|
|
898
|
+
/** Seed the focused cell on open: the valid current value, else today, clamped into `[min,max]`. */
|
|
555
899
|
seedFocus() {
|
|
556
|
-
const
|
|
557
|
-
const
|
|
558
|
-
|
|
900
|
+
const value = this.value();
|
|
901
|
+
const precision = this.precision();
|
|
902
|
+
const base = isValidAt(value, precision) ? value : todayAt(precision);
|
|
903
|
+
return clampAt(base, this.min(), this.max(), precision);
|
|
559
904
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
const
|
|
563
|
-
|
|
905
|
+
// ── Grid builders ──
|
|
906
|
+
buildDayRows() {
|
|
907
|
+
const { y, m } = this.displayed();
|
|
908
|
+
const locale = this.locale();
|
|
909
|
+
const selected = reKey(this.selectedKey(), 'day');
|
|
910
|
+
const focused = this.focusedAt();
|
|
911
|
+
const min = this.min();
|
|
912
|
+
const max = this.max();
|
|
913
|
+
return monthGrid(y, m, this.firstDow()).map((week, rowIndex) => ({
|
|
914
|
+
key: rowIndex,
|
|
915
|
+
cells: week.map((iso, columnIndex) => iso === null
|
|
916
|
+
? this.emptyCell(`${rowIndex}-${columnIndex}`)
|
|
917
|
+
: {
|
|
918
|
+
key: iso,
|
|
919
|
+
iso,
|
|
920
|
+
label: formatDayNumber(parseIso(iso).d, locale),
|
|
921
|
+
srExt: this.srExtension(formatDayNameExtension(iso, locale)),
|
|
922
|
+
today: iso === this.todayIso,
|
|
923
|
+
selected: iso === selected,
|
|
924
|
+
disabled: !isInRangeAt(iso, min, max, 'day'),
|
|
925
|
+
focused: iso === focused,
|
|
926
|
+
}),
|
|
927
|
+
}));
|
|
928
|
+
}
|
|
929
|
+
buildMonthRows() {
|
|
930
|
+
const { y } = this.displayed();
|
|
931
|
+
const locale = this.locale();
|
|
932
|
+
const selected = reKey(this.selectedKey(), 'month');
|
|
933
|
+
const focused = this.focusedAt();
|
|
934
|
+
const todayKey = todayAt('month');
|
|
935
|
+
const min = this.min();
|
|
936
|
+
const max = this.max();
|
|
937
|
+
const yearLabel = this.srExtension(formatYear(y, locale));
|
|
938
|
+
return yearGrid(y).map((quarter, rowIndex) => ({
|
|
939
|
+
key: rowIndex,
|
|
940
|
+
cells: quarter.map((key) => ({
|
|
941
|
+
key,
|
|
942
|
+
iso: key,
|
|
943
|
+
label: formatMonthCell(y, parseIsoMonth(key).m, locale),
|
|
944
|
+
srExt: yearLabel,
|
|
945
|
+
today: key === todayKey,
|
|
946
|
+
selected: key === selected,
|
|
947
|
+
disabled: !isInRangeAt(key, min, max, 'month'),
|
|
948
|
+
focused: key === focused,
|
|
949
|
+
})),
|
|
950
|
+
}));
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* The accessible-name separator is baked into the extension rather than left to the template or to the
|
|
954
|
+
* engine: `[skra-sr-only]` is absolutely positioned, so whether the name computation inserts a space
|
|
955
|
+
* between the visible text and the extension is engine-dependent. One explicit space, asserted by test.
|
|
956
|
+
*/
|
|
957
|
+
srExtension(text) {
|
|
958
|
+
return text === '' ? '' : ` ${text}`;
|
|
959
|
+
}
|
|
960
|
+
periodOffsetLabel(offset) {
|
|
961
|
+
const { y, m } = this.displayed();
|
|
962
|
+
const locale = this.locale();
|
|
963
|
+
if (this.view() === 'month')
|
|
964
|
+
return formatYear(y + offset, locale);
|
|
965
|
+
const ref = parseIso(addMonthsClamped(toIso({ y, m, d: 1 }), offset));
|
|
966
|
+
return ref ? formatMonthYear(ref.y, ref.m, locale) : '';
|
|
564
967
|
}
|
|
565
968
|
emptyCell(key) {
|
|
566
969
|
return {
|
|
567
970
|
key,
|
|
568
971
|
iso: null,
|
|
569
|
-
|
|
570
|
-
|
|
972
|
+
label: '',
|
|
973
|
+
srExt: '',
|
|
571
974
|
today: false,
|
|
572
975
|
selected: false,
|
|
573
976
|
disabled: true,
|
|
@@ -575,11 +978,11 @@ class SkraDatePicker {
|
|
|
575
978
|
};
|
|
576
979
|
}
|
|
577
980
|
scheduleFocus() {
|
|
578
|
-
afterNextRender(() => this.
|
|
981
|
+
afterNextRender(() => this.focusActiveCell(), { injector: this.injector });
|
|
579
982
|
}
|
|
580
|
-
|
|
983
|
+
focusActiveCell() {
|
|
581
984
|
this.panelRef()
|
|
582
|
-
?.nativeElement.querySelector('.skra-datepicker-
|
|
985
|
+
?.nativeElement.querySelector('.skra-datepicker-cell[tabindex="0"]')
|
|
583
986
|
?.focus();
|
|
584
987
|
}
|
|
585
988
|
/** Read a length comp token (px/rem) off the panel computed style for the positioner. */
|
|
@@ -597,7 +1000,7 @@ class SkraDatePicker {
|
|
|
597
1000
|
return parseFloat(raw) || 0;
|
|
598
1001
|
}
|
|
599
1002
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraDatePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
600
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", 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: `
|
|
1003
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: SkraDatePicker, isStandalone: true, selector: "skra-datepicker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, precision: { classPropertyName: "precision", publicName: "precision", 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: `
|
|
601
1004
|
<button
|
|
602
1005
|
#trigger
|
|
603
1006
|
class="skra-datepicker-trigger"
|
|
@@ -641,7 +1044,7 @@ class SkraDatePicker {
|
|
|
641
1044
|
popover="auto"
|
|
642
1045
|
role="dialog"
|
|
643
1046
|
aria-modal="false"
|
|
644
|
-
[attr.aria-labelledby]="
|
|
1047
|
+
[attr.aria-labelledby]="periodId"
|
|
645
1048
|
(toggle)="onPanelToggle($event)"
|
|
646
1049
|
>
|
|
647
1050
|
<div class="skra-datepicker-header">
|
|
@@ -649,8 +1052,8 @@ class SkraDatePicker {
|
|
|
649
1052
|
type="button"
|
|
650
1053
|
class="skra-datepicker-nav"
|
|
651
1054
|
tabindex="-1"
|
|
652
|
-
[attr.aria-label]="
|
|
653
|
-
(click)="
|
|
1055
|
+
[attr.aria-label]="prevPeriodLabel()"
|
|
1056
|
+
(click)="goToPreviousPeriod()"
|
|
654
1057
|
>
|
|
655
1058
|
<svg
|
|
656
1059
|
viewBox="0 0 24 24"
|
|
@@ -663,17 +1066,17 @@ class SkraDatePicker {
|
|
|
663
1066
|
</svg>
|
|
664
1067
|
</button>
|
|
665
1068
|
<span
|
|
666
|
-
class="skra-datepicker-
|
|
667
|
-
[id]="
|
|
1069
|
+
class="skra-datepicker-period"
|
|
1070
|
+
[id]="periodId"
|
|
668
1071
|
aria-live="polite"
|
|
669
|
-
>{{
|
|
1072
|
+
>{{ periodLabel() }}</span
|
|
670
1073
|
>
|
|
671
1074
|
<button
|
|
672
1075
|
type="button"
|
|
673
1076
|
class="skra-datepicker-nav"
|
|
674
1077
|
tabindex="-1"
|
|
675
|
-
[attr.aria-label]="
|
|
676
|
-
(click)="
|
|
1078
|
+
[attr.aria-label]="nextPeriodLabel()"
|
|
1079
|
+
(click)="goToNextPeriod()"
|
|
677
1080
|
>
|
|
678
1081
|
<svg
|
|
679
1082
|
viewBox="0 0 24 24"
|
|
@@ -690,21 +1093,24 @@ class SkraDatePicker {
|
|
|
690
1093
|
<table
|
|
691
1094
|
class="skra-datepicker-grid"
|
|
692
1095
|
role="grid"
|
|
693
|
-
[
|
|
1096
|
+
[class.skra-datepicker-grid--month]="view() === 'month'"
|
|
1097
|
+
[attr.aria-labelledby]="periodId"
|
|
694
1098
|
>
|
|
695
|
-
|
|
696
|
-
<
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
<
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
1099
|
+
@if (view() !== 'month') {
|
|
1100
|
+
<thead>
|
|
1101
|
+
<tr>
|
|
1102
|
+
@for (wd of weekdayHeadersView(); track wd.key) {
|
|
1103
|
+
<th scope="col" class="skra-datepicker-weekday">
|
|
1104
|
+
<abbr [title]="wd.long">{{ wd.short }}</abbr>
|
|
1105
|
+
</th>
|
|
1106
|
+
}
|
|
1107
|
+
</tr>
|
|
1108
|
+
</thead>
|
|
1109
|
+
}
|
|
704
1110
|
<tbody>
|
|
705
|
-
@for (
|
|
1111
|
+
@for (row of rows(); track row.key) {
|
|
706
1112
|
<tr>
|
|
707
|
-
@for (cell of
|
|
1113
|
+
@for (cell of row.cells; track cell.key) {
|
|
708
1114
|
@if (cell.iso) {
|
|
709
1115
|
<td
|
|
710
1116
|
role="gridcell"
|
|
@@ -712,23 +1118,26 @@ class SkraDatePicker {
|
|
|
712
1118
|
>
|
|
713
1119
|
<button
|
|
714
1120
|
type="button"
|
|
715
|
-
class="skra-datepicker-
|
|
1121
|
+
class="skra-datepicker-cell"
|
|
716
1122
|
[attr.tabindex]="cell.focused ? 0 : -1"
|
|
717
|
-
[attr.aria-current]="cell.today ?
|
|
1123
|
+
[attr.aria-current]="cell.today ? currentToken() : null"
|
|
718
1124
|
[attr.aria-disabled]="cell.disabled ? 'true' : null"
|
|
719
1125
|
[attr.data-skra-selected]="cell.selected ? '' : null"
|
|
720
1126
|
[attr.data-skra-today]="cell.today ? '' : null"
|
|
721
|
-
(click)="
|
|
1127
|
+
(click)="selectCell(cell.iso)"
|
|
722
1128
|
(keydown)="onGridKeydown($event)"
|
|
723
1129
|
>
|
|
724
|
-
{{ cell.
|
|
725
|
-
}}<span class="skra-datepicker-
|
|
726
|
-
cell.
|
|
1130
|
+
{{ cell.label
|
|
1131
|
+
}}<span class="skra-datepicker-cell-sr" skra-sr-only>{{
|
|
1132
|
+
cell.srExt
|
|
727
1133
|
}}</span>
|
|
728
1134
|
</button>
|
|
729
1135
|
</td>
|
|
730
1136
|
} @else {
|
|
731
|
-
<td
|
|
1137
|
+
<td
|
|
1138
|
+
class="skra-datepicker-cell-empty"
|
|
1139
|
+
aria-hidden="true"
|
|
1140
|
+
></td>
|
|
732
1141
|
}
|
|
733
1142
|
}
|
|
734
1143
|
</tr>
|
|
@@ -736,7 +1145,7 @@ class SkraDatePicker {
|
|
|
736
1145
|
</tbody>
|
|
737
1146
|
</table>
|
|
738
1147
|
</div>
|
|
739
|
-
`, 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-
|
|
1148
|
+
`, 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-period{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%}@media(hover:hover)and (pointer:fine){.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-cell-block-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-cell-empty{padding:0}.skra-datepicker-cell{display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-datepicker-cell-block-size);block-size:var(--skra-comp-datepicker-cell-block-size);margin:0;padding:0;background:transparent;border:0;border-radius:var(--skra-comp-datepicker-cell-radius);color:var(--skra-comp-datepicker-cell-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)}@media(hover:hover)and (pointer:fine){.skra-datepicker-cell:hover:not([aria-disabled=true]){background:var(--skra-comp-datepicker-cell-background-hover)}}.skra-datepicker-cell:focus-visible{outline:var(--skra-comp-datepicker-focus-ring-width) solid var(--skra-comp-datepicker-focus-ring-color);outline-offset:-.0625rem}.skra-datepicker-cell[data-skra-today]{box-shadow:inset 0 0 0 .125rem var(--skra-comp-datepicker-cell-today-indicator-color)}.skra-datepicker-cell[data-skra-selected]{background:var(--skra-comp-datepicker-cell-background-selected);color:var(--skra-comp-datepicker-cell-foreground-selected)}.skra-datepicker-cell[aria-disabled=true]{color:var(--skra-comp-datepicker-cell-foreground-disabled);cursor:default}.skra-datepicker-grid--month .skra-datepicker-cell{inline-size:auto;min-inline-size:max(var(--skra-comp-datepicker-month-cell-min-inline-size),var(--skra-comp-datepicker-cell-block-size) * 7 / 3)}@media(prefers-reduced-motion:reduce){.skra-datepicker-trigger,.skra-datepicker-panel,.skra-datepicker-cell{transition-duration:0ms}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
740
1149
|
}
|
|
741
1150
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraDatePicker, decorators: [{
|
|
742
1151
|
type: Component,
|
|
@@ -784,7 +1193,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
784
1193
|
popover="auto"
|
|
785
1194
|
role="dialog"
|
|
786
1195
|
aria-modal="false"
|
|
787
|
-
[attr.aria-labelledby]="
|
|
1196
|
+
[attr.aria-labelledby]="periodId"
|
|
788
1197
|
(toggle)="onPanelToggle($event)"
|
|
789
1198
|
>
|
|
790
1199
|
<div class="skra-datepicker-header">
|
|
@@ -792,8 +1201,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
792
1201
|
type="button"
|
|
793
1202
|
class="skra-datepicker-nav"
|
|
794
1203
|
tabindex="-1"
|
|
795
|
-
[attr.aria-label]="
|
|
796
|
-
(click)="
|
|
1204
|
+
[attr.aria-label]="prevPeriodLabel()"
|
|
1205
|
+
(click)="goToPreviousPeriod()"
|
|
797
1206
|
>
|
|
798
1207
|
<svg
|
|
799
1208
|
viewBox="0 0 24 24"
|
|
@@ -806,17 +1215,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
806
1215
|
</svg>
|
|
807
1216
|
</button>
|
|
808
1217
|
<span
|
|
809
|
-
class="skra-datepicker-
|
|
810
|
-
[id]="
|
|
1218
|
+
class="skra-datepicker-period"
|
|
1219
|
+
[id]="periodId"
|
|
811
1220
|
aria-live="polite"
|
|
812
|
-
>{{
|
|
1221
|
+
>{{ periodLabel() }}</span
|
|
813
1222
|
>
|
|
814
1223
|
<button
|
|
815
1224
|
type="button"
|
|
816
1225
|
class="skra-datepicker-nav"
|
|
817
1226
|
tabindex="-1"
|
|
818
|
-
[attr.aria-label]="
|
|
819
|
-
(click)="
|
|
1227
|
+
[attr.aria-label]="nextPeriodLabel()"
|
|
1228
|
+
(click)="goToNextPeriod()"
|
|
820
1229
|
>
|
|
821
1230
|
<svg
|
|
822
1231
|
viewBox="0 0 24 24"
|
|
@@ -833,21 +1242,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
833
1242
|
<table
|
|
834
1243
|
class="skra-datepicker-grid"
|
|
835
1244
|
role="grid"
|
|
836
|
-
[
|
|
1245
|
+
[class.skra-datepicker-grid--month]="view() === 'month'"
|
|
1246
|
+
[attr.aria-labelledby]="periodId"
|
|
837
1247
|
>
|
|
838
|
-
|
|
839
|
-
<
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
<
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
1248
|
+
@if (view() !== 'month') {
|
|
1249
|
+
<thead>
|
|
1250
|
+
<tr>
|
|
1251
|
+
@for (wd of weekdayHeadersView(); track wd.key) {
|
|
1252
|
+
<th scope="col" class="skra-datepicker-weekday">
|
|
1253
|
+
<abbr [title]="wd.long">{{ wd.short }}</abbr>
|
|
1254
|
+
</th>
|
|
1255
|
+
}
|
|
1256
|
+
</tr>
|
|
1257
|
+
</thead>
|
|
1258
|
+
}
|
|
847
1259
|
<tbody>
|
|
848
|
-
@for (
|
|
1260
|
+
@for (row of rows(); track row.key) {
|
|
849
1261
|
<tr>
|
|
850
|
-
@for (cell of
|
|
1262
|
+
@for (cell of row.cells; track cell.key) {
|
|
851
1263
|
@if (cell.iso) {
|
|
852
1264
|
<td
|
|
853
1265
|
role="gridcell"
|
|
@@ -855,23 +1267,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
855
1267
|
>
|
|
856
1268
|
<button
|
|
857
1269
|
type="button"
|
|
858
|
-
class="skra-datepicker-
|
|
1270
|
+
class="skra-datepicker-cell"
|
|
859
1271
|
[attr.tabindex]="cell.focused ? 0 : -1"
|
|
860
|
-
[attr.aria-current]="cell.today ?
|
|
1272
|
+
[attr.aria-current]="cell.today ? currentToken() : null"
|
|
861
1273
|
[attr.aria-disabled]="cell.disabled ? 'true' : null"
|
|
862
1274
|
[attr.data-skra-selected]="cell.selected ? '' : null"
|
|
863
1275
|
[attr.data-skra-today]="cell.today ? '' : null"
|
|
864
|
-
(click)="
|
|
1276
|
+
(click)="selectCell(cell.iso)"
|
|
865
1277
|
(keydown)="onGridKeydown($event)"
|
|
866
1278
|
>
|
|
867
|
-
{{ cell.
|
|
868
|
-
}}<span class="skra-datepicker-
|
|
869
|
-
cell.
|
|
1279
|
+
{{ cell.label
|
|
1280
|
+
}}<span class="skra-datepicker-cell-sr" skra-sr-only>{{
|
|
1281
|
+
cell.srExt
|
|
870
1282
|
}}</span>
|
|
871
1283
|
</button>
|
|
872
1284
|
</td>
|
|
873
1285
|
} @else {
|
|
874
|
-
<td
|
|
1286
|
+
<td
|
|
1287
|
+
class="skra-datepicker-cell-empty"
|
|
1288
|
+
aria-hidden="true"
|
|
1289
|
+
></td>
|
|
875
1290
|
}
|
|
876
1291
|
}
|
|
877
1292
|
</tr>
|
|
@@ -883,8 +1298,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
883
1298
|
'[attr.data-skra-disabled]': 'isDisabled() ? "" : null',
|
|
884
1299
|
'[attr.data-skra-readonly]': 'isReadonly() ? "" : null',
|
|
885
1300
|
'[attr.data-skra-validation]': 'hasError() ? "invalid" : null',
|
|
886
|
-
}, 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-
|
|
887
|
-
}], 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 }] }] } });
|
|
1301
|
+
}, 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-period{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%}@media(hover:hover)and (pointer:fine){.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-cell-block-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-cell-empty{padding:0}.skra-datepicker-cell{display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-datepicker-cell-block-size);block-size:var(--skra-comp-datepicker-cell-block-size);margin:0;padding:0;background:transparent;border:0;border-radius:var(--skra-comp-datepicker-cell-radius);color:var(--skra-comp-datepicker-cell-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)}@media(hover:hover)and (pointer:fine){.skra-datepicker-cell:hover:not([aria-disabled=true]){background:var(--skra-comp-datepicker-cell-background-hover)}}.skra-datepicker-cell:focus-visible{outline:var(--skra-comp-datepicker-focus-ring-width) solid var(--skra-comp-datepicker-focus-ring-color);outline-offset:-.0625rem}.skra-datepicker-cell[data-skra-today]{box-shadow:inset 0 0 0 .125rem var(--skra-comp-datepicker-cell-today-indicator-color)}.skra-datepicker-cell[data-skra-selected]{background:var(--skra-comp-datepicker-cell-background-selected);color:var(--skra-comp-datepicker-cell-foreground-selected)}.skra-datepicker-cell[aria-disabled=true]{color:var(--skra-comp-datepicker-cell-foreground-disabled);cursor:default}.skra-datepicker-grid--month .skra-datepicker-cell{inline-size:auto;min-inline-size:max(var(--skra-comp-datepicker-month-cell-min-inline-size),var(--skra-comp-datepicker-cell-block-size) * 7 / 3)}@media(prefers-reduced-motion:reduce){.skra-datepicker-trigger,.skra-datepicker-panel,.skra-datepicker-cell{transition-duration:0ms}}\n"] }]
|
|
1302
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], precision: [{ type: i0.Input, args: [{ isSignal: true, alias: "precision", 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 }] }] } });
|
|
888
1303
|
|
|
889
1304
|
/**
|
|
890
1305
|
* Generated bundle index. Do not edit.
|