@rt-tools/ui-kit-v2 0.39.1 → 0.40.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/rt-tools-ui-kit-v2-ai-prompt-input.mjs +13 -1
- package/fesm2022/rt-tools-ui-kit-v2-ai-prompt-input.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs +12 -0
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs +3 -1
- package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs +60 -7
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs +91 -10
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-detail-list.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-detail-list.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs +76 -5
- package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +111 -35
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-stat-tile.mjs +37 -16
- package/fesm2022/rt-tools-ui-kit-v2-stat-tile.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs +94 -5
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/components/date-picker/panel/rt-date-panel.component.scss +51 -2
- package/src/lib/components/date-range/panel/rt-date-range-panel.component.scss +95 -2
- package/src/lib/components/detail-list/CONTEXT.md +3 -0
- package/src/lib/components/detail-list/rt-detail-row.component.scss +1 -0
- package/src/lib/components/input-number/CONTEXT.md +8 -0
- package/src/lib/components/input-number/rt-input-number.component.scss +50 -0
- package/src/lib/components/stat-tile/CONTEXT.md +11 -6
- package/src/lib/components/stat-tile/rt-stat-tile.component.scss +21 -0
- package/src/lib/components/stat-tile/stories/component/test-stat-tile-matrix.component.scss +15 -0
- package/src/lib/components/table/CONTEXT.md +20 -0
- package/src/lib/components/table/rt-table.component.scss +77 -22
- package/src/lib/components/table/stories/component/test-table-pinned.component.scss +76 -0
- package/src/rich-editor/lib/components/chat/CONTEXT.md +5 -0
- package/src/styles/_primitives.scss +2 -0
- package/src/styles/_semantic.scss +9 -0
- package/src/styles/_theme-dark.scss +2 -0
- package/styles/tokens.css +66 -0
- package/types/rt-tools-ui-kit-v2-ai-prompt-input.d.ts +4 -0
- package/types/rt-tools-ui-kit-v2-core.d.ts +9 -1
- package/types/rt-tools-ui-kit-v2-date-picker.d.ts +14 -0
- package/types/rt-tools-ui-kit-v2-input-number.d.ts +30 -2
- package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +13 -1
- package/types/rt-tools-ui-kit-v2-stat-tile.d.ts +29 -6
- package/types/rt-tools-ui-kit-v2-table.d.ts +31 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rt-tools-ui-kit-v2-detail-list.mjs","names":["BEM_BLOCK"],"sources":["../../../projects/ui-kit-v2/src/lib/components/detail-list/rt-detail-list.component.ts","../../../projects/ui-kit-v2/src/lib/components/detail-list/rt-detail-list.component.html","../../../projects/ui-kit-v2/src/lib/components/detail-list/rt-detail-row.component.ts","../../../projects/ui-kit-v2/src/lib/components/detail-list/rt-detail-row.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';\n\nimport { BlockDirective } from '@rt-tools/core';\n\nconst BEM_BLOCK: string = 'rt-detail-list';\n\n/**\n * Контейнер для стопки `rt-detail-row` — задаёт единый вертикальный gap между\n * строками «лейбл + значение». Используется в read-only details-панелях асайдов\n * (детали заявки, профиля) вместо самопальных `div`-обёрток.\n *\n * Контент — только `rt-detail-row` через проекцию.\n */\n@Component({\n selector: 'rt-detail-list',\n templateUrl: './rt-detail-list.component.html',\n styleUrl: './rt-detail-list.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n BlockDirective,\n ],\n host: {\n class: BEM_BLOCK,\n },\n})\nexport class RtDetailListComponent {}\n","<ng-container rtBlock=\"rt-detail-list\">\n <ng-content />\n</ng-container>\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n input,\n InputSignal,\n InputSignalWithTransform,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\n\nimport { RtSkeletonWrapperComponent } from '@rt-tools/ui-kit-v2/skeleton-wrapper';\n\nconst BEM_BLOCK: string = 'rt-detail-row';\n\n/**\n * Одна read-only строка «лейбл + значение» в stacked-раскладке: приглушённый\n * лейбл сверху, значение снизу. Единый паттерн info-строки для всех асайдов\n * (раньше — пять разных реализаций).\n *\n * Значение проецируется через `<ng-content>` — допустим текст, дата через pipe\n * или `rt-tag`. При `[loading]=\"true\"` значение замещается скелетоном (потребитель\n * не оборачивает `rt-skeleton-wrapper` вручную).\n */\n@Component({\n selector: 'rt-detail-row',\n templateUrl: './rt-detail-row.component.html',\n styleUrl: './rt-detail-row.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtSkeletonWrapperComponent,\n BlockDirective,\n ElemDirective,\n ],\n host: {\n class: BEM_BLOCK,\n },\n})\nexport class RtDetailRowComponent {\n public readonly label: InputSignal<string> = input<string>('');\n\n public readonly loading: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n}\n","<ng-container rtBlock=\"rt-detail-row\">\n <span rtElem=\"label\" qa-dataid=\"detail-row-label\">{{ label() }}</span>\n <div rtElem=\"value\" qa-dataid=\"detail-row-value\">\n <rt-skeleton-wrapper width=\"180px\" height=\"16px\" [isLoading]=\"loading()\">\n <ng-content />\n </rt-skeleton-wrapper>\n </div>\n</ng-container>\n"],"mappings":";;;;AAIA,MAAMA,cAAoB;AAuB1B,IAAa,wBAAb,MAAa,sBAAqB;CAArB;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAqB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAArB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAqB,cAAA;GAAA,UAAA;GAAA,MAAA,EAAA,gBAAA,iBAAA;GAAA,UAAA;GAAA,UC3BlC;GAGA,QAAA,CAAA,2HAAA;GAAA,cAAA,CAAA;IAAA,MAAA;IAAA,MDkBQ;IAAc,UAAA;IAAA,QAAA,CAAA,OAAA;GAAA,CAAA;GAAA,iBAAA,GAAA,wBAAA;GAAA,eAAA,GAAA,kBAAA;EAAA,CAAA;CAAA;;AAMT,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAqB,YAAA,CAAA;EAdjC,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAgB,iBAGT,wBAAwB;GAAM,eAChC,kBAAkB;GAAI,SAC5B,CAEL,cACH;GAAA,MACK,EACF,OAAOA,YACV;GAAA,UAAA;GAAA,QAAA,CAAA,2HAAA;EAAA,CAAA;;;AEVL,MAAM,YAAoB;AA2B1B,IAAa,uBAAb,MAAa,qBAAoB;CAhBjC,cAAA;EAiBoB,KAAA,QAA6B,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;EAE9C,KAAA,UAA2D,MAA6B,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,UAAA,+BAAA,CAAA;GACzG,WAAW;EAAgB,CAAA;CAElC;CANY;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAoB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAApB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAoB,cAAA;GAAA,UAAA;GAAA,QAAA;IAAA,OAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,SAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;GAAA;GAAA,MAAA,EAAA,gBAAA,gBAAA;GAAA,UAAA;GAAA,UC1CjC;GAQA,QAAA,CAAA,
|
|
1
|
+
{"version":3,"file":"rt-tools-ui-kit-v2-detail-list.mjs","names":["BEM_BLOCK"],"sources":["../../../projects/ui-kit-v2/src/lib/components/detail-list/rt-detail-list.component.ts","../../../projects/ui-kit-v2/src/lib/components/detail-list/rt-detail-list.component.html","../../../projects/ui-kit-v2/src/lib/components/detail-list/rt-detail-row.component.ts","../../../projects/ui-kit-v2/src/lib/components/detail-list/rt-detail-row.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';\n\nimport { BlockDirective } from '@rt-tools/core';\n\nconst BEM_BLOCK: string = 'rt-detail-list';\n\n/**\n * Контейнер для стопки `rt-detail-row` — задаёт единый вертикальный gap между\n * строками «лейбл + значение». Используется в read-only details-панелях асайдов\n * (детали заявки, профиля) вместо самопальных `div`-обёрток.\n *\n * Контент — только `rt-detail-row` через проекцию.\n */\n@Component({\n selector: 'rt-detail-list',\n templateUrl: './rt-detail-list.component.html',\n styleUrl: './rt-detail-list.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n BlockDirective,\n ],\n host: {\n class: BEM_BLOCK,\n },\n})\nexport class RtDetailListComponent {}\n","<ng-container rtBlock=\"rt-detail-list\">\n <ng-content />\n</ng-container>\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n input,\n InputSignal,\n InputSignalWithTransform,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\n\nimport { RtSkeletonWrapperComponent } from '@rt-tools/ui-kit-v2/skeleton-wrapper';\n\nconst BEM_BLOCK: string = 'rt-detail-row';\n\n/**\n * Одна read-only строка «лейбл + значение» в stacked-раскладке: приглушённый\n * лейбл сверху, значение снизу. Единый паттерн info-строки для всех асайдов\n * (раньше — пять разных реализаций).\n *\n * Значение проецируется через `<ng-content>` — допустим текст, дата через pipe\n * или `rt-tag`. При `[loading]=\"true\"` значение замещается скелетоном (потребитель\n * не оборачивает `rt-skeleton-wrapper` вручную).\n */\n@Component({\n selector: 'rt-detail-row',\n templateUrl: './rt-detail-row.component.html',\n styleUrl: './rt-detail-row.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtSkeletonWrapperComponent,\n BlockDirective,\n ElemDirective,\n ],\n host: {\n class: BEM_BLOCK,\n },\n})\nexport class RtDetailRowComponent {\n public readonly label: InputSignal<string> = input<string>('');\n\n public readonly loading: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n}\n","<ng-container rtBlock=\"rt-detail-row\">\n <span rtElem=\"label\" qa-dataid=\"detail-row-label\">{{ label() }}</span>\n <div rtElem=\"value\" qa-dataid=\"detail-row-value\">\n <rt-skeleton-wrapper width=\"180px\" height=\"16px\" [isLoading]=\"loading()\">\n <ng-content />\n </rt-skeleton-wrapper>\n </div>\n</ng-container>\n"],"mappings":";;;;AAIA,MAAMA,cAAoB;AAuB1B,IAAa,wBAAb,MAAa,sBAAqB;CAArB;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAqB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAArB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAqB,cAAA;GAAA,UAAA;GAAA,MAAA,EAAA,gBAAA,iBAAA;GAAA,UAAA;GAAA,UC3BlC;GAGA,QAAA,CAAA,2HAAA;GAAA,cAAA,CAAA;IAAA,MAAA;IAAA,MDkBQ;IAAc,UAAA;IAAA,QAAA,CAAA,OAAA;GAAA,CAAA;GAAA,iBAAA,GAAA,wBAAA;GAAA,eAAA,GAAA,kBAAA;EAAA,CAAA;CAAA;;AAMT,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAqB,YAAA,CAAA;EAdjC,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAgB,iBAGT,wBAAwB;GAAM,eAChC,kBAAkB;GAAI,SAC5B,CAEL,cACH;GAAA,MACK,EACF,OAAOA,YACV;GAAA,UAAA;GAAA,QAAA,CAAA,2HAAA;EAAA,CAAA;;;AEVL,MAAM,YAAoB;AA2B1B,IAAa,uBAAb,MAAa,qBAAoB;CAhBjC,cAAA;EAiBoB,KAAA,QAA6B,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;EAE9C,KAAA,UAA2D,MAA6B,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,UAAA,+BAAA,CAAA;GACzG,WAAW;EAAgB,CAAA;CAElC;CANY;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAoB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAApB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAoB,cAAA;GAAA,UAAA;GAAA,QAAA;IAAA,OAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,SAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;GAAA;GAAA,MAAA,EAAA,gBAAA,gBAAA;GAAA,UAAA;GAAA,UC1CjC;GAQA,QAAA,CAAA,2dAAA;GAAA,cAAA;IAAA;KAAA,MAAA;KAAA,MD0BQ;KAA0B,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAC1B;KAAc,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACd;KAAa,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;GAAA;GAAA,iBAAA,GAAA,wBAAA;GAAA,eAAA,GAAA,kBAAA;EAAA,CAAA;CAAA;;AAMR,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAoB,YAAA,CAAA;EAhBhC,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAe,iBAGR,wBAAwB;GAAM,eAChC,kBAAkB;GAAI,SAC5B;IAEL;IACA;IACA;GACH;GAAA,MACK,EACF,OAAO,UACV;GAAA,UAAA;GAAA,QAAA,CAAA,2dAAA;EAAA,CAAA"}
|
|
@@ -8,6 +8,7 @@ import { RtIconButtonComponent } from "@rt-tools/ui-kit-v2/icon-button";
|
|
|
8
8
|
import * as i1 from "@rt-tools/ui-kit-v2/radius";
|
|
9
9
|
import { RtRadiusDirective } from "@rt-tools/ui-kit-v2/radius";
|
|
10
10
|
const BEM_BLOCK = "rt-input-number";
|
|
11
|
+
const SMALL_STEP_ICON_PX = 10;
|
|
11
12
|
function parseNumber(text, groupSep) {
|
|
12
13
|
const withoutSpaces = text.replace(/\s/g, "");
|
|
13
14
|
const cleaned = (groupSep.trim() === "" ? withoutSpaces : withoutSpaces.split(groupSep).join("")).replace(/,/g, ".");
|
|
@@ -21,6 +22,16 @@ function clamp(value, min, max) {
|
|
|
21
22
|
if (max !== null && result > max) result = max;
|
|
22
23
|
return result;
|
|
23
24
|
}
|
|
25
|
+
function fractionDigitsOf(value) {
|
|
26
|
+
const text = String(value);
|
|
27
|
+
const dot = text.indexOf(".");
|
|
28
|
+
return dot < 0 ? 0 : Math.min(20, text.length - dot - 1);
|
|
29
|
+
}
|
|
30
|
+
function stepValue(current, delta, min, max) {
|
|
31
|
+
const base = current ?? 0;
|
|
32
|
+
const digits = Math.max(fractionDigitsOf(base), fractionDigitsOf(delta));
|
|
33
|
+
return clamp(Number((base + delta).toFixed(digits)), min, max);
|
|
34
|
+
}
|
|
24
35
|
function formatNumber(value, minFractionDigits, maxFractionDigits, locale, grouped) {
|
|
25
36
|
if (value === null) return "";
|
|
26
37
|
const precision = Math.min(20, Math.max(minFractionDigits, Math.min(maxFractionDigits, 20)));
|
|
@@ -40,6 +51,17 @@ var RtInputNumberComponent = class RtInputNumberComponent extends RtFormControlB
|
|
|
40
51
|
this.fieldEl = viewChild("fieldEl", ...ngDevMode ? [{ debugName: "fieldEl" }] : /* istanbul ignore next */ []);
|
|
41
52
|
this.displayValue = signal("", ...ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []);
|
|
42
53
|
this.hasValue = computed(() => this.displayValue() !== "" && this.value() !== null && this.value() !== 0, ...ngDevMode ? [{ debugName: "hasValue" }] : /* istanbul ignore next */ []);
|
|
54
|
+
this.stepIconSize = computed(() => this.size() === "sm" ? SMALL_STEP_ICON_PX : "xs", ...ngDevMode ? [{ debugName: "stepIconSize" }] : /* istanbul ignore next */ []);
|
|
55
|
+
this.canStepUp = computed(() => {
|
|
56
|
+
const value = this.value();
|
|
57
|
+
const max = this.max();
|
|
58
|
+
return !this.isDisabled() && (value === null || max === null || value < max);
|
|
59
|
+
}, ...ngDevMode ? [{ debugName: "canStepUp" }] : /* istanbul ignore next */ []);
|
|
60
|
+
this.canStepDown = computed(() => {
|
|
61
|
+
const value = this.value();
|
|
62
|
+
const min = this.min();
|
|
63
|
+
return !this.isDisabled() && (value === null || min === null || value > min);
|
|
64
|
+
}, ...ngDevMode ? [{ debugName: "canStepDown" }] : /* istanbul ignore next */ []);
|
|
43
65
|
this.fieldLabelInside = true;
|
|
44
66
|
this.appearance = input("outline", ...ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []);
|
|
45
67
|
this.displayText = computed(() => this.displayValue(), ...ngDevMode ? [{ debugName: "displayText" }] : /* istanbul ignore next */ []);
|
|
@@ -54,6 +76,11 @@ var RtInputNumberComponent = class RtInputNumberComponent extends RtFormControlB
|
|
|
54
76
|
...ngDevMode ? { debugName: "grouped" } : /* istanbul ignore next */ {},
|
|
55
77
|
transform: booleanAttribute
|
|
56
78
|
});
|
|
79
|
+
this.step = input(1, ...ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []);
|
|
80
|
+
this.stepper = input(true, {
|
|
81
|
+
...ngDevMode ? { debugName: "stepper" } : /* istanbul ignore next */ {},
|
|
82
|
+
transform: booleanAttribute
|
|
83
|
+
});
|
|
57
84
|
}
|
|
58
85
|
#locale;
|
|
59
86
|
#groupSep;
|
|
@@ -72,7 +99,7 @@ var RtInputNumberComponent = class RtInputNumberComponent extends RtFormControlB
|
|
|
72
99
|
if (this.isDisabled()) return;
|
|
73
100
|
const field = this.fieldEl()?.nativeElement;
|
|
74
101
|
const target = event.target;
|
|
75
|
-
if (!field || target === field || target.closest("
|
|
102
|
+
if (!field || target === field || target.closest("rt-icon-button")) return;
|
|
76
103
|
event.preventDefault();
|
|
77
104
|
field.focus();
|
|
78
105
|
}
|
|
@@ -117,6 +144,20 @@ var RtInputNumberComponent = class RtInputNumberComponent extends RtFormControlB
|
|
|
117
144
|
this.emitChange(rounded);
|
|
118
145
|
this.markTouched();
|
|
119
146
|
}
|
|
147
|
+
stepBy(direction) {
|
|
148
|
+
if (!(direction > 0 ? this.canStepUp() : this.canStepDown())) return;
|
|
149
|
+
const next = stepValue(this.value(), direction * this.step(), this.min(), this.max());
|
|
150
|
+
const formatted = formatNumber(next, this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped());
|
|
151
|
+
const rounded = parseNumber(formatted, this.#groupSep) ?? next;
|
|
152
|
+
this.value.set(rounded);
|
|
153
|
+
this.displayValue.set(formatted);
|
|
154
|
+
this.emitChange(rounded);
|
|
155
|
+
}
|
|
156
|
+
onKeydown(event) {
|
|
157
|
+
if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
|
|
158
|
+
event.preventDefault();
|
|
159
|
+
this.stepBy(event.key === "ArrowUp" ? 1 : -1);
|
|
160
|
+
}
|
|
120
161
|
#groupDisplay(raw) {
|
|
121
162
|
const negative = raw.trimStart().startsWith("-");
|
|
122
163
|
let sep = "";
|
|
@@ -244,6 +285,20 @@ var RtInputNumberComponent = class RtInputNumberComponent extends RtFormControlB
|
|
|
244
285
|
isSignal: true,
|
|
245
286
|
isRequired: false,
|
|
246
287
|
transformFunction: null
|
|
288
|
+
},
|
|
289
|
+
step: {
|
|
290
|
+
classPropertyName: "step",
|
|
291
|
+
publicName: "step",
|
|
292
|
+
isSignal: true,
|
|
293
|
+
isRequired: false,
|
|
294
|
+
transformFunction: null
|
|
295
|
+
},
|
|
296
|
+
stepper: {
|
|
297
|
+
classPropertyName: "stepper",
|
|
298
|
+
publicName: "stepper",
|
|
299
|
+
isSignal: true,
|
|
300
|
+
isRequired: false,
|
|
301
|
+
transformFunction: null
|
|
247
302
|
}
|
|
248
303
|
},
|
|
249
304
|
host: {
|
|
@@ -277,8 +332,8 @@ var RtInputNumberComponent = class RtInputNumberComponent extends RtFormControlB
|
|
|
277
332
|
inputs: ["radius", "radius"]
|
|
278
333
|
}],
|
|
279
334
|
ngImport: i0,
|
|
280
|
-
template: "<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '—' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"clearTabIndex()\"\n (clicked)=\"clear($event)\" />\n }\n }\n</ng-container>\n",
|
|
281
|
-
styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-input-number{--rt-input-number-radius: var(--rt-input-radius);--rt-input-number-fill-radius: var(--rt-radius-sm);--rt-input-number-color-border-hover: var(--rt-color-text-primary);--rt-input-number-edge: var(--rt-input-border-width);position:relative;display:inline-flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-number-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-input-number:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):where(:hover:not(:has(:disabled))){border-color:var(--rt-input-number-color-border-hover)}.rt-input-number--borderless{border-color:transparent}.rt-input-number--appearance--fill{--rt-input-number-edge: var(--rt-input-fill-edge);border-width:var(--rt-input-fill-edge) var(--rt-input-fill-edge) var(--rt-input-border-width);border-color:transparent;border-radius:var(--rt-input-number-fill-radius) var(--rt-input-number-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--appearance--fill:where([data-rt-radius]){border-radius:var(--rt-input-number-radius);border-bottom-color:transparent}.rt-input-number--appearance--pill{--rt-input-number-radius: var(--rt-radius-full);border-color:transparent;background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--invalid{border-color:var(--rt-input-color-border-error)}.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-input-number--appearance--fill:focus-within{box-shadow:var(--rt-input-fill-focus-shadow)}.rt-input-number--appearance--fill.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-fill-focus-shadow-error)}.rt-input-number--disabled{color:var(--rt-input-color-text-disabled);cursor:not-allowed;opacity:var(--rt-input-disabled-field-opacity)}.rt-input-number--appearance--fill.rt-input-number--disabled{background-color:var(--rt-input-fill-color-bg-disabled)}.rt-input-number--disabled:not(.rt-input-number--invalid,.rt-input-number--appearance--fill,.rt-input-number--appearance--pill,.rt-input-number--borderless,.rt-input-number--readonly){border-color:var(--rt-input-color-border-disabled)}.rt-input-number--disabled .rt-input-number__field::placeholder{color:var(--rt-input-color-placeholder-disabled)}.rt-input-number--size--sm{min-height:var(--rt-input-height-sm)}.rt-input-number--size--lg{min-height:var(--rt-input-height-lg)}.rt-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){padding-inline:calc(var(--rt-input-padding-x) - var(--rt-input-number-edge))}.rt-input-number--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-input-number__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-input-number__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-input-number__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;letter-spacing:var(--rt-input-letter-spacing, normal);outline:none}.rt-input-number__field::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-input-number__field:disabled{cursor:not-allowed}.rt-input-number__icon-left,.rt-input-number__prefix,.rt-input-number__clear{flex-shrink:0}.rt-input-number__prefix{color:var(--rt-color-text-muted);-webkit-user-select:none;user-select:none}.rt-input-number[data-rt-radius=none]{--rt-input-number-radius: var(--rt-radius-none)}.rt-input-number[data-rt-radius=xs]{--rt-input-number-radius: var(--rt-radius-xs)}.rt-input-number[data-rt-radius=sm]{--rt-input-number-radius: var(--rt-radius-sm)}.rt-input-number[data-rt-radius=ms]{--rt-input-number-radius: var(--rt-radius-ms)}.rt-input-number[data-rt-radius=md]{--rt-input-number-radius: var(--rt-radius-md)}.rt-input-number[data-rt-radius=lg]{--rt-input-number-radius: var(--rt-radius-lg)}.rt-input-number[data-rt-radius=xl]{--rt-input-number-radius: var(--rt-radius-xl)}.rt-input-number[data-rt-radius=\"2xl\"]{--rt-input-number-radius: var(--rt-radius-2xl)}.rt-input-number[data-rt-radius=full]{--rt-input-number-radius: var(--rt-radius-full)}}\n"],
|
|
335
|
+
template: "<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '—' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"clearTabIndex()\"\n (clicked)=\"clear($event)\" />\n }\n @if (stepper()) {\n <span rtElem=\"stepper\" qa-dataid=\"input-number-stepper\">\n <button\n type=\"button\"\n rtElem=\"step\"\n qa-dataid=\"input-number-step-up\"\n tabindex=\"-1\"\n [attr.aria-label]=\"t().uiIncrease\"\n [disabled]=\"!canStepUp()\"\n (click)=\"stepBy(1)\">\n <rt-icon rtElem=\"step-icon\" name=\"chevron-up\" [size]=\"stepIconSize()\" />\n </button>\n <button\n type=\"button\"\n rtElem=\"step\"\n qa-dataid=\"input-number-step-down\"\n tabindex=\"-1\"\n [attr.aria-label]=\"t().uiDecrease\"\n [disabled]=\"!canStepDown()\"\n (click)=\"stepBy(-1)\">\n <rt-icon rtElem=\"step-icon\" name=\"chevron-up\" [size]=\"stepIconSize()\" [rtMod]=\"{ flipped: true }\" />\n </button>\n </span>\n }\n }\n</ng-container>\n",
|
|
336
|
+
styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-input-number{--rt-input-number-radius: var(--rt-input-radius);--rt-input-number-fill-radius: var(--rt-radius-sm);--rt-input-number-color-border-hover: var(--rt-color-text-primary);--rt-input-number-step-radius: var(--rt-radius-sm);--rt-input-number-edge: var(--rt-input-border-width);position:relative;display:inline-flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-number-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-input-number:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):where(:hover:not(:has(:disabled))){border-color:var(--rt-input-number-color-border-hover)}.rt-input-number--borderless{border-color:transparent}.rt-input-number--appearance--fill{--rt-input-number-edge: var(--rt-input-fill-edge);border-width:var(--rt-input-fill-edge) var(--rt-input-fill-edge) var(--rt-input-border-width);border-color:transparent;border-radius:var(--rt-input-number-fill-radius) var(--rt-input-number-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--appearance--fill:where([data-rt-radius]){border-radius:var(--rt-input-number-radius);border-bottom-color:transparent}.rt-input-number--appearance--pill{--rt-input-number-radius: var(--rt-radius-full);border-color:transparent;background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--invalid{border-color:var(--rt-input-color-border-error)}.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-input-number--appearance--fill:focus-within{box-shadow:var(--rt-input-fill-focus-shadow)}.rt-input-number--appearance--fill.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-fill-focus-shadow-error)}.rt-input-number--disabled{color:var(--rt-input-color-text-disabled);cursor:not-allowed;opacity:var(--rt-input-disabled-field-opacity)}.rt-input-number--appearance--fill.rt-input-number--disabled{background-color:var(--rt-input-fill-color-bg-disabled)}.rt-input-number--disabled:not(.rt-input-number--invalid,.rt-input-number--appearance--fill,.rt-input-number--appearance--pill,.rt-input-number--borderless,.rt-input-number--readonly){border-color:var(--rt-input-color-border-disabled)}.rt-input-number--disabled .rt-input-number__field::placeholder{color:var(--rt-input-color-placeholder-disabled)}.rt-input-number--size--sm{min-height:var(--rt-input-height-sm)}.rt-input-number--size--lg{min-height:var(--rt-input-height-lg)}.rt-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){padding-inline:calc(var(--rt-input-padding-x) - var(--rt-input-number-edge))}.rt-input-number--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-input-number__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-input-number__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-input-number__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;letter-spacing:var(--rt-input-letter-spacing, normal);outline:none}.rt-input-number__field::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-input-number__field:disabled{cursor:not-allowed}.rt-input-number__icon-left,.rt-input-number__prefix,.rt-input-number__clear{flex-shrink:0}.rt-input-number__stepper{display:flex;flex-direction:column;flex-shrink:0}.rt-input-number--size--sm .rt-input-number__step{width:var(--rt-size-4);height:var(--rt-size-3)}.rt-input-number__step{display:inline-flex;width:var(--rt-size-5);height:var(--rt-size-4);align-items:center;justify-content:center;padding:0;border:none;border-radius:var(--rt-input-number-step-radius);background:transparent;color:var(--rt-color-text-muted);cursor:pointer}.rt-input-number__step:hover:not(:disabled){background-color:var(--rt-color-bg-hover);color:var(--rt-color-text-primary)}.rt-input-number__step:disabled{color:var(--rt-color-text-disabled);cursor:not-allowed}.rt-input-number__step-icon{pointer-events:none}.rt-input-number__step-icon--flipped{transform:rotate(180deg)}.rt-input-number__prefix{color:var(--rt-color-text-muted);-webkit-user-select:none;user-select:none}.rt-input-number[data-rt-radius=none]{--rt-input-number-radius: var(--rt-radius-none)}.rt-input-number[data-rt-radius=xs]{--rt-input-number-radius: var(--rt-radius-xs)}.rt-input-number[data-rt-radius=sm]{--rt-input-number-radius: var(--rt-radius-sm)}.rt-input-number[data-rt-radius=ms]{--rt-input-number-radius: var(--rt-radius-ms)}.rt-input-number[data-rt-radius=md]{--rt-input-number-radius: var(--rt-radius-md)}.rt-input-number[data-rt-radius=lg]{--rt-input-number-radius: var(--rt-radius-lg)}.rt-input-number[data-rt-radius=xl]{--rt-input-number-radius: var(--rt-radius-xl)}.rt-input-number[data-rt-radius=\"2xl\"]{--rt-input-number-radius: var(--rt-radius-2xl)}.rt-input-number[data-rt-radius=full]{--rt-input-number-radius: var(--rt-radius-full)}}\n"],
|
|
282
337
|
dependencies: [
|
|
283
338
|
{
|
|
284
339
|
kind: "component",
|
|
@@ -383,8 +438,8 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
383
438
|
"[class.rt-input-number--size--lg]": "size() === 'lg'",
|
|
384
439
|
"(mousedown)": "onHostMousedown($event)"
|
|
385
440
|
},
|
|
386
|
-
template: "<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '—' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"clearTabIndex()\"\n (clicked)=\"clear($event)\" />\n }\n }\n</ng-container>\n",
|
|
387
|
-
styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-input-number{--rt-input-number-radius: var(--rt-input-radius);--rt-input-number-fill-radius: var(--rt-radius-sm);--rt-input-number-color-border-hover: var(--rt-color-text-primary);--rt-input-number-edge: var(--rt-input-border-width);position:relative;display:inline-flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-number-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-input-number:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):where(:hover:not(:has(:disabled))){border-color:var(--rt-input-number-color-border-hover)}.rt-input-number--borderless{border-color:transparent}.rt-input-number--appearance--fill{--rt-input-number-edge: var(--rt-input-fill-edge);border-width:var(--rt-input-fill-edge) var(--rt-input-fill-edge) var(--rt-input-border-width);border-color:transparent;border-radius:var(--rt-input-number-fill-radius) var(--rt-input-number-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--appearance--fill:where([data-rt-radius]){border-radius:var(--rt-input-number-radius);border-bottom-color:transparent}.rt-input-number--appearance--pill{--rt-input-number-radius: var(--rt-radius-full);border-color:transparent;background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--invalid{border-color:var(--rt-input-color-border-error)}.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-input-number--appearance--fill:focus-within{box-shadow:var(--rt-input-fill-focus-shadow)}.rt-input-number--appearance--fill.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-fill-focus-shadow-error)}.rt-input-number--disabled{color:var(--rt-input-color-text-disabled);cursor:not-allowed;opacity:var(--rt-input-disabled-field-opacity)}.rt-input-number--appearance--fill.rt-input-number--disabled{background-color:var(--rt-input-fill-color-bg-disabled)}.rt-input-number--disabled:not(.rt-input-number--invalid,.rt-input-number--appearance--fill,.rt-input-number--appearance--pill,.rt-input-number--borderless,.rt-input-number--readonly){border-color:var(--rt-input-color-border-disabled)}.rt-input-number--disabled .rt-input-number__field::placeholder{color:var(--rt-input-color-placeholder-disabled)}.rt-input-number--size--sm{min-height:var(--rt-input-height-sm)}.rt-input-number--size--lg{min-height:var(--rt-input-height-lg)}.rt-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){padding-inline:calc(var(--rt-input-padding-x) - var(--rt-input-number-edge))}.rt-input-number--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-input-number__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-input-number__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-input-number__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;letter-spacing:var(--rt-input-letter-spacing, normal);outline:none}.rt-input-number__field::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-input-number__field:disabled{cursor:not-allowed}.rt-input-number__icon-left,.rt-input-number__prefix,.rt-input-number__clear{flex-shrink:0}.rt-input-number__prefix{color:var(--rt-color-text-muted);-webkit-user-select:none;user-select:none}.rt-input-number[data-rt-radius=none]{--rt-input-number-radius: var(--rt-radius-none)}.rt-input-number[data-rt-radius=xs]{--rt-input-number-radius: var(--rt-radius-xs)}.rt-input-number[data-rt-radius=sm]{--rt-input-number-radius: var(--rt-radius-sm)}.rt-input-number[data-rt-radius=ms]{--rt-input-number-radius: var(--rt-radius-ms)}.rt-input-number[data-rt-radius=md]{--rt-input-number-radius: var(--rt-radius-md)}.rt-input-number[data-rt-radius=lg]{--rt-input-number-radius: var(--rt-radius-lg)}.rt-input-number[data-rt-radius=xl]{--rt-input-number-radius: var(--rt-radius-xl)}.rt-input-number[data-rt-radius=\"2xl\"]{--rt-input-number-radius: var(--rt-radius-2xl)}.rt-input-number[data-rt-radius=full]{--rt-input-number-radius: var(--rt-radius-full)}}\n"]
|
|
441
|
+
template: "<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '—' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"clearTabIndex()\"\n (clicked)=\"clear($event)\" />\n }\n @if (stepper()) {\n <span rtElem=\"stepper\" qa-dataid=\"input-number-stepper\">\n <button\n type=\"button\"\n rtElem=\"step\"\n qa-dataid=\"input-number-step-up\"\n tabindex=\"-1\"\n [attr.aria-label]=\"t().uiIncrease\"\n [disabled]=\"!canStepUp()\"\n (click)=\"stepBy(1)\">\n <rt-icon rtElem=\"step-icon\" name=\"chevron-up\" [size]=\"stepIconSize()\" />\n </button>\n <button\n type=\"button\"\n rtElem=\"step\"\n qa-dataid=\"input-number-step-down\"\n tabindex=\"-1\"\n [attr.aria-label]=\"t().uiDecrease\"\n [disabled]=\"!canStepDown()\"\n (click)=\"stepBy(-1)\">\n <rt-icon rtElem=\"step-icon\" name=\"chevron-up\" [size]=\"stepIconSize()\" [rtMod]=\"{ flipped: true }\" />\n </button>\n </span>\n }\n }\n</ng-container>\n",
|
|
442
|
+
styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-input-number{--rt-input-number-radius: var(--rt-input-radius);--rt-input-number-fill-radius: var(--rt-radius-sm);--rt-input-number-color-border-hover: var(--rt-color-text-primary);--rt-input-number-step-radius: var(--rt-radius-sm);--rt-input-number-edge: var(--rt-input-border-width);position:relative;display:inline-flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-number-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-input-number:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):where(:hover:not(:has(:disabled))){border-color:var(--rt-input-number-color-border-hover)}.rt-input-number--borderless{border-color:transparent}.rt-input-number--appearance--fill{--rt-input-number-edge: var(--rt-input-fill-edge);border-width:var(--rt-input-fill-edge) var(--rt-input-fill-edge) var(--rt-input-border-width);border-color:transparent;border-radius:var(--rt-input-number-fill-radius) var(--rt-input-number-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--appearance--fill:where([data-rt-radius]){border-radius:var(--rt-input-number-radius);border-bottom-color:transparent}.rt-input-number--appearance--pill{--rt-input-number-radius: var(--rt-radius-full);border-color:transparent;background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--invalid{border-color:var(--rt-input-color-border-error)}.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-input-number--appearance--fill:focus-within{box-shadow:var(--rt-input-fill-focus-shadow)}.rt-input-number--appearance--fill.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-fill-focus-shadow-error)}.rt-input-number--disabled{color:var(--rt-input-color-text-disabled);cursor:not-allowed;opacity:var(--rt-input-disabled-field-opacity)}.rt-input-number--appearance--fill.rt-input-number--disabled{background-color:var(--rt-input-fill-color-bg-disabled)}.rt-input-number--disabled:not(.rt-input-number--invalid,.rt-input-number--appearance--fill,.rt-input-number--appearance--pill,.rt-input-number--borderless,.rt-input-number--readonly){border-color:var(--rt-input-color-border-disabled)}.rt-input-number--disabled .rt-input-number__field::placeholder{color:var(--rt-input-color-placeholder-disabled)}.rt-input-number--size--sm{min-height:var(--rt-input-height-sm)}.rt-input-number--size--lg{min-height:var(--rt-input-height-lg)}.rt-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){padding-inline:calc(var(--rt-input-padding-x) - var(--rt-input-number-edge))}.rt-input-number--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-input-number__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-input-number__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-input-number__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;letter-spacing:var(--rt-input-letter-spacing, normal);outline:none}.rt-input-number__field::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-input-number__field:disabled{cursor:not-allowed}.rt-input-number__icon-left,.rt-input-number__prefix,.rt-input-number__clear{flex-shrink:0}.rt-input-number__stepper{display:flex;flex-direction:column;flex-shrink:0}.rt-input-number--size--sm .rt-input-number__step{width:var(--rt-size-4);height:var(--rt-size-3)}.rt-input-number__step{display:inline-flex;width:var(--rt-size-5);height:var(--rt-size-4);align-items:center;justify-content:center;padding:0;border:none;border-radius:var(--rt-input-number-step-radius);background:transparent;color:var(--rt-color-text-muted);cursor:pointer}.rt-input-number__step:hover:not(:disabled){background-color:var(--rt-color-bg-hover);color:var(--rt-color-text-primary)}.rt-input-number__step:disabled{color:var(--rt-color-text-disabled);cursor:not-allowed}.rt-input-number__step-icon{pointer-events:none}.rt-input-number__step-icon--flipped{transform:rotate(180deg)}.rt-input-number__prefix{color:var(--rt-color-text-muted);-webkit-user-select:none;user-select:none}.rt-input-number[data-rt-radius=none]{--rt-input-number-radius: var(--rt-radius-none)}.rt-input-number[data-rt-radius=xs]{--rt-input-number-radius: var(--rt-radius-xs)}.rt-input-number[data-rt-radius=sm]{--rt-input-number-radius: var(--rt-radius-sm)}.rt-input-number[data-rt-radius=ms]{--rt-input-number-radius: var(--rt-radius-ms)}.rt-input-number[data-rt-radius=md]{--rt-input-number-radius: var(--rt-radius-md)}.rt-input-number[data-rt-radius=lg]{--rt-input-number-radius: var(--rt-radius-lg)}.rt-input-number[data-rt-radius=xl]{--rt-input-number-radius: var(--rt-radius-xl)}.rt-input-number[data-rt-radius=\"2xl\"]{--rt-input-number-radius: var(--rt-radius-2xl)}.rt-input-number[data-rt-radius=full]{--rt-input-number-radius: var(--rt-radius-full)}}\n"]
|
|
388
443
|
}]
|
|
389
444
|
}],
|
|
390
445
|
propDecorators: {
|
|
@@ -463,6 +518,22 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
463
518
|
alias: "grouped",
|
|
464
519
|
required: false
|
|
465
520
|
}]
|
|
521
|
+
}],
|
|
522
|
+
step: [{
|
|
523
|
+
type: i0.Input,
|
|
524
|
+
args: [{
|
|
525
|
+
isSignal: true,
|
|
526
|
+
alias: "step",
|
|
527
|
+
required: false
|
|
528
|
+
}]
|
|
529
|
+
}],
|
|
530
|
+
stepper: [{
|
|
531
|
+
type: i0.Input,
|
|
532
|
+
args: [{
|
|
533
|
+
isSignal: true,
|
|
534
|
+
alias: "stepper",
|
|
535
|
+
required: false
|
|
536
|
+
}]
|
|
466
537
|
}]
|
|
467
538
|
}
|
|
468
539
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rt-tools-ui-kit-v2-input-number.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/input-number/rt-input-number.component.ts","../../../projects/ui-kit-v2/src/lib/components/input-number/rt-input-number.component.html"],"sourcesContent":["import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n forwardRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n LOCALE_ID,\n signal,\n Signal,\n viewChild,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { RtFormControlBase } from '@rt-tools/ui-kit-v2/form-control';\nimport { IRtInput } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\n\nconst BEM_BLOCK: string = 'rt-input-number';\n\n/**\n * Парсит строку в `number | null` по разделителям локали: `groupSep` и любые\n * пробелы удаляются, оставшийся разделитель дробной части приводится к точке.\n *\n * Разбор обязан знать локаль, иначе он расходится с показом: в `en-US` разряды\n * группируются запятой, и набранное `1000` показывалось как `1,000`, а в форму\n * уходила `1` — безусловная замена первой запятой на точку читала разделитель\n * разрядов как десятичный.\n *\n * Пустая строка / NaN → null.\n */\nfunction parseNumber(text: string, groupSep: string): number | null {\n const withoutSpaces: string = text.replace(/\\s/g, '');\n // Пробельный разделитель разрядов (ru) уже снят предыдущей строкой.\n const withoutGroups: string = groupSep.trim() === '' ? withoutSpaces : withoutSpaces.split(groupSep).join('');\n const cleaned: string = withoutGroups.replace(/,/g, '.');\n if (cleaned === '') {\n return null;\n }\n const parsed: number = parseFloat(cleaned);\n return Number.isNaN(parsed) ? null : parsed;\n}\n\n/**\n * Клампит `value` в диапазон `[min, max]`. `null` границы игнорируются —\n * соответствующая сторона остаётся неограниченной.\n */\nfunction clamp(value: number, min: number | null, max: number | null): number {\n let result: number = value;\n if (min !== null && result < min) {\n result = min;\n }\n if (max !== null && result > max) {\n result = max;\n }\n return result;\n}\n\n/**\n * Форматирует `number | null` в строку с группировкой разрядов (неразрывный\n * пробел) и заданным fractionDigits diapason. `value === null` → \"\".\n * `maxFractionDigits` ограничивается сверху 20 (лимит Intl). Итоговый\n * precision = `max(min, min(max, 20))` и фиксирует одинаковую нижнюю/верхнюю\n * границу дробной части (как прежний `toFixed`), меняется только группировка\n * и десятичный разделитель — их даёт `locale`.\n *\n * `grouped === false` снимает разделители разрядов: у года, номера дома и кода\n * разряды значения не несут, и `2026` показывалось как `2 026`.\n */\nfunction formatNumber(\n value: number | null,\n minFractionDigits: number,\n maxFractionDigits: number,\n locale: string,\n grouped: boolean\n): string {\n if (value === null) {\n return '';\n }\n const cappedMax: number = Math.min(maxFractionDigits, 20);\n const precision: number = Math.min(20, Math.max(minFractionDigits, cappedMax));\n return value.toLocaleString(locale, {\n minimumFractionDigits: precision,\n maximumFractionDigits: precision,\n useGrouping: grouped,\n });\n}\n\n/**\n * Numeric input с parse/format/clamp logic. Использует native\n * `<input type=\"text\" inputmode=\"decimal\">` (НЕ `type=\"number\"` — избегаем\n * browser spinner quirks: scroll-wheel value changes, locale-specific decimal\n * separators, inconsistent step rounding).\n *\n * CVA через общий `RtFormControlBase` — авто-подсветка invalid, `clearable`\n * (крестик при непустом display). Parser принимает `,`/`.`, невалидный ввод\n * (NaN) не эмитится во время набора; на blur невалидное → null, валидное —\n * clamp + format.\n *\n * Визуально и по токенам идентичен rt-input (`--rt-input-*`): host — box,\n * `<input>` — прозрачный `__field`.\n */\n@Component({\n selector: 'rt-input-number',\n templateUrl: './rt-input-number.component.html',\n styleUrl: './rt-input-number.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconButtonComponent,\n RtIconComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n providers: [\n // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.\n { provide: RtFormControlBase, useExisting: forwardRef(() => RtInputNumberComponent) },\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[class.rt-input-number--disabled]': 'isDisabled()',\n '[class.rt-input-number--invalid]': 'isInvalid()',\n '[class.rt-input-number--readonly]': 'isReadonly()',\n '[class.rt-input-number--borderless]': '!bordered()',\n '[class.rt-input-number--appearance--fill]': \"appearance() === 'fill'\",\n '[class.rt-input-number--appearance--pill]': \"appearance() === 'pill'\",\n '[class.rt-input-number--size--sm]': \"size() === 'sm'\",\n '[class.rt-input-number--size--lg]': \"size() === 'lg'\",\n '(mousedown)': 'onHostMousedown($event)',\n },\n})\nexport class RtInputNumberComponent extends RtFormControlBase<number | null> {\n readonly #locale: string = inject(LOCALE_ID);\n\n // Символ группировки разрядов из локали (для ru — неразрывный пробел). Берём\n // тот же, что отдаёт toLocaleString на blur — тогда live и blur выглядят\n // одинаково, а parseNumber (стрипает \\s) парсит обратно обе формы.\n readonly #groupSep: string = (1000).toLocaleString(this.#locale).replace(/\\d/g, '') || ' ';\n\n /**\n * Что в этой локали можно набрать как разделитель дробной части: всё из\n * «.,», кроме того, чем локаль разделяет разряды. В `ru` разряды разделяет\n * неразрывный пробел, поэтому годятся оба знака; в `en-US` запятая — это\n * разряды, и десятичной остаётся только точка. Иначе набранное `1,5`\n * значило бы в показе одно, а в разборе другое.\n */\n readonly #decimalSepPattern: RegExp = new RegExp(`[${['.', ','].filter((char: string): boolean => char !== this.#groupSep).join('')}]`);\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly fieldEl: Signal<ElementRef<HTMLInputElement> | undefined> = viewChild<ElementRef<HTMLInputElement>>('fieldEl');\n\n protected readonly displayValue: WritableSignal<string> = signal<string>('');\n\n // Крестик показываем только при непустом и ненулевом значении: 0 — это и\n // есть результат очистки, поэтому при нём кнопка сброса не нужна.\n protected readonly hasValue: Signal<boolean> = computed(\n (): boolean => this.displayValue() !== '' && this.value() !== null && this.value() !== 0\n );\n\n /** Вид рамки: `outline` — рамка со всех сторон, `fill` — залитое поле с чертой снизу. */\n public override readonly fieldLabelInside: boolean = true;\n public readonly appearance: InputSignal<IRtInput.Appearance> = input<IRtInput.Appearance>('outline');\n\n public readonly displayText: Signal<string> = computed((): string => this.displayValue());\n\n public readonly iconLeft: InputSignal<IRtIcon.Name | null> = input<IRtIcon.Name | null>(null);\n /** Текстовый префикс слева от значения (напр. «₽» для рублёвых сумм). */\n public readonly prefix: InputSignal<string | null> = input<string | null>(null);\n public readonly placeholder: InputSignal<string> = input<string>('');\n public readonly min: InputSignal<number | null> = input<number | null>(null);\n public readonly max: InputSignal<number | null> = input<number | null>(null);\n public readonly minFractionDigits: InputSignal<number> = input<number>(0);\n public readonly maxFractionDigits: InputSignal<number> = input<number>(2);\n /**\n * Группировать ли разряды. Снимается у чисел, которые разрядами не читаются:\n * год, номер дома, код. У года разделитель превращает `2026` в `2 026`.\n */\n public readonly grouped: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(true, {\n transform: booleanAttribute,\n });\n\n public override writeValue(value: number | null): void {\n super.writeValue(value);\n this.displayValue.set(formatNumber(this.value(), this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped()));\n }\n\n protected getEmptyValue(): number | null {\n return null;\n }\n\n protected focusAfterClear(): void {\n this.fieldEl()?.nativeElement.focus();\n }\n\n /**\n * Падинг/гэп рамки принадлежат host-контейнеру, а не `<input>`, поэтому клик\n * по этой зоне сам по себе не фокусирует поле. Перехватываем mousedown и\n * переводим фокус в input — но только если клик не пришёлся на само поле\n * (там нужно нативное позиционирование курсора) и не на интерактивный\n * постфикс (крестик-очистка со своим обработчиком).\n */\n protected onHostMousedown(event: MouseEvent): void {\n if (this.isDisabled()) {\n return;\n }\n const field: HTMLInputElement | undefined = this.fieldEl()?.nativeElement;\n const target: HTMLElement = event.target as HTMLElement;\n if (!field || target === field || target.closest('button, rt-icon-button')) {\n return;\n }\n event.preventDefault();\n field.focus();\n }\n\n /**\n * Крестик сбрасывает поле не в пустое/null, а в 0 — отформатированный по\n * текущему fractionDigits (например `0,00`). Числовые поля всегда имеют\n * осмысленный ноль, поэтому «очистка» = обнуление, а не пустое значение.\n */\n protected override clearValue(): void {\n this.value.set(0);\n this.displayValue.set(formatNumber(0, this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped()));\n }\n\n protected onInput(event: Event): void {\n const target: HTMLInputElement = event.target as HTMLInputElement;\n const raw: string = target.value;\n const caret: number = target.selectionStart ?? raw.length;\n\n // Live-группировка разрядов прямо во время набора (1000000 → 1 000 000).\n const formatted: string = this.#groupDisplay(raw);\n const nextCaret: number = this.#restoreCaret(raw, caret, formatted);\n\n // Пишем value императивно ДО Angular CD: тогда повторная запись биндинга\n // `[value]` будет той же строкой и не сбросит каретку в конец. Затем\n // восстанавливаем позицию по числу значимых символов слева от курсора.\n target.value = formatted;\n target.setSelectionRange(nextCaret, nextCaret);\n this.displayValue.set(formatted);\n\n const parsed: number | null = parseNumber(formatted, this.#groupSep);\n if (parsed === null) {\n // Только реально пустой ввод → emit null (пользователь очистил поле).\n // Нечисловой ввод стрипается из отображения, но prev value держится до\n // blur — поэтому проверяем исходный `raw`, а не очищенный `formatted`.\n if (raw.trim() === '') {\n this.value.set(null);\n this.emitChange(null);\n }\n return;\n }\n this.value.set(parsed);\n this.emitChange(parsed);\n }\n\n protected onBlur(): void {\n const parsed: number | null = parseNumber(this.displayValue(), this.#groupSep);\n if (parsed === null) {\n this.value.set(null);\n this.displayValue.set('');\n this.emitChange(null);\n this.markTouched();\n return;\n }\n const clamped: number = clamp(parsed, this.min(), this.max());\n const formatted: string = formatNumber(clamped, this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped());\n // `formatted` содержит разделители локали — обратный парс через\n // parseNumber (а не parseFloat) даёт округлённое числовое значение.\n const rounded: number = parseNumber(formatted, this.#groupSep) ?? clamped;\n this.value.set(rounded);\n this.displayValue.set(formatted);\n this.emitChange(rounded);\n this.markTouched();\n }\n\n /**\n * Live-форматирование строки во время набора: целая часть группируется по\n * разрядам (`#groupSep`), дробная остаётся как набрана (хвостовая запятая и\n * нули сохраняются, чтобы их можно было дописать). Разделителем дробной\n * части считается только то, что им может быть в этой локали\n * (`#decimalSepPattern`), — иначе запятая-разряды в `en-US` читалась бы как\n * десятичная. Разделитель допускается всегда — округление по\n * `maxFractionDigits` происходит на blur\n * (поэтому в целочисленном поле «1000,5» парсится и округлится, а не склеится\n * в «10005»). Группировка — линейным проходом, а не `Number()`, чтобы не\n * терять точность на суммах длиннее 2^53.\n */\n #groupDisplay(raw: string): string {\n const negative: boolean = raw.trimStart().startsWith('-');\n\n let sep: string = '';\n let fracDigits: string = '';\n let intDigits: string;\n\n const sepIndex: number = raw.search(this.#decimalSepPattern);\n if (sepIndex >= 0) {\n sep = raw.charAt(sepIndex);\n intDigits = raw.slice(0, sepIndex).replace(/\\D/g, '');\n fracDigits = raw.slice(sepIndex + 1).replace(/\\D/g, '');\n } else {\n intDigits = raw.replace(/\\D/g, '');\n }\n\n // Ведущие нули убираем, но сохраняем один «0» перед десятичным разделителем.\n intDigits = intDigits.replace(/^0+(?=\\d)/, '');\n\n let intPart: string;\n if (intDigits === '') {\n intPart = sep === '' ? '' : '0';\n } else {\n // Набор идёт тем же видом, что и показ на blur: иначе разделители\n // появлялись бы при наборе и пропадали после ухода из поля.\n intPart = this.grouped() ? this.#groupInteger(intDigits) : intDigits;\n }\n\n let out: string = negative ? `-${intPart}` : intPart;\n if (sep !== '') {\n out += sep + fracDigits;\n }\n return out;\n }\n\n /**\n * Группирует строку цифр по три справа налево, вставляя `#groupSep`. Без\n * регэкспа намеренно — `(\\d{3})+`-lookahead флагуется как ReDoS-уязвимый, а\n * линейный проход и быстрее, и безопасен.\n */\n #groupInteger(digits: string): string {\n const length: number = digits.length;\n let out: string = '';\n for (let i: number = 0; i < length; i++) {\n if (i > 0 && (length - i) % 3 === 0) {\n out += this.#groupSep;\n }\n out += digits.charAt(i);\n }\n return out;\n }\n\n /**\n * Пересчитывает позицию каретки после вставки разделителей разрядов: считает\n * значимые символы (цифры и десятичный разделитель — не group-separator)\n * слева от старой каретки и находит ту же точку в новой строке. Иначе курсор\n * прыгал бы в конец при каждом вставленном пробеле.\n */\n #restoreCaret(raw: string, caret: number, formatted: string): number {\n const isAnchor: (ch: string) => boolean = (ch: string): boolean => /[\\d.,]/.test(ch);\n\n let need: number = 0;\n for (let i: number = 0; i < caret && i < raw.length; i++) {\n if (isAnchor(raw.charAt(i))) {\n need++;\n }\n }\n if (need === 0) {\n return formatted.startsWith('-') ? 1 : 0;\n }\n\n let seen: number = 0;\n let pos: number = 0;\n while (pos < formatted.length) {\n if (isAnchor(formatted.charAt(pos))) {\n seen++;\n if (seen === need) {\n pos++;\n break;\n }\n }\n pos++;\n }\n return pos;\n }\n}\n","<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '—' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"clearTabIndex()\"\n (clicked)=\"clear($event)\" />\n }\n }\n</ng-container>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rt-tools-ui-kit-v2-input-number.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/input-number/rt-input-number.component.ts","../../../projects/ui-kit-v2/src/lib/components/input-number/rt-input-number.component.html"],"sourcesContent":["import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n forwardRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n LOCALE_ID,\n signal,\n Signal,\n viewChild,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { RtFormControlBase } from '@rt-tools/ui-kit-v2/form-control';\nimport { IRtInput } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\n\nconst BEM_BLOCK: string = 'rt-input-number';\n\n/** Значок стрелки шага в поле `sm`: в шкале значков такой ступени нет. */\nconst SMALL_STEP_ICON_PX: number = 10;\n\n/**\n * Парсит строку в `number | null` по разделителям локали: `groupSep` и любые\n * пробелы удаляются, оставшийся разделитель дробной части приводится к точке.\n *\n * Разбор обязан знать локаль, иначе он расходится с показом: в `en-US` разряды\n * группируются запятой, и набранное `1000` показывалось как `1,000`, а в форму\n * уходила `1` — безусловная замена первой запятой на точку читала разделитель\n * разрядов как десятичный.\n *\n * Пустая строка / NaN → null.\n */\nfunction parseNumber(text: string, groupSep: string): number | null {\n const withoutSpaces: string = text.replace(/\\s/g, '');\n // Пробельный разделитель разрядов (ru) уже снят предыдущей строкой.\n const withoutGroups: string = groupSep.trim() === '' ? withoutSpaces : withoutSpaces.split(groupSep).join('');\n const cleaned: string = withoutGroups.replace(/,/g, '.');\n if (cleaned === '') {\n return null;\n }\n const parsed: number = parseFloat(cleaned);\n return Number.isNaN(parsed) ? null : parsed;\n}\n\n/**\n * Клампит `value` в диапазон `[min, max]`. `null` границы игнорируются —\n * соответствующая сторона остаётся неограниченной.\n */\nfunction clamp(value: number, min: number | null, max: number | null): number {\n let result: number = value;\n if (min !== null && result < min) {\n result = min;\n }\n if (max !== null && result > max) {\n result = max;\n }\n return result;\n}\n\n/** Число знаков после точки в записи числа: у 0,25 — два, у 3 — ноль. */\nfunction fractionDigitsOf(value: number): number {\n const text: string = String(value);\n const dot: number = text.indexOf('.');\n return dot < 0 ? 0 : Math.min(20, text.length - dot - 1);\n}\n\n/**\n * Значение после шага стрелкой. Пустое поле шагает от нуля, как числовое поле\n * браузера. Сумма округляется до знаков шага и значения — иначе 0,1 + 0,2 даёт\n * 0,30000000000000004, — и зажимается в диапазон.\n */\nfunction stepValue(current: number | null, delta: number, min: number | null, max: number | null): number {\n const base: number = current ?? 0;\n const digits: number = Math.max(fractionDigitsOf(base), fractionDigitsOf(delta));\n return clamp(Number((base + delta).toFixed(digits)), min, max);\n}\n\n/**\n * Форматирует `number | null` в строку с группировкой разрядов (неразрывный\n * пробел) и заданным fractionDigits diapason. `value === null` → \"\".\n * `maxFractionDigits` ограничивается сверху 20 (лимит Intl). Итоговый\n * precision = `max(min, min(max, 20))` и фиксирует одинаковую нижнюю/верхнюю\n * границу дробной части (как прежний `toFixed`), меняется только группировка\n * и десятичный разделитель — их даёт `locale`.\n *\n * `grouped === false` снимает разделители разрядов: у года, номера дома и кода\n * разряды значения не несут, и `2026` показывалось как `2 026`.\n */\nfunction formatNumber(\n value: number | null,\n minFractionDigits: number,\n maxFractionDigits: number,\n locale: string,\n grouped: boolean\n): string {\n if (value === null) {\n return '';\n }\n const cappedMax: number = Math.min(maxFractionDigits, 20);\n const precision: number = Math.min(20, Math.max(minFractionDigits, cappedMax));\n return value.toLocaleString(locale, {\n minimumFractionDigits: precision,\n maximumFractionDigits: precision,\n useGrouping: grouped,\n });\n}\n\n/**\n * Numeric input с parse/format/clamp logic. Использует native\n * `<input type=\"text\" inputmode=\"decimal\">` (НЕ `type=\"number\"` — избегаем\n * browser spinner quirks: scroll-wheel value changes, locale-specific decimal\n * separators, inconsistent step rounding).\n *\n * CVA через общий `RtFormControlBase` — авто-подсветка invalid, `clearable`\n * (крестик при непустом display). Parser принимает `,`/`.`, невалидный ввод\n * (NaN) не эмитится во время набора; на blur невалидное → null, валидное —\n * clamp + format.\n *\n * Визуально и по токенам идентичен rt-input (`--rt-input-*`): host — box,\n * `<input>` — прозрачный `__field`.\n */\n@Component({\n selector: 'rt-input-number',\n templateUrl: './rt-input-number.component.html',\n styleUrl: './rt-input-number.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconButtonComponent,\n RtIconComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n providers: [\n // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.\n { provide: RtFormControlBase, useExisting: forwardRef(() => RtInputNumberComponent) },\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[class.rt-input-number--disabled]': 'isDisabled()',\n '[class.rt-input-number--invalid]': 'isInvalid()',\n '[class.rt-input-number--readonly]': 'isReadonly()',\n '[class.rt-input-number--borderless]': '!bordered()',\n '[class.rt-input-number--appearance--fill]': \"appearance() === 'fill'\",\n '[class.rt-input-number--appearance--pill]': \"appearance() === 'pill'\",\n '[class.rt-input-number--size--sm]': \"size() === 'sm'\",\n '[class.rt-input-number--size--lg]': \"size() === 'lg'\",\n '(mousedown)': 'onHostMousedown($event)',\n },\n})\nexport class RtInputNumberComponent extends RtFormControlBase<number | null> {\n readonly #locale: string = inject(LOCALE_ID);\n\n // Символ группировки разрядов из локали (для ru — неразрывный пробел). Берём\n // тот же, что отдаёт toLocaleString на blur — тогда live и blur выглядят\n // одинаково, а parseNumber (стрипает \\s) парсит обратно обе формы.\n readonly #groupSep: string = (1000).toLocaleString(this.#locale).replace(/\\d/g, '') || ' ';\n\n /**\n * Что в этой локали можно набрать как разделитель дробной части: всё из\n * «.,», кроме того, чем локаль разделяет разряды. В `ru` разряды разделяет\n * неразрывный пробел, поэтому годятся оба знака; в `en-US` запятая — это\n * разряды, и десятичной остаётся только точка. Иначе набранное `1,5`\n * значило бы в показе одно, а в разборе другое.\n */\n readonly #decimalSepPattern: RegExp = new RegExp(`[${['.', ','].filter((char: string): boolean => char !== this.#groupSep).join('')}]`);\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly fieldEl: Signal<ElementRef<HTMLInputElement> | undefined> = viewChild<ElementRef<HTMLInputElement>>('fieldEl');\n\n protected readonly displayValue: WritableSignal<string> = signal<string>('');\n\n // Крестик показываем только при непустом и ненулевом значении: 0 — это и\n // есть результат очистки, поэтому при нём кнопка сброса не нужна.\n protected readonly hasValue: Signal<boolean> = computed(\n (): boolean => this.displayValue() !== '' && this.value() !== null && this.value() !== 0\n );\n\n /** Значок стрелки: в поле `sm` он 10 px, как в макете, в остальных — ступень `xs` (12 px). */\n protected readonly stepIconSize: Signal<IRtIcon.SizeInput> = computed((): IRtIcon.SizeInput =>\n this.size() === 'sm' ? SMALL_STEP_ICON_PX : 'xs'\n );\n\n /** Шаг вверх возможен, пока значение не дошло до `max`; пустое поле шагает всегда. */\n protected readonly canStepUp: Signal<boolean> = computed((): boolean => {\n const value: number | null = this.value();\n const max: number | null = this.max();\n return !this.isDisabled() && (value === null || max === null || value < max);\n });\n\n /** Шаг вниз возможен, пока значение не дошло до `min`. */\n protected readonly canStepDown: Signal<boolean> = computed((): boolean => {\n const value: number | null = this.value();\n const min: number | null = this.min();\n return !this.isDisabled() && (value === null || min === null || value > min);\n });\n\n public override readonly fieldLabelInside: boolean = true;\n /** Вид рамки: `outline` — рамка со всех сторон, `fill` — залитое поле с чертой снизу. */\n public readonly appearance: InputSignal<IRtInput.Appearance> = input<IRtInput.Appearance>('outline');\n\n public readonly displayText: Signal<string> = computed((): string => this.displayValue());\n\n public readonly iconLeft: InputSignal<IRtIcon.Name | null> = input<IRtIcon.Name | null>(null);\n /** Текстовый префикс слева от значения (напр. «₽» для рублёвых сумм). */\n public readonly prefix: InputSignal<string | null> = input<string | null>(null);\n public readonly placeholder: InputSignal<string> = input<string>('');\n public readonly min: InputSignal<number | null> = input<number | null>(null);\n public readonly max: InputSignal<number | null> = input<number | null>(null);\n public readonly minFractionDigits: InputSignal<number> = input<number>(0);\n public readonly maxFractionDigits: InputSignal<number> = input<number>(2);\n /**\n * Группировать ли разряды. Снимается у чисел, которые разрядами не читаются:\n * год, номер дома, код. У года разделитель превращает `2026` в `2 026`.\n */\n public readonly grouped: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(true, {\n transform: booleanAttribute,\n });\n\n /** Сколько прибавляет и вычитает одна стрелка или клавиша «вверх» и «вниз». */\n public readonly step: InputSignal<number> = input<number>(1);\n\n /** Показывать ли стрелки шага справа в поле. Клавиши шагают и без них. */\n public readonly stepper: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(true, {\n transform: booleanAttribute,\n });\n\n public override writeValue(value: number | null): void {\n super.writeValue(value);\n this.displayValue.set(formatNumber(this.value(), this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped()));\n }\n\n protected getEmptyValue(): number | null {\n return null;\n }\n\n protected focusAfterClear(): void {\n this.fieldEl()?.nativeElement.focus();\n }\n\n /**\n * Падинг/гэп рамки принадлежат host-контейнеру, а не `<input>`, поэтому клик\n * по этой зоне сам по себе не фокусирует поле. Перехватываем mousedown и\n * переводим фокус в input — но только если клик не пришёлся на само поле\n * (там нужно нативное позиционирование курсора) и не на интерактивный\n * постфикс (крестик-очистка со своим обработчиком). Стрелки шага фокус не\n * забирают: нажатие на них оставляет его в поле, как в макете.\n */\n protected onHostMousedown(event: MouseEvent): void {\n if (this.isDisabled()) {\n return;\n }\n const field: HTMLInputElement | undefined = this.fieldEl()?.nativeElement;\n const target: HTMLElement = event.target as HTMLElement;\n if (!field || target === field || target.closest('rt-icon-button')) {\n return;\n }\n event.preventDefault();\n field.focus();\n }\n\n /**\n * Крестик сбрасывает поле не в пустое/null, а в 0 — отформатированный по\n * текущему fractionDigits (например `0,00`). Числовые поля всегда имеют\n * осмысленный ноль, поэтому «очистка» = обнуление, а не пустое значение.\n */\n protected override clearValue(): void {\n this.value.set(0);\n this.displayValue.set(formatNumber(0, this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped()));\n }\n\n protected onInput(event: Event): void {\n const target: HTMLInputElement = event.target as HTMLInputElement;\n const raw: string = target.value;\n const caret: number = target.selectionStart ?? raw.length;\n\n // Live-группировка разрядов прямо во время набора (1000000 → 1 000 000).\n const formatted: string = this.#groupDisplay(raw);\n const nextCaret: number = this.#restoreCaret(raw, caret, formatted);\n\n // Пишем value императивно ДО Angular CD: тогда повторная запись биндинга\n // `[value]` будет той же строкой и не сбросит каретку в конец. Затем\n // восстанавливаем позицию по числу значимых символов слева от курсора.\n target.value = formatted;\n target.setSelectionRange(nextCaret, nextCaret);\n this.displayValue.set(formatted);\n\n const parsed: number | null = parseNumber(formatted, this.#groupSep);\n if (parsed === null) {\n // Только реально пустой ввод → emit null (пользователь очистил поле).\n // Нечисловой ввод стрипается из отображения, но prev value держится до\n // blur — поэтому проверяем исходный `raw`, а не очищенный `formatted`.\n if (raw.trim() === '') {\n this.value.set(null);\n this.emitChange(null);\n }\n return;\n }\n this.value.set(parsed);\n this.emitChange(parsed);\n }\n\n protected onBlur(): void {\n const parsed: number | null = parseNumber(this.displayValue(), this.#groupSep);\n if (parsed === null) {\n this.value.set(null);\n this.displayValue.set('');\n this.emitChange(null);\n this.markTouched();\n return;\n }\n const clamped: number = clamp(parsed, this.min(), this.max());\n const formatted: string = formatNumber(clamped, this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped());\n // `formatted` содержит разделители локали — обратный парс через\n // parseNumber (а не parseFloat) даёт округлённое числовое значение.\n const rounded: number = parseNumber(formatted, this.#groupSep) ?? clamped;\n this.value.set(rounded);\n this.displayValue.set(formatted);\n this.emitChange(rounded);\n this.markTouched();\n }\n\n /**\n * Шаг стрелкой: `1` — вверх, `-1` — вниз. Стрелка к достигнутой границе и\n * выключенное поле не шагают. Значение уходит в форму сразу, как при наборе.\n */\n protected stepBy(direction: 1 | -1): void {\n if (!(direction > 0 ? this.canStepUp() : this.canStepDown())) {\n return;\n }\n const next: number = stepValue(this.value(), direction * this.step(), this.min(), this.max());\n const formatted: string = formatNumber(next, this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped());\n const rounded: number = parseNumber(formatted, this.#groupSep) ?? next;\n this.value.set(rounded);\n this.displayValue.set(formatted);\n this.emitChange(rounded);\n }\n\n /** Клавиши «вверх» и «вниз» шагают, как стрелки, и не двигают каретку в край текста. */\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') {\n return;\n }\n event.preventDefault();\n this.stepBy(event.key === 'ArrowUp' ? 1 : -1);\n }\n\n /**\n * Live-форматирование строки во время набора: целая часть группируется по\n * разрядам (`#groupSep`), дробная остаётся как набрана (хвостовая запятая и\n * нули сохраняются, чтобы их можно было дописать). Разделителем дробной\n * части считается только то, что им может быть в этой локали\n * (`#decimalSepPattern`), — иначе запятая-разряды в `en-US` читалась бы как\n * десятичная. Разделитель допускается всегда — округление по\n * `maxFractionDigits` происходит на blur\n * (поэтому в целочисленном поле «1000,5» парсится и округлится, а не склеится\n * в «10005»). Группировка — линейным проходом, а не `Number()`, чтобы не\n * терять точность на суммах длиннее 2^53.\n */\n #groupDisplay(raw: string): string {\n const negative: boolean = raw.trimStart().startsWith('-');\n\n let sep: string = '';\n let fracDigits: string = '';\n let intDigits: string;\n\n const sepIndex: number = raw.search(this.#decimalSepPattern);\n if (sepIndex >= 0) {\n sep = raw.charAt(sepIndex);\n intDigits = raw.slice(0, sepIndex).replace(/\\D/g, '');\n fracDigits = raw.slice(sepIndex + 1).replace(/\\D/g, '');\n } else {\n intDigits = raw.replace(/\\D/g, '');\n }\n\n // Ведущие нули убираем, но сохраняем один «0» перед десятичным разделителем.\n intDigits = intDigits.replace(/^0+(?=\\d)/, '');\n\n let intPart: string;\n if (intDigits === '') {\n intPart = sep === '' ? '' : '0';\n } else {\n // Набор идёт тем же видом, что и показ на blur: иначе разделители\n // появлялись бы при наборе и пропадали после ухода из поля.\n intPart = this.grouped() ? this.#groupInteger(intDigits) : intDigits;\n }\n\n let out: string = negative ? `-${intPart}` : intPart;\n if (sep !== '') {\n out += sep + fracDigits;\n }\n return out;\n }\n\n /**\n * Группирует строку цифр по три справа налево, вставляя `#groupSep`. Без\n * регэкспа намеренно — `(\\d{3})+`-lookahead флагуется как ReDoS-уязвимый, а\n * линейный проход и быстрее, и безопасен.\n */\n #groupInteger(digits: string): string {\n const length: number = digits.length;\n let out: string = '';\n for (let i: number = 0; i < length; i++) {\n if (i > 0 && (length - i) % 3 === 0) {\n out += this.#groupSep;\n }\n out += digits.charAt(i);\n }\n return out;\n }\n\n /**\n * Пересчитывает позицию каретки после вставки разделителей разрядов: считает\n * значимые символы (цифры и десятичный разделитель — не group-separator)\n * слева от старой каретки и находит ту же точку в новой строке. Иначе курсор\n * прыгал бы в конец при каждом вставленном пробеле.\n */\n #restoreCaret(raw: string, caret: number, formatted: string): number {\n const isAnchor: (ch: string) => boolean = (ch: string): boolean => /[\\d.,]/.test(ch);\n\n let need: number = 0;\n for (let i: number = 0; i < caret && i < raw.length; i++) {\n if (isAnchor(raw.charAt(i))) {\n need++;\n }\n }\n if (need === 0) {\n return formatted.startsWith('-') ? 1 : 0;\n }\n\n let seen: number = 0;\n let pos: number = 0;\n while (pos < formatted.length) {\n if (isAnchor(formatted.charAt(pos))) {\n seen++;\n if (seen === need) {\n pos++;\n break;\n }\n }\n pos++;\n }\n return pos;\n }\n}\n","<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '—' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"clearTabIndex()\"\n (clicked)=\"clear($event)\" />\n }\n @if (stepper()) {\n <span rtElem=\"stepper\" qa-dataid=\"input-number-stepper\">\n <button\n type=\"button\"\n rtElem=\"step\"\n qa-dataid=\"input-number-step-up\"\n tabindex=\"-1\"\n [attr.aria-label]=\"t().uiIncrease\"\n [disabled]=\"!canStepUp()\"\n (click)=\"stepBy(1)\">\n <rt-icon rtElem=\"step-icon\" name=\"chevron-up\" [size]=\"stepIconSize()\" />\n </button>\n <button\n type=\"button\"\n rtElem=\"step\"\n qa-dataid=\"input-number-step-down\"\n tabindex=\"-1\"\n [attr.aria-label]=\"t().uiDecrease\"\n [disabled]=\"!canStepDown()\"\n (click)=\"stepBy(-1)\">\n <rt-icon rtElem=\"step-icon\" name=\"chevron-up\" [size]=\"stepIconSize()\" [rtMod]=\"{ flipped: true }\" />\n </button>\n </span>\n }\n }\n</ng-container>\n"],"mappings":"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"}
|