@nanahoshi/mona-ui 0.24.5 → 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.
- package/README.md +2 -0
- package/fesm2022/nanahoshi-mona-ui-calendar.mjs +196 -137
- package/fesm2022/nanahoshi-mona-ui-date-input.mjs +164 -3
- package/fesm2022/nanahoshi-mona-ui-date-picker.mjs +124 -140
- package/fesm2022/nanahoshi-mona-ui-datetime-picker.mjs +205 -202
- package/fesm2022/nanahoshi-mona-ui-filter.mjs +1 -1
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-row.component-BMCM0W8s.mjs → nanahoshi-mona-ui-grid-grid-footer-row.component-CD_ODHpU.mjs} +2 -2
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-list.component-BSlV8Sua.mjs → nanahoshi-mona-ui-grid-grid-list.component-0jN7I1jo.mjs} +4 -4
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-row.component-CTOdnH29.mjs → nanahoshi-mona-ui-grid-grid-row.component-BI_iE7pl.mjs} +3 -3
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-C2kf5MwO.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-Sa1aAtJ-.mjs} +4 -4
- package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-Dh42tUui.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-BvUM9nPt.mjs} +8 -8
- package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-numeric-text-box.mjs +195 -122
- package/fesm2022/nanahoshi-mona-ui-time-picker.mjs +149 -167
- package/fesm2022/nanahoshi-mona-ui-time-selector.mjs +182 -137
- package/package.json +2 -2
- package/types/nanahoshi-mona-ui-calendar.d.ts +30 -11
- package/types/nanahoshi-mona-ui-date-input.d.ts +74 -3
- package/types/nanahoshi-mona-ui-date-picker.d.ts +61 -33
- package/types/nanahoshi-mona-ui-datetime-picker.d.ts +79 -38
- package/types/nanahoshi-mona-ui-numeric-text-box.d.ts +57 -16
- package/types/nanahoshi-mona-ui-time-picker.d.ts +72 -42
- 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,
|
|
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-
|
|
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-
|
|
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.
|
|
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
|
|
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, /*
|
|
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
|
|
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, /*
|
|
304
|
-
|
|
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
|
|
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.
|
|
309
|
-
...(ngDevMode ? [{ debugName: "
|
|
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, /*
|
|
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, /*
|
|
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, /*
|
|
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
|
|
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, /*
|
|
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
|
-
|
|
389
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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),
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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),
|
|
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),
|
|
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
|
-
|
|
627
|
-
this.increase();
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
this.decrease();
|
|
631
|
-
}
|
|
697
|
+
this.spinValue(event.deltaY < 0 ? "+" : "-");
|
|
632
698
|
});
|
|
633
699
|
}
|
|
634
|
-
|
|
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
|
-
|
|
640
|
-
|
|
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
|
-
|
|
649
|
-
|
|
650
|
-
|
|
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
|
-
|
|
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 [
|
|
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,
|
|
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
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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
|
-
|
|
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 [
|
|
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,
|
|
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
|
/*
|