@lifecorner/angular-ui-kit 3.28.0 → 3.29.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/lifecorner-angular-ui-kit-card.mjs +23 -3
- package/fesm2022/lifecorner-angular-ui-kit-card.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs +56 -1
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-datepicker.mjs +257 -10
- package/fesm2022/lifecorner-angular-ui-kit-datepicker.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-money.mjs +90 -7
- package/fesm2022/lifecorner-angular-ui-kit-money.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs +93 -9
- package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs.map +1 -1
- package/package.json +1 -1
- package/types/lifecorner-angular-ui-kit-card.d.ts +34 -0
- package/types/lifecorner-angular-ui-kit-combobox.d.ts +32 -1
- package/types/lifecorner-angular-ui-kit-datepicker.d.ts +143 -2
- package/types/lifecorner-angular-ui-kit-money.d.ts +81 -2
- package/types/lifecorner-angular-ui-kit-number-input.d.ts +61 -0
|
@@ -9,17 +9,25 @@ function decimalsOf(n) {
|
|
|
9
9
|
const decimals = (mantissa.split(".")[1]?.length ?? 0) - (exponent ? Number(exponent) : 0);
|
|
10
10
|
return Math.max(0, decimals);
|
|
11
11
|
}
|
|
12
|
-
function sanitizeNumericText(raw) {
|
|
12
|
+
function sanitizeNumericText(raw, separator) {
|
|
13
13
|
let out = "";
|
|
14
14
|
let seenPoint = false;
|
|
15
15
|
for (const ch of raw) if (ch >= "0" && ch <= "9") out += ch;
|
|
16
16
|
else if (ch === "-" && out.length === 0) out += ch;
|
|
17
17
|
else if ((ch === "." || ch === ",") && !seenPoint) {
|
|
18
18
|
seenPoint = true;
|
|
19
|
-
out +=
|
|
19
|
+
out += separator;
|
|
20
20
|
}
|
|
21
21
|
return out;
|
|
22
22
|
}
|
|
23
|
+
function decimalSeparatorOf(locale) {
|
|
24
|
+
if (!locale) return ".";
|
|
25
|
+
try {
|
|
26
|
+
return new Intl.NumberFormat(locale).formatToParts(1.5).find((p) => p.type === "decimal")?.value === "," ? "," : ".";
|
|
27
|
+
} catch {
|
|
28
|
+
return ".";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
23
31
|
var NumberInputComponent = class NumberInputComponent {
|
|
24
32
|
static nextId = 0;
|
|
25
33
|
min = input(void 0, ...ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []);
|
|
@@ -33,6 +41,10 @@ var NumberInputComponent = class NumberInputComponent {
|
|
|
33
41
|
disabled = input(false, ...ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []);
|
|
34
42
|
decrementAriaLabel = input("Decrease value", ...ngDevMode ? [{ debugName: "decrementAriaLabel" }] : /* istanbul ignore next */ []);
|
|
35
43
|
incrementAriaLabel = input("Increase value", ...ngDevMode ? [{ debugName: "incrementAriaLabel" }] : /* istanbul ignore next */ []);
|
|
44
|
+
locale = input("", ...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
|
|
45
|
+
suffix = input("", ...ngDevMode ? [{ debugName: "suffix" }] : /* istanbul ignore next */ []);
|
|
46
|
+
suffixAriaLabel = input("", ...ngDevMode ? [{ debugName: "suffixAriaLabel" }] : /* istanbul ignore next */ []);
|
|
47
|
+
size = input("md", ...ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []);
|
|
36
48
|
valueChange = output();
|
|
37
49
|
inputId = `lc-number-input-${++NumberInputComponent.nextId}`;
|
|
38
50
|
value = signal(null, ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
|
|
@@ -42,14 +54,20 @@ var NumberInputComponent = class NumberInputComponent {
|
|
|
42
54
|
inputEl = viewChild("inputEl", ...ngDevMode ? [{ debugName: "inputEl" }] : /* istanbul ignore next */ []);
|
|
43
55
|
stepPrecision = computed(() => decimalsOf(this.step()), ...ngDevMode ? [{ debugName: "stepPrecision" }] : /* istanbul ignore next */ []);
|
|
44
56
|
inputMode = computed(() => this.stepPrecision() > 0 ? "decimal" : "numeric", ...ngDevMode ? [{ debugName: "inputMode" }] : /* istanbul ignore next */ []);
|
|
57
|
+
separator = computed(() => decimalSeparatorOf(this.locale()), ...ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []);
|
|
58
|
+
spokenSuffix = computed(() => this.suffixAriaLabel() || this.suffix(), ...ngDevMode ? [{ debugName: "spokenSuffix" }] : /* istanbul ignore next */ []);
|
|
59
|
+
iconSize = computed(() => this.size() === "xs" || this.size() === "sm" ? "xs" : "sm", ...ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []);
|
|
45
60
|
describedBy = computed(() => {
|
|
46
61
|
const ids = [];
|
|
62
|
+
if (this.spokenSuffix()) ids.push(`${this.inputId}-suffix`);
|
|
47
63
|
if (this.error()) ids.push(`${this.inputId}-error`);
|
|
48
64
|
else if (this.helperText()) ids.push(`${this.inputId}-helper`);
|
|
49
65
|
return ids.length ? ids.join(" ") : null;
|
|
50
66
|
}, ...ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []);
|
|
51
67
|
containerClasses = computed(() => {
|
|
52
68
|
const classes = ["number-input"];
|
|
69
|
+
const size = this.size();
|
|
70
|
+
if (size === "xs" || size === "sm" || size === "lg") classes.push(`number-input--${size}`);
|
|
53
71
|
if (this.isDisabled()) classes.push("number-input--disabled");
|
|
54
72
|
if (this.error()) classes.push("number-input--error");
|
|
55
73
|
return classes.join(" ");
|
|
@@ -93,14 +111,14 @@ var NumberInputComponent = class NumberInputComponent {
|
|
|
93
111
|
onInput(event) {
|
|
94
112
|
const target = event.target;
|
|
95
113
|
const raw = target.value;
|
|
96
|
-
const text = sanitizeNumericText(raw);
|
|
114
|
+
const text = sanitizeNumericText(raw, this.separator());
|
|
97
115
|
if (text !== raw) {
|
|
98
116
|
const caret = Math.max(0, (target.selectionStart ?? text.length) - (raw.length - text.length));
|
|
99
117
|
target.value = text;
|
|
100
118
|
target.setSelectionRange(caret, caret);
|
|
101
119
|
}
|
|
102
120
|
this.text.set(text);
|
|
103
|
-
const parsed = text === "" ? NaN : Number(text);
|
|
121
|
+
const parsed = text === "" ? NaN : Number(text.replace(",", "."));
|
|
104
122
|
if (Number.isNaN(parsed)) {
|
|
105
123
|
if (this.value() !== null) {
|
|
106
124
|
this.value.set(null);
|
|
@@ -123,7 +141,13 @@ var NumberInputComponent = class NumberInputComponent {
|
|
|
123
141
|
if (el) el.value = canonical;
|
|
124
142
|
}
|
|
125
143
|
format(val) {
|
|
126
|
-
|
|
144
|
+
if (val === null) return "";
|
|
145
|
+
const text = String(val);
|
|
146
|
+
return this.separator() === "," ? text.replace(".", ",") : text;
|
|
147
|
+
}
|
|
148
|
+
focusField(event) {
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
if (!this.isDisabled()) this.inputEl()?.nativeElement.focus();
|
|
127
151
|
}
|
|
128
152
|
clamp(val) {
|
|
129
153
|
const minV = this.min();
|
|
@@ -242,6 +266,34 @@ var NumberInputComponent = class NumberInputComponent {
|
|
|
242
266
|
isSignal: true,
|
|
243
267
|
isRequired: false,
|
|
244
268
|
transformFunction: null
|
|
269
|
+
},
|
|
270
|
+
locale: {
|
|
271
|
+
classPropertyName: "locale",
|
|
272
|
+
publicName: "locale",
|
|
273
|
+
isSignal: true,
|
|
274
|
+
isRequired: false,
|
|
275
|
+
transformFunction: null
|
|
276
|
+
},
|
|
277
|
+
suffix: {
|
|
278
|
+
classPropertyName: "suffix",
|
|
279
|
+
publicName: "suffix",
|
|
280
|
+
isSignal: true,
|
|
281
|
+
isRequired: false,
|
|
282
|
+
transformFunction: null
|
|
283
|
+
},
|
|
284
|
+
suffixAriaLabel: {
|
|
285
|
+
classPropertyName: "suffixAriaLabel",
|
|
286
|
+
publicName: "suffixAriaLabel",
|
|
287
|
+
isSignal: true,
|
|
288
|
+
isRequired: false,
|
|
289
|
+
transformFunction: null
|
|
290
|
+
},
|
|
291
|
+
size: {
|
|
292
|
+
classPropertyName: "size",
|
|
293
|
+
publicName: "size",
|
|
294
|
+
isSignal: true,
|
|
295
|
+
isRequired: false,
|
|
296
|
+
transformFunction: null
|
|
245
297
|
}
|
|
246
298
|
},
|
|
247
299
|
outputs: { valueChange: "valueChange" },
|
|
@@ -258,8 +310,8 @@ var NumberInputComponent = class NumberInputComponent {
|
|
|
258
310
|
isSignal: true
|
|
259
311
|
}],
|
|
260
312
|
ngImport: i0,
|
|
261
|
-
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"number-input__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n <div class=\"number-input__control\">\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--decrement\"\n [disabled]=\"isDisabled() || !canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"decrementAriaLabel()\"\n >\n <lc-icon name=\"minus\" size=\"
|
|
262
|
-
styles: [".number-input{display:inline-flex;flex-direction:column;gap:.375rem}.number-input__label{font-size:var(--typography-font-size-sm, .875rem);font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary)}.number-input__control{display:inline-flex;align-items:center;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);overflow:hidden;background:var(--color-surface-2);transition:border-color .15s ease}.number-input__control:hover{border-color:var(--color-text-secondary)}.number-input__control:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px #20849733}.number-input__button{display:flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;border:none;background:transparent;color:var(--color-text-secondary);cursor:pointer;flex-shrink:0;transition:background-color .15s ease,color .15s ease}.number-input__button:hover:not(:disabled){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.number-input__button:disabled{opacity:.4;cursor:not-allowed}.number-input__button:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.number-input__button--decrement{border-right:1px solid var(--color-border)}.number-input__button--increment{border-left:1px solid var(--color-border)}.number-input__input{width:4rem;text-align:center;border:none;outline:none;background:transparent;font-size:var(--typography-font-size-base, 1rem);font-variant-numeric:tabular-nums;color:var(--color-text-primary);padding:.5rem .25rem}.number-input__input::placeholder{color:var(--color-text-tertiary)}.number-input__input:disabled{cursor:not-allowed}.number-input__helper,.number-input__error{margin:0;font-size:var(--typography-font-size-xs, .75rem);line-height:1.4}.number-input__helper{color:var(--color-text-tertiary)}.number-input__error{color:var(--color-text-error)}.number-input--error .number-input__control{border-color:var(--color-error-600)}.number-input--error .number-input__control:focus-within{border-color:var(--color-error-600);outline:2px solid var(--color-error-600);outline-offset:0;box-shadow:none}.number-input--disabled{opacity:.5;pointer-events:none}@media(forced-colors:active){.number-input--error .number-input__control:focus-within,.number-input__button:focus-visible{outline-color:Highlight}}\n"],
|
|
313
|
+
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"number-input__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n <div class=\"number-input__control\">\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--decrement\"\n [disabled]=\"isDisabled() || !canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"decrementAriaLabel()\"\n >\n <lc-icon name=\"minus\" [size]=\"iconSize()\" [decorative]=\"true\" />\n </button>\n <input\n #inputEl\n type=\"text\"\n [id]=\"inputId\"\n [attr.inputmode]=\"inputMode()\"\n class=\"number-input__input\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [attr.min]=\"min() ?? null\"\n [attr.max]=\"max() ?? null\"\n [attr.aria-label]=\"label() ? null : ariaLabel()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n @if (suffix()) {\n <!-- The visible unit (3.29): not read; the field's description says it once. A press focuses the field. -->\n <span class=\"number-input__suffix\" aria-hidden=\"true\" (mousedown)=\"focusField($event)\">{{ suffix() }}</span>\n }\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--increment\"\n [disabled]=\"isDisabled() || !canIncrement()\"\n (click)=\"increment()\"\n [attr.aria-label]=\"incrementAriaLabel()\"\n >\n <lc-icon name=\"plus\" [size]=\"iconSize()\" [decorative]=\"true\" />\n </button>\n </div>\n @if (spokenSuffix()) {\n <span hidden [id]=\"inputId + '-suffix'\">{{ spokenSuffix() }}</span>\n }\n @if (error()) {\n <p [id]=\"inputId + '-error'\" class=\"number-input__error\" role=\"alert\">{{ error() }}</p>\n } @else if (helperText()) {\n <p [id]=\"inputId + '-helper'\" class=\"number-input__helper\">{{ helperText() }}</p>\n }\n</div>\n",
|
|
314
|
+
styles: [".number-input{display:inline-flex;flex-direction:column;gap:.375rem}.number-input__label{font-size:var(--typography-font-size-sm, .875rem);font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary)}.number-input__control{display:inline-flex;align-items:center;align-self:flex-start;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);overflow:hidden;background:var(--color-surface-2);transition:border-color .15s ease}.number-input__control:hover{border-color:var(--color-text-secondary)}.number-input__control:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px #20849733}.number-input__button{display:flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;border:none;background:transparent;color:var(--color-text-secondary);cursor:pointer;flex-shrink:0;transition:background-color .15s ease,color .15s ease}.number-input__button:hover:not(:disabled){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.number-input__button:disabled{opacity:.4;cursor:not-allowed}.number-input__button:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.number-input__button--decrement{border-right:1px solid var(--color-border)}.number-input__button--increment{border-left:1px solid var(--color-border)}.number-input__input{width:4rem;text-align:center;border:none;outline:none;background:transparent;font-size:var(--typography-font-size-base, 1rem);font-variant-numeric:tabular-nums;color:var(--color-text-primary);padding:.5rem .25rem}.number-input__input::placeholder{color:var(--color-text-tertiary)}.number-input__input:disabled{cursor:not-allowed}.number-input__suffix{flex-shrink:0;padding-inline:.25em var(--spacing-2);font-size:var(--typography-font-size-base, 1rem);color:var(--color-text-secondary)}.number-input__control:has(.number-input__suffix) .number-input__input{text-align:end;padding-inline-end:0}.number-input--xs .number-input__input,.number-input--xs .number-input__suffix,.number-input--sm .number-input__input,.number-input--sm .number-input__suffix,.number-input--lg .number-input__input,.number-input--lg .number-input__suffix{align-self:stretch}.number-input--xs .number-input__input,.number-input--sm .number-input__input,.number-input--lg .number-input__input{padding-block:0}.number-input--xs .number-input__suffix,.number-input--sm .number-input__suffix,.number-input--lg .number-input__suffix{display:flex;align-items:center}.number-input--xs .number-input__button{width:var(--size-interactive-xs-height);height:var(--size-interactive-xs-height)}.number-input--xs .number-input__input{width:3rem}.number-input--xs .number-input__input,.number-input--xs .number-input__suffix{font-size:var(--size-interactive-xs-font-size)}.number-input--sm .number-input__button{width:var(--size-interactive-sm-height);height:var(--size-interactive-sm-height)}.number-input--sm .number-input__input{width:3.5rem}.number-input--sm .number-input__input,.number-input--sm .number-input__suffix{font-size:var(--size-interactive-sm-font-size)}.number-input--lg .number-input__button{width:var(--size-interactive-lg-height);height:var(--size-interactive-lg-height)}.number-input--lg .number-input__input{width:4.5rem}.number-input--lg .number-input__input,.number-input--lg .number-input__suffix{font-size:var(--size-interactive-lg-font-size)}.number-input__helper,.number-input__error{margin:0;font-size:var(--typography-font-size-xs, .75rem);line-height:1.4}.number-input__helper{color:var(--color-text-tertiary)}.number-input__error{color:var(--color-text-error)}.number-input--error .number-input__control{border-color:var(--color-error-600)}.number-input--error .number-input__control:focus-within{border-color:var(--color-error-600);outline:2px solid var(--color-error-600);outline-offset:0;box-shadow:none}.number-input--disabled{opacity:.5;pointer-events:none}@media(forced-colors:active){.number-input--error .number-input__control:focus-within,.number-input__button:focus-visible{outline-color:Highlight}}\n"],
|
|
263
315
|
dependencies: [{
|
|
264
316
|
kind: "component",
|
|
265
317
|
type: IconComponent,
|
|
@@ -296,8 +348,8 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
296
348
|
useExisting: forwardRef(() => NumberInputComponent),
|
|
297
349
|
multi: true
|
|
298
350
|
}],
|
|
299
|
-
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"number-input__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n <div class=\"number-input__control\">\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--decrement\"\n [disabled]=\"isDisabled() || !canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"decrementAriaLabel()\"\n >\n <lc-icon name=\"minus\" size=\"
|
|
300
|
-
styles: [".number-input{display:inline-flex;flex-direction:column;gap:.375rem}.number-input__label{font-size:var(--typography-font-size-sm, .875rem);font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary)}.number-input__control{display:inline-flex;align-items:center;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);overflow:hidden;background:var(--color-surface-2);transition:border-color .15s ease}.number-input__control:hover{border-color:var(--color-text-secondary)}.number-input__control:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px #20849733}.number-input__button{display:flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;border:none;background:transparent;color:var(--color-text-secondary);cursor:pointer;flex-shrink:0;transition:background-color .15s ease,color .15s ease}.number-input__button:hover:not(:disabled){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.number-input__button:disabled{opacity:.4;cursor:not-allowed}.number-input__button:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.number-input__button--decrement{border-right:1px solid var(--color-border)}.number-input__button--increment{border-left:1px solid var(--color-border)}.number-input__input{width:4rem;text-align:center;border:none;outline:none;background:transparent;font-size:var(--typography-font-size-base, 1rem);font-variant-numeric:tabular-nums;color:var(--color-text-primary);padding:.5rem .25rem}.number-input__input::placeholder{color:var(--color-text-tertiary)}.number-input__input:disabled{cursor:not-allowed}.number-input__helper,.number-input__error{margin:0;font-size:var(--typography-font-size-xs, .75rem);line-height:1.4}.number-input__helper{color:var(--color-text-tertiary)}.number-input__error{color:var(--color-text-error)}.number-input--error .number-input__control{border-color:var(--color-error-600)}.number-input--error .number-input__control:focus-within{border-color:var(--color-error-600);outline:2px solid var(--color-error-600);outline-offset:0;box-shadow:none}.number-input--disabled{opacity:.5;pointer-events:none}@media(forced-colors:active){.number-input--error .number-input__control:focus-within,.number-input__button:focus-visible{outline-color:Highlight}}\n"]
|
|
351
|
+
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"number-input__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n <div class=\"number-input__control\">\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--decrement\"\n [disabled]=\"isDisabled() || !canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"decrementAriaLabel()\"\n >\n <lc-icon name=\"minus\" [size]=\"iconSize()\" [decorative]=\"true\" />\n </button>\n <input\n #inputEl\n type=\"text\"\n [id]=\"inputId\"\n [attr.inputmode]=\"inputMode()\"\n class=\"number-input__input\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [attr.min]=\"min() ?? null\"\n [attr.max]=\"max() ?? null\"\n [attr.aria-label]=\"label() ? null : ariaLabel()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n @if (suffix()) {\n <!-- The visible unit (3.29): not read; the field's description says it once. A press focuses the field. -->\n <span class=\"number-input__suffix\" aria-hidden=\"true\" (mousedown)=\"focusField($event)\">{{ suffix() }}</span>\n }\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--increment\"\n [disabled]=\"isDisabled() || !canIncrement()\"\n (click)=\"increment()\"\n [attr.aria-label]=\"incrementAriaLabel()\"\n >\n <lc-icon name=\"plus\" [size]=\"iconSize()\" [decorative]=\"true\" />\n </button>\n </div>\n @if (spokenSuffix()) {\n <span hidden [id]=\"inputId + '-suffix'\">{{ spokenSuffix() }}</span>\n }\n @if (error()) {\n <p [id]=\"inputId + '-error'\" class=\"number-input__error\" role=\"alert\">{{ error() }}</p>\n } @else if (helperText()) {\n <p [id]=\"inputId + '-helper'\" class=\"number-input__helper\">{{ helperText() }}</p>\n }\n</div>\n",
|
|
352
|
+
styles: [".number-input{display:inline-flex;flex-direction:column;gap:.375rem}.number-input__label{font-size:var(--typography-font-size-sm, .875rem);font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary)}.number-input__control{display:inline-flex;align-items:center;align-self:flex-start;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);overflow:hidden;background:var(--color-surface-2);transition:border-color .15s ease}.number-input__control:hover{border-color:var(--color-text-secondary)}.number-input__control:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px #20849733}.number-input__button{display:flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;border:none;background:transparent;color:var(--color-text-secondary);cursor:pointer;flex-shrink:0;transition:background-color .15s ease,color .15s ease}.number-input__button:hover:not(:disabled){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.number-input__button:disabled{opacity:.4;cursor:not-allowed}.number-input__button:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.number-input__button--decrement{border-right:1px solid var(--color-border)}.number-input__button--increment{border-left:1px solid var(--color-border)}.number-input__input{width:4rem;text-align:center;border:none;outline:none;background:transparent;font-size:var(--typography-font-size-base, 1rem);font-variant-numeric:tabular-nums;color:var(--color-text-primary);padding:.5rem .25rem}.number-input__input::placeholder{color:var(--color-text-tertiary)}.number-input__input:disabled{cursor:not-allowed}.number-input__suffix{flex-shrink:0;padding-inline:.25em var(--spacing-2);font-size:var(--typography-font-size-base, 1rem);color:var(--color-text-secondary)}.number-input__control:has(.number-input__suffix) .number-input__input{text-align:end;padding-inline-end:0}.number-input--xs .number-input__input,.number-input--xs .number-input__suffix,.number-input--sm .number-input__input,.number-input--sm .number-input__suffix,.number-input--lg .number-input__input,.number-input--lg .number-input__suffix{align-self:stretch}.number-input--xs .number-input__input,.number-input--sm .number-input__input,.number-input--lg .number-input__input{padding-block:0}.number-input--xs .number-input__suffix,.number-input--sm .number-input__suffix,.number-input--lg .number-input__suffix{display:flex;align-items:center}.number-input--xs .number-input__button{width:var(--size-interactive-xs-height);height:var(--size-interactive-xs-height)}.number-input--xs .number-input__input{width:3rem}.number-input--xs .number-input__input,.number-input--xs .number-input__suffix{font-size:var(--size-interactive-xs-font-size)}.number-input--sm .number-input__button{width:var(--size-interactive-sm-height);height:var(--size-interactive-sm-height)}.number-input--sm .number-input__input{width:3.5rem}.number-input--sm .number-input__input,.number-input--sm .number-input__suffix{font-size:var(--size-interactive-sm-font-size)}.number-input--lg .number-input__button{width:var(--size-interactive-lg-height);height:var(--size-interactive-lg-height)}.number-input--lg .number-input__input{width:4.5rem}.number-input--lg .number-input__input,.number-input--lg .number-input__suffix{font-size:var(--size-interactive-lg-font-size)}.number-input__helper,.number-input__error{margin:0;font-size:var(--typography-font-size-xs, .75rem);line-height:1.4}.number-input__helper{color:var(--color-text-tertiary)}.number-input__error{color:var(--color-text-error)}.number-input--error .number-input__control{border-color:var(--color-error-600)}.number-input--error .number-input__control:focus-within{border-color:var(--color-error-600);outline:2px solid var(--color-error-600);outline-offset:0;box-shadow:none}.number-input--disabled{opacity:.5;pointer-events:none}@media(forced-colors:active){.number-input--error .number-input__control:focus-within,.number-input__button:focus-visible{outline-color:Highlight}}\n"]
|
|
301
353
|
}]
|
|
302
354
|
}],
|
|
303
355
|
propDecorators: {
|
|
@@ -389,6 +441,38 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
389
441
|
required: false
|
|
390
442
|
}]
|
|
391
443
|
}],
|
|
444
|
+
locale: [{
|
|
445
|
+
type: i0.Input,
|
|
446
|
+
args: [{
|
|
447
|
+
isSignal: true,
|
|
448
|
+
alias: "locale",
|
|
449
|
+
required: false
|
|
450
|
+
}]
|
|
451
|
+
}],
|
|
452
|
+
suffix: [{
|
|
453
|
+
type: i0.Input,
|
|
454
|
+
args: [{
|
|
455
|
+
isSignal: true,
|
|
456
|
+
alias: "suffix",
|
|
457
|
+
required: false
|
|
458
|
+
}]
|
|
459
|
+
}],
|
|
460
|
+
suffixAriaLabel: [{
|
|
461
|
+
type: i0.Input,
|
|
462
|
+
args: [{
|
|
463
|
+
isSignal: true,
|
|
464
|
+
alias: "suffixAriaLabel",
|
|
465
|
+
required: false
|
|
466
|
+
}]
|
|
467
|
+
}],
|
|
468
|
+
size: [{
|
|
469
|
+
type: i0.Input,
|
|
470
|
+
args: [{
|
|
471
|
+
isSignal: true,
|
|
472
|
+
alias: "size",
|
|
473
|
+
required: false
|
|
474
|
+
}]
|
|
475
|
+
}],
|
|
392
476
|
valueChange: [{
|
|
393
477
|
type: i0.Output,
|
|
394
478
|
args: ["valueChange"]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-number-input.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/number-input/number-input.component.ts","../../../../libs/angular-ui-kit/src/lib/number-input/number-input.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n input,\n output,\n computed,\n signal,\n forwardRef,\n viewChild,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_PLUS } from '@lifecorner/angular-ui-kit/icons';\n\n/** Number of decimals a number is written with (`0.001` → 3, `1e-7` → 7, `5` → 0). */\nfunction decimalsOf(n: number): number {\n if (!Number.isFinite(n)) return 0;\n const [mantissa, exponent] = String(n).split('e');\n const decimals = (mantissa.split('.')[1]?.length ?? 0) - (exponent ? Number(exponent) : 0);\n return Math.max(0, decimals);\n}\n\n/**\n * Reduces raw text to something `Number()` can parse: digits, one leading\n * minus and one decimal point. A comma is taken as a decimal separator so a\n * German keyboard layout does not silently lose the fraction.\n */\nfunction sanitizeNumericText(raw: string): string {\n let out = '';\n let seenPoint = false;\n for (const ch of raw) {\n if (ch >= '0' && ch <= '9') {\n out += ch;\n } else if (ch === '-' && out.length === 0) {\n out += ch;\n } else if ((ch === '.' || ch === ',') && !seenPoint) {\n seenPoint = true;\n out += '.';\n }\n }\n return out;\n}\n\n@Component({\n selector: 'lc-number-input',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_PLUS])],\n templateUrl: './number-input.component.html',\n styleUrls: ['./number-input.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => NumberInputComponent),\n multi: true,\n },\n ],\n})\n/**\n * Number input component with increment/decrement controls.\n *\n * Features:\n * - Increment and decrement buttons (also ArrowUp / ArrowDown)\n * - Configurable min, max, and step values\n * - Value clamping within bounds; stepping is rounded to the step's precision\n * - Non-numeric characters are rejected as they are typed\n * - Disabled state support\n * - ControlValueAccessor integration for reactive forms\n *\n * @example\n * ```html\n * <lc-number-input label=\"Quantity\" [min]=\"0\" [max]=\"100\" [step]=\"1\" />\n * ```\n */\nexport class NumberInputComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Minimum value */\n min = input<number | undefined>(undefined);\n\n /** Maximum value */\n max = input<number | undefined>(undefined);\n\n /** Step increment */\n step = input<number>(1);\n\n /** Label text */\n label = input<string>();\n\n /** Accessible name for the field when no visible `label` is rendered. */\n ariaLabel = input<string>();\n\n /** Placeholder */\n placeholder = input<string>('0');\n\n /** Helper text shown below the control (linked via `aria-describedby`). */\n helperText = input<string>();\n\n /** Error message shown below the control; marks the field `aria-invalid`. */\n error = input<string>();\n\n /** Disabled state */\n disabled = input<boolean>(false);\n\n /** Accessible name of the minus button. */\n decrementAriaLabel = input('Decrease value');\n\n /** Accessible name of the plus button. */\n incrementAriaLabel = input('Increase value');\n\n /** Emits value on change */\n valueChange = output<number>();\n\n /** Per-instance id wiring the label, hint and error to the field. */\n readonly inputId = `lc-number-input-${++NumberInputComponent.nextId}`;\n\n protected value = signal<number | null>(null);\n\n /**\n * Text shown in the field. It is re-derived from the model on `writeValue`,\n * stepping and blur — never while typing — so an in-progress entry such as\n * \"1.\" or \"-\" is not snapped back to \"1\" under the user's cursor.\n */\n protected readonly text = signal('');\n\n /** Disabled through the form control (`setDisabledState`), as opposed to the input. */\n private readonly formDisabled = signal(false);\n\n /** Disabled by either route — the one flag the template consults. */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n private readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n\n /** Decimals implied by `step` — stepped results are rounded to it so 0.1 + 0.2 does not drift. */\n private readonly stepPrecision = computed(() => decimalsOf(this.step()));\n\n /** Fractions need a decimal key on touch keyboards; integers do not. */\n protected readonly inputMode = computed(() => (this.stepPrecision() > 0 ? 'decimal' : 'numeric'));\n\n protected readonly describedBy = computed(() => {\n const ids: string[] = [];\n if (this.error()) ids.push(`${this.inputId}-error`);\n else if (this.helperText()) ids.push(`${this.inputId}-helper`);\n return ids.length ? ids.join(' ') : null;\n });\n\n protected containerClasses = computed(() => {\n const classes = ['number-input'];\n if (this.isDisabled()) classes.push('number-input--disabled');\n if (this.error()) classes.push('number-input--error');\n return classes.join(' ');\n });\n\n protected canDecrement = computed(() => {\n const val = this.value();\n const minV = this.min();\n if (val === null || minV === undefined) return true;\n return val > minV;\n });\n\n protected canIncrement = computed(() => {\n const val = this.value();\n const maxV = this.max();\n if (val === null || maxV === undefined) return true;\n return val < maxV;\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (val: number | null) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n decrement(): void {\n this.stepBy(-1);\n }\n\n increment(): void {\n this.stepBy(1);\n }\n\n private stepBy(direction: 1 | -1): void {\n const cur = this.value() ?? 0;\n // Keep whatever precision the user typed (1.25 + 0.1 → 1.35), but never\n // less than the step's own — that is what removes the binary drift.\n const precision = Math.max(this.stepPrecision(), decimalsOf(cur));\n const next = this.clamp(Number((cur + direction * this.step()).toFixed(precision)));\n this.setValue(next);\n this.text.set(this.format(next));\n }\n\n onKeydown(event: KeyboardEvent): void {\n if (event.key === 'ArrowUp') {\n event.preventDefault();\n if (this.canIncrement()) this.increment();\n } else if (event.key === 'ArrowDown') {\n event.preventDefault();\n if (this.canDecrement()) this.decrement();\n }\n }\n\n onInput(event: Event): void {\n const target = event.target as HTMLInputElement;\n const raw = target.value;\n const text = sanitizeNumericText(raw);\n // Property binding alone would not repaint the field when the model does\n // not change (typing \"a\" after \"12\" leaves 12 as-is), so the stripped\n // text is written straight back to the element.\n if (text !== raw) {\n const caret = Math.max(0, (target.selectionStart ?? text.length) - (raw.length - text.length));\n target.value = text;\n target.setSelectionRange(caret, caret);\n }\n this.text.set(text);\n // \"\" would parse as 0; it and the other in-progress entries (\"-\", \".\")\n // mean the model has no number yet.\n const parsed = text === '' ? NaN : Number(text);\n if (Number.isNaN(parsed)) {\n if (this.value() !== null) {\n this.value.set(null);\n this.onChange(null);\n }\n return;\n }\n this.setValue(parsed);\n }\n\n onBlur(): void {\n this.onTouched();\n // Clamp on blur\n const val = this.value();\n if (val !== null) {\n const clamped = this.clamp(val);\n if (clamped !== val) this.setValue(clamped);\n }\n // Whatever half-typed text is left (\"1.\", \"-\", \"007\") is replaced by the\n // model's canonical form. The element is written directly as well, in\n // case the DOM drifted from the bound text without an input event.\n const canonical = this.format(this.value());\n this.text.set(canonical);\n const el = this.inputEl()?.nativeElement;\n if (el) el.value = canonical;\n }\n\n private format(val: number | null): string {\n return val === null ? '' : String(val);\n }\n\n private clamp(val: number): number {\n const minV = this.min();\n const maxV = this.max();\n if (minV !== undefined) val = Math.max(minV, val);\n if (maxV !== undefined) val = Math.min(maxV, val);\n return val;\n }\n\n private setValue(val: number): void {\n if (val === this.value()) return;\n this.value.set(val);\n this.valueChange.emit(val);\n this.onChange(val);\n }\n\n // ControlValueAccessor\n writeValue(val: number | null): void {\n this.value.set(val);\n this.text.set(this.format(val));\n }\n\n registerOnChange(fn: (val: number | null) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n}\n","<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"number-input__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n <div class=\"number-input__control\">\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--decrement\"\n [disabled]=\"isDisabled() || !canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"decrementAriaLabel()\"\n >\n <lc-icon name=\"minus\" size=\"sm\" [decorative]=\"true\" />\n </button>\n <input\n #inputEl\n type=\"text\"\n [id]=\"inputId\"\n [attr.inputmode]=\"inputMode()\"\n class=\"number-input__input\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [attr.min]=\"min() ?? null\"\n [attr.max]=\"max() ?? null\"\n [attr.aria-label]=\"label() ? null : ariaLabel()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--increment\"\n [disabled]=\"isDisabled() || !canIncrement()\"\n (click)=\"increment()\"\n [attr.aria-label]=\"incrementAriaLabel()\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n </button>\n </div>\n @if (error()) {\n <p [id]=\"inputId + '-error'\" class=\"number-input__error\" role=\"alert\">{{ error() }}</p>\n } @else if (helperText()) {\n <p [id]=\"inputId + '-helper'\" class=\"number-input__helper\">{{ helperText() }}</p>\n }\n</div>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-number-input.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/number-input/number-input.component.ts","../../../../libs/angular-ui-kit/src/lib/number-input/number-input.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n input,\n output,\n computed,\n signal,\n forwardRef,\n viewChild,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_PLUS } from '@lifecorner/angular-ui-kit/icons';\n\n/** Number of decimals a number is written with (`0.001` → 3, `1e-7` → 7, `5` → 0). */\nfunction decimalsOf(n: number): number {\n if (!Number.isFinite(n)) return 0;\n const [mantissa, exponent] = String(n).split('e');\n const decimals = (mantissa.split('.')[1]?.length ?? 0) - (exponent ? Number(exponent) : 0);\n return Math.max(0, decimals);\n}\n\n/**\n * Reduces raw text to digits, one leading minus and one decimal separator.\n * A comma and a point are both taken as the decimal separator, so a German\n * keyboard layout does not silently lose the fraction; the separator is\n * written as `separator` (the locale's, a point by default).\n */\nfunction sanitizeNumericText(raw: string, separator: '.' | ','): string {\n let out = '';\n let seenPoint = false;\n for (const ch of raw) {\n if (ch >= '0' && ch <= '9') {\n out += ch;\n } else if (ch === '-' && out.length === 0) {\n out += ch;\n } else if ((ch === '.' || ch === ',') && !seenPoint) {\n seenPoint = true;\n out += separator;\n }\n }\n return out;\n}\n\n/** The decimal separator of `locale`: `,` or `.`; any other separator, `''` and an invalid locale give `.`. */\nfunction decimalSeparatorOf(locale: string): '.' | ',' {\n if (!locale) return '.';\n try {\n const part = new Intl.NumberFormat(locale).formatToParts(1.5).find((p) => p.type === 'decimal')?.value;\n return part === ',' ? ',' : '.';\n } catch {\n return '.';\n }\n}\n\n/** Height of `lc-number-input`'s field and steppers: `xs` 24, `sm` 32, `md` 40 (the default), `lg` 44 px. */\nexport type NumberInputSize = 'xs' | 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'lc-number-input',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_PLUS])],\n templateUrl: './number-input.component.html',\n styleUrls: ['./number-input.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => NumberInputComponent),\n multi: true,\n },\n ],\n})\n/**\n * Number input component with increment/decrement controls.\n *\n * Features:\n * - Increment and decrement buttons (also ArrowUp / ArrowDown)\n * - Configurable min, max, and step values\n * - Value clamping within bounds; stepping is rounded to the step's precision\n * - Non-numeric characters are rejected as they are typed; a comma and a\n * point are both accepted as the decimal separator, in every locale\n * - The value is shown in `locale` („7,5“ in `de-DE`), with an optional unit\n * after it (`suffix`, „7,5 %“), read once (`suffixAriaLabel`)\n * - Sizes `xs`, `sm`, `md` (default), `lg` (44 px touch targets)\n * - Disabled state support\n * - ControlValueAccessor integration for reactive forms; the model is\n * always a `number`, clamped to `min`/`max` on blur\n *\n * @example\n * ```html\n * <lc-number-input label=\"Quantity\" [min]=\"0\" [max]=\"100\" [step]=\"1\" />\n * <lc-number-input label=\"Anteil\" locale=\"de-DE\" suffix=\"%\" suffixAriaLabel=\"Prozent\"\n * size=\"lg\" [step]=\"0.5\" [(ngModel)]=\"share\" />\n * ```\n */\nexport class NumberInputComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Minimum value */\n min = input<number | undefined>(undefined);\n\n /** Maximum value */\n max = input<number | undefined>(undefined);\n\n /** Step increment */\n step = input<number>(1);\n\n /** Label text */\n label = input<string>();\n\n /** Accessible name for the field when no visible `label` is rendered. */\n ariaLabel = input<string>();\n\n /** Placeholder */\n placeholder = input<string>('0');\n\n /** Helper text shown below the control (linked via `aria-describedby`). */\n helperText = input<string>();\n\n /** Error message shown below the control; marks the field `aria-invalid`. */\n error = input<string>();\n\n /** Disabled state */\n disabled = input<boolean>(false);\n\n /** Accessible name of the minus button. */\n decrementAriaLabel = input('Decrease value');\n\n /** Accessible name of the plus button. */\n incrementAriaLabel = input('Increase value');\n\n /**\n * BCP 47 locale of the shown number (3.29, regeln row 3). Only the decimal\n * separator follows it: `de-DE` shows „7,5“, `en-US` \"7.5\"; a locale whose\n * separator is neither `,` nor `.` shows a point. No grouping. `''`: a\n * point, as before. Typing accepts `,` and `.` in every locale and shows\n * the locale's separator; the model stays a `number`; blur clamps to\n * `min`/`max`.\n * @default ''\n */\n locale = input<string>('');\n\n /**\n * Unit shown after the value inside the field („7,5 %“, 3.29). Not typed\n * and not part of the model; with a suffix the value aligns to the end.\n * Screen readers read `suffixAriaLabel` (or the suffix) once after the value.\n * @default ''\n */\n suffix = input<string>('');\n\n /**\n * Spoken unit („Prozent“), read once after the value as the field's\n * description (before the helper or error text). `''`: the `suffix`.\n * @default ''\n */\n suffixAriaLabel = input<string>('');\n\n /**\n * Height of the field and the steppers (3.29, regeln row 4): `xs` 24,\n * `sm` 32, `md` 40 (as before), `lg` 44 px (touch: field and both\n * steppers at least 44 × 44 px).\n * @default 'md'\n */\n size = input<NumberInputSize>('md');\n\n /** Emits value on change */\n valueChange = output<number>();\n\n /** Per-instance id wiring the label, hint and error to the field. */\n readonly inputId = `lc-number-input-${++NumberInputComponent.nextId}`;\n\n protected value = signal<number | null>(null);\n\n /**\n * Text shown in the field. It is re-derived from the model on `writeValue`,\n * stepping and blur — never while typing — so an in-progress entry such as\n * \"1.\" or \"-\" is not snapped back to \"1\" under the user's cursor.\n */\n protected readonly text = signal('');\n\n /** Disabled through the form control (`setDisabledState`), as opposed to the input. */\n private readonly formDisabled = signal(false);\n\n /** Disabled by either route — the one flag the template consults. */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n private readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n\n /** Decimals implied by `step` — stepped results are rounded to it so 0.1 + 0.2 does not drift. */\n private readonly stepPrecision = computed(() => decimalsOf(this.step()));\n\n /** Fractions need a decimal key on touch keyboards; integers do not. */\n protected readonly inputMode = computed(() => (this.stepPrecision() > 0 ? 'decimal' : 'numeric'));\n\n /** The decimal separator shown in the field: the `locale`'s `,` or `.`. */\n private readonly separator = computed(() => decimalSeparatorOf(this.locale()));\n\n /** The spoken unit: `suffixAriaLabel`, else the `suffix`; `''` = none. */\n protected readonly spokenSuffix = computed(() => this.suffixAriaLabel() || this.suffix());\n\n /** Icon size of the steppers: `xs` (16 px) for the `xs`/`sm` fields, `sm` (20 px) as before. */\n protected readonly iconSize = computed(() => (this.size() === 'xs' || this.size() === 'sm' ? 'xs' : 'sm'));\n\n protected readonly describedBy = computed(() => {\n const ids: string[] = [];\n if (this.spokenSuffix()) ids.push(`${this.inputId}-suffix`);\n if (this.error()) ids.push(`${this.inputId}-error`);\n else if (this.helperText()) ids.push(`${this.inputId}-helper`);\n return ids.length ? ids.join(' ') : null;\n });\n\n protected containerClasses = computed(() => {\n const classes = ['number-input'];\n // `md` (the default) keeps the base rules: no class.\n const size = this.size();\n if (size === 'xs' || size === 'sm' || size === 'lg') classes.push(`number-input--${size}`);\n if (this.isDisabled()) classes.push('number-input--disabled');\n if (this.error()) classes.push('number-input--error');\n return classes.join(' ');\n });\n\n protected canDecrement = computed(() => {\n const val = this.value();\n const minV = this.min();\n if (val === null || minV === undefined) return true;\n return val > minV;\n });\n\n protected canIncrement = computed(() => {\n const val = this.value();\n const maxV = this.max();\n if (val === null || maxV === undefined) return true;\n return val < maxV;\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (val: number | null) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n decrement(): void {\n this.stepBy(-1);\n }\n\n increment(): void {\n this.stepBy(1);\n }\n\n private stepBy(direction: 1 | -1): void {\n const cur = this.value() ?? 0;\n // Keep whatever precision the user typed (1.25 + 0.1 → 1.35), but never\n // less than the step's own — that is what removes the binary drift.\n const precision = Math.max(this.stepPrecision(), decimalsOf(cur));\n const next = this.clamp(Number((cur + direction * this.step()).toFixed(precision)));\n this.setValue(next);\n this.text.set(this.format(next));\n }\n\n onKeydown(event: KeyboardEvent): void {\n if (event.key === 'ArrowUp') {\n event.preventDefault();\n if (this.canIncrement()) this.increment();\n } else if (event.key === 'ArrowDown') {\n event.preventDefault();\n if (this.canDecrement()) this.decrement();\n }\n }\n\n onInput(event: Event): void {\n const target = event.target as HTMLInputElement;\n const raw = target.value;\n const text = sanitizeNumericText(raw, this.separator());\n // Property binding alone would not repaint the field when the model does\n // not change (typing \"a\" after \"12\" leaves 12 as-is), so the stripped\n // text is written straight back to the element.\n if (text !== raw) {\n const caret = Math.max(0, (target.selectionStart ?? text.length) - (raw.length - text.length));\n target.value = text;\n target.setSelectionRange(caret, caret);\n }\n this.text.set(text);\n // \"\" would parse as 0; it and the other in-progress entries (\"-\", \".\")\n // mean the model has no number yet.\n const parsed = text === '' ? NaN : Number(text.replace(',', '.'));\n if (Number.isNaN(parsed)) {\n if (this.value() !== null) {\n this.value.set(null);\n this.onChange(null);\n }\n return;\n }\n this.setValue(parsed);\n }\n\n onBlur(): void {\n this.onTouched();\n // Clamp on blur\n const val = this.value();\n if (val !== null) {\n const clamped = this.clamp(val);\n if (clamped !== val) this.setValue(clamped);\n }\n // Whatever half-typed text is left (\"1.\", \"-\", \"007\") is replaced by the\n // model's canonical form. The element is written directly as well, in\n // case the DOM drifted from the bound text without an input event.\n const canonical = this.format(this.value());\n this.text.set(canonical);\n const el = this.inputEl()?.nativeElement;\n if (el) el.value = canonical;\n }\n\n /** The model as text: `String(val)` with the locale's decimal separator, no grouping. */\n private format(val: number | null): string {\n if (val === null) return '';\n const text = String(val);\n return this.separator() === ',' ? text.replace('.', ',') : text;\n }\n\n /** A press on the visible unit puts the caret in the field, as a click on the field would. */\n protected focusField(event: MouseEvent): void {\n event.preventDefault();\n if (!this.isDisabled()) this.inputEl()?.nativeElement.focus();\n }\n\n private clamp(val: number): number {\n const minV = this.min();\n const maxV = this.max();\n if (minV !== undefined) val = Math.max(minV, val);\n if (maxV !== undefined) val = Math.min(maxV, val);\n return val;\n }\n\n private setValue(val: number): void {\n if (val === this.value()) return;\n this.value.set(val);\n this.valueChange.emit(val);\n this.onChange(val);\n }\n\n // ControlValueAccessor\n writeValue(val: number | null): void {\n this.value.set(val);\n this.text.set(this.format(val));\n }\n\n registerOnChange(fn: (val: number | null) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n}\n","<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"number-input__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n <div class=\"number-input__control\">\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--decrement\"\n [disabled]=\"isDisabled() || !canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"decrementAriaLabel()\"\n >\n <lc-icon name=\"minus\" [size]=\"iconSize()\" [decorative]=\"true\" />\n </button>\n <input\n #inputEl\n type=\"text\"\n [id]=\"inputId\"\n [attr.inputmode]=\"inputMode()\"\n class=\"number-input__input\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [attr.min]=\"min() ?? null\"\n [attr.max]=\"max() ?? null\"\n [attr.aria-label]=\"label() ? null : ariaLabel()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n @if (suffix()) {\n <!-- The visible unit (3.29): not read; the field's description says it once. A press focuses the field. -->\n <span class=\"number-input__suffix\" aria-hidden=\"true\" (mousedown)=\"focusField($event)\">{{ suffix() }}</span>\n }\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--increment\"\n [disabled]=\"isDisabled() || !canIncrement()\"\n (click)=\"increment()\"\n [attr.aria-label]=\"incrementAriaLabel()\"\n >\n <lc-icon name=\"plus\" [size]=\"iconSize()\" [decorative]=\"true\" />\n </button>\n </div>\n @if (spokenSuffix()) {\n <span hidden [id]=\"inputId + '-suffix'\">{{ spokenSuffix() }}</span>\n }\n @if (error()) {\n <p [id]=\"inputId + '-error'\" class=\"number-input__error\" role=\"alert\">{{ error() }}</p>\n } @else if (helperText()) {\n <p [id]=\"inputId + '-helper'\" class=\"number-input__helper\">{{ helperText() }}</p>\n }\n</div>\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -4,6 +4,8 @@ import * as i0 from "@angular/core";
|
|
|
4
4
|
export type CardVariant = 'elevated' | 'outlined' | 'flat' | 'raised' | 'dashed';
|
|
5
5
|
/** Heading level of `lc-card`'s built-in title. */
|
|
6
6
|
export type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;
|
|
7
|
+
/** Layout of `lc-card`'s `[card-footer]` slot: `default` (a flex row, end-aligned) or `equal` (children at equal width). */
|
|
8
|
+
export type CardFooterLayout = 'default' | 'equal';
|
|
7
9
|
/**
|
|
8
10
|
* Card component for content grouping and visual hierarchy.
|
|
9
11
|
*
|
|
@@ -15,6 +17,8 @@ export type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;
|
|
|
15
17
|
* - Content projection for flexible body content
|
|
16
18
|
* - Link mode (3.24): `href` or `route` make the whole card a real link,
|
|
17
19
|
* the title stays a heading
|
|
20
|
+
* - Equal footer (3.29): `footerLayout="equal"` gives two answers of equal
|
|
21
|
+
* weight in the `[card-footer]` slot the same width
|
|
18
22
|
*
|
|
19
23
|
* @example
|
|
20
24
|
* ```html
|
|
@@ -32,6 +36,15 @@ export type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;
|
|
|
32
36
|
* [route]="['/quellen', 'sap-ist']" [queryParams]="{ ansicht: 'laeufe' }">
|
|
33
37
|
* ...
|
|
34
38
|
* </lc-card>
|
|
39
|
+
*
|
|
40
|
+
* <!-- Two answers of equal weight (3.29): primary answer first -->
|
|
41
|
+
* <lc-card title="Netflix" variant="outlined" footerLayout="equal">
|
|
42
|
+
* ...
|
|
43
|
+
* <div card-footer>
|
|
44
|
+
* <lc-button variant="outline" size="lg" [fullWidth]="true">Stimmt</lc-button>
|
|
45
|
+
* <lc-button variant="outline" size="lg" [fullWidth]="true">Kein Vertrag</lc-button>
|
|
46
|
+
* </div>
|
|
47
|
+
* </lc-card>
|
|
35
48
|
* ```
|
|
36
49
|
*/
|
|
37
50
|
export declare class CardComponent {
|
|
@@ -152,6 +165,22 @@ export declare class CardComponent {
|
|
|
152
165
|
* @default undefined
|
|
153
166
|
*/
|
|
154
167
|
queryParams: import("@angular/core").InputSignal<Params | null | undefined>;
|
|
168
|
+
/**
|
|
169
|
+
* Layout of the `[card-footer]` slot (3.29, CC-2). `equal`: its children
|
|
170
|
+
* share the row at equal width once the card is at least 480 px (30rem)
|
|
171
|
+
* wide, and stack full width, 8 px apart, below that (the card's own width,
|
|
172
|
+
* at any viewport; long labels wrap inside the buttons, which get the
|
|
173
|
+
* row's height). For two answers of equal weight, put
|
|
174
|
+
* `<lc-button variant="outline" size="lg" [fullWidth]="true">` in it,
|
|
175
|
+
* primary answer first; the card adds layout only (gap, divider and
|
|
176
|
+
* spacing as `default`) and never restyles the buttons. The card becomes a
|
|
177
|
+
* size container and takes its parent's full width. Keep the default
|
|
178
|
+
* `padding` (with `none` the buttons' focus ring is clipped). Works in link
|
|
179
|
+
* cards (the buttons stay clickable above the link) and in prerendered
|
|
180
|
+
* HTML (CSS only).
|
|
181
|
+
* @default 'default'
|
|
182
|
+
*/
|
|
183
|
+
footerLayout: import("@angular/core").InputSignal<CardFooterLayout>;
|
|
155
184
|
/**
|
|
156
185
|
* Emitted when a `clickable` card is clicked or activated with Enter or
|
|
157
186
|
* Space. Not emitted on a link card (`href`/`route`): the link navigates by
|
|
@@ -275,6 +304,11 @@ export declare class CardComponent {
|
|
|
275
304
|
"required": false;
|
|
276
305
|
"isSignal": true;
|
|
277
306
|
};
|
|
307
|
+
"footerLayout": {
|
|
308
|
+
"alias": "footerLayout";
|
|
309
|
+
"required": false;
|
|
310
|
+
"isSignal": true;
|
|
311
|
+
};
|
|
278
312
|
}, {
|
|
279
313
|
"cardClick": "cardClick";
|
|
280
314
|
}, never, ["[card-badge]", "[card-header-action]", "[card-header]", "*", "[card-footer]"], true, never>;
|
|
@@ -19,12 +19,16 @@ export type ComboboxSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
|
19
19
|
* The suggestion list is rendered in a CDK overlay so it is never clipped
|
|
20
20
|
* by `overflow: hidden` ancestors (cards, modals, table cells).
|
|
21
21
|
*
|
|
22
|
+
* The selection is two-way bindable by an option's `value` string
|
|
23
|
+
* (`[(selectedValue)]`, single mode), next to the `valueChange` output with
|
|
24
|
+
* the option object and the forms binding.
|
|
25
|
+
*
|
|
22
26
|
* @example
|
|
23
27
|
* ```html
|
|
24
28
|
* <lc-combobox
|
|
25
29
|
* label="Assign to user"
|
|
26
30
|
* [loadOptions]="searchUsers"
|
|
27
|
-
* [(
|
|
31
|
+
* [(selectedValue)]="assigneeId"
|
|
28
32
|
* allowCreate
|
|
29
33
|
* />
|
|
30
34
|
* ```
|
|
@@ -71,6 +75,19 @@ export declare class ComboboxComponent implements ControlValueAccessor {
|
|
|
71
75
|
readonly createOptionLabel: import("@angular/core").InputSignal<string>;
|
|
72
76
|
/** Size */
|
|
73
77
|
readonly size: import("@angular/core").InputSignal<ComboboxSize>;
|
|
78
|
+
/**
|
|
79
|
+
* The `value` of the selected option, two-way (3.29, regeln row 2):
|
|
80
|
+
* `[(selectedValue)]="month"`. Shows the matching option's label (from
|
|
81
|
+
* `options`, or the current selection); an unknown value shows an empty
|
|
82
|
+
* field and is filled in when `options` with it arrive. Page writes
|
|
83
|
+
* (including back to `null`) select or clear silently: no
|
|
84
|
+
* `selectedValueChange`, no `valueChange`, no forms `onChange`. Every user
|
|
85
|
+
* choice or clear emits `selectedValueChange`, next to the unchanged
|
|
86
|
+
* `valueChange` (the option object). Single mode only: ignored with
|
|
87
|
+
* `multiple` (a dev warning). Use it or a forms binding, not both.
|
|
88
|
+
* @default null
|
|
89
|
+
*/
|
|
90
|
+
readonly selectedValue: import("@angular/core").ModelSignal<string | null>;
|
|
74
91
|
/** Value change event */
|
|
75
92
|
readonly valueChange: import("@angular/core").OutputEmitterRef<ComboboxValue>;
|
|
76
93
|
/** Query change event */
|
|
@@ -92,6 +109,10 @@ export declare class ComboboxComponent implements ControlValueAccessor {
|
|
|
92
109
|
/** Selected values */
|
|
93
110
|
protected selectedSingle: import("@angular/core").WritableSignal<ComboboxOption | null>;
|
|
94
111
|
protected selectedMultiple: import("@angular/core").WritableSignal<ComboboxOption[]>;
|
|
112
|
+
/** The last `selectedValue` the component applied or emitted: a different one is a page write. */
|
|
113
|
+
private seenSelectedValue;
|
|
114
|
+
/** A page-written `selectedValue` no option matched yet: selected silently once `options` hold it. */
|
|
115
|
+
private pendingSelectedValue;
|
|
95
116
|
/** Resolved visible options */
|
|
96
117
|
protected visibleOptions: import("@angular/core").Signal<ComboboxOption[]>;
|
|
97
118
|
/** Grouped options */
|
|
@@ -113,6 +134,10 @@ export declare class ComboboxComponent implements ControlValueAccessor {
|
|
|
113
134
|
private onChange;
|
|
114
135
|
private onTouched;
|
|
115
136
|
constructor();
|
|
137
|
+
/** Shows the option with `value` (or clears for `null`) without emitting anything. */
|
|
138
|
+
private applySelectedValue;
|
|
139
|
+
/** Emits a user's single choice or clear through `selectedValueChange` (not in `multiple` mode). */
|
|
140
|
+
private emitSelectedValue;
|
|
116
141
|
writeValue(value: ComboboxValue): void;
|
|
117
142
|
registerOnChange(fn: (value: ComboboxValue) => void): void;
|
|
118
143
|
registerOnTouched(fn: () => void): void;
|
|
@@ -213,7 +238,13 @@ export declare class ComboboxComponent implements ControlValueAccessor {
|
|
|
213
238
|
"required": false;
|
|
214
239
|
"isSignal": true;
|
|
215
240
|
};
|
|
241
|
+
"selectedValue": {
|
|
242
|
+
"alias": "selectedValue";
|
|
243
|
+
"required": false;
|
|
244
|
+
"isSignal": true;
|
|
245
|
+
};
|
|
216
246
|
}, {
|
|
247
|
+
"selectedValue": "selectedValueChange";
|
|
217
248
|
"valueChange": "valueChange";
|
|
218
249
|
"queryChange": "queryChange";
|
|
219
250
|
"optionSelected": "optionSelected";
|