@nanahoshi/mona-ui 0.24.4 → 0.24.6

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.
Files changed (80) hide show
  1. package/README.md +3 -0
  2. package/fesm2022/nanahoshi-mona-ui-auto-complete.mjs +95 -70
  3. package/fesm2022/nanahoshi-mona-ui-calendar.mjs +196 -137
  4. package/fesm2022/nanahoshi-mona-ui-combo-box.mjs +136 -89
  5. package/fesm2022/nanahoshi-mona-ui-date-input.mjs +164 -3
  6. package/fesm2022/nanahoshi-mona-ui-date-picker.mjs +124 -140
  7. package/fesm2022/nanahoshi-mona-ui-datetime-picker.mjs +205 -202
  8. package/fesm2022/nanahoshi-mona-ui-dropdown-list.mjs +108 -56
  9. package/fesm2022/nanahoshi-mona-ui-dropdowns.mjs +68 -59
  10. package/fesm2022/nanahoshi-mona-ui-editor.mjs +3 -3
  11. package/fesm2022/nanahoshi-mona-ui-filter.mjs +1 -1
  12. package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-row.component-DLOPRuFv.mjs → nanahoshi-mona-ui-grid-grid-footer-row.component-CD_ODHpU.mjs} +2 -2
  13. package/fesm2022/{nanahoshi-mona-ui-grid-grid-list.component-Dw5U9H0a.mjs → nanahoshi-mona-ui-grid-grid-list.component-0jN7I1jo.mjs} +4 -4
  14. package/fesm2022/{nanahoshi-mona-ui-grid-grid-row.component-D1kyAzD8.mjs → nanahoshi-mona-ui-grid-grid-row.component-BI_iE7pl.mjs} +3 -3
  15. package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-ECK1_smx.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-Sa1aAtJ-.mjs} +4 -4
  16. package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-D4lUDbfx.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-BvUM9nPt.mjs} +8 -8
  17. package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
  18. package/fesm2022/nanahoshi-mona-ui-i18n-icu.mjs +2 -1
  19. package/fesm2022/nanahoshi-mona-ui-internal-indicator-icon.mjs +2 -2
  20. package/fesm2022/nanahoshi-mona-ui-internal.mjs +4 -2
  21. package/fesm2022/nanahoshi-mona-ui-locales-ar-sa.mjs +2 -1
  22. package/fesm2022/nanahoshi-mona-ui-locales-de-de.mjs +4 -1
  23. package/fesm2022/nanahoshi-mona-ui-locales-es-es.mjs +4 -1
  24. package/fesm2022/nanahoshi-mona-ui-locales-fr-fr.mjs +4 -1
  25. package/fesm2022/nanahoshi-mona-ui-locales-id-id.mjs +2 -1
  26. package/fesm2022/nanahoshi-mona-ui-locales-it-it.mjs +2 -1
  27. package/fesm2022/nanahoshi-mona-ui-locales-ja-jp.mjs +2 -1
  28. package/fesm2022/nanahoshi-mona-ui-locales-ko-kr.mjs +2 -1
  29. package/fesm2022/nanahoshi-mona-ui-locales-pt-br.mjs +2 -1
  30. package/fesm2022/nanahoshi-mona-ui-locales-ru-ru.mjs +2 -1
  31. package/fesm2022/nanahoshi-mona-ui-locales-tr-tr.mjs +2 -1
  32. package/fesm2022/nanahoshi-mona-ui-locales-zh-cn.mjs +2 -1
  33. package/fesm2022/nanahoshi-mona-ui-locales-zh-tw.mjs +2 -1
  34. package/fesm2022/nanahoshi-mona-ui-multi-select.mjs +118 -66
  35. package/fesm2022/nanahoshi-mona-ui-numeric-text-box.mjs +195 -122
  36. package/fesm2022/nanahoshi-mona-ui-pager.mjs +1 -1
  37. package/fesm2022/nanahoshi-mona-ui-time-picker.mjs +149 -167
  38. package/fesm2022/nanahoshi-mona-ui-time-selector.mjs +182 -137
  39. package/i18n/catalogs/en-US.messages.json +2 -1
  40. package/locales/ar-sa/ar-sa.messages.json +2 -1
  41. package/locales/de-de/de-de.messages.json +2 -1
  42. package/locales/es-es/es-es.messages.json +2 -1
  43. package/locales/fr-fr/fr-fr.messages.json +2 -1
  44. package/locales/id-id/id-id.messages.json +2 -1
  45. package/locales/it-it/it-it.messages.json +2 -1
  46. package/locales/ja-jp/ja-jp.messages.json +2 -1
  47. package/locales/ko-kr/ko-kr.messages.json +2 -1
  48. package/locales/pt-br/pt-br.messages.json +2 -1
  49. package/locales/ru-ru/ru-ru.messages.json +2 -1
  50. package/locales/tr-tr/tr-tr.messages.json +2 -1
  51. package/locales/zh-cn/zh-cn.messages.json +2 -1
  52. package/locales/zh-tw/zh-tw.messages.json +2 -1
  53. package/package.json +2 -2
  54. package/types/nanahoshi-mona-ui-auto-complete.d.ts +34 -20
  55. package/types/nanahoshi-mona-ui-calendar.d.ts +30 -11
  56. package/types/nanahoshi-mona-ui-combo-box.d.ts +42 -23
  57. package/types/nanahoshi-mona-ui-date-input.d.ts +74 -3
  58. package/types/nanahoshi-mona-ui-date-picker.d.ts +61 -33
  59. package/types/nanahoshi-mona-ui-datetime-picker.d.ts +79 -38
  60. package/types/nanahoshi-mona-ui-dropdown-list.d.ts +29 -15
  61. package/types/nanahoshi-mona-ui-dropdowns.d.ts +30 -19
  62. package/types/nanahoshi-mona-ui-i18n.d.ts +1 -0
  63. package/types/nanahoshi-mona-ui-internal.d.ts +3 -1
  64. package/types/nanahoshi-mona-ui-locales-ar-sa.d.ts +1 -0
  65. package/types/nanahoshi-mona-ui-locales-de-de.d.ts +1 -0
  66. package/types/nanahoshi-mona-ui-locales-es-es.d.ts +1 -0
  67. package/types/nanahoshi-mona-ui-locales-fr-fr.d.ts +1 -0
  68. package/types/nanahoshi-mona-ui-locales-id-id.d.ts +1 -0
  69. package/types/nanahoshi-mona-ui-locales-it-it.d.ts +1 -0
  70. package/types/nanahoshi-mona-ui-locales-ja-jp.d.ts +1 -0
  71. package/types/nanahoshi-mona-ui-locales-ko-kr.d.ts +1 -0
  72. package/types/nanahoshi-mona-ui-locales-pt-br.d.ts +1 -0
  73. package/types/nanahoshi-mona-ui-locales-ru-ru.d.ts +1 -0
  74. package/types/nanahoshi-mona-ui-locales-tr-tr.d.ts +1 -0
  75. package/types/nanahoshi-mona-ui-locales-zh-cn.d.ts +1 -0
  76. package/types/nanahoshi-mona-ui-locales-zh-tw.d.ts +1 -0
  77. package/types/nanahoshi-mona-ui-multi-select.d.ts +45 -21
  78. package/types/nanahoshi-mona-ui-numeric-text-box.d.ts +57 -16
  79. package/types/nanahoshi-mona-ui-time-picker.d.ts +72 -42
  80. package/types/nanahoshi-mona-ui-time-selector.d.ts +78 -46
@@ -1,14 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, DestroyRef, ElementRef, signal, computed, input, effect, Directive, viewChild, output, model, afterNextRender, untracked, Component } from '@angular/core';
3
- import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
2
+ import { inject, DestroyRef, ElementRef, signal, computed, input, effect, Directive, viewChild, booleanAttribute, output, model, afterNextRender, untracked, Component } from '@angular/core';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
4
  import { ButtonDirective } from '@nanahoshi/mona-ui/button';
5
5
  import { PreventableEvent } from '@nanahoshi/mona-ui/common';
6
- import { TimeSelectorService, generateMinuteSet, generateSecondSet, generateHourSet, TimeLimiterPipe } from '@nanahoshi/mona-ui/date-input';
6
+ import { TimeLimiterPipe, TimeSelectorService, getLocaleHourFormat, generateMinuteSet, generateSecondSet, generateHourSet } from '@nanahoshi/mona-ui/date-input';
7
7
  import { injectComponentDirection, MonaI18nService, formatNumber } from '@nanahoshi/mona-ui/i18n';
8
8
  import { rxTimeout, createElementControlId } from '@nanahoshi/mona-ui/internal';
9
9
  import { DateTime } from 'luxon';
10
- import { tap, fromEvent } from 'rxjs';
11
- import { filter } from 'rxjs/operators';
10
+ import { fromEvent, filter } from 'rxjs';
12
11
  import { cva } from 'class-variance-authority';
13
12
 
14
13
  const timeSelectorBaseThemeVariants = cva(`
@@ -198,6 +197,7 @@ const TIME_SELECTOR_DEFAULT_MESSAGES = {
198
197
  timeSelector: "Time selector"
199
198
  };
200
199
 
200
+ const TIME_LIMITER = new TimeLimiterPipe();
201
201
  class TimeSelectorComponent {
202
202
  #destroyRef;
203
203
  #direction;
@@ -213,8 +213,6 @@ class TimeSelectorComponent {
213
213
  this.#hostElementRef = inject((ElementRef));
214
214
  this.#i18n = inject(MonaI18nService);
215
215
  this.#timeSelectorService = inject(TimeSelectorService, { optional: true });
216
- this.isRtl = computed(() => this.#direction() === "rtl", /* @ts-ignore */
217
- ...(ngDevMode ? [{ debugName: "isRtl" }] : /* istanbul ignore next */ []));
218
216
  this.amMeridiemVisible = computed(() => {
219
217
  const min = this.min();
220
218
  return !(min && min.getHours() >= 12);
@@ -226,6 +224,9 @@ class TimeSelectorComponent {
226
224
  }, /* @ts-ignore */
227
225
  ...(ngDevMode ? [{ debugName: "baseClass" }] : /* istanbul ignore next */ []));
228
226
  this.computedPopupHeight = this.#height.asReadonly();
227
+ /** The clock in use: the `hourFormat` input, or the locale's own when it isn't set. */
228
+ this.effectiveHourFormat = computed(() => this.hourFormat() ?? getLocaleHourFormat(this.#i18n.localeId()), /* @ts-ignore */
229
+ ...(ngDevMode ? [{ debugName: "effectiveHourFormat" }] : /* istanbul ignore next */ []));
229
230
  this.focusedList = signal("hours", /* @ts-ignore */
230
231
  ...(ngDevMode ? [{ debugName: "focusedList" }] : /* istanbul ignore next */ []));
231
232
  this.footerClass = computed(() => {
@@ -238,8 +239,7 @@ class TimeSelectorComponent {
238
239
  ...(ngDevMode ? [{ debugName: "headerClass" }] : /* istanbul ignore next */ []));
239
240
  this.hour = computed(() => {
240
241
  const hour = this.navigatedDate().hour;
241
- const hourFormat = this.hourFormat();
242
- if (hourFormat === "24") {
242
+ if (this.effectiveHourFormat() === "24") {
243
243
  return hour;
244
244
  }
245
245
  return hour % 12 || 12;
@@ -248,12 +248,17 @@ class TimeSelectorComponent {
248
248
  this.hourListId = createElementControlId();
249
249
  this.hourListRef = viewChild.required("hourList", /* @ts-ignore */
250
250
  ...(ngDevMode ? [{ debugName: "hourListRef" }] : /* istanbul ignore next */ []));
251
+ /** The hours shown and reachable with the keyboard: on the step, within min and max. */
252
+ this.hourOptions = computed(() => this.limit(this.viewHours(), "h"), /* @ts-ignore */
253
+ ...(ngDevMode ? [{ debugName: "hourOptions" }] : /* istanbul ignore next */ []));
251
254
  this.infoContainerClass = computed(() => {
252
255
  return timeSelectorInfoContainerThemeVariants();
253
256
  }, /* @ts-ignore */
254
257
  ...(ngDevMode ? [{ debugName: "infoContainerClass" }] : /* istanbul ignore next */ []));
255
258
  this.invalidState = computed(() => this.touched() && this.invalid(), /* @ts-ignore */
256
259
  ...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
260
+ this.isRtl = computed(() => this.#direction() === "rtl", /* @ts-ignore */
261
+ ...(ngDevMode ? [{ debugName: "isRtl" }] : /* istanbul ignore next */ []));
257
262
  this.listContainerClass = computed(() => {
258
263
  return timeSelectorListContainerThemeVariants();
259
264
  }, /* @ts-ignore */
@@ -281,16 +286,15 @@ class TimeSelectorComponent {
281
286
  this.minuteListId = createElementControlId();
282
287
  this.minuteListRef = viewChild.required("minuteList", /* @ts-ignore */
283
288
  ...(ngDevMode ? [{ debugName: "minuteListRef" }] : /* istanbul ignore next */ []));
284
- this.minutes = computed(() => generateMinuteSet(this.minuteStep()), /* @ts-ignore */
285
- ...(ngDevMode ? [{ debugName: "minutes" }] : /* istanbul ignore next */ []));
289
+ this.minuteOptions = computed(() => this.limit(generateMinuteSet(this.minuteStep()), "m"), /* @ts-ignore */
290
+ ...(ngDevMode ? [{ debugName: "minuteOptions" }] : /* istanbul ignore next */ []));
286
291
  this.navigatedDate = signal(DateTime.now(), /* @ts-ignore */
287
292
  ...(ngDevMode ? [{ debugName: "navigatedDate" }] : /* istanbul ignore next */ []));
288
293
  this.navigatedDateText = computed(() => {
289
- const hourFormat = this.hourFormat();
290
294
  const locale = this.#i18n.localeId();
291
295
  return this.navigatedDate()
292
296
  .setLocale(locale)
293
- .toLocaleString({ hour: "numeric", minute: "numeric", hour12: hourFormat === "12" });
297
+ .toLocaleString({ hour: "numeric", minute: "numeric", hour12: this.effectiveHourFormat() === "12" });
294
298
  }, /* @ts-ignore */
295
299
  ...(ngDevMode ? [{ debugName: "navigatedDateText" }] : /* istanbul ignore next */ []));
296
300
  this.pmMeridiemVisible = computed(() => {
@@ -303,27 +307,34 @@ class TimeSelectorComponent {
303
307
  this.secondListId = createElementControlId();
304
308
  this.secondListRef = viewChild("secondList", /* @ts-ignore */
305
309
  ...(ngDevMode ? [{ debugName: "secondListRef" }] : /* istanbul ignore next */ []));
306
- this.seconds = computed(() => generateSecondSet(this.secondStep()), /* @ts-ignore */
307
- ...(ngDevMode ? [{ debugName: "seconds" }] : /* istanbul ignore next */ []));
308
- this.viewHours = computed(() => {
309
- const meridiem = this.meridiem();
310
- const hourFormat = this.hourFormat();
311
- const hourStep = this.hourStep();
312
- return generateHourSet(hourFormat, meridiem, hourStep);
313
- }, /* @ts-ignore */
310
+ this.secondOptions = computed(() => this.limit(generateSecondSet(this.secondStep()), "s"), /* @ts-ignore */
311
+ ...(ngDevMode ? [{ debugName: "secondOptions" }] : /* istanbul ignore next */ []));
312
+ this.viewHours = computed(() => generateHourSet(this.effectiveHourFormat(), this.meridiem(), this.hourStep()), /* @ts-ignore */
314
313
  ...(ngDevMode ? [{ debugName: "viewHours" }] : /* istanbul ignore next */ []));
315
314
  /**
316
315
  * @description Accessible label for the time selector group.
317
316
  * @default ""
318
317
  */
319
318
  this.ariaLabel = input("", { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: "aria-label" });
320
- this.disabled = input(false, /* @ts-ignore */
321
- ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
322
- this.focusOnMount = input(true, /* @ts-ignore */
323
- ...(ngDevMode ? [{ debugName: "focusOnMount" }] : /* istanbul ignore next */ []));
324
- this.footer = input(true, /* @ts-ignore */
325
- ...(ngDevMode ? [{ debugName: "footer" }] : /* istanbul ignore next */ []));
326
- this.hourFormat = input("24", /* @ts-ignore */
319
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
320
+ /**
321
+ * @description Moves focus to the hour list when the selector first renders. The pickers set it for their popups;
322
+ * an inline selector leaves focus where it is, so the page doesn't jump to it on load.
323
+ * @default false
324
+ */
325
+ this.focusOnMount = input(false, { ...(ngDevMode ? { debugName: "focusOnMount" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
326
+ /**
327
+ * @description Shows the Set button. Without it, every change of hour, minute, second or AM/PM made by the user
328
+ * is written to the value at once.
329
+ * @default true
330
+ */
331
+ this.footer = input(true, { ...(ngDevMode ? { debugName: "footer" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
332
+ /**
333
+ * @description The clock to show: "12" with an AM/PM list, or "24". When not set, the active locale's own clock
334
+ * is used (12-hour for en-US, 24-hour for de-DE).
335
+ * @default null
336
+ */
337
+ this.hourFormat = input(null, /* @ts-ignore */
327
338
  ...(ngDevMode ? [{ debugName: "hourFormat" }] : /* istanbul ignore next */ []));
328
339
  this.hourStep = input(1, /* @ts-ignore */
329
340
  ...(ngDevMode ? [{ debugName: "hourStep" }] : /* istanbul ignore next */ []));
@@ -332,8 +343,7 @@ class TimeSelectorComponent {
332
343
  * this is written by the `FormField` directive.
333
344
  * @default false
334
345
  */
335
- this.invalid = input(false, /* @ts-ignore */
336
- ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
346
+ this.invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
337
347
  this.max = input(undefined, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}), transform: value => (value instanceof Date ? value : undefined) });
338
348
  this.min = input(undefined, { ...(ngDevMode ? { debugName: "min" } : /* istanbul ignore next */ {}), transform: value => (value instanceof Date ? value : undefined) });
339
349
  this.minuteStep = input(1, /* @ts-ignore */
@@ -342,26 +352,23 @@ class TimeSelectorComponent {
342
352
  * @description Prevents value changes while preserving the component's visual state.
343
353
  * @default false
344
354
  */
345
- this.readonly = input(false, /* @ts-ignore */
346
- ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
355
+ this.readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
347
356
  /**
348
357
  * @description Sets whether the time selector is required. When bound to a signal form field via `[formField]`,
349
358
  * this is written by the `FormField` directive.
350
359
  * @default false
351
360
  */
352
- this.required = input(false, /* @ts-ignore */
353
- ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
361
+ this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
354
362
  this.rounded = input("medium", /* @ts-ignore */
355
363
  ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
356
364
  this.secondStep = input(1, /* @ts-ignore */
357
365
  ...(ngDevMode ? [{ debugName: "secondStep" }] : /* istanbul ignore next */ []));
358
- this.showSeconds = input(false, /* @ts-ignore */
359
- ...(ngDevMode ? [{ debugName: "showSeconds" }] : /* istanbul ignore next */ []));
366
+ this.showSeconds = input(false, { ...(ngDevMode ? { debugName: "showSeconds" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
360
367
  this.size = input("medium", /* @ts-ignore */
361
368
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
362
369
  /**
363
- * @description Emitted when the time selector is interacted with via blur or value selection.
364
- * The `FormField` directive listens to this to mark the field as touched.
370
+ * @description Emitted when focus leaves the time selector, as a native input is touched on blur. Moving between
371
+ * its lists and buttons doesn't count. The `FormField` directive listens to this to mark the field as touched.
365
372
  */
366
373
  this.touch = output();
367
374
  /**
@@ -369,10 +376,10 @@ class TimeSelectorComponent {
369
376
  * this is written by the `FormField` directive.
370
377
  * @default false
371
378
  */
372
- this.touched = input(false, /* @ts-ignore */
373
- ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
379
+ this.touched = input(false, { ...(ngDevMode ? { debugName: "touched" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
374
380
  /**
375
- * @description Two-way bindable current time value.
381
+ * @description Two-way bindable current time value. It is written by the Set button and Enter, or, without a
382
+ * footer, by each change the user makes; never on its own.
376
383
  * @default null
377
384
  */
378
385
  this.value = model(null, /* @ts-ignore */
@@ -401,12 +408,22 @@ class TimeSelectorComponent {
401
408
  const value = this.value();
402
409
  untracked(() => this.setDateValues(value));
403
410
  });
404
- toObservable(this.navigatedDate)
405
- .pipe(takeUntilDestroyed(), filter(() => !this.footer()), tap(() => this.updateValue(this.navigatedDate().toJSDate(), true)))
406
- .subscribe();
407
411
  }
408
- focus() {
409
- this.focusList(this.focusedList());
412
+ /**
413
+ * Moves focus to the active list.
414
+ * @param options passed to the native `focus()` call, for example `{ preventScroll: true }`.
415
+ */
416
+ focus(options) {
417
+ this.focusList(this.focusedList(), options);
418
+ }
419
+ /** The id of the active option, or null while the time isn't one of the options (between steps, say). */
420
+ activeOptionId(listId, options, value) {
421
+ for (const option of options) {
422
+ if (option.value === value) {
423
+ return `${listId}${value}`;
424
+ }
425
+ }
426
+ return null;
410
427
  }
411
428
  formatTwoDigit(value) {
412
429
  return formatNumber(value, this.#i18n.localeId(), {
@@ -414,14 +431,22 @@ class TimeSelectorComponent {
414
431
  useGrouping: false
415
432
  });
416
433
  }
417
- onBlur() {
434
+ onFocusOut(event) {
435
+ // Removing a focused selector from the page fires focusout after it is destroyed.
436
+ if (this.#destroyRef.destroyed) {
437
+ return;
438
+ }
439
+ const nextTarget = event.relatedTarget;
440
+ if (nextTarget instanceof Node && this.#hostElementRef.nativeElement.contains(nextTarget)) {
441
+ return;
442
+ }
418
443
  this.touch.emit();
419
444
  }
420
445
  onHourChange(hour) {
421
446
  if (this.disabled() || this.readonly()) {
422
447
  return;
423
448
  }
424
- this.navigatedDate.update(d => d.set({ hour }));
449
+ this.navigateTo(this.navigatedDate().set({ hour }));
425
450
  }
426
451
  onMeridiemClick(meridiem) {
427
452
  if (this.disabled() || this.readonly()) {
@@ -432,57 +457,67 @@ class TimeSelectorComponent {
432
457
  }
433
458
  const hour = this.navigatedDate().hour;
434
459
  if (meridiem === "PM" && hour < 12) {
435
- this.navigatedDate.update(date => date.set({ hour: hour + 12 }));
460
+ this.navigateTo(this.navigatedDate().set({ hour: hour + 12 }));
436
461
  }
437
462
  else if (meridiem === "AM" && hour >= 12) {
438
- this.navigatedDate.update(date => date.set({ hour: hour - 12 }));
463
+ this.navigateTo(this.navigatedDate().set({ hour: hour - 12 }));
439
464
  }
440
- this.meridiem.set(meridiem);
441
465
  }
442
466
  onMinuteChange(minute) {
443
467
  if (this.disabled() || this.readonly()) {
444
468
  return;
445
469
  }
446
- this.navigatedDate.update(date => date.set({ minute }));
470
+ this.navigateTo(this.navigatedDate().set({ minute }));
447
471
  }
448
- onNowClick(_event) {
472
+ onNowClick() {
449
473
  if (this.disabled() || this.readonly()) {
450
474
  return;
451
475
  }
452
- let now = DateTime.now().setLocale(this.#i18n.localeId());
453
- const min = this.minDate();
454
- const max = this.maxDate();
455
- if (min && now < min) {
456
- if (min.isValid) {
457
- now = min;
458
- }
459
- }
460
- else if (max && now > max) {
461
- if (max.isValid) {
462
- now = max;
463
- }
464
- }
465
- this.navigatedDate.update(date => date.set({ hour: now.hour, minute: now.minute, second: now.second, millisecond: now.millisecond }));
466
- this.meridiem.set(now.hour >= 12 ? "PM" : "AM");
476
+ const now = DateTime.now();
477
+ this.navigateTo(this.navigatedDate().set({
478
+ hour: now.hour,
479
+ minute: now.minute,
480
+ second: now.second,
481
+ millisecond: now.millisecond
482
+ }));
467
483
  }
468
484
  onSecondChange(second) {
469
485
  if (this.disabled() || this.readonly()) {
470
486
  return;
471
487
  }
472
- this.navigatedDate.update(date => date.set({ second }));
488
+ this.navigateTo(this.navigatedDate().set({ second }));
473
489
  }
474
490
  onSetTimeClick() {
475
491
  if (this.disabled() || this.readonly()) {
476
492
  return;
477
493
  }
478
- this.updateValue(this.navigatedDate().toJSDate(), true);
494
+ this.updateValue(this.clampToRange(this.navigatedDate()).toJSDate());
495
+ }
496
+ /** Moves a time inside min and max, on the same day. */
497
+ clampToRange(date) {
498
+ const day = { year: date.year, month: date.month, day: date.day };
499
+ const min = this.minDate()?.set(day);
500
+ const max = this.maxDate()?.set(day);
501
+ const timeOf = (bound) => ({
502
+ hour: bound.hour,
503
+ minute: bound.minute,
504
+ second: bound.second,
505
+ millisecond: bound.millisecond
506
+ });
507
+ if (min && date < min) {
508
+ return date.set(timeOf(min));
509
+ }
510
+ if (max && date > max) {
511
+ return date.set(timeOf(max));
512
+ }
513
+ return date;
479
514
  }
480
515
  dateValuesEqual(date1, date2) {
481
516
  return date1?.getTime() === date2?.getTime();
482
517
  }
483
- focusList(listType) {
518
+ focusList(listType, options) {
484
519
  const listRef = this.getListRef(listType);
485
- listRef?.nativeElement.focus();
520
+ listRef?.nativeElement.focus(options);
486
521
  }
487
522
  getListRef(listType) {
488
523
  switch (listType) {
@@ -501,7 +536,7 @@ class TimeSelectorComponent {
501
536
  if (this.showSeconds()) {
502
537
  lists.push("seconds");
503
538
  }
504
- if (this.hourFormat() === "12") {
539
+ if (this.effectiveHourFormat() === "12") {
505
540
  lists.push("meridiem");
506
541
  }
507
542
  const currentIndex = lists.indexOf(current);
@@ -514,6 +549,10 @@ class TimeSelectorComponent {
514
549
  return lists[(currentIndex - 1 + lists.length) % lists.length];
515
550
  }
516
551
  }
552
+ getOptions(listType) {
553
+ const options = listType === "hours" ? this.hourOptions : listType === "minutes" ? this.minuteOptions : this.secondOptions;
554
+ return Array.from(options());
555
+ }
517
556
  handleKeydown(event) {
518
557
  if (this.disabled() || this.readonly()) {
519
558
  return;
@@ -523,6 +562,10 @@ class TimeSelectorComponent {
523
562
  if (preventableEvent.isDefaultPrevented()) {
524
563
  return;
525
564
  }
565
+ // Keys pressed on the Now and Set buttons belong to the buttons, which act on click.
566
+ if (event.target.closest("button")) {
567
+ return;
568
+ }
526
569
  const focusedList = this.focusedList();
527
570
  switch (event.key) {
528
571
  case "ArrowUp":
@@ -559,7 +602,9 @@ class TimeSelectorComponent {
559
602
  break;
560
603
  case "Enter":
561
604
  case " ":
562
- this.updateValue(this.navigatedDate().toJSDate(), true);
605
+ // Space would otherwise scroll the focused list.
606
+ event.preventDefault();
607
+ this.updateValue(this.clampToRange(this.navigatedDate()).toJSDate());
563
608
  break;
564
609
  }
565
610
  }
@@ -589,60 +634,55 @@ class TimeSelectorComponent {
589
634
  }
590
635
  }
591
636
  }
637
+ /** The options of a list that pass the step and the min/max bounds for the time being edited. */
638
+ limit(values, type) {
639
+ return TIME_LIMITER.transform(values, type, this.navigatedDate(), this.minDate(), this.maxDate());
640
+ }
641
+ /** Moves through the options of a list, so the keyboard reaches what the mouse can: steps within min and max. */
592
642
  navigateItem(listType, direction) {
593
- switch (listType) {
594
- case "hours": {
595
- const hours = Array.from(this.viewHours());
596
- const currentIndex = hours.findIndex(h => h.viewValue === this.hour());
597
- const newIndex = Math.max(0, Math.min(hours.length - 1, currentIndex + direction));
598
- if (hours[newIndex]) {
599
- this.onHourChange(hours[newIndex].value);
600
- }
601
- break;
602
- }
603
- case "minutes": {
604
- const currentMinute = this.minute();
605
- const newMinute = Math.max(0, Math.min(59, currentMinute + direction));
606
- this.onMinuteChange(newMinute);
607
- break;
608
- }
609
- case "seconds": {
610
- const currentSecond = this.second();
611
- const newSecond = Math.max(0, Math.min(59, currentSecond + direction));
612
- this.onSecondChange(newSecond);
613
- break;
614
- }
615
- case "meridiem": {
616
- const current = this.meridiem();
617
- if (direction !== 0) {
618
- this.onMeridiemClick(current === "AM" ? "PM" : "AM");
619
- }
620
- break;
643
+ if (listType === "meridiem") {
644
+ const target = this.meridiem() === "AM" ? "PM" : "AM";
645
+ if (target === "AM" ? this.amMeridiemVisible() : this.pmMeridiemVisible()) {
646
+ this.onMeridiemClick(target);
621
647
  }
648
+ return;
649
+ }
650
+ const options = this.getOptions(listType);
651
+ if (options.length === 0) {
652
+ return;
653
+ }
654
+ const current = listType === "hours" ? this.navigatedDate().hour : listType === "minutes" ? this.minute() : this.second();
655
+ const index = options.findIndex(option => option.value === current);
656
+ let target;
657
+ if (index >= 0) {
658
+ target = options[Math.max(0, Math.min(options.length - 1, index + direction))];
659
+ }
660
+ else {
661
+ // Between steps: go to the nearest option in the direction of the key.
662
+ target =
663
+ direction > 0
664
+ ? (options.find(option => option.value > current) ?? options[options.length - 1])
665
+ : ([...options].reverse().find(option => option.value < current) ?? options[0]);
666
+ }
667
+ this.setUnit(listType, target.value);
668
+ }
669
+ navigateTo(date) {
670
+ const clamped = this.clampToRange(date);
671
+ this.navigatedDate.set(clamped);
672
+ this.meridiem.set(clamped.hour >= 12 ? "PM" : "AM");
673
+ if (!this.footer()) {
674
+ this.updateValue(clamped.toJSDate());
622
675
  }
623
676
  }
624
677
  navigateToEdge(listType, edge) {
625
- switch (listType) {
626
- case "hours": {
627
- const hours = Array.from(this.viewHours());
628
- const target = edge === "first" ? hours[0] : hours[hours.length - 1];
629
- if (target) {
630
- this.onHourChange(target.value);
631
- }
632
- break;
633
- }
634
- case "minutes": {
635
- this.onMinuteChange(edge === "first" ? 0 : 59);
636
- break;
637
- }
638
- case "seconds": {
639
- this.onSecondChange(edge === "first" ? 0 : 59);
640
- break;
641
- }
642
- case "meridiem": {
643
- this.onMeridiemClick(edge === "first" ? "AM" : "PM");
644
- break;
645
- }
678
+ if (listType === "meridiem") {
679
+ this.onMeridiemClick(edge === "first" ? "AM" : "PM");
680
+ return;
681
+ }
682
+ const options = this.getOptions(listType);
683
+ const target = edge === "first" ? options[0] : options[options.length - 1];
684
+ if (target) {
685
+ this.setUnit(listType, target.value);
646
686
  }
647
687
  }
648
688
  setDateValues(value = this.value()) {
@@ -653,41 +693,46 @@ class TimeSelectorComponent {
653
693
  const meridiem = this.navigatedDate().hour >= 12 ? "PM" : "AM";
654
694
  this.meridiem.set(meridiem);
655
695
  }
696
+ setUnit(listType, value) {
697
+ switch (listType) {
698
+ case "hours":
699
+ this.onHourChange(value);
700
+ break;
701
+ case "minutes":
702
+ this.onMinuteChange(value);
703
+ break;
704
+ case "seconds":
705
+ this.onSecondChange(value);
706
+ break;
707
+ }
708
+ }
656
709
  setupKeyboardNavigation() {
657
710
  fromEvent(this.#hostElementRef.nativeElement, "keydown")
658
711
  .pipe(takeUntilDestroyed(this.#destroyRef), filter(() => !this.disabled()))
659
712
  .subscribe(event => this.handleKeydown(event));
660
713
  }
661
- updateValue(date, notify) {
714
+ updateValue(date) {
662
715
  if (this.dateValuesEqual(this.value(), date)) {
663
- if (notify) {
664
- this.touch.emit();
665
- }
666
716
  return;
667
717
  }
668
718
  this.value.set(date);
669
- if (notify) {
670
- this.touch.emit();
671
- }
672
719
  }
673
720
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: TimeSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
674
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: TimeSelectorComponent, isStandalone: true, selector: "mona-time-selector", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, focusOnMount: { classPropertyName: "focusOnMount", publicName: "focusOnMount", isSignal: true, isRequired: false, transformFunction: null }, footer: { classPropertyName: "footer", publicName: "footer", isSignal: true, isRequired: false, transformFunction: null }, hourFormat: { classPropertyName: "hourFormat", publicName: "hourFormat", isSignal: true, isRequired: false, transformFunction: null }, hourStep: { classPropertyName: "hourStep", publicName: "hourStep", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, secondStep: { classPropertyName: "secondStep", publicName: "secondStep", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { touch: "touch", value: "valueChange" }, host: { attributes: { "role": "group" }, listeners: { "blur": "onBlur()" }, properties: { "attr.aria-label": "ariaLabel() || messages().timeSelector", "attr.aria-disabled": "disabled()", "attr.aria-invalid": "invalidState() ? 'true' : null", "attr.aria-readonly": "readonly()", "attr.aria-required": "required()", "attr.data-invalid": "invalidState() || null", "attr.data-readonly": "readonly() || null", "attr.data-required": "required() || null", "class": "baseClass()" } }, viewQueries: [{ propertyName: "hourListRef", first: true, predicate: ["hourList"], descendants: true, isSignal: true }, { propertyName: "meridiemListRef", first: true, predicate: ["meridiemList"], descendants: true, isSignal: true }, { propertyName: "minuteListRef", first: true, predicate: ["minuteList"], descendants: true, isSignal: true }, { propertyName: "secondListRef", first: true, predicate: ["secondList"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [class]=\"headerClass()\">\n <div>{{ messages().headerHours }}</div>\n <div>{{ messages().headerMinutes }}</div>\n @if (showSeconds()) {\n <div>{{ messages().headerSeconds }}</div>\n }\n</div>\n\n<div [class]=\"infoContainerClass()\">\n <span>{{ navigatedDateText() }}</span>\n <button\n monaButton\n look=\"ghost\"\n [rounded]=\"rounded()\"\n [size]=\"'small'\"\n (click)=\"onNowClick($event)\"\n (keydown.enter)=\"onNowClick($event)\"\n (keydown.space)=\"onNowClick($event)\">\n {{ messages().now }}\n </button>\n</div>\n\n<div [class]=\"listContainerClass()\">\n <ol\n #hourList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().hours\"\n [attr.aria-activedescendant]=\"`${hourListId}${navigatedDate().hour}`\"\n [active]=\"focusedList() === 'hours'\"\n [id]=\"hourListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (h of viewHours() | timeLimiter: \"h\" : navigatedDate() : minDate() : maxDate(); track h) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"h.viewValue === hour()\"\n [value]=\"h.value\"\n [id]=\"`${hourListId}${h.value}`\"\n (click)=\"onHourChange(h.value)\">\n {{ formatTwoDigit(h.viewValue) }}\n </li>\n }\n </ol>\n <ol\n #minuteList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().minutes\"\n [attr.aria-activedescendant]=\"`${minuteListId}${navigatedDate().minute}`\"\n [active]=\"focusedList() === 'minutes'\"\n [id]=\"minuteListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (m of minutes() | timeLimiter: \"m\" : navigatedDate() : minDate() : maxDate(); track m) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"m.value === minute()\"\n [value]=\"m.value\"\n [id]=\"`${minuteListId}${m.value}`\"\n (click)=\"onMinuteChange(m.value)\">\n {{ formatTwoDigit(m.viewValue) }}\n </li>\n }\n </ol>\n @if (showSeconds()) {\n <ol\n #secondList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().seconds\"\n [attr.aria-activedescendant]=\"`${secondListId}${navigatedDate().second}`\"\n [active]=\"focusedList() === 'seconds'\"\n [id]=\"secondListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (s of seconds() | timeLimiter: \"s\" : navigatedDate() : minDate() : maxDate(); track s) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"s.value === second()\"\n [value]=\"s.value\"\n [id]=\"`${secondListId}${s.value}`\"\n (click)=\"onSecondChange(s.value)\">\n {{ formatTwoDigit(s.value) }}\n </li>\n }\n </ol>\n }\n @if (hourFormat() === \"12\") {\n <ol\n #meridiemList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().amPm\"\n [attr.aria-activedescendant]=\"`${meridiemListId}${meridiem()}`\"\n [active]=\"focusedList() === 'meridiem'\"\n [id]=\"meridiemListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @if (amMeridiemVisible()) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"meridiem() === 'AM'\"\n [value]=\"'AM'\"\n (click)=\"onMeridiemClick('AM')\"\n [id]=\"`${meridiemListId}AM`\">\n {{ messages().am }}\n </li>\n }\n @if (pmMeridiemVisible()) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"meridiem() === 'PM'\"\n [value]=\"'PM'\"\n (click)=\"onMeridiemClick('PM')\"\n [id]=\"`${meridiemListId}PM`\">\n {{ messages().pm }}\n </li>\n }\n </ol>\n }\n</div>\n\n@if (footer()) {\n <div [class]=\"footerClass()\">\n <button monaButton look=\"primary\" [rounded]=\"rounded()\" [size]=\"size()\" (click)=\"onSetTimeClick()\">\n {{ messages().set }}\n </button>\n </div>\n}\n", dependencies: [{ kind: "directive", type: TimeSelectorItemDirective, selector: "li[monaTimeSelectorItem]", inputs: ["selected", "size", "value"] }, { kind: "directive", type: ButtonDirective, selector: "button[monaButton]", inputs: ["aria-haspopup", "disabled", "iconOnly", "loading", "look", "rounded", "selected", "size", "tabindex", "toggleable", "type", "class"], outputs: ["lookChange", "selectedChange"] }, { kind: "directive", type: TimeSelectorListDirective, selector: "ol[monaTimeSelectorList]", inputs: ["active", "popupHeight", "size"] }, { kind: "pipe", type: TimeLimiterPipe, name: "timeLimiter" }] }); }
721
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: TimeSelectorComponent, isStandalone: true, selector: "mona-time-selector", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, focusOnMount: { classPropertyName: "focusOnMount", publicName: "focusOnMount", isSignal: true, isRequired: false, transformFunction: null }, footer: { classPropertyName: "footer", publicName: "footer", isSignal: true, isRequired: false, transformFunction: null }, hourFormat: { classPropertyName: "hourFormat", publicName: "hourFormat", isSignal: true, isRequired: false, transformFunction: null }, hourStep: { classPropertyName: "hourStep", publicName: "hourStep", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, secondStep: { classPropertyName: "secondStep", publicName: "secondStep", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { touch: "touch", value: "valueChange" }, host: { attributes: { "role": "group" }, listeners: { "focusout": "onFocusOut($event)" }, properties: { "attr.aria-label": "ariaLabel() || messages().timeSelector", "attr.aria-disabled": "disabled() || null", "attr.aria-invalid": "invalidState() ? 'true' : null", "attr.data-invalid": "invalidState() || null", "attr.data-readonly": "readonly() || null", "attr.data-required": "required() || null", "class": "baseClass()" } }, viewQueries: [{ propertyName: "hourListRef", first: true, predicate: ["hourList"], descendants: true, isSignal: true }, { propertyName: "meridiemListRef", first: true, predicate: ["meridiemList"], descendants: true, isSignal: true }, { propertyName: "minuteListRef", first: true, predicate: ["minuteList"], descendants: true, isSignal: true }, { propertyName: "secondListRef", first: true, predicate: ["secondList"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [class]=\"headerClass()\">\n <div>{{ messages().headerHours }}</div>\n <div>{{ messages().headerMinutes }}</div>\n @if (showSeconds()) {\n <div>{{ messages().headerSeconds }}</div>\n }\n</div>\n\n<div [class]=\"infoContainerClass()\">\n <span>{{ navigatedDateText() }}</span>\n <button\n monaButton\n look=\"ghost\"\n [rounded]=\"rounded()\"\n [size]=\"'small'\"\n (click)=\"onNowClick()\">\n {{ messages().now }}\n </button>\n</div>\n\n<div [class]=\"listContainerClass()\">\n <ol\n #hourList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().hours\"\n [attr.aria-activedescendant]=\"activeOptionId(hourListId, hourOptions(), navigatedDate().hour)\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-required]=\"required() || null\"\n [active]=\"focusedList() === 'hours'\"\n [id]=\"hourListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (h of hourOptions(); track h.value) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"h.viewValue === hour()\"\n [value]=\"h.value\"\n [id]=\"`${hourListId}${h.value}`\"\n (click)=\"onHourChange(h.value)\">\n {{ formatTwoDigit(h.viewValue) }}\n </li>\n }\n </ol>\n <ol\n #minuteList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().minutes\"\n [attr.aria-activedescendant]=\"activeOptionId(minuteListId, minuteOptions(), minute())\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-required]=\"required() || null\"\n [active]=\"focusedList() === 'minutes'\"\n [id]=\"minuteListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (m of minuteOptions(); track m.value) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"m.value === minute()\"\n [value]=\"m.value\"\n [id]=\"`${minuteListId}${m.value}`\"\n (click)=\"onMinuteChange(m.value)\">\n {{ formatTwoDigit(m.viewValue) }}\n </li>\n }\n </ol>\n @if (showSeconds()) {\n <ol\n #secondList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().seconds\"\n [attr.aria-activedescendant]=\"activeOptionId(secondListId, secondOptions(), second())\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-required]=\"required() || null\"\n [active]=\"focusedList() === 'seconds'\"\n [id]=\"secondListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (s of secondOptions(); track s.value) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"s.value === second()\"\n [value]=\"s.value\"\n [id]=\"`${secondListId}${s.value}`\"\n (click)=\"onSecondChange(s.value)\">\n {{ formatTwoDigit(s.value) }}\n </li>\n }\n </ol>\n }\n @if (effectiveHourFormat() === \"12\") {\n <ol\n #meridiemList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().amPm\"\n [attr.aria-activedescendant]=\"`${meridiemListId}${meridiem()}`\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-required]=\"required() || null\"\n [active]=\"focusedList() === 'meridiem'\"\n [id]=\"meridiemListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @if (amMeridiemVisible()) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"meridiem() === 'AM'\"\n [value]=\"'AM'\"\n (click)=\"onMeridiemClick('AM')\"\n [id]=\"`${meridiemListId}AM`\">\n {{ messages().am }}\n </li>\n }\n @if (pmMeridiemVisible()) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"meridiem() === 'PM'\"\n [value]=\"'PM'\"\n (click)=\"onMeridiemClick('PM')\"\n [id]=\"`${meridiemListId}PM`\">\n {{ messages().pm }}\n </li>\n }\n </ol>\n }\n</div>\n\n@if (footer()) {\n <div [class]=\"footerClass()\">\n <button monaButton look=\"primary\" [rounded]=\"rounded()\" [size]=\"size()\" (click)=\"onSetTimeClick()\">\n {{ messages().set }}\n </button>\n </div>\n}\n", dependencies: [{ kind: "directive", type: TimeSelectorItemDirective, selector: "li[monaTimeSelectorItem]", inputs: ["selected", "size", "value"] }, { kind: "directive", type: ButtonDirective, selector: "button[monaButton]", inputs: ["aria-haspopup", "disabled", "iconOnly", "loading", "look", "rounded", "selected", "size", "tabindex", "toggleable", "type", "class"], outputs: ["lookChange", "selectedChange"] }, { kind: "directive", type: TimeSelectorListDirective, selector: "ol[monaTimeSelectorList]", inputs: ["active", "popupHeight", "size"] }] }); }
675
722
  }
676
723
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: TimeSelectorComponent, decorators: [{
677
724
  type: Component,
678
- args: [{ selector: "mona-time-selector", imports: [TimeLimiterPipe, TimeSelectorItemDirective, ButtonDirective, TimeSelectorListDirective], host: {
725
+ args: [{ selector: "mona-time-selector", imports: [TimeSelectorItemDirective, ButtonDirective, TimeSelectorListDirective], host: {
679
726
  role: "group",
680
727
  "[attr.aria-label]": "ariaLabel() || messages().timeSelector",
681
- "[attr.aria-disabled]": "disabled()",
728
+ "[attr.aria-disabled]": "disabled() || null",
682
729
  "[attr.aria-invalid]": "invalidState() ? 'true' : null",
683
- "[attr.aria-readonly]": "readonly()",
684
- "[attr.aria-required]": "required()",
685
730
  "[attr.data-invalid]": "invalidState() || null",
686
731
  "[attr.data-readonly]": "readonly() || null",
687
732
  "[attr.data-required]": "required() || null",
688
733
  "[class]": "baseClass()",
689
- "(blur)": "onBlur()"
690
- }, template: "<div [class]=\"headerClass()\">\n <div>{{ messages().headerHours }}</div>\n <div>{{ messages().headerMinutes }}</div>\n @if (showSeconds()) {\n <div>{{ messages().headerSeconds }}</div>\n }\n</div>\n\n<div [class]=\"infoContainerClass()\">\n <span>{{ navigatedDateText() }}</span>\n <button\n monaButton\n look=\"ghost\"\n [rounded]=\"rounded()\"\n [size]=\"'small'\"\n (click)=\"onNowClick($event)\"\n (keydown.enter)=\"onNowClick($event)\"\n (keydown.space)=\"onNowClick($event)\">\n {{ messages().now }}\n </button>\n</div>\n\n<div [class]=\"listContainerClass()\">\n <ol\n #hourList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().hours\"\n [attr.aria-activedescendant]=\"`${hourListId}${navigatedDate().hour}`\"\n [active]=\"focusedList() === 'hours'\"\n [id]=\"hourListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (h of viewHours() | timeLimiter: \"h\" : navigatedDate() : minDate() : maxDate(); track h) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"h.viewValue === hour()\"\n [value]=\"h.value\"\n [id]=\"`${hourListId}${h.value}`\"\n (click)=\"onHourChange(h.value)\">\n {{ formatTwoDigit(h.viewValue) }}\n </li>\n }\n </ol>\n <ol\n #minuteList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().minutes\"\n [attr.aria-activedescendant]=\"`${minuteListId}${navigatedDate().minute}`\"\n [active]=\"focusedList() === 'minutes'\"\n [id]=\"minuteListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (m of minutes() | timeLimiter: \"m\" : navigatedDate() : minDate() : maxDate(); track m) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"m.value === minute()\"\n [value]=\"m.value\"\n [id]=\"`${minuteListId}${m.value}`\"\n (click)=\"onMinuteChange(m.value)\">\n {{ formatTwoDigit(m.viewValue) }}\n </li>\n }\n </ol>\n @if (showSeconds()) {\n <ol\n #secondList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().seconds\"\n [attr.aria-activedescendant]=\"`${secondListId}${navigatedDate().second}`\"\n [active]=\"focusedList() === 'seconds'\"\n [id]=\"secondListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (s of seconds() | timeLimiter: \"s\" : navigatedDate() : minDate() : maxDate(); track s) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"s.value === second()\"\n [value]=\"s.value\"\n [id]=\"`${secondListId}${s.value}`\"\n (click)=\"onSecondChange(s.value)\">\n {{ formatTwoDigit(s.value) }}\n </li>\n }\n </ol>\n }\n @if (hourFormat() === \"12\") {\n <ol\n #meridiemList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().amPm\"\n [attr.aria-activedescendant]=\"`${meridiemListId}${meridiem()}`\"\n [active]=\"focusedList() === 'meridiem'\"\n [id]=\"meridiemListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @if (amMeridiemVisible()) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"meridiem() === 'AM'\"\n [value]=\"'AM'\"\n (click)=\"onMeridiemClick('AM')\"\n [id]=\"`${meridiemListId}AM`\">\n {{ messages().am }}\n </li>\n }\n @if (pmMeridiemVisible()) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"meridiem() === 'PM'\"\n [value]=\"'PM'\"\n (click)=\"onMeridiemClick('PM')\"\n [id]=\"`${meridiemListId}PM`\">\n {{ messages().pm }}\n </li>\n }\n </ol>\n }\n</div>\n\n@if (footer()) {\n <div [class]=\"footerClass()\">\n <button monaButton look=\"primary\" [rounded]=\"rounded()\" [size]=\"size()\" (click)=\"onSetTimeClick()\">\n {{ messages().set }}\n </button>\n </div>\n}\n" }]
734
+ "(focusout)": "onFocusOut($event)"
735
+ }, template: "<div [class]=\"headerClass()\">\n <div>{{ messages().headerHours }}</div>\n <div>{{ messages().headerMinutes }}</div>\n @if (showSeconds()) {\n <div>{{ messages().headerSeconds }}</div>\n }\n</div>\n\n<div [class]=\"infoContainerClass()\">\n <span>{{ navigatedDateText() }}</span>\n <button\n monaButton\n look=\"ghost\"\n [rounded]=\"rounded()\"\n [size]=\"'small'\"\n (click)=\"onNowClick()\">\n {{ messages().now }}\n </button>\n</div>\n\n<div [class]=\"listContainerClass()\">\n <ol\n #hourList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().hours\"\n [attr.aria-activedescendant]=\"activeOptionId(hourListId, hourOptions(), navigatedDate().hour)\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-required]=\"required() || null\"\n [active]=\"focusedList() === 'hours'\"\n [id]=\"hourListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (h of hourOptions(); track h.value) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"h.viewValue === hour()\"\n [value]=\"h.value\"\n [id]=\"`${hourListId}${h.value}`\"\n (click)=\"onHourChange(h.value)\">\n {{ formatTwoDigit(h.viewValue) }}\n </li>\n }\n </ol>\n <ol\n #minuteList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().minutes\"\n [attr.aria-activedescendant]=\"activeOptionId(minuteListId, minuteOptions(), minute())\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-required]=\"required() || null\"\n [active]=\"focusedList() === 'minutes'\"\n [id]=\"minuteListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (m of minuteOptions(); track m.value) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"m.value === minute()\"\n [value]=\"m.value\"\n [id]=\"`${minuteListId}${m.value}`\"\n (click)=\"onMinuteChange(m.value)\">\n {{ formatTwoDigit(m.viewValue) }}\n </li>\n }\n </ol>\n @if (showSeconds()) {\n <ol\n #secondList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().seconds\"\n [attr.aria-activedescendant]=\"activeOptionId(secondListId, secondOptions(), second())\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-required]=\"required() || null\"\n [active]=\"focusedList() === 'seconds'\"\n [id]=\"secondListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @for (s of secondOptions(); track s.value) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"s.value === second()\"\n [value]=\"s.value\"\n [id]=\"`${secondListId}${s.value}`\"\n (click)=\"onSecondChange(s.value)\">\n {{ formatTwoDigit(s.value) }}\n </li>\n }\n </ol>\n }\n @if (effectiveHourFormat() === \"12\") {\n <ol\n #meridiemList\n monaTimeSelectorList\n [attr.aria-label]=\"messages().amPm\"\n [attr.aria-activedescendant]=\"`${meridiemListId}${meridiem()}`\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-required]=\"required() || null\"\n [active]=\"focusedList() === 'meridiem'\"\n [id]=\"meridiemListId\"\n [popupHeight]=\"computedPopupHeight()\"\n [size]=\"size()\">\n @if (amMeridiemVisible()) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"meridiem() === 'AM'\"\n [value]=\"'AM'\"\n (click)=\"onMeridiemClick('AM')\"\n [id]=\"`${meridiemListId}AM`\">\n {{ messages().am }}\n </li>\n }\n @if (pmMeridiemVisible()) {\n <li\n monaTimeSelectorItem\n [size]=\"size()\"\n [selected]=\"meridiem() === 'PM'\"\n [value]=\"'PM'\"\n (click)=\"onMeridiemClick('PM')\"\n [id]=\"`${meridiemListId}PM`\">\n {{ messages().pm }}\n </li>\n }\n </ol>\n }\n</div>\n\n@if (footer()) {\n <div [class]=\"footerClass()\">\n <button monaButton look=\"primary\" [rounded]=\"rounded()\" [size]=\"size()\" (click)=\"onSetTimeClick()\">\n {{ messages().set }}\n </button>\n </div>\n}\n" }]
691
736
  }], ctorParameters: () => [], propDecorators: { hourListRef: [{ type: i0.ViewChild, args: ["hourList", { isSignal: true }] }], meridiemListRef: [{ type: i0.ViewChild, args: ["meridiemList", { isSignal: true }] }], minuteListRef: [{ type: i0.ViewChild, args: ["minuteList", { isSignal: true }] }], secondListRef: [{ type: i0.ViewChild, args: ["secondList", { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], focusOnMount: [{ type: i0.Input, args: [{ isSignal: true, alias: "focusOnMount", required: false }] }], footer: [{ type: i0.Input, args: [{ isSignal: true, alias: "footer", required: false }] }], hourFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "hourFormat", required: false }] }], hourStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "hourStep", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], secondStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondStep", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
692
737
 
693
738
  /*
@@ -251,7 +251,8 @@
251
251
  },
252
252
  "multiSelect": {
253
253
  "clear": "Clear",
254
- "itemsCount": "{count, plural, =1 {+ # item} other {+ # items}}"
254
+ "itemsCount": "{count, plural, =1 {+ # item} other {+ # items}}",
255
+ "removeMoreItems": "{count, plural, =1 {Remove # more item} other {Remove # more items}}"
255
256
  },
256
257
  "notification": {
257
258
  "close": "Close",