@nanahoshi/mona-ui 0.23.2 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/nanahoshi-mona-ui-auto-complete.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-breadcrumb.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-button-group.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-calendar.mjs +15 -10
- package/fesm2022/nanahoshi-mona-ui-card.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-chart.mjs +4 -2
- package/fesm2022/nanahoshi-mona-ui-chip.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-color-gradient.mjs +8 -3
- package/fesm2022/nanahoshi-mona-ui-color-palette.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-color-picker.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-combo-box.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-date-picker.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-datetime-picker.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-dialog.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-dropdown-list.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-dropdowns.mjs +8 -3
- package/fesm2022/nanahoshi-mona-ui-editor.mjs +8 -3
- package/fesm2022/nanahoshi-mona-ui-filter.mjs +3 -1
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-row.component-CIMXz60d.mjs → nanahoshi-mona-ui-grid-grid-footer-row.component-CnPhVIr4.mjs} +2 -2
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-list.component-CpeheGXU.mjs → nanahoshi-mona-ui-grid-grid-list.component-DxY6bfTX.mjs} +4 -4
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-row.component-B_5ay_Nz.mjs → nanahoshi-mona-ui-grid-grid-row.component-CaO5Lae6.mjs} +3 -3
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-BzKywU5b.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-jNYtqg5B.mjs} +4 -4
- package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-IewqeT4m.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-9FtfCutf.mjs} +18 -13
- package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-i18n-icu.mjs +888 -0
- package/fesm2022/nanahoshi-mona-ui-i18n.mjs +398 -116
- package/fesm2022/nanahoshi-mona-ui-internal-list.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-internal-tree.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-list-box.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-locales-ar-sa.mjs +401 -0
- package/fesm2022/nanahoshi-mona-ui-locales-de-de.mjs +399 -0
- package/fesm2022/nanahoshi-mona-ui-locales-es-es.mjs +399 -0
- package/fesm2022/nanahoshi-mona-ui-locales-fr-fr.mjs +399 -0
- package/fesm2022/nanahoshi-mona-ui-locales-id-id.mjs +397 -0
- package/fesm2022/nanahoshi-mona-ui-locales-it-it.mjs +399 -0
- package/fesm2022/nanahoshi-mona-ui-locales-ja-jp.mjs +397 -0
- package/fesm2022/nanahoshi-mona-ui-locales-ko-kr.mjs +397 -0
- package/fesm2022/nanahoshi-mona-ui-locales-pt-br.mjs +399 -0
- package/fesm2022/nanahoshi-mona-ui-locales-ru-ru.mjs +401 -0
- package/fesm2022/nanahoshi-mona-ui-locales-tr-tr.mjs +397 -0
- package/fesm2022/nanahoshi-mona-ui-locales-zh-cn.mjs +397 -0
- package/fesm2022/nanahoshi-mona-ui-locales-zh-tw.mjs +397 -0
- package/fesm2022/nanahoshi-mona-ui-locales.mjs +77 -4988
- package/fesm2022/nanahoshi-mona-ui-multi-select.mjs +7 -2
- package/fesm2022/nanahoshi-mona-ui-notification.mjs +19 -2
- package/fesm2022/nanahoshi-mona-ui-numeric-text-box.mjs +299 -167
- package/fesm2022/nanahoshi-mona-ui-otp-input.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-pager.mjs +13 -8
- package/fesm2022/nanahoshi-mona-ui-popup.mjs +15 -1
- package/fesm2022/nanahoshi-mona-ui-rating.mjs +7 -2
- package/fesm2022/nanahoshi-mona-ui-scroll-view.mjs +9 -4
- package/fesm2022/nanahoshi-mona-ui-sheet.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-slider.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-spinner.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-split-button.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-splitter.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-stepper.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-tabs.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-text-box.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-time-picker.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-time-selector.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-window.mjs +2 -0
- package/i18n/catalogs/en-US.messages.json +373 -0
- package/i18n/icu/package.json +4 -0
- package/locales/ar-sa/ar-sa.messages.json +373 -0
- package/locales/ar-sa/package.json +4 -0
- package/locales/de-de/de-de.messages.json +373 -0
- package/locales/de-de/package.json +4 -0
- package/locales/es-es/es-es.messages.json +373 -0
- package/locales/es-es/package.json +4 -0
- package/locales/fr-fr/fr-fr.messages.json +373 -0
- package/locales/fr-fr/package.json +4 -0
- package/locales/id-id/id-id.messages.json +373 -0
- package/locales/id-id/package.json +4 -0
- package/locales/it-it/it-it.messages.json +373 -0
- package/locales/it-it/package.json +4 -0
- package/locales/ja-jp/ja-jp.messages.json +373 -0
- package/locales/ja-jp/package.json +4 -0
- package/locales/ko-kr/ko-kr.messages.json +373 -0
- package/locales/ko-kr/package.json +4 -0
- package/locales/pt-br/package.json +4 -0
- package/locales/pt-br/pt-br.messages.json +373 -0
- package/locales/ru-ru/package.json +4 -0
- package/locales/ru-ru/ru-ru.messages.json +373 -0
- package/locales/tr-tr/package.json +4 -0
- package/locales/tr-tr/tr-tr.messages.json +373 -0
- package/locales/zh-cn/package.json +4 -0
- package/locales/zh-cn/zh-cn.messages.json +373 -0
- package/locales/zh-tw/package.json +4 -0
- package/locales/zh-tw/zh-tw.messages.json +373 -0
- package/package.json +111 -53
- package/types/nanahoshi-mona-ui-i18n-icu.d.ts +74 -0
- package/types/nanahoshi-mona-ui-i18n.d.ts +139 -9
- package/types/nanahoshi-mona-ui-locales-ar-sa.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-de-de.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-es-es.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-fr-fr.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-id-id.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-it-it.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-ja-jp.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-ko-kr.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-pt-br.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-ru-ru.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-tr-tr.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-zh-cn.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales-zh-tw.d.ts +379 -0
- package/types/nanahoshi-mona-ui-locales.d.ts +30 -4915
- package/types/nanahoshi-mona-ui-notification.d.ts +1 -0
- package/types/nanahoshi-mona-ui-numeric-text-box.d.ts +113 -37
|
@@ -1,19 +1,79 @@
|
|
|
1
|
-
import { FocusMonitor } from '@angular/cdk/a11y';
|
|
2
1
|
import { NgTemplateOutlet } from '@angular/common';
|
|
3
2
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { Directive, inject, DestroyRef, ElementRef, computed, signal, contentChildren, TemplateRef, viewChild, input, output,
|
|
5
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
3
|
+
import { Directive, inject, DestroyRef, ElementRef, computed, signal, contentChildren, TemplateRef, viewChild, input, output, effect, untracked, afterNextRender, model, Component } from '@angular/core';
|
|
6
4
|
import * as i1 from '@angular/forms';
|
|
7
5
|
import { FormsModule } from '@angular/forms';
|
|
8
6
|
import { LucideChevronUp, LucideChevronDown } from '@lucide/angular';
|
|
9
7
|
import { ButtonDirective } from '@nanahoshi/mona-ui/button';
|
|
8
|
+
import { MonaI18nService, formatNumber, getNumberSymbols, validateLocalizedDecimalEdit, validateLocalizedDecimal, validateLocalizedNumberEdit, validateLocalizedNumber } from '@nanahoshi/mona-ui/i18n';
|
|
9
|
+
import { TextBoxDirective } from '@nanahoshi/mona-ui/text-box';
|
|
10
|
+
import { FocusMonitor } from '@angular/cdk/a11y';
|
|
11
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
10
12
|
import { themeControlSurfaceClasses, rxTimeout } from '@nanahoshi/mona-ui/internal';
|
|
11
13
|
import { Subject, filter, switchMap, of, tap, delay, concatMap, timer, takeUntil, distinctUntilChanged, map } from 'rxjs';
|
|
12
14
|
import { twMerge } from 'tailwind-merge';
|
|
13
|
-
import { MonaI18nService, formatNumber, validateLocalizedNumber, getNumberSymbols, validateLocalizedNumberEdit } from '@nanahoshi/mona-ui/i18n';
|
|
14
|
-
import { TextBoxDirective } from '@nanahoshi/mona-ui/text-box';
|
|
15
15
|
import { cva } from 'class-variance-authority';
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Exact arithmetic on canonical decimal strings (see `parseLocalizedDecimal`), using a `bigint` scaled by a power
|
|
19
|
+
* of ten. Only what the decimal text box needs: adding a step and comparing against bounds.
|
|
20
|
+
*/
|
|
21
|
+
const DECIMAL_REGEX = /^(-)?(\d+)(?:\.(\d+))?$/;
|
|
22
|
+
/** Writes a number in plain decimal notation, never with an exponent. */
|
|
23
|
+
const PLAIN_NUMBER_FORMAT = new Intl.NumberFormat("en-US", { maximumFractionDigits: 100, useGrouping: false });
|
|
24
|
+
function toScaled(value) {
|
|
25
|
+
const match = DECIMAL_REGEX.exec(value);
|
|
26
|
+
if (!match) {
|
|
27
|
+
throw new Error(`"${value}" is not a decimal string.`);
|
|
28
|
+
}
|
|
29
|
+
const fraction = match[3] ?? "";
|
|
30
|
+
const units = BigInt(match[2] + fraction);
|
|
31
|
+
return { scale: fraction.length, units: match[1] ? -units : units };
|
|
32
|
+
}
|
|
33
|
+
function toCommonScale(a, b) {
|
|
34
|
+
const scale = Math.max(a.scale, b.scale);
|
|
35
|
+
return [a.units * 10n ** BigInt(scale - a.scale), b.units * 10n ** BigInt(scale - b.scale), scale];
|
|
36
|
+
}
|
|
37
|
+
function fromScaled(units, scale) {
|
|
38
|
+
const negative = units < 0n;
|
|
39
|
+
const digits = (negative ? -units : units).toString().padStart(scale + 1, "0");
|
|
40
|
+
const integer = digits.slice(0, digits.length - scale);
|
|
41
|
+
const fraction = digits.slice(digits.length - scale).replace(/0+$/, "");
|
|
42
|
+
const text = fraction ? `${integer}.${fraction}` : integer;
|
|
43
|
+
return negative ? `-${text}` : text;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Canonical decimal string for a decimal string or a finite number, or `null` for anything else. Leading zeros,
|
|
47
|
+
* trailing fraction zeros and the sign of zero are dropped, so equal values have equal strings.
|
|
48
|
+
*/
|
|
49
|
+
function toDecimalString(value) {
|
|
50
|
+
if (value == null) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (typeof value === "number") {
|
|
54
|
+
return Number.isFinite(value) ? toDecimalString(PLAIN_NUMBER_FORMAT.format(value)) : null;
|
|
55
|
+
}
|
|
56
|
+
const text = value.trim();
|
|
57
|
+
if (!DECIMAL_REGEX.test(text)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const { scale, units } = toScaled(text);
|
|
61
|
+
return fromScaled(units, scale);
|
|
62
|
+
}
|
|
63
|
+
function addDecimals(a, b) {
|
|
64
|
+
const [x, y, scale] = toCommonScale(toScaled(a), toScaled(b));
|
|
65
|
+
return fromScaled(x + y, scale);
|
|
66
|
+
}
|
|
67
|
+
function subtractDecimals(a, b) {
|
|
68
|
+
const [x, y, scale] = toCommonScale(toScaled(a), toScaled(b));
|
|
69
|
+
return fromScaled(x - y, scale);
|
|
70
|
+
}
|
|
71
|
+
/** Negative when `a < b`, zero when equal, positive when `a > b`. */
|
|
72
|
+
function compareDecimals(a, b) {
|
|
73
|
+
const [x, y] = toCommonScale(toScaled(a), toScaled(b));
|
|
74
|
+
return x === y ? 0 : x < y ? -1 : 1;
|
|
75
|
+
}
|
|
76
|
+
|
|
17
77
|
class NumericTextBoxPrefixTemplateDirective {
|
|
18
78
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NumericTextBoxPrefixTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
19
79
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: NumericTextBoxPrefixTemplateDirective, isStandalone: true, selector: "ng-template[monaNumericTextBoxPrefixTemplate]", ngImport: i0 }); }
|
|
@@ -26,6 +86,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
26
86
|
}]
|
|
27
87
|
}] });
|
|
28
88
|
|
|
89
|
+
// Generated from i18n/catalogs/en-US.messages.json by scripts/generate-locale-catalogs.ts. Do not edit.
|
|
90
|
+
// Edit the JSON file and run `npm run generate:locale-catalogs`.
|
|
29
91
|
const NUMERIC_TEXT_BOX_DEFAULT_MESSAGES = {
|
|
30
92
|
decrease: "Decrease value",
|
|
31
93
|
increase: "Increase value"
|
|
@@ -135,7 +197,11 @@ const numericTextboxButtonThemeVariants = cva(`
|
|
|
135
197
|
}
|
|
136
198
|
});
|
|
137
199
|
|
|
138
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Typing, pasting, spinning, formatting and ARIA for a numeric input, shared by the number and decimal text boxes.
|
|
202
|
+
* Subclasses declare the value-typed inputs and say how to parse, compare and step their value type.
|
|
203
|
+
*/
|
|
204
|
+
class NumericTextBoxBaseComponent {
|
|
139
205
|
#destroyRef;
|
|
140
206
|
#focusMonitor;
|
|
141
207
|
#hostElementRef;
|
|
@@ -217,11 +283,6 @@ class NumericTextBoxComponent {
|
|
|
217
283
|
*/
|
|
218
284
|
this.disabled = input(false, /* @ts-ignore */
|
|
219
285
|
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
220
|
-
/**
|
|
221
|
-
* @description Formats the value to be displayed in the input when the input is not focused.
|
|
222
|
-
*/
|
|
223
|
-
this.formatter = input(null, /* @ts-ignore */
|
|
224
|
-
...(ngDevMode ? [{ debugName: "formatter" }] : /* istanbul ignore next */ []));
|
|
225
286
|
/**
|
|
226
287
|
* @description Emits when the inner input element is blurred.
|
|
227
288
|
*/
|
|
@@ -235,22 +296,12 @@ class NumericTextBoxComponent {
|
|
|
235
296
|
*/
|
|
236
297
|
this.inputFocusOut = output();
|
|
237
298
|
/**
|
|
238
|
-
* @description Marks the
|
|
299
|
+
* @description Marks the text box as invalid. When bound to a signal form field via `[formField]`,
|
|
239
300
|
* this is written by the `FormField` directive.
|
|
240
301
|
* @default false
|
|
241
302
|
*/
|
|
242
303
|
this.invalid = input(false, /* @ts-ignore */
|
|
243
304
|
...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
|
|
244
|
-
/**
|
|
245
|
-
* @description Maximum value that can be entered.
|
|
246
|
-
*/
|
|
247
|
-
this.maxValue = input(null, /* @ts-ignore */
|
|
248
|
-
...(ngDevMode ? [{ debugName: "maxValue" }] : /* istanbul ignore next */ []));
|
|
249
|
-
/**
|
|
250
|
-
* @description Minimum value that can be entered.
|
|
251
|
-
*/
|
|
252
|
-
this.minValue = input(null, /* @ts-ignore */
|
|
253
|
-
...(ngDevMode ? [{ debugName: "minValue" }] : /* istanbul ignore next */ []));
|
|
254
305
|
/**
|
|
255
306
|
* @description Sets whether the input can be empty.
|
|
256
307
|
*/
|
|
@@ -281,23 +332,18 @@ class NumericTextBoxComponent {
|
|
|
281
332
|
*/
|
|
282
333
|
this.spinners = input(true, /* @ts-ignore */
|
|
283
334
|
...(ngDevMode ? [{ debugName: "spinners" }] : /* istanbul ignore next */ []));
|
|
284
|
-
/**
|
|
285
|
-
* @description Step value to increment or decrement the value.
|
|
286
|
-
*/
|
|
287
|
-
this.step = input(1, /* @ts-ignore */
|
|
288
|
-
...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
|
|
289
335
|
/**
|
|
290
336
|
* @description Tab index of the input.
|
|
291
337
|
*/
|
|
292
338
|
this.tabindex = input(0, /* @ts-ignore */
|
|
293
339
|
...(ngDevMode ? [{ debugName: "tabindex" }] : /* istanbul ignore next */ []));
|
|
294
340
|
/**
|
|
295
|
-
* @description Emitted when the
|
|
341
|
+
* @description Emitted when the text box is interacted with on blur, value change, or spinner update.
|
|
296
342
|
* The `FormField` directive listens to this to mark the field as touched.
|
|
297
343
|
*/
|
|
298
344
|
this.touch = output();
|
|
299
345
|
/**
|
|
300
|
-
* @description Sets the touched state of the
|
|
346
|
+
* @description Sets the touched state of the text box. When bound to a signal form field via `[formField]`,
|
|
301
347
|
* this is written by the `FormField` directive.
|
|
302
348
|
* @default false
|
|
303
349
|
*/
|
|
@@ -308,12 +354,6 @@ class NumericTextBoxComponent {
|
|
|
308
354
|
* @default ""
|
|
309
355
|
*/
|
|
310
356
|
this.userClass = input("", { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: "class" });
|
|
311
|
-
/**
|
|
312
|
-
* @description Two-way bindable current value of the numeric text box.
|
|
313
|
-
* @default null
|
|
314
|
-
*/
|
|
315
|
-
this.value = model(null, /* @ts-ignore */
|
|
316
|
-
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
317
357
|
effect(() => {
|
|
318
358
|
this.#i18n.localeId();
|
|
319
359
|
untracked(() => {
|
|
@@ -342,50 +382,8 @@ class NumericTextBoxComponent {
|
|
|
342
382
|
});
|
|
343
383
|
this.#destroyRef.onDestroy(() => this.#focusMonitor.stopMonitoring(this.#hostElementRef.nativeElement));
|
|
344
384
|
}
|
|
345
|
-
static calculate(value, step, type) {
|
|
346
|
-
const precision = Math.max(NumericTextBoxComponent.getPrecision(value), NumericTextBoxComponent.getPrecision(step));
|
|
347
|
-
const factor = Math.pow(10, precision);
|
|
348
|
-
const signFactor = type === "+" ? 1 : -1;
|
|
349
|
-
const newValue = (value * factor + signFactor * step * factor) / factor;
|
|
350
|
-
return precision > 0 ? parseFloat(newValue.toFixed(precision)) : newValue;
|
|
351
|
-
}
|
|
352
|
-
static getPrecision(value) {
|
|
353
|
-
const valueString = value.toString();
|
|
354
|
-
if (valueString.includes(".")) {
|
|
355
|
-
const parts = valueString.split(".");
|
|
356
|
-
return parts[1].length;
|
|
357
|
-
}
|
|
358
|
-
return 0;
|
|
359
|
-
}
|
|
360
|
-
static isNumeric(value) {
|
|
361
|
-
if (value === "" || value === "-") {
|
|
362
|
-
return true;
|
|
363
|
-
}
|
|
364
|
-
return ((typeof value === "number" || (typeof value === "string" && value.trim() !== "")) && !isNaN(value));
|
|
365
|
-
}
|
|
366
385
|
decrease() {
|
|
367
|
-
|
|
368
|
-
if (value == null) {
|
|
369
|
-
const min = this.minValue();
|
|
370
|
-
const max = this.maxValue();
|
|
371
|
-
let initial = 0;
|
|
372
|
-
if (min != null && initial < min) {
|
|
373
|
-
initial = min;
|
|
374
|
-
}
|
|
375
|
-
if (max != null && initial > max) {
|
|
376
|
-
initial = max;
|
|
377
|
-
}
|
|
378
|
-
this.commitNumericValue(initial);
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
let result = NumericTextBoxComponent.calculate(value, this.step(), "-");
|
|
382
|
-
const min = this.minValue();
|
|
383
|
-
if (min != null && result < min) {
|
|
384
|
-
result = min;
|
|
385
|
-
}
|
|
386
|
-
this.commitNumericValue(result);
|
|
387
|
-
}
|
|
388
|
-
this.focus();
|
|
386
|
+
this.spinValue("-");
|
|
389
387
|
}
|
|
390
388
|
focus() {
|
|
391
389
|
this.#focusMonitor.focusVia(this.valueTextBoxRef(), "keyboard");
|
|
@@ -394,28 +392,7 @@ class NumericTextBoxComponent {
|
|
|
394
392
|
});
|
|
395
393
|
}
|
|
396
394
|
increase() {
|
|
397
|
-
|
|
398
|
-
if (value == null) {
|
|
399
|
-
const min = this.minValue();
|
|
400
|
-
const max = this.maxValue();
|
|
401
|
-
let initial = 0;
|
|
402
|
-
if (min != null && initial < min) {
|
|
403
|
-
initial = min;
|
|
404
|
-
}
|
|
405
|
-
if (max != null && initial > max) {
|
|
406
|
-
initial = max;
|
|
407
|
-
}
|
|
408
|
-
this.commitNumericValue(initial);
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
let result = NumericTextBoxComponent.calculate(value, this.step(), "+");
|
|
412
|
-
const max = this.maxValue();
|
|
413
|
-
if (max != null && result > max) {
|
|
414
|
-
result = max;
|
|
415
|
-
}
|
|
416
|
-
this.commitNumericValue(result);
|
|
417
|
-
}
|
|
418
|
-
this.focus();
|
|
395
|
+
this.spinValue("+");
|
|
419
396
|
}
|
|
420
397
|
onBlur(event) {
|
|
421
398
|
this.#pendingPaste = false;
|
|
@@ -434,11 +411,32 @@ class NumericTextBoxComponent {
|
|
|
434
411
|
this.rawInputValue.set(text);
|
|
435
412
|
this.valueChange$.next(text);
|
|
436
413
|
}
|
|
437
|
-
|
|
414
|
+
onPaste(event) {
|
|
415
|
+
const pastedText = event.clipboardData?.getData("text/plain");
|
|
416
|
+
if (pastedText == null) {
|
|
417
|
+
this.#pendingPaste = false;
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const inputElement = this.valueTextBoxRef()?.nativeElement;
|
|
421
|
+
if (!inputElement) {
|
|
422
|
+
this.#pendingPaste = false;
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const { value, selectionStart, selectionEnd } = inputElement;
|
|
426
|
+
const start = selectionStart ?? value.length;
|
|
427
|
+
const end = selectionEnd ?? value.length;
|
|
428
|
+
const proposedValue = value.slice(0, start) + pastedText + value.slice(end);
|
|
429
|
+
const validation = this.readLocaleText(proposedValue, this.#i18n.localeId(), this.decimals());
|
|
430
|
+
if (!validation.valid || validation.value === null) {
|
|
431
|
+
event.preventDefault();
|
|
432
|
+
this.#pendingPaste = false;
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
this.#pendingPaste = true;
|
|
436
|
+
}
|
|
437
|
+
commitValue(value) {
|
|
438
438
|
this.value.set(value);
|
|
439
|
-
this.rawInputValue.set(this.focused() && !this.readonly()
|
|
440
|
-
? this.formatEditValue(value)
|
|
441
|
-
: this.formatValueForDisplay(value));
|
|
439
|
+
this.rawInputValue.set(this.focused() && !this.readonly() ? this.formatEditValue(value) : this.formatValueForDisplay(value));
|
|
442
440
|
this.touch.emit();
|
|
443
441
|
}
|
|
444
442
|
correctValue() {
|
|
@@ -453,20 +451,20 @@ class NumericTextBoxComponent {
|
|
|
453
451
|
return false;
|
|
454
452
|
}
|
|
455
453
|
else if (min != null) {
|
|
456
|
-
this.
|
|
454
|
+
this.commitValue(min);
|
|
457
455
|
return true;
|
|
458
456
|
}
|
|
459
457
|
else {
|
|
460
|
-
this.
|
|
458
|
+
this.commitValue(this.zeroValue);
|
|
461
459
|
return true;
|
|
462
460
|
}
|
|
463
461
|
}
|
|
464
|
-
if (min != null && value <
|
|
465
|
-
this.
|
|
462
|
+
if (min != null && this.compareValues(value, min) < 0) {
|
|
463
|
+
this.commitValue(min);
|
|
466
464
|
return true;
|
|
467
465
|
}
|
|
468
|
-
if (max != null && value >
|
|
469
|
-
this.
|
|
466
|
+
if (max != null && this.compareValues(value, max) > 0) {
|
|
467
|
+
this.commitValue(max);
|
|
470
468
|
return true;
|
|
471
469
|
}
|
|
472
470
|
return false;
|
|
@@ -476,7 +474,7 @@ class NumericTextBoxComponent {
|
|
|
476
474
|
return "";
|
|
477
475
|
}
|
|
478
476
|
return formatNumber(value, this.#i18n.localeId(), {
|
|
479
|
-
maximumFractionDigits:
|
|
477
|
+
maximumFractionDigits: this.editFractionDigits,
|
|
480
478
|
useGrouping: false
|
|
481
479
|
});
|
|
482
480
|
}
|
|
@@ -495,32 +493,6 @@ class NumericTextBoxComponent {
|
|
|
495
493
|
useGrouping: false
|
|
496
494
|
});
|
|
497
495
|
}
|
|
498
|
-
onPaste(event) {
|
|
499
|
-
const pastedText = event.clipboardData?.getData("text/plain");
|
|
500
|
-
if (pastedText == null) {
|
|
501
|
-
this.#pendingPaste = false;
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
const inputElement = this.valueTextBoxRef()?.nativeElement;
|
|
505
|
-
if (!inputElement) {
|
|
506
|
-
this.#pendingPaste = false;
|
|
507
|
-
return;
|
|
508
|
-
}
|
|
509
|
-
const { value, selectionStart, selectionEnd } = inputElement;
|
|
510
|
-
const start = selectionStart ?? value.length;
|
|
511
|
-
const end = selectionEnd ?? value.length;
|
|
512
|
-
const proposedValue = value.slice(0, start) + pastedText + value.slice(end);
|
|
513
|
-
const validation = validateLocalizedNumber(proposedValue, this.#i18n.localeId(), {
|
|
514
|
-
mode: "locale",
|
|
515
|
-
decimals: this.decimals()
|
|
516
|
-
});
|
|
517
|
-
if (!validation.valid || validation.value === null) {
|
|
518
|
-
event.preventDefault();
|
|
519
|
-
this.#pendingPaste = false;
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
this.#pendingPaste = true;
|
|
523
|
-
}
|
|
524
496
|
/**
|
|
525
497
|
* The numpad decimal key types "." or "," depending on the keyboard layout, not the locale. Insert the
|
|
526
498
|
* locale's decimal separator instead, so a de-DE layout used with an en-US locale still types a decimal.
|
|
@@ -537,7 +509,7 @@ class NumericTextBoxComponent {
|
|
|
537
509
|
const start = selectionStart ?? value.length;
|
|
538
510
|
const end = selectionEnd ?? value.length;
|
|
539
511
|
const proposedValue = value.slice(0, start) + decimal + value.slice(end);
|
|
540
|
-
if (!
|
|
512
|
+
if (!this.readEditText(proposedValue, localeId, this.decimals()).valid) {
|
|
541
513
|
return;
|
|
542
514
|
}
|
|
543
515
|
inputElement.setRangeText(decimal, start, end, "end");
|
|
@@ -549,10 +521,7 @@ class NumericTextBoxComponent {
|
|
|
549
521
|
const isPaste = this.#pendingPaste;
|
|
550
522
|
this.#pendingPaste = false;
|
|
551
523
|
if (isPaste) {
|
|
552
|
-
const validation =
|
|
553
|
-
mode: "locale",
|
|
554
|
-
decimals: this.decimals()
|
|
555
|
-
});
|
|
524
|
+
const validation = this.readLocaleText(raw, this.#i18n.localeId(), this.decimals());
|
|
556
525
|
return validation.valid && validation.value !== null ? validation.value : this.value();
|
|
557
526
|
}
|
|
558
527
|
if (raw === "") {
|
|
@@ -561,16 +530,11 @@ class NumericTextBoxComponent {
|
|
|
561
530
|
if (raw.trim() !== raw) {
|
|
562
531
|
return this.value();
|
|
563
532
|
}
|
|
564
|
-
const edit =
|
|
565
|
-
decimals: this.decimals()
|
|
566
|
-
});
|
|
533
|
+
const edit = this.readEditText(raw, this.#i18n.localeId(), this.decimals());
|
|
567
534
|
if (edit.valid) {
|
|
568
535
|
return edit.value === null ? this.value() : edit.value;
|
|
569
536
|
}
|
|
570
|
-
const localeValidation =
|
|
571
|
-
mode: "locale",
|
|
572
|
-
decimals: this.decimals()
|
|
573
|
-
});
|
|
537
|
+
const localeValidation = this.readLocaleText(raw, this.#i18n.localeId(), this.decimals());
|
|
574
538
|
if (localeValidation.valid && localeValidation.value !== null) {
|
|
575
539
|
return localeValidation.value;
|
|
576
540
|
}
|
|
@@ -583,8 +547,7 @@ class NumericTextBoxComponent {
|
|
|
583
547
|
if (!isPaste) {
|
|
584
548
|
this.#pendingPaste = false;
|
|
585
549
|
}
|
|
586
|
-
const insertedText = event.data ??
|
|
587
|
-
event.dataTransfer?.getData("text/plain");
|
|
550
|
+
const insertedText = event.data ?? event.dataTransfer?.getData("text/plain");
|
|
588
551
|
if (insertedText == null) {
|
|
589
552
|
return;
|
|
590
553
|
}
|
|
@@ -596,10 +559,7 @@ class NumericTextBoxComponent {
|
|
|
596
559
|
const localeId = this.#i18n.localeId();
|
|
597
560
|
const decimals = this.decimals();
|
|
598
561
|
if (isPaste) {
|
|
599
|
-
const validation =
|
|
600
|
-
mode: "locale",
|
|
601
|
-
decimals
|
|
602
|
-
});
|
|
562
|
+
const validation = this.readLocaleText(proposedValue, localeId, decimals);
|
|
603
563
|
if (!validation.valid || validation.value === null) {
|
|
604
564
|
event.preventDefault();
|
|
605
565
|
this.#pendingPaste = false;
|
|
@@ -610,10 +570,7 @@ class NumericTextBoxComponent {
|
|
|
610
570
|
return;
|
|
611
571
|
}
|
|
612
572
|
this.#pendingPaste = false;
|
|
613
|
-
|
|
614
|
-
decimals
|
|
615
|
-
});
|
|
616
|
-
if (!editValidation.valid) {
|
|
573
|
+
if (!this.readEditText(proposedValue, localeId, decimals).valid) {
|
|
617
574
|
event.preventDefault();
|
|
618
575
|
}
|
|
619
576
|
});
|
|
@@ -674,21 +631,196 @@ class NumericTextBoxComponent {
|
|
|
674
631
|
}
|
|
675
632
|
});
|
|
676
633
|
}
|
|
677
|
-
|
|
678
|
-
|
|
634
|
+
spinValue(direction) {
|
|
635
|
+
const value = this.value();
|
|
636
|
+
const min = this.minValue();
|
|
637
|
+
const max = this.maxValue();
|
|
638
|
+
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);
|
|
647
|
+
}
|
|
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);
|
|
657
|
+
}
|
|
658
|
+
this.focus();
|
|
659
|
+
}
|
|
660
|
+
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 }); }
|
|
679
662
|
}
|
|
680
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type:
|
|
681
|
-
type:
|
|
682
|
-
args: [{
|
|
663
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NumericTextBoxBaseComponent, decorators: [{
|
|
664
|
+
type: Directive,
|
|
665
|
+
args: [{
|
|
666
|
+
host: {
|
|
683
667
|
"[class]": "classes()",
|
|
684
668
|
"[attr.data-disabled]": "disabled() || null",
|
|
685
669
|
"[attr.data-invalid]": "invalidInput() || null",
|
|
686
670
|
"[attr.data-readonly]": "readonly() || null",
|
|
687
671
|
"[attr.data-required]": "required() || null"
|
|
688
|
-
}
|
|
672
|
+
}
|
|
673
|
+
}]
|
|
689
674
|
}], ctorParameters: () => [], propDecorators: { prefixTemplateList: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NumericTextBoxPrefixTemplateDirective), { ...{
|
|
690
675
|
read: TemplateRef
|
|
691
|
-
}, 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 }] }],
|
|
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 }] }] } });
|
|
677
|
+
|
|
678
|
+
function warnInvalidDecimal(name, value) {
|
|
679
|
+
if (typeof ngDevMode !== "undefined" && ngDevMode) {
|
|
680
|
+
console.warn(`mona-decimal-text-box: ${name} ${JSON.stringify(value)} is not a decimal string; it is ignored.`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
function decimalInput(name) {
|
|
684
|
+
return value => {
|
|
685
|
+
const decimal = toDecimalString(value);
|
|
686
|
+
if (decimal === null && value != null) {
|
|
687
|
+
warnInvalidDecimal(name, value);
|
|
688
|
+
}
|
|
689
|
+
return decimal;
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* A numeric input whose value is a decimal string, for amounts a JavaScript `number` cannot hold exactly (money,
|
|
694
|
+
* quantities with many digits, identifiers). Typing, paste, spin buttons and bounds use exact decimal arithmetic,
|
|
695
|
+
* so there is no precision limit. The value is canonical: `"-1234.5"`, with `.` as the separator and no grouping,
|
|
696
|
+
* exponent, leading zeros or trailing fraction zeros.
|
|
697
|
+
*/
|
|
698
|
+
class DecimalTextBoxComponent extends NumericTextBoxBaseComponent {
|
|
699
|
+
constructor() {
|
|
700
|
+
super();
|
|
701
|
+
// Intl.NumberFormat's limit: a decimal string can carry more digits than a number.
|
|
702
|
+
this.editFractionDigits = 100;
|
|
703
|
+
this.zeroValue = "0";
|
|
704
|
+
/**
|
|
705
|
+
* @description Formats the value to be displayed in the input when the input is not focused.
|
|
706
|
+
*/
|
|
707
|
+
this.formatter = input(null, /* @ts-ignore */
|
|
708
|
+
...(ngDevMode ? [{ debugName: "formatter" }] : /* istanbul ignore next */ []));
|
|
709
|
+
/**
|
|
710
|
+
* @description Maximum value that can be entered, as a decimal string or a number.
|
|
711
|
+
*/
|
|
712
|
+
this.maxValue = input(null, { ...(ngDevMode ? { debugName: "maxValue" } : /* istanbul ignore next */ {}), transform: decimalInput("maxValue") });
|
|
713
|
+
/**
|
|
714
|
+
* @description Minimum value that can be entered, as a decimal string or a number.
|
|
715
|
+
*/
|
|
716
|
+
this.minValue = input(null, { ...(ngDevMode ? { debugName: "minValue" } : /* istanbul ignore next */ {}), transform: decimalInput("minValue") });
|
|
717
|
+
/**
|
|
718
|
+
* @description Step value to increment or decrement the value, as a decimal string or a number.
|
|
719
|
+
* @default "1"
|
|
720
|
+
*/
|
|
721
|
+
this.step = input("1", { ...(ngDevMode ? { debugName: "step" } : /* istanbul ignore next */ {}), transform: value => decimalInput("step")(value) ?? "1" });
|
|
722
|
+
/**
|
|
723
|
+
* @description Two-way bindable current value, as a canonical decimal string such as `"-1234.5"`.
|
|
724
|
+
* @default null
|
|
725
|
+
*/
|
|
726
|
+
this.value = model(null, /* @ts-ignore */
|
|
727
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
728
|
+
effect(() => {
|
|
729
|
+
const value = this.value();
|
|
730
|
+
if (value != null && toDecimalString(value) === null) {
|
|
731
|
+
warnInvalidDecimal("value", value);
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
compareValues(a, b) {
|
|
736
|
+
return compareDecimals(toDecimalString(a) ?? "0", toDecimalString(b) ?? "0");
|
|
737
|
+
}
|
|
738
|
+
readEditText(text, localeId, decimals) {
|
|
739
|
+
return validateLocalizedDecimalEdit(text, localeId, { decimals });
|
|
740
|
+
}
|
|
741
|
+
readLocaleText(text, localeId, decimals) {
|
|
742
|
+
return validateLocalizedDecimal(text, localeId, { mode: "locale", decimals });
|
|
743
|
+
}
|
|
744
|
+
stepValue(value, direction) {
|
|
745
|
+
const current = toDecimalString(value) ?? "0";
|
|
746
|
+
return direction === "+" ? addDecimals(current, this.step()) : subtractDecimals(current, this.step());
|
|
747
|
+
}
|
|
748
|
+
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", "type", "toggleable", "class"], outputs: ["disabledChange", "loadingChange", "lookChange", "selectedChange"] }, { kind: "component", type: LucideChevronUp, selector: "svg[lucideChevronUp]" }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }] }); }
|
|
750
|
+
}
|
|
751
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: DecimalTextBoxComponent, decorators: [{
|
|
752
|
+
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" }]
|
|
754
|
+
}], 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
|
+
|
|
756
|
+
class NumericTextBoxComponent extends NumericTextBoxBaseComponent {
|
|
757
|
+
constructor() {
|
|
758
|
+
super(...arguments);
|
|
759
|
+
this.editFractionDigits = 20;
|
|
760
|
+
this.zeroValue = 0;
|
|
761
|
+
/**
|
|
762
|
+
* @description Formats the value to be displayed in the input when the input is not focused.
|
|
763
|
+
*/
|
|
764
|
+
this.formatter = input(null, /* @ts-ignore */
|
|
765
|
+
...(ngDevMode ? [{ debugName: "formatter" }] : /* istanbul ignore next */ []));
|
|
766
|
+
/**
|
|
767
|
+
* @description Maximum value that can be entered.
|
|
768
|
+
*/
|
|
769
|
+
this.maxValue = input(null, /* @ts-ignore */
|
|
770
|
+
...(ngDevMode ? [{ debugName: "maxValue" }] : /* istanbul ignore next */ []));
|
|
771
|
+
/**
|
|
772
|
+
* @description Minimum value that can be entered.
|
|
773
|
+
*/
|
|
774
|
+
this.minValue = input(null, /* @ts-ignore */
|
|
775
|
+
...(ngDevMode ? [{ debugName: "minValue" }] : /* istanbul ignore next */ []));
|
|
776
|
+
/**
|
|
777
|
+
* @description Step value to increment or decrement the value.
|
|
778
|
+
*/
|
|
779
|
+
this.step = input(1, /* @ts-ignore */
|
|
780
|
+
...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
|
|
781
|
+
/**
|
|
782
|
+
* @description Two-way bindable current value of the numeric text box.
|
|
783
|
+
* @default null
|
|
784
|
+
*/
|
|
785
|
+
this.value = model(null, /* @ts-ignore */
|
|
786
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
787
|
+
}
|
|
788
|
+
static calculate(value, step, type) {
|
|
789
|
+
const precision = Math.max(NumericTextBoxComponent.getPrecision(value), NumericTextBoxComponent.getPrecision(step));
|
|
790
|
+
const factor = Math.pow(10, precision);
|
|
791
|
+
const signFactor = type === "+" ? 1 : -1;
|
|
792
|
+
const newValue = (value * factor + signFactor * step * factor) / factor;
|
|
793
|
+
return precision > 0 ? parseFloat(newValue.toFixed(precision)) : newValue;
|
|
794
|
+
}
|
|
795
|
+
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;
|
|
802
|
+
}
|
|
803
|
+
compareValues(a, b) {
|
|
804
|
+
return a - b;
|
|
805
|
+
}
|
|
806
|
+
readEditText(text, localeId, decimals) {
|
|
807
|
+
return validateLocalizedNumberEdit(text, localeId, { decimals });
|
|
808
|
+
}
|
|
809
|
+
readLocaleText(text, localeId, decimals) {
|
|
810
|
+
return validateLocalizedNumber(text, localeId, { mode: "locale", decimals });
|
|
811
|
+
}
|
|
812
|
+
stepValue(value, direction) {
|
|
813
|
+
const result = NumericTextBoxComponent.calculate(value, this.step(), direction);
|
|
814
|
+
// 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);
|
|
816
|
+
}
|
|
817
|
+
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", "type", "toggleable", "class"], outputs: ["disabledChange", "loadingChange", "lookChange", "selectedChange"] }, { kind: "component", type: LucideChevronUp, selector: "svg[lucideChevronUp]" }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }] }); }
|
|
819
|
+
}
|
|
820
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NumericTextBoxComponent, decorators: [{
|
|
821
|
+
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" }]
|
|
823
|
+
}], 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"] }] } });
|
|
692
824
|
|
|
693
825
|
/*
|
|
694
826
|
* Public API Surface of @nanahoshi/mona-ui/numeric-text-box
|
|
@@ -698,5 +830,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
698
830
|
* Generated bundle index. Do not edit.
|
|
699
831
|
*/
|
|
700
832
|
|
|
701
|
-
export { NUMERIC_TEXT_BOX_DEFAULT_MESSAGES, NumericTextBoxComponent, NumericTextBoxPrefixTemplateDirective };
|
|
833
|
+
export { DecimalTextBoxComponent, NUMERIC_TEXT_BOX_DEFAULT_MESSAGES, NumericTextBoxComponent, NumericTextBoxPrefixTemplateDirective };
|
|
702
834
|
//# sourceMappingURL=nanahoshi-mona-ui-numeric-text-box.mjs.map
|