@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,16 +1,14 @@
1
1
  import { NgTemplateOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { Directive, inject, DestroyRef, ElementRef, computed, signal, contentChildren, TemplateRef, viewChild, input, output, effect, untracked, afterNextRender, model, Component } from '@angular/core';
4
- import * as i1 from '@angular/forms';
5
- import { FormsModule } from '@angular/forms';
3
+ import { Directive, inject, DestroyRef, ElementRef, computed, signal, contentChildren, TemplateRef, viewChild, input, booleanAttribute, output, effect, untracked, afterNextRender, model, Component } from '@angular/core';
6
4
  import { LucideChevronUp, LucideChevronDown } from '@lucide/angular';
7
5
  import { ButtonDirective } from '@nanahoshi/mona-ui/button';
8
6
  import { MonaI18nService, formatNumber, getNumberSymbols, validateLocalizedDecimalEdit, validateLocalizedDecimal, validateLocalizedNumberEdit, validateLocalizedNumber } from '@nanahoshi/mona-ui/i18n';
9
7
  import { TextBoxDirective } from '@nanahoshi/mona-ui/text-box';
10
8
  import { FocusMonitor } from '@angular/cdk/a11y';
11
9
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
12
- import { themeControlSurfaceClasses, rxTimeout } from '@nanahoshi/mona-ui/internal';
13
- import { Subject, filter, switchMap, of, tap, delay, concatMap, timer, takeUntil, distinctUntilChanged, map } from 'rxjs';
10
+ import { themeControlSurfaceClasses, createElementControlId, rxTimeout } from '@nanahoshi/mona-ui/internal';
11
+ import { Subject, filter, switchMap, of, tap, delay, concatMap, timer, takeUntil, map } from 'rxjs';
14
12
  import { twMerge } from 'tailwind-merge';
15
13
  import { cva } from 'class-variance-authority';
16
14
 
@@ -81,8 +79,7 @@ class NumericTextBoxPrefixTemplateDirective {
81
79
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NumericTextBoxPrefixTemplateDirective, decorators: [{
82
80
  type: Directive,
83
81
  args: [{
84
- selector: "ng-template[monaNumericTextBoxPrefixTemplate]",
85
- standalone: true
82
+ selector: "ng-template[monaNumericTextBoxPrefixTemplate]"
86
83
  }]
87
84
  }] });
88
85
 
@@ -98,10 +95,9 @@ const numericTextboxThemeVariants = cva(`
98
95
  overflow-hidden p-0
99
96
  ${themeControlSurfaceClasses} text-foreground
100
97
  border border-input-border shadow-(--shadow-control)
101
- outline-none
98
+ outline-hidden
102
99
  selection:bg-primary selection:text-primary-foreground
103
100
  transition-[color,box-shadow,border] duration-300 ease-in-out
104
- placeholder:text-muted-foreground
105
101
 
106
102
  data-[disabled='true']:pointer-events-none
107
103
  data-[disabled='true']:cursor-not-allowed
@@ -139,7 +135,7 @@ const numericTextboxInputThemeVariants = cva(`
139
135
  bg-transparent
140
136
  border-0 shadow-none
141
137
  rounded-none
142
- outline-none ring-0
138
+ outline-hidden ring-0
143
139
  focus-visible:border-transparent focus-visible:ring-0
144
140
  `, {
145
141
  variants: {
@@ -197,6 +193,8 @@ const numericTextboxButtonThemeVariants = cva(`
197
193
  }
198
194
  });
199
195
 
196
+ /** PageUp and PageDown move by this many steps. */
197
+ const PAGE_STEP_COUNT = 10;
200
198
  /**
201
199
  * Typing, pasting, spinning, formatting and ARIA for a numeric input, shared by the number and decimal text boxes.
202
200
  * Subclasses declare the value-typed inputs and say how to parse, compare and step their value type.
@@ -206,12 +204,16 @@ class NumericTextBoxBaseComponent {
206
204
  #focusMonitor;
207
205
  #hostElementRef;
208
206
  #i18n;
207
+ /** The value this component last wrote, so a value set from outside can be told apart. */
208
+ #ownValue;
209
209
  #pendingPaste;
210
210
  constructor() {
211
211
  this.#destroyRef = inject(DestroyRef);
212
212
  this.#focusMonitor = inject(FocusMonitor);
213
213
  this.#hostElementRef = inject((ElementRef));
214
214
  this.#i18n = inject(MonaI18nService);
215
+ /** The value this component last wrote, so a value set from outside can be told apart. */
216
+ this.#ownValue = undefined;
215
217
  this.#pendingPaste = false;
216
218
  this.beforeInput$ = new Subject();
217
219
  this.classes = computed(() => {
@@ -234,7 +236,7 @@ class NumericTextBoxBaseComponent {
234
236
  this.inputClasses = computed(() => {
235
237
  const hasPrefixTemplate = this.prefixTemplateList().length > 0;
236
238
  const startRounded = hasPrefixTemplate ? "none" : this.rounded();
237
- const endRounded = this.spinners() ? "none" : this.rounded();
239
+ const endRounded = this.showSpinners() ? "none" : this.rounded();
238
240
  const inputVariants = numericTextboxInputThemeVariants({ endRounded, startRounded });
239
241
  return twMerge(inputVariants);
240
242
  }, /* @ts-ignore */
@@ -246,6 +248,9 @@ class NumericTextBoxBaseComponent {
246
248
  this.prefixTemplateList = contentChildren(NumericTextBoxPrefixTemplateDirective, { ...(ngDevMode ? { debugName: "prefixTemplateList" } : /* istanbul ignore next */ {}), read: TemplateRef });
247
249
  this.rawInputValue = signal("", /* @ts-ignore */
248
250
  ...(ngDevMode ? [{ debugName: "rawInputValue" }] : /* istanbul ignore next */ []));
251
+ /** Spin buttons on a read-only field would look usable but do nothing, so they are hidden there. */
252
+ this.showSpinners = computed(() => this.spinners() && !this.readonly(), /* @ts-ignore */
253
+ ...(ngDevMode ? [{ debugName: "showSpinners" }] : /* istanbul ignore next */ []));
249
254
  this.spin$ = new Subject();
250
255
  this.spinButtonClasses = computed(() => {
251
256
  const size = this.size();
@@ -263,9 +268,21 @@ class NumericTextBoxBaseComponent {
263
268
  ...(ngDevMode ? [{ debugName: "valueTextBoxRef" }] : /* istanbul ignore next */ []));
264
269
  this.wheel$ = new Subject();
265
270
  /**
266
- * @description ARIA label for the input.
271
+ * @description ID of an element that provides an extended description for the input, such as a hint or an
272
+ * error message.
273
+ * @default ""
274
+ */
275
+ this.ariaDescribedBy = input("", { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: "aria-describedby" });
276
+ /**
277
+ * @description Accessible name for the input. Prefer a visible label, linked through {@link inputId} or
278
+ * {@link ariaLabelledBy}.
267
279
  */
268
280
  this.ariaLabel = input(null, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: "aria-label" });
281
+ /**
282
+ * @description ID of an external element that provides the accessible name for the input.
283
+ * @default ""
284
+ */
285
+ this.ariaLabelledBy = input("", { ...(ngDevMode ? { debugName: "ariaLabelledBy" } : /* istanbul ignore next */ {}), alias: "aria-labelledby" });
269
286
  /**
270
287
  * @description Number of fractional decimal places to display and accept. Values are truncated to an integer
271
288
  * and clamped to the range 0–20. Non-finite and negative values normalize to 0.
@@ -281,10 +298,10 @@ class NumericTextBoxBaseComponent {
281
298
  /**
282
299
  * @description Sets whether the input is disabled.
283
300
  */
284
- this.disabled = input(false, /* @ts-ignore */
285
- ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
301
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
286
302
  /**
287
- * @description Emits when the inner input element is blurred.
303
+ * @description Emits when focus leaves the component, after the value has been clamped to the bounds. Moving
304
+ * focus between the input and elements inside the component (such as a prefix template) doesn't count.
288
305
  */
289
306
  this.inputBlur = output();
290
307
  /**
@@ -295,28 +312,38 @@ class NumericTextBoxBaseComponent {
295
312
  * @description Emits when the inner input element loses focus.
296
313
  */
297
314
  this.inputFocusOut = output();
315
+ /**
316
+ * @description ID of the inner input, so a `<label for>` or `<mona-label for>` can point at it.
317
+ * @default a generated ID
318
+ */
319
+ this.inputId = input(createElementControlId(), /* @ts-ignore */
320
+ ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
298
321
  /**
299
322
  * @description Marks the text box as invalid. When bound to a signal form field via `[formField]`,
300
323
  * this is written by the `FormField` directive.
301
324
  * @default false
302
325
  */
303
- this.invalid = input(false, /* @ts-ignore */
304
- ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
326
+ this.invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
327
+ /**
328
+ * @description Sets whether the input can be empty. When `false`, leaving an empty field sets it to the
329
+ * minimum, or to zero when there is no minimum (clamped to the maximum).
330
+ */
331
+ this.nullable = input(true, { ...(ngDevMode ? { debugName: "nullable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
305
332
  /**
306
- * @description Sets whether the input can be empty.
333
+ * @description Text shown in the input while it is empty. It isn't a label: it disappears once there is a
334
+ * value.
335
+ * @default ""
307
336
  */
308
- this.nullable = input(true, /* @ts-ignore */
309
- ...(ngDevMode ? [{ debugName: "nullable" }] : /* istanbul ignore next */ []));
337
+ this.placeholder = input("", /* @ts-ignore */
338
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
310
339
  /**
311
- * @description Sets whether the input is readonly.
340
+ * @description Sets whether the input is readonly. The spin buttons are hidden while it is.
312
341
  */
313
- this.readonly = input(false, /* @ts-ignore */
314
- ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
342
+ this.readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
315
343
  /**
316
344
  * @description Sets whether the input is required.
317
345
  */
318
- this.required = input(false, /* @ts-ignore */
319
- ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
346
+ this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
320
347
  /**
321
348
  * @description Sets the border radius of the input.
322
349
  */
@@ -330,16 +357,15 @@ class NumericTextBoxBaseComponent {
330
357
  /**
331
358
  * @description Sets whether the spin buttons are visible.
332
359
  */
333
- this.spinners = input(true, /* @ts-ignore */
334
- ...(ngDevMode ? [{ debugName: "spinners" }] : /* istanbul ignore next */ []));
360
+ this.spinners = input(true, { ...(ngDevMode ? { debugName: "spinners" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
335
361
  /**
336
362
  * @description Tab index of the input.
337
363
  */
338
364
  this.tabindex = input(0, /* @ts-ignore */
339
365
  ...(ngDevMode ? [{ debugName: "tabindex" }] : /* istanbul ignore next */ []));
340
366
  /**
341
- * @description Emitted when the text box is interacted with on blur, value change, or spinner update.
342
- * The `FormField` directive listens to this to mark the field as touched.
367
+ * @description Emitted when focus leaves the component, as a native input is touched on blur. Typing and
368
+ * spinning don't count. The `FormField` directive listens to this to mark the field as touched.
343
369
  */
344
370
  this.touch = output();
345
371
  /**
@@ -347,13 +373,25 @@ class NumericTextBoxBaseComponent {
347
373
  * this is written by the `FormField` directive.
348
374
  * @default false
349
375
  */
350
- this.touched = input(false, /* @ts-ignore */
351
- ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
376
+ this.touched = input(false, { ...(ngDevMode ? { debugName: "touched" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
352
377
  /**
353
378
  * @description Additional CSS classes merged onto the host element via `tailwind-merge`.
354
379
  * @default ""
355
380
  */
356
381
  this.userClass = input("", { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: "class" });
382
+ effect(() => {
383
+ const value = this.value();
384
+ untracked(() => {
385
+ if (value === this.#ownValue) {
386
+ return;
387
+ }
388
+ // Set from outside (a form patch, a binding): show it, even while the user is in the field.
389
+ this.#ownValue = value;
390
+ if (this.focused() && !this.readonly()) {
391
+ this.rawInputValue.set(this.formatEditValue(value));
392
+ }
393
+ });
394
+ });
357
395
  effect(() => {
358
396
  this.#i18n.localeId();
359
397
  untracked(() => {
@@ -382,29 +420,40 @@ class NumericTextBoxBaseComponent {
382
420
  });
383
421
  this.#destroyRef.onDestroy(() => this.#focusMonitor.stopMonitoring(this.#hostElementRef.nativeElement));
384
422
  }
423
+ /** Steps the value down by `step` and moves focus to the input. */
385
424
  decrease() {
386
425
  this.spinValue("-");
426
+ this.focus();
387
427
  }
388
- focus() {
389
- this.#focusMonitor.focusVia(this.valueTextBoxRef(), "keyboard");
428
+ /**
429
+ * Moves focus to the input, with the caret at the end.
430
+ * @param options passed to the native `focus()` call, for example `{ preventScroll: true }`.
431
+ */
432
+ focus(options) {
433
+ this.#focusMonitor.focusVia(this.valueTextBoxRef(), "keyboard", options);
390
434
  rxTimeout(this.#destroyRef, () => {
391
435
  this.valueTextBoxRef().nativeElement.scrollLeft = this.valueTextBoxRef().nativeElement.scrollWidth;
392
436
  });
393
437
  }
438
+ /** Steps the value up by `step` and moves focus to the input. */
394
439
  increase() {
395
440
  this.spinValue("+");
441
+ this.focus();
396
442
  }
443
+ /** Runs when focus leaves an element inside the component: clamps the value and marks the field touched. */
397
444
  onBlur(event) {
445
+ // Removing a focused text box from the page fires focusout after it is destroyed.
446
+ if (this.#destroyRef.destroyed) {
447
+ return;
448
+ }
398
449
  this.#pendingPaste = false;
399
450
  const relatedTarget = event.relatedTarget;
400
- if (relatedTarget && this.#hostElementRef.nativeElement.contains(relatedTarget)) {
451
+ if (relatedTarget instanceof Node && this.#hostElementRef.nativeElement.contains(relatedTarget)) {
401
452
  return;
402
453
  }
403
- const corrected = this.correctValue();
404
- if (!corrected) {
405
- this.touch.emit();
406
- }
454
+ this.correctValue();
407
455
  this.rawInputValue.set(this.formatValueForDisplay(this.value()));
456
+ this.touch.emit();
408
457
  this.inputBlur.emit(event);
409
458
  }
410
459
  applyRawValue(text) {
@@ -434,40 +483,45 @@ class NumericTextBoxBaseComponent {
434
483
  }
435
484
  this.#pendingPaste = true;
436
485
  }
486
+ onSpinPointerDown(event, direction) {
487
+ // Only the primary button spins: a right-click opens the context menu and would never see a pointerup.
488
+ if (event.button !== 0) {
489
+ return;
490
+ }
491
+ this.spin$.next(direction);
492
+ }
493
+ /** Limits a value to the min and max bounds. */
494
+ clampValue(value) {
495
+ const min = this.minValue();
496
+ const max = this.maxValue();
497
+ if (min != null && this.compareValues(value, min) < 0) {
498
+ return min;
499
+ }
500
+ if (max != null && this.compareValues(value, max) > 0) {
501
+ return max;
502
+ }
503
+ return value;
504
+ }
437
505
  commitValue(value) {
438
- this.value.set(value);
506
+ this.setOwnValue(value);
439
507
  this.rawInputValue.set(this.focused() && !this.readonly() ? this.formatEditValue(value) : this.formatValueForDisplay(value));
440
- this.touch.emit();
441
508
  }
442
509
  correctValue() {
443
510
  const value = this.value();
444
- const min = this.minValue();
445
- const max = this.maxValue();
446
511
  if (value == null) {
447
512
  if (this.nullable()) {
448
513
  if (this.rawInputValue() !== "") {
449
514
  this.rawInputValue.set("");
450
515
  }
451
- return false;
452
- }
453
- else if (min != null) {
454
- this.commitValue(min);
455
- return true;
456
- }
457
- else {
458
- this.commitValue(this.zeroValue);
459
- return true;
516
+ return;
460
517
  }
518
+ this.commitValue(this.clampValue(this.minValue() ?? this.zeroValue));
519
+ return;
461
520
  }
462
- if (min != null && this.compareValues(value, min) < 0) {
463
- this.commitValue(min);
464
- return true;
521
+ const clamped = this.clampValue(value);
522
+ if (this.compareValues(clamped, value) !== 0) {
523
+ this.commitValue(clamped);
465
524
  }
466
- if (max != null && this.compareValues(value, max) > 0) {
467
- this.commitValue(max);
468
- return true;
469
- }
470
- return false;
471
525
  }
472
526
  formatEditValue(value) {
473
527
  if (value == null) {
@@ -575,28 +629,46 @@ class NumericTextBoxBaseComponent {
575
629
  }
576
630
  });
577
631
  }
578
- setInputFocusSubscription() {
579
- this.inputFocus.subscribe(() => this.#hostElementRef.nativeElement.focus());
580
- }
581
632
  setKeydownSubscription() {
582
633
  this.keydown$
583
- .pipe(takeUntilDestroyed(this.#destroyRef), filter(() => !this.readonly() && !this.disabled()))
634
+ .pipe(takeUntilDestroyed(this.#destroyRef),
635
+ // Keys pressed while an IME is composing belong to the IME (Safari reports them as keyCode 229).
636
+ filter(event => !event.isComposing && event.keyCode !== 229), filter(() => !this.readonly() && !this.disabled()))
584
637
  .subscribe((event) => {
585
- if (event.key === "ArrowUp") {
586
- event.preventDefault();
587
- this.increase();
588
- return;
589
- }
590
- if (event.key === "ArrowDown") {
591
- event.preventDefault();
592
- this.decrease();
593
- return;
638
+ switch (event.key) {
639
+ case "ArrowUp":
640
+ event.preventDefault();
641
+ this.increase();
642
+ return;
643
+ case "ArrowDown":
644
+ event.preventDefault();
645
+ this.decrease();
646
+ return;
647
+ case "PageUp":
648
+ event.preventDefault();
649
+ this.spinValue("+", PAGE_STEP_COUNT);
650
+ return;
651
+ case "PageDown":
652
+ event.preventDefault();
653
+ this.spinValue("-", PAGE_STEP_COUNT);
654
+ return;
655
+ case "Home":
656
+ // Without a minimum, Home keeps moving the caret.
657
+ this.spinToBound(event, this.minValue());
658
+ return;
659
+ case "End":
660
+ this.spinToBound(event, this.maxValue());
661
+ return;
594
662
  }
595
663
  if (event.code === "NumpadDecimal") {
596
664
  this.insertLocaleDecimal(event);
597
665
  }
598
666
  });
599
667
  }
668
+ setOwnValue(value) {
669
+ this.#ownValue = value;
670
+ this.value.set(value);
671
+ }
600
672
  setSpinSubscription() {
601
673
  this.spin$
602
674
  .pipe(filter(() => !this.readonly() && !this.disabled()), switchMap(sign => of(sign).pipe(tap(sign => (sign === "-" ? this.decrease() : this.increase())), delay(300), concatMap(() => timer(0, 30).pipe(tap(() => (sign === "-" ? this.decrease() : this.increase())), takeUntil(this.spinStop$))), takeUntil(this.spinStop$))), takeUntilDestroyed(this.#destroyRef))
@@ -607,73 +679,67 @@ class NumericTextBoxBaseComponent {
607
679
  this.setKeydownSubscription();
608
680
  this.setSpinSubscription();
609
681
  this.setWheelSubscription();
610
- this.setInputFocusSubscription();
611
682
  this.setBeforeInputSubscription();
612
683
  }
613
684
  setValueChangeSubscription() {
614
685
  this.valueChange$
615
- .pipe(takeUntilDestroyed(this.#destroyRef), distinctUntilChanged(), map(v => this.parseValue(v)))
616
- .subscribe(value => {
617
- this.value.set(value);
618
- this.touch.emit();
619
- });
686
+ .pipe(takeUntilDestroyed(this.#destroyRef), map(v => this.parseValue(v)))
687
+ .subscribe(value => this.setOwnValue(value));
620
688
  }
621
689
  setWheelSubscription() {
622
690
  this.wheel$
623
- .pipe(takeUntilDestroyed(this.#destroyRef), filter(() => !this.readonly() && !this.disabled()))
691
+ .pipe(takeUntilDestroyed(this.#destroyRef),
692
+ // The wheel spins only the field being edited: scrolling the page over an unfocused field, a
693
+ // horizontal scroll and Ctrl+wheel (zoom) are left to the browser.
694
+ filter(event => this.focused() && event.deltaY !== 0 && !event.ctrlKey), filter(() => !this.readonly() && !this.disabled()))
624
695
  .subscribe((event) => {
625
696
  event.preventDefault();
626
- if (event.deltaY < 0) {
627
- this.increase();
628
- }
629
- else {
630
- this.decrease();
631
- }
697
+ this.spinValue(event.deltaY < 0 ? "+" : "-");
632
698
  });
633
699
  }
634
- spinValue(direction) {
700
+ spinToBound(event, bound) {
701
+ if (bound == null) {
702
+ return;
703
+ }
704
+ event.preventDefault();
705
+ this.commitValue(bound);
706
+ }
707
+ spinValue(direction, steps = 1) {
635
708
  const value = this.value();
636
- const min = this.minValue();
637
- const max = this.maxValue();
638
709
  if (value == null) {
639
- let initial = this.zeroValue;
640
- if (min != null && this.compareValues(initial, min) < 0) {
641
- initial = min;
642
- }
643
- if (max != null && this.compareValues(initial, max) > 0) {
644
- initial = max;
645
- }
646
- this.commitValue(initial);
710
+ this.commitValue(this.clampValue(this.zeroValue));
711
+ return;
647
712
  }
648
- else {
649
- let result = this.stepValue(value, direction);
650
- if (direction === "-" && min != null && this.compareValues(result, min) < 0) {
651
- result = min;
652
- }
653
- if (direction === "+" && max != null && this.compareValues(result, max) > 0) {
654
- result = max;
655
- }
656
- this.commitValue(result);
713
+ let result = value;
714
+ for (let index = 0; index < steps; index++) {
715
+ result = this.stepValue(result, direction);
657
716
  }
658
- this.focus();
717
+ // A value outside the bounds (set from outside) comes back inside them in the direction of the step.
718
+ this.commitValue(this.clampValue(result));
659
719
  }
660
720
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NumericTextBoxBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
661
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.6", type: NumericTextBoxBaseComponent, isStandalone: true, inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, decimals: { classPropertyName: "decimals", publicName: "decimals", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, nullable: { classPropertyName: "nullable", publicName: "nullable", 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 }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, spinners: { classPropertyName: "spinners", publicName: "spinners", isSignal: true, isRequired: false, transformFunction: null }, tabindex: { classPropertyName: "tabindex", publicName: "tabindex", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { inputBlur: "inputBlur", inputFocus: "inputFocus", inputFocusOut: "inputFocusOut", touch: "touch" }, host: { properties: { "class": "classes()", "attr.data-disabled": "disabled() || null", "attr.data-invalid": "invalidInput() || null", "attr.data-readonly": "readonly() || null", "attr.data-required": "required() || null" } }, queries: [{ propertyName: "prefixTemplateList", predicate: NumericTextBoxPrefixTemplateDirective, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "valueTextBoxRef", first: true, predicate: ["valueTextBox"], descendants: true, isSignal: true }], ngImport: i0 }); }
721
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.6", type: NumericTextBoxBaseComponent, isStandalone: true, inputs: { ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, decimals: { classPropertyName: "decimals", publicName: "decimals", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, nullable: { classPropertyName: "nullable", publicName: "nullable", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", 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 }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, spinners: { classPropertyName: "spinners", publicName: "spinners", isSignal: true, isRequired: false, transformFunction: null }, tabindex: { classPropertyName: "tabindex", publicName: "tabindex", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { inputBlur: "inputBlur", inputFocus: "inputFocus", inputFocusOut: "inputFocusOut", touch: "touch" }, host: { listeners: { "focusout": "onBlur($event)" }, properties: { "attr.aria-describedby": "null", "attr.aria-label": "null", "attr.aria-labelledby": "null", "class": "classes()", "attr.data-disabled": "disabled() || null", "attr.data-invalid": "invalidInput() || null", "attr.data-readonly": "readonly() || null", "attr.data-required": "required() || null" } }, queries: [{ propertyName: "prefixTemplateList", predicate: NumericTextBoxPrefixTemplateDirective, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "valueTextBoxRef", first: true, predicate: ["valueTextBox"], descendants: true, isSignal: true }], ngImport: i0 }); }
662
722
  }
663
723
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NumericTextBoxBaseComponent, decorators: [{
664
724
  type: Directive,
665
725
  args: [{
666
726
  host: {
727
+ // The host has no role: the inner input is the spinbutton and carries the name, description and state. A
728
+ // static aria-* attribute written on the host element would otherwise stay on the host as well.
729
+ "[attr.aria-describedby]": "null",
730
+ "[attr.aria-label]": "null",
731
+ "[attr.aria-labelledby]": "null",
667
732
  "[class]": "classes()",
668
733
  "[attr.data-disabled]": "disabled() || null",
669
734
  "[attr.data-invalid]": "invalidInput() || null",
670
735
  "[attr.data-readonly]": "readonly() || null",
671
- "[attr.data-required]": "required() || null"
736
+ "[attr.data-required]": "required() || null",
737
+ "(focusout)": "onBlur($event)"
672
738
  }
673
739
  }]
674
740
  }], ctorParameters: () => [], propDecorators: { prefixTemplateList: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NumericTextBoxPrefixTemplateDirective), { ...{
675
741
  read: TemplateRef
676
- }, isSignal: true }] }], valueTextBoxRef: [{ type: i0.ViewChild, args: ["valueTextBox", { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], decimals: [{ type: i0.Input, args: [{ isSignal: true, alias: "decimals", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], inputBlur: [{ type: i0.Output, args: ["inputBlur"] }], inputFocus: [{ type: i0.Output, args: ["inputFocus"] }], inputFocusOut: [{ type: i0.Output, args: ["inputFocusOut"] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], nullable: [{ type: i0.Input, args: [{ isSignal: true, alias: "nullable", 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 }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], spinners: [{ type: i0.Input, args: [{ isSignal: true, alias: "spinners", required: false }] }], tabindex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabindex", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
742
+ }, isSignal: true }] }], valueTextBoxRef: [{ type: i0.ViewChild, args: ["valueTextBox", { isSignal: true }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], decimals: [{ type: i0.Input, args: [{ isSignal: true, alias: "decimals", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], inputBlur: [{ type: i0.Output, args: ["inputBlur"] }], inputFocus: [{ type: i0.Output, args: ["inputFocus"] }], inputFocusOut: [{ type: i0.Output, args: ["inputFocusOut"] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], nullable: [{ type: i0.Input, args: [{ isSignal: true, alias: "nullable", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", 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 }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], spinners: [{ type: i0.Input, args: [{ isSignal: true, alias: "spinners", required: false }] }], tabindex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabindex", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
677
743
 
678
744
  function warnInvalidDecimal(name, value) {
679
745
  if (typeof ngDevMode !== "undefined" && ngDevMode) {
@@ -746,11 +812,11 @@ class DecimalTextBoxComponent extends NumericTextBoxBaseComponent {
746
812
  return direction === "+" ? addDecimals(current, this.step()) : subtractDecimals(current, this.step());
747
813
  }
748
814
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: DecimalTextBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
749
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: DecimalTextBoxComponent, isStandalone: true, selector: "mona-decimal-text-box", inputs: { formatter: { classPropertyName: "formatter", publicName: "formatter", isSignal: true, isRequired: false, transformFunction: null }, maxValue: { classPropertyName: "maxValue", publicName: "maxValue", isSignal: true, isRequired: false, transformFunction: null }, minValue: { classPropertyName: "minValue", publicName: "minValue", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, usesInheritance: true, ngImport: i0, template: "@for (prefixTemplate of prefixTemplateList(); track $index) {\n <span class=\"flex h-full flex-none flex-wrap items-center overflow-hidden border-none\">\n <ng-container *ngTemplateOutlet=\"prefixTemplate\"></ng-container>\n </span>\n}\n<input\n monaTextBox\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [type]=\"'text'\"\n [attr.inputmode]=\"decimals() > 0 ? 'decimal' : 'numeric'\"\n [readonly]=\"readonly()\"\n [tabIndex]=\"tabindex()\"\n [ngModel]=\"formattedValue()\"\n (ngModelChange)=\"applyRawValue($event)\"\n (keydown)=\"keydown$.next($event)\"\n (focus)=\"inputFocus.emit($event)\"\n (focusout)=\"inputFocusOut.emit($event)\"\n (blur)=\"onBlur($event)\"\n (beforeinput)=\"beforeInput$.next($event)\"\n (paste)=\"onPaste($event)\"\n (wheel)=\"wheel$.next($event)\"\n [size]=\"size()\"\n [class]=\"inputClasses()\"\n autocomplete=\"off\"\n role=\"spinbutton\"\n [attr.aria-disabled]=\"disabled()\"\n [attr.aria-invalid]=\"invalidInput() || null\"\n [attr.aria-readonly]=\"readonly()\"\n [attr.aria-required]=\"required()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuemin]=\"minValue()\"\n [attr.aria-valuemax]=\"maxValue()\"\n [attr.aria-valuetext]=\"formattedValue()\"\n [attr.data-spinners]=\"spinners()\"\n #valueTextBox />\n@if (spinners()) {\n <div [class]=\"spinButtonClasses()\">\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"spin$.next('+')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().increase\">\n <svg lucideChevronUp [size]=\"spinButtonIconSize()\"></svg>\n </button>\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"spin$.next('-')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().decrease\">\n <svg lucideChevronDown [size]=\"spinButtonIconSize()\"></svg>\n </button>\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TextBoxDirective, selector: "input[monaTextBox]", inputs: ["invalid", "rounded", "size", "touched", "class"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: "component", type: LucideChevronUp, selector: "svg[lucideChevronUp]" }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }] }); }
815
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: DecimalTextBoxComponent, isStandalone: true, selector: "mona-decimal-text-box", inputs: { formatter: { classPropertyName: "formatter", publicName: "formatter", isSignal: true, isRequired: false, transformFunction: null }, maxValue: { classPropertyName: "maxValue", publicName: "maxValue", isSignal: true, isRequired: false, transformFunction: null }, minValue: { classPropertyName: "minValue", publicName: "minValue", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, usesInheritance: true, ngImport: i0, template: "@for (prefixTemplate of prefixTemplateList(); track $index) {\n <span class=\"flex h-full flex-none flex-wrap items-center overflow-hidden border-none\">\n <ng-container *ngTemplateOutlet=\"prefixTemplate\"></ng-container>\n </span>\n}\n<input\n monaTextBox\n [id]=\"inputId()\"\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [type]=\"'text'\"\n [attr.inputmode]=\"decimals() > 0 ? 'decimal' : 'numeric'\"\n [readonly]=\"readonly()\"\n [tabIndex]=\"tabindex()\"\n [placeholder]=\"placeholder()\"\n [value]=\"formattedValue()\"\n (input)=\"applyRawValue(valueTextBox.value)\"\n (keydown)=\"keydown$.next($event)\"\n (focus)=\"inputFocus.emit($event)\"\n (focusout)=\"inputFocusOut.emit($event)\"\n (beforeinput)=\"beforeInput$.next($event)\"\n (paste)=\"onPaste($event)\"\n (wheel)=\"wheel$.next($event)\"\n [size]=\"size()\"\n [class]=\"inputClasses()\"\n autocomplete=\"off\"\n role=\"spinbutton\"\n [attr.aria-describedby]=\"ariaDescribedBy() || null\"\n [attr.aria-invalid]=\"invalidInput() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || null\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuemin]=\"minValue()\"\n [attr.aria-valuemax]=\"maxValue()\"\n [attr.aria-valuetext]=\"formattedValue() || null\"\n [attr.data-spinners]=\"showSpinners()\"\n #valueTextBox />\n@if (showSpinners()) {\n <div [class]=\"spinButtonClasses()\">\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"onSpinPointerDown($event, '+')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().increase\">\n <svg lucideChevronUp [size]=\"spinButtonIconSize()\"></svg>\n </button>\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"onSpinPointerDown($event, '-')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().decrease\">\n <svg lucideChevronDown [size]=\"spinButtonIconSize()\"></svg>\n </button>\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TextBoxDirective, selector: "input[monaTextBox]", inputs: ["invalid", "rounded", "size", "touched", "class"] }, { 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: "component", type: LucideChevronUp, selector: "svg[lucideChevronUp]" }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }] }); }
750
816
  }
751
817
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: DecimalTextBoxComponent, decorators: [{
752
818
  type: Component,
753
- args: [{ selector: "mona-decimal-text-box", imports: [NgTemplateOutlet, TextBoxDirective, FormsModule, ButtonDirective, LucideChevronUp, LucideChevronDown], template: "@for (prefixTemplate of prefixTemplateList(); track $index) {\n <span class=\"flex h-full flex-none flex-wrap items-center overflow-hidden border-none\">\n <ng-container *ngTemplateOutlet=\"prefixTemplate\"></ng-container>\n </span>\n}\n<input\n monaTextBox\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [type]=\"'text'\"\n [attr.inputmode]=\"decimals() > 0 ? 'decimal' : 'numeric'\"\n [readonly]=\"readonly()\"\n [tabIndex]=\"tabindex()\"\n [ngModel]=\"formattedValue()\"\n (ngModelChange)=\"applyRawValue($event)\"\n (keydown)=\"keydown$.next($event)\"\n (focus)=\"inputFocus.emit($event)\"\n (focusout)=\"inputFocusOut.emit($event)\"\n (blur)=\"onBlur($event)\"\n (beforeinput)=\"beforeInput$.next($event)\"\n (paste)=\"onPaste($event)\"\n (wheel)=\"wheel$.next($event)\"\n [size]=\"size()\"\n [class]=\"inputClasses()\"\n autocomplete=\"off\"\n role=\"spinbutton\"\n [attr.aria-disabled]=\"disabled()\"\n [attr.aria-invalid]=\"invalidInput() || null\"\n [attr.aria-readonly]=\"readonly()\"\n [attr.aria-required]=\"required()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuemin]=\"minValue()\"\n [attr.aria-valuemax]=\"maxValue()\"\n [attr.aria-valuetext]=\"formattedValue()\"\n [attr.data-spinners]=\"spinners()\"\n #valueTextBox />\n@if (spinners()) {\n <div [class]=\"spinButtonClasses()\">\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"spin$.next('+')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().increase\">\n <svg lucideChevronUp [size]=\"spinButtonIconSize()\"></svg>\n </button>\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"spin$.next('-')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().decrease\">\n <svg lucideChevronDown [size]=\"spinButtonIconSize()\"></svg>\n </button>\n </div>\n}\n" }]
819
+ args: [{ selector: "mona-decimal-text-box", imports: [NgTemplateOutlet, TextBoxDirective, ButtonDirective, LucideChevronUp, LucideChevronDown], template: "@for (prefixTemplate of prefixTemplateList(); track $index) {\n <span class=\"flex h-full flex-none flex-wrap items-center overflow-hidden border-none\">\n <ng-container *ngTemplateOutlet=\"prefixTemplate\"></ng-container>\n </span>\n}\n<input\n monaTextBox\n [id]=\"inputId()\"\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [type]=\"'text'\"\n [attr.inputmode]=\"decimals() > 0 ? 'decimal' : 'numeric'\"\n [readonly]=\"readonly()\"\n [tabIndex]=\"tabindex()\"\n [placeholder]=\"placeholder()\"\n [value]=\"formattedValue()\"\n (input)=\"applyRawValue(valueTextBox.value)\"\n (keydown)=\"keydown$.next($event)\"\n (focus)=\"inputFocus.emit($event)\"\n (focusout)=\"inputFocusOut.emit($event)\"\n (beforeinput)=\"beforeInput$.next($event)\"\n (paste)=\"onPaste($event)\"\n (wheel)=\"wheel$.next($event)\"\n [size]=\"size()\"\n [class]=\"inputClasses()\"\n autocomplete=\"off\"\n role=\"spinbutton\"\n [attr.aria-describedby]=\"ariaDescribedBy() || null\"\n [attr.aria-invalid]=\"invalidInput() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || null\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuemin]=\"minValue()\"\n [attr.aria-valuemax]=\"maxValue()\"\n [attr.aria-valuetext]=\"formattedValue() || null\"\n [attr.data-spinners]=\"showSpinners()\"\n #valueTextBox />\n@if (showSpinners()) {\n <div [class]=\"spinButtonClasses()\">\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"onSpinPointerDown($event, '+')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().increase\">\n <svg lucideChevronUp [size]=\"spinButtonIconSize()\"></svg>\n </button>\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"onSpinPointerDown($event, '-')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().decrease\">\n <svg lucideChevronDown [size]=\"spinButtonIconSize()\"></svg>\n </button>\n </div>\n}\n" }]
754
820
  }], ctorParameters: () => [], propDecorators: { formatter: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatter", required: false }] }], maxValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxValue", required: false }] }], minValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "minValue", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
755
821
 
756
822
  class NumericTextBoxComponent extends NumericTextBoxBaseComponent {
@@ -792,34 +858,41 @@ class NumericTextBoxComponent extends NumericTextBoxBaseComponent {
792
858
  const newValue = (value * factor + signFactor * step * factor) / factor;
793
859
  return precision > 0 ? parseFloat(newValue.toFixed(precision)) : newValue;
794
860
  }
861
+ /** The number of fraction digits, including those of exponent forms such as `1e-7`. */
795
862
  static getPrecision(value) {
796
- const valueString = value.toString();
797
- if (valueString.includes(".")) {
798
- const parts = valueString.split(".");
799
- return parts[1].length;
800
- }
801
- return 0;
863
+ const [mantissa, exponent] = value.toString().split("e");
864
+ const fractionDigits = mantissa.split(".")[1]?.length ?? 0;
865
+ return Math.max(0, fractionDigits - Number(exponent ?? 0));
866
+ }
867
+ /** -0 reads and shows as "-0"; the field treats it as zero. */
868
+ static withoutNegativeZero(result) {
869
+ return Object.is(result.value, -0) ? { ...result, value: 0 } : result;
802
870
  }
803
871
  compareValues(a, b) {
804
872
  return a - b;
805
873
  }
806
874
  readEditText(text, localeId, decimals) {
807
- return validateLocalizedNumberEdit(text, localeId, { decimals });
875
+ return NumericTextBoxComponent.withoutNegativeZero(validateLocalizedNumberEdit(text, localeId, { decimals }));
808
876
  }
809
877
  readLocaleText(text, localeId, decimals) {
810
- return validateLocalizedNumber(text, localeId, { mode: "locale", decimals });
878
+ return NumericTextBoxComponent.withoutNegativeZero(validateLocalizedNumber(text, localeId, { mode: "locale", decimals }));
811
879
  }
812
880
  stepValue(value, direction) {
813
881
  const result = NumericTextBoxComponent.calculate(value, this.step(), direction);
814
882
  // Past the safe range a number can no longer hold every step exactly.
815
- return Math.min(Math.max(result, Number.MIN_SAFE_INTEGER), Number.MAX_SAFE_INTEGER);
883
+ const limited = Math.min(Math.max(result, Number.MIN_SAFE_INTEGER), Number.MAX_SAFE_INTEGER);
884
+ // A value already past that range (set from outside) stays put rather than jumping the wrong way.
885
+ if ((direction === "+" && limited < value) || (direction === "-" && limited > value)) {
886
+ return value;
887
+ }
888
+ return limited;
816
889
  }
817
890
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NumericTextBoxComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
818
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: NumericTextBoxComponent, isStandalone: true, selector: "mona-numeric-text-box", inputs: { formatter: { classPropertyName: "formatter", publicName: "formatter", isSignal: true, isRequired: false, transformFunction: null }, maxValue: { classPropertyName: "maxValue", publicName: "maxValue", isSignal: true, isRequired: false, transformFunction: null }, minValue: { classPropertyName: "minValue", publicName: "minValue", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, usesInheritance: true, ngImport: i0, template: "@for (prefixTemplate of prefixTemplateList(); track $index) {\n <span class=\"flex h-full flex-none flex-wrap items-center overflow-hidden border-none\">\n <ng-container *ngTemplateOutlet=\"prefixTemplate\"></ng-container>\n </span>\n}\n<input\n monaTextBox\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [type]=\"'text'\"\n [attr.inputmode]=\"decimals() > 0 ? 'decimal' : 'numeric'\"\n [readonly]=\"readonly()\"\n [tabIndex]=\"tabindex()\"\n [ngModel]=\"formattedValue()\"\n (ngModelChange)=\"applyRawValue($event)\"\n (keydown)=\"keydown$.next($event)\"\n (focus)=\"inputFocus.emit($event)\"\n (focusout)=\"inputFocusOut.emit($event)\"\n (blur)=\"onBlur($event)\"\n (beforeinput)=\"beforeInput$.next($event)\"\n (paste)=\"onPaste($event)\"\n (wheel)=\"wheel$.next($event)\"\n [size]=\"size()\"\n [class]=\"inputClasses()\"\n autocomplete=\"off\"\n role=\"spinbutton\"\n [attr.aria-disabled]=\"disabled()\"\n [attr.aria-invalid]=\"invalidInput() || null\"\n [attr.aria-readonly]=\"readonly()\"\n [attr.aria-required]=\"required()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuemin]=\"minValue()\"\n [attr.aria-valuemax]=\"maxValue()\"\n [attr.aria-valuetext]=\"formattedValue()\"\n [attr.data-spinners]=\"spinners()\"\n #valueTextBox />\n@if (spinners()) {\n <div [class]=\"spinButtonClasses()\">\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"spin$.next('+')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().increase\">\n <svg lucideChevronUp [size]=\"spinButtonIconSize()\"></svg>\n </button>\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"spin$.next('-')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().decrease\">\n <svg lucideChevronDown [size]=\"spinButtonIconSize()\"></svg>\n </button>\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TextBoxDirective, selector: "input[monaTextBox]", inputs: ["invalid", "rounded", "size", "touched", "class"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: "component", type: LucideChevronUp, selector: "svg[lucideChevronUp]" }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }] }); }
891
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: NumericTextBoxComponent, isStandalone: true, selector: "mona-numeric-text-box", inputs: { formatter: { classPropertyName: "formatter", publicName: "formatter", isSignal: true, isRequired: false, transformFunction: null }, maxValue: { classPropertyName: "maxValue", publicName: "maxValue", isSignal: true, isRequired: false, transformFunction: null }, minValue: { classPropertyName: "minValue", publicName: "minValue", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, usesInheritance: true, ngImport: i0, template: "@for (prefixTemplate of prefixTemplateList(); track $index) {\n <span class=\"flex h-full flex-none flex-wrap items-center overflow-hidden border-none\">\n <ng-container *ngTemplateOutlet=\"prefixTemplate\"></ng-container>\n </span>\n}\n<input\n monaTextBox\n [id]=\"inputId()\"\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [type]=\"'text'\"\n [attr.inputmode]=\"decimals() > 0 ? 'decimal' : 'numeric'\"\n [readonly]=\"readonly()\"\n [tabIndex]=\"tabindex()\"\n [placeholder]=\"placeholder()\"\n [value]=\"formattedValue()\"\n (input)=\"applyRawValue(valueTextBox.value)\"\n (keydown)=\"keydown$.next($event)\"\n (focus)=\"inputFocus.emit($event)\"\n (focusout)=\"inputFocusOut.emit($event)\"\n (beforeinput)=\"beforeInput$.next($event)\"\n (paste)=\"onPaste($event)\"\n (wheel)=\"wheel$.next($event)\"\n [size]=\"size()\"\n [class]=\"inputClasses()\"\n autocomplete=\"off\"\n role=\"spinbutton\"\n [attr.aria-describedby]=\"ariaDescribedBy() || null\"\n [attr.aria-invalid]=\"invalidInput() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || null\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuemin]=\"minValue()\"\n [attr.aria-valuemax]=\"maxValue()\"\n [attr.aria-valuetext]=\"formattedValue() || null\"\n [attr.data-spinners]=\"showSpinners()\"\n #valueTextBox />\n@if (showSpinners()) {\n <div [class]=\"spinButtonClasses()\">\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"onSpinPointerDown($event, '+')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().increase\">\n <svg lucideChevronUp [size]=\"spinButtonIconSize()\"></svg>\n </button>\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"onSpinPointerDown($event, '-')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().decrease\">\n <svg lucideChevronDown [size]=\"spinButtonIconSize()\"></svg>\n </button>\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TextBoxDirective, selector: "input[monaTextBox]", inputs: ["invalid", "rounded", "size", "touched", "class"] }, { 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: "component", type: LucideChevronUp, selector: "svg[lucideChevronUp]" }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }] }); }
819
892
  }
820
893
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NumericTextBoxComponent, decorators: [{
821
894
  type: Component,
822
- args: [{ selector: "mona-numeric-text-box", imports: [NgTemplateOutlet, TextBoxDirective, FormsModule, ButtonDirective, LucideChevronUp, LucideChevronDown], template: "@for (prefixTemplate of prefixTemplateList(); track $index) {\n <span class=\"flex h-full flex-none flex-wrap items-center overflow-hidden border-none\">\n <ng-container *ngTemplateOutlet=\"prefixTemplate\"></ng-container>\n </span>\n}\n<input\n monaTextBox\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [type]=\"'text'\"\n [attr.inputmode]=\"decimals() > 0 ? 'decimal' : 'numeric'\"\n [readonly]=\"readonly()\"\n [tabIndex]=\"tabindex()\"\n [ngModel]=\"formattedValue()\"\n (ngModelChange)=\"applyRawValue($event)\"\n (keydown)=\"keydown$.next($event)\"\n (focus)=\"inputFocus.emit($event)\"\n (focusout)=\"inputFocusOut.emit($event)\"\n (blur)=\"onBlur($event)\"\n (beforeinput)=\"beforeInput$.next($event)\"\n (paste)=\"onPaste($event)\"\n (wheel)=\"wheel$.next($event)\"\n [size]=\"size()\"\n [class]=\"inputClasses()\"\n autocomplete=\"off\"\n role=\"spinbutton\"\n [attr.aria-disabled]=\"disabled()\"\n [attr.aria-invalid]=\"invalidInput() || null\"\n [attr.aria-readonly]=\"readonly()\"\n [attr.aria-required]=\"required()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuemin]=\"minValue()\"\n [attr.aria-valuemax]=\"maxValue()\"\n [attr.aria-valuetext]=\"formattedValue()\"\n [attr.data-spinners]=\"spinners()\"\n #valueTextBox />\n@if (spinners()) {\n <div [class]=\"spinButtonClasses()\">\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"spin$.next('+')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().increase\">\n <svg lucideChevronUp [size]=\"spinButtonIconSize()\"></svg>\n </button>\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"spin$.next('-')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().decrease\">\n <svg lucideChevronDown [size]=\"spinButtonIconSize()\"></svg>\n </button>\n </div>\n}\n" }]
895
+ args: [{ selector: "mona-numeric-text-box", imports: [NgTemplateOutlet, TextBoxDirective, ButtonDirective, LucideChevronUp, LucideChevronDown], template: "@for (prefixTemplate of prefixTemplateList(); track $index) {\n <span class=\"flex h-full flex-none flex-wrap items-center overflow-hidden border-none\">\n <ng-container *ngTemplateOutlet=\"prefixTemplate\"></ng-container>\n </span>\n}\n<input\n monaTextBox\n [id]=\"inputId()\"\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [type]=\"'text'\"\n [attr.inputmode]=\"decimals() > 0 ? 'decimal' : 'numeric'\"\n [readonly]=\"readonly()\"\n [tabIndex]=\"tabindex()\"\n [placeholder]=\"placeholder()\"\n [value]=\"formattedValue()\"\n (input)=\"applyRawValue(valueTextBox.value)\"\n (keydown)=\"keydown$.next($event)\"\n (focus)=\"inputFocus.emit($event)\"\n (focusout)=\"inputFocusOut.emit($event)\"\n (beforeinput)=\"beforeInput$.next($event)\"\n (paste)=\"onPaste($event)\"\n (wheel)=\"wheel$.next($event)\"\n [size]=\"size()\"\n [class]=\"inputClasses()\"\n autocomplete=\"off\"\n role=\"spinbutton\"\n [attr.aria-describedby]=\"ariaDescribedBy() || null\"\n [attr.aria-invalid]=\"invalidInput() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || null\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuemin]=\"minValue()\"\n [attr.aria-valuemax]=\"maxValue()\"\n [attr.aria-valuetext]=\"formattedValue() || null\"\n [attr.data-spinners]=\"showSpinners()\"\n #valueTextBox />\n@if (showSpinners()) {\n <div [class]=\"spinButtonClasses()\">\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"onSpinPointerDown($event, '+')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().increase\">\n <svg lucideChevronUp [size]=\"spinButtonIconSize()\"></svg>\n </button>\n <button\n monaButton\n [disabled]=\"disabled()\"\n [tabindex]=\"-1\"\n look=\"ghost\"\n [size]=\"size()\"\n (mousedown)=\"$event.preventDefault()\"\n (pointerdown)=\"onSpinPointerDown($event, '-')\"\n (pointerup)=\"spinStop$.next()\"\n (pointerleave)=\"spinStop$.next()\"\n [attr.aria-label]=\"messages().decrease\">\n <svg lucideChevronDown [size]=\"spinButtonIconSize()\"></svg>\n </button>\n </div>\n}\n" }]
823
896
  }], propDecorators: { formatter: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatter", required: false }] }], maxValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxValue", required: false }] }], minValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "minValue", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
824
897
 
825
898
  /*