@besaitech/ng-design-system 0.0.41 → 0.0.42
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, input, computed, ChangeDetectionStrategy, Component, booleanAttribute, isDevMode, effect, output, signal, forwardRef, numberAttribute, viewChildren, afterRenderEffect,
|
|
2
|
+
import { inject, input, computed, ChangeDetectionStrategy, Component, booleanAttribute, isDevMode, effect, output, signal, forwardRef, viewChild, numberAttribute, viewChildren, afterRenderEffect, ElementRef, model, Injector, afterNextRender, Injectable, TemplateRef, Directive, contentChildren, contentChild } from '@angular/core';
|
|
3
3
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
4
4
|
import * as i1 from '@angular/forms';
|
|
5
5
|
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, FormsModule } from '@angular/forms';
|
|
@@ -48,6 +48,11 @@ const SDS_ICONS = {
|
|
|
48
48
|
'chevron-right': '<path d="m9 18 6-6-6-6"/>',
|
|
49
49
|
'chevron-left': '<path d="m15 18-6-6 6-6"/>',
|
|
50
50
|
'chevron-down': '<path d="m6 9 6 6 6-6"/>',
|
|
51
|
+
// Stepper glyphs for sds-input-number: chevrons for the stacked spinner, plus/minus for the
|
|
52
|
+
// horizontal/vertical layouts.
|
|
53
|
+
'chevron-up': '<path d="m18 15-6-6-6 6"/>',
|
|
54
|
+
plus: '<path d="M5 12h14"/><path d="M12 5v14"/>',
|
|
55
|
+
minus: '<path d="M5 12h14"/>',
|
|
51
56
|
'chevrons-left': '<path d="m11 17-5-5 5-5"/><path d="m18 17-5-5 5-5"/>',
|
|
52
57
|
'chevrons-right': '<path d="m6 17 5-5-5-5"/><path d="m13 17 5-5-5-5"/>',
|
|
53
58
|
x: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
|
|
@@ -267,7 +272,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
267
272
|
`, styles: [".sds-spinner{width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:9999px;animation:sds-spin .6s linear infinite}@keyframes sds-spin{to{transform:rotate(360deg)}}\n"] }]
|
|
268
273
|
}], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
269
274
|
|
|
270
|
-
let nextId$
|
|
275
|
+
let nextId$d = 0;
|
|
271
276
|
const SIZES$a = {
|
|
272
277
|
sm: 'h-9 text-sm',
|
|
273
278
|
md: 'h-10 text-sm',
|
|
@@ -280,7 +285,7 @@ const SIZES$a = {
|
|
|
280
285
|
* sidebar search field).
|
|
281
286
|
*/
|
|
282
287
|
class SdsInput {
|
|
283
|
-
inputId = `sds-input-${nextId$
|
|
288
|
+
inputId = `sds-input-${nextId$d++}`;
|
|
284
289
|
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
285
290
|
/** Accessible name used when no visible [label] is provided. */
|
|
286
291
|
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
@@ -601,6 +606,736 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
601
606
|
`, styles: ["input:-webkit-autofill,input:-webkit-autofill:hover,input:-webkit-autofill:focus,input:-webkit-autofill:active{transition:background-color 100000s ease-in-out 0s;-webkit-text-fill-color:currentColor;caret-color:currentColor}\n"] }]
|
|
602
607
|
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], leadingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "leadingIcon", required: false }] }], trailingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingIcon", required: false }] }], trailingAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingAction", required: false }] }], trailingActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingActionLabel", required: false }] }], trailingActionPressed: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingActionPressed", required: false }] }], trailingActionClick: [{ type: i0.Output, args: ["trailingActionClick"] }] } });
|
|
603
608
|
|
|
609
|
+
let nextId$c = 0;
|
|
610
|
+
const HEIGHTS = {
|
|
611
|
+
sm: 'h-9',
|
|
612
|
+
md: 'h-10',
|
|
613
|
+
lg: 'h-12',
|
|
614
|
+
};
|
|
615
|
+
const TEXT = {
|
|
616
|
+
sm: 'text-sm',
|
|
617
|
+
md: 'text-sm',
|
|
618
|
+
lg: 'text-base',
|
|
619
|
+
};
|
|
620
|
+
/**
|
|
621
|
+
* Numeric input with locale-aware formatting, optional currency, prefix/suffix affixes, min/max/step
|
|
622
|
+
* boundaries and spinner buttons — the design-system counterpart of PrimeNG's `InputNumber`.
|
|
623
|
+
*
|
|
624
|
+
* The value is a `number | null` bound through ControlValueAccessor, so it works with reactive forms
|
|
625
|
+
* or `[(ngModel)]`: `<sds-input-number [(ngModel)]="amount" mode="currency" currency="EUR" />`.
|
|
626
|
+
*
|
|
627
|
+
* The field formats **live as you type**: the currency symbol, grouping separators and prefix/suffix
|
|
628
|
+
* stay visible on every keystroke, and the caret is kept where it belongs. The value is clamped to
|
|
629
|
+
* {@link min}/{@link max} on blur and on every spinner step; ArrowUp / ArrowDown also step it.
|
|
630
|
+
*/
|
|
631
|
+
class SdsInputNumber {
|
|
632
|
+
inputId = `sds-input-number-${nextId$c++}`;
|
|
633
|
+
fieldRef = viewChild('field', ...(ngDevMode ? [{ debugName: "fieldRef" }] : []));
|
|
634
|
+
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
635
|
+
/** Accessible name used when no visible [label] is provided. */
|
|
636
|
+
ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
637
|
+
placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
638
|
+
hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : []));
|
|
639
|
+
error = input('', ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
640
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
|
|
641
|
+
/** `filled` is a borderless field on a muted ground; `default` is the bordered white field. */
|
|
642
|
+
appearance = input('default', ...(ngDevMode ? [{ debugName: "appearance" }] : []));
|
|
643
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
644
|
+
/** Read-only: the value is shown and selectable but cannot be typed or stepped. */
|
|
645
|
+
readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
646
|
+
// --- Formatting -----------------------------------------------------------
|
|
647
|
+
mode = input('decimal', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
648
|
+
/** BCP 47 locale for grouping/decimal symbols (e.g. `fr-FR`). Empty follows the runtime locale. */
|
|
649
|
+
locale = input('', ...(ngDevMode ? [{ debugName: "locale" }] : []));
|
|
650
|
+
/** ISO 4217 code, required when {@link mode} is `currency` (e.g. `EUR`, `USD`). */
|
|
651
|
+
currency = input('', ...(ngDevMode ? [{ debugName: "currency" }] : []));
|
|
652
|
+
currencyDisplay = input('symbol', ...(ngDevMode ? [{ debugName: "currencyDisplay" }] : []));
|
|
653
|
+
/** Thousands/grouping separators. */
|
|
654
|
+
useGrouping = input(true, ...(ngDevMode ? [{ debugName: "useGrouping", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
655
|
+
minFractionDigits = input(null, ...(ngDevMode ? [{ debugName: "minFractionDigits" }] : []));
|
|
656
|
+
maxFractionDigits = input(null, ...(ngDevMode ? [{ debugName: "maxFractionDigits" }] : []));
|
|
657
|
+
/** Text shown before the value (e.g. `↑ `). */
|
|
658
|
+
prefix = input('', ...(ngDevMode ? [{ debugName: "prefix" }] : []));
|
|
659
|
+
/** Text shown after the value (e.g. ` kg`, `%`). */
|
|
660
|
+
suffix = input('', ...(ngDevMode ? [{ debugName: "suffix" }] : []));
|
|
661
|
+
// --- Boundaries & spinner -------------------------------------------------
|
|
662
|
+
min = input(null, ...(ngDevMode ? [{ debugName: "min" }] : []));
|
|
663
|
+
max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : []));
|
|
664
|
+
step = input(1, ...(ngDevMode ? [{ debugName: "step", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
665
|
+
/** Show the increment/decrement spinner buttons. */
|
|
666
|
+
showButtons = input(false, ...(ngDevMode ? [{ debugName: "showButtons", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
667
|
+
buttonLayout = input('stacked', ...(ngDevMode ? [{ debugName: "buttonLayout" }] : []));
|
|
668
|
+
/** Override the increment button icon (defaults: chevron-up when stacked, plus otherwise). */
|
|
669
|
+
incrementIcon = input(null, ...(ngDevMode ? [{ debugName: "incrementIcon" }] : []));
|
|
670
|
+
/** Override the decrement button icon (defaults: chevron-down when stacked, minus otherwise). */
|
|
671
|
+
decrementIcon = input(null, ...(ngDevMode ? [{ debugName: "decrementIcon" }] : []));
|
|
672
|
+
/** Allow an empty (null) value. When false, blurring an empty field falls back to {@link min} or 0. */
|
|
673
|
+
allowEmpty = input(true, ...(ngDevMode ? [{ debugName: "allowEmpty", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
674
|
+
clearable = input(false, ...(ngDevMode ? [{ debugName: "clearable", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
675
|
+
/**
|
|
676
|
+
* Accessible names for the icon-only controls. French defaults so existing consumers are unchanged;
|
|
677
|
+
* translate them in a multi-language app.
|
|
678
|
+
*/
|
|
679
|
+
clearLabel = input('Effacer', ...(ngDevMode ? [{ debugName: "clearLabel" }] : []));
|
|
680
|
+
incrementLabel = input('Augmenter', ...(ngDevMode ? [{ debugName: "incrementLabel" }] : []));
|
|
681
|
+
decrementLabel = input('Diminuer', ...(ngDevMode ? [{ debugName: "decrementLabel" }] : []));
|
|
682
|
+
/** The committed numeric value (driven by CVA + user input). */
|
|
683
|
+
value = signal(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
684
|
+
cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : []));
|
|
685
|
+
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
686
|
+
describedBy = computed(() => {
|
|
687
|
+
if (this.error())
|
|
688
|
+
return `${this.inputId}-err`;
|
|
689
|
+
if (this.hint())
|
|
690
|
+
return `${this.inputId}-hint`;
|
|
691
|
+
return null;
|
|
692
|
+
}, ...(ngDevMode ? [{ debugName: "describedBy" }] : []));
|
|
693
|
+
clearVisible = computed(() => this.clearable() && this.value() !== null && !this.isDisabled() && !this.readonly(), ...(ngDevMode ? [{ debugName: "clearVisible" }] : []));
|
|
694
|
+
incrementIconName = computed(() => this.incrementIcon() ?? (this.buttonLayout() === 'stacked' ? 'chevron-up' : 'plus'), ...(ngDevMode ? [{ debugName: "incrementIconName" }] : []));
|
|
695
|
+
decrementIconName = computed(() => this.decrementIcon() ?? (this.buttonLayout() === 'stacked' ? 'chevron-down' : 'minus'), ...(ngDevMode ? [{ debugName: "decrementIconName" }] : []));
|
|
696
|
+
canIncrement = computed(() => {
|
|
697
|
+
if (this.isDisabled() || this.readonly())
|
|
698
|
+
return false;
|
|
699
|
+
const max = this.max();
|
|
700
|
+
return max === null || (this.value() ?? this.min() ?? 0) < max;
|
|
701
|
+
}, ...(ngDevMode ? [{ debugName: "canIncrement" }] : []));
|
|
702
|
+
canDecrement = computed(() => {
|
|
703
|
+
if (this.isDisabled() || this.readonly())
|
|
704
|
+
return false;
|
|
705
|
+
const min = this.min();
|
|
706
|
+
return min === null || (this.value() ?? min ?? 0) > min;
|
|
707
|
+
}, ...(ngDevMode ? [{ debugName: "canDecrement" }] : []));
|
|
708
|
+
/** Locale grouping/decimal separators, derived from Intl so parsing matches formatting. */
|
|
709
|
+
separators = computed(() => {
|
|
710
|
+
const sample = new Intl.NumberFormat(this.locale() || undefined, {
|
|
711
|
+
useGrouping: true,
|
|
712
|
+
}).formatToParts(1234567.8);
|
|
713
|
+
return {
|
|
714
|
+
group: sample.find((p) => p.type === 'group')?.value ?? ',',
|
|
715
|
+
decimal: sample.find((p) => p.type === 'decimal')?.value ?? '.',
|
|
716
|
+
};
|
|
717
|
+
}, ...(ngDevMode ? [{ debugName: "separators" }] : []));
|
|
718
|
+
/** Base Intl options (style/currency/grouping) without any fraction-digit constraints. */
|
|
719
|
+
baseOptions() {
|
|
720
|
+
const opts = { useGrouping: this.useGrouping() };
|
|
721
|
+
if (this.mode() === 'currency') {
|
|
722
|
+
opts.style = 'currency';
|
|
723
|
+
opts.currency = this.currency() || 'USD';
|
|
724
|
+
opts.currencyDisplay = this.currencyDisplay();
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
opts.style = 'decimal';
|
|
728
|
+
}
|
|
729
|
+
return opts;
|
|
730
|
+
}
|
|
731
|
+
committedOptions = computed(() => {
|
|
732
|
+
const opts = this.baseOptions();
|
|
733
|
+
const min = this.minFractionDigits();
|
|
734
|
+
const max = this.maxFractionDigits();
|
|
735
|
+
if (min !== null)
|
|
736
|
+
opts.minimumFractionDigits = min;
|
|
737
|
+
if (max !== null)
|
|
738
|
+
opts.maximumFractionDigits = min !== null ? Math.max(max, min) : max;
|
|
739
|
+
return opts;
|
|
740
|
+
}, ...(ngDevMode ? [{ debugName: "committedOptions" }] : []));
|
|
741
|
+
/** The most fraction digits the field will keep — the resolved Intl maximum (2 for USD, 0 for JPY…). */
|
|
742
|
+
maxFrac = computed(() => {
|
|
743
|
+
const fallback = this.maxFractionDigits() ?? (this.mode() === 'currency' ? 2 : 3);
|
|
744
|
+
try {
|
|
745
|
+
const resolved = new Intl.NumberFormat(this.locale() || undefined, this.committedOptions()).resolvedOptions().maximumFractionDigits;
|
|
746
|
+
return resolved ?? fallback;
|
|
747
|
+
}
|
|
748
|
+
catch {
|
|
749
|
+
return fallback;
|
|
750
|
+
}
|
|
751
|
+
}, ...(ngDevMode ? [{ debugName: "maxFrac" }] : []));
|
|
752
|
+
wrapperClasses = computed(() => {
|
|
753
|
+
const vertical = this.buttonLayout() === 'vertical' && this.showButtons();
|
|
754
|
+
const parts = [
|
|
755
|
+
'overflow-hidden rounded-control',
|
|
756
|
+
vertical ? 'flex flex-col' : `flex items-stretch ${HEIGHTS[this.size()]}`,
|
|
757
|
+
];
|
|
758
|
+
if (this.isDisabled())
|
|
759
|
+
parts.push('opacity-60 cursor-not-allowed');
|
|
760
|
+
if (this.appearance() === 'filled') {
|
|
761
|
+
parts.push('border-transparent bg-muted text-ink');
|
|
762
|
+
parts.push(this.error()
|
|
763
|
+
? 'border-danger focus-within:ring-2 focus-within:ring-danger/30'
|
|
764
|
+
: 'focus-within:ring-2 focus-within:ring-brand-500/25');
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
parts.push('bg-white text-ink');
|
|
768
|
+
parts.push(this.error()
|
|
769
|
+
? 'border-danger focus-within:border-danger focus-within:ring-2 focus-within:ring-danger/25'
|
|
770
|
+
: 'border-border focus-within:border-brand-500 focus-within:ring-2 focus-within:ring-brand-500/25');
|
|
771
|
+
}
|
|
772
|
+
return parts.join(' ');
|
|
773
|
+
}, ...(ngDevMode ? [{ debugName: "wrapperClasses" }] : []));
|
|
774
|
+
fieldCellClasses = computed(() => this.buttonLayout() === 'vertical' && this.showButtons() ? HEIGHTS[this.size()] : 'h-full', ...(ngDevMode ? [{ debugName: "fieldCellClasses" }] : []));
|
|
775
|
+
inputClasses = computed(() => {
|
|
776
|
+
const parts = [TEXT[this.size()], 'pl-3.5', this.clearVisible() ? 'pr-9' : 'pr-3.5'];
|
|
777
|
+
if (this.showButtons() && this.buttonLayout() !== 'stacked')
|
|
778
|
+
parts.push('text-center');
|
|
779
|
+
return parts.join(' ');
|
|
780
|
+
}, ...(ngDevMode ? [{ debugName: "inputClasses" }] : []));
|
|
781
|
+
buttonBase = 'inline-flex shrink-0 items-center justify-center text-ink-soft transition-colors ' +
|
|
782
|
+
'hover:bg-muted hover:text-ink disabled:cursor-not-allowed disabled:opacity-40 select-none';
|
|
783
|
+
edgeButtonClasses = computed(() => this.buttonLayout() === 'vertical'
|
|
784
|
+
? `${this.buttonBase} ${HEIGHTS[this.size()]} border-border`
|
|
785
|
+
: `${this.buttonBase} w-10 border-border`, ...(ngDevMode ? [{ debugName: "edgeButtonClasses" }] : []));
|
|
786
|
+
stackedButtonClasses = computed(() => this.buttonBase, ...(ngDevMode ? [{ debugName: "stackedButtonClasses" }] : []));
|
|
787
|
+
constructor() {
|
|
788
|
+
// Reflect the committed value into the field whenever it — or a formatting input — changes from
|
|
789
|
+
// OUTSIDE typing (initial render, writeValue, a spin while blurred, a currency/locale switch).
|
|
790
|
+
// While the field is the active element the input handler owns the DOM value and its caret, so this
|
|
791
|
+
// stands down: gating on the live `document.activeElement` (not a signal) is immune to focus-event
|
|
792
|
+
// timing, which is what makes live formatting safe.
|
|
793
|
+
effect(() => {
|
|
794
|
+
const el = this.fieldRef()?.nativeElement;
|
|
795
|
+
const v = this.value();
|
|
796
|
+
// Touch the formatting inputs so a currency/locale/affix change repaints a blurred field.
|
|
797
|
+
this.committedOptions();
|
|
798
|
+
this.prefix();
|
|
799
|
+
this.suffix();
|
|
800
|
+
if (!el || this.isActive(el))
|
|
801
|
+
return;
|
|
802
|
+
el.value = v === null ? '' : this.format(v);
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
isActive(el) {
|
|
806
|
+
return typeof document !== 'undefined' && document.activeElement === el;
|
|
807
|
+
}
|
|
808
|
+
/** Paint the committed value into the field imperatively (used by spin/clear/blur). */
|
|
809
|
+
paintCommitted() {
|
|
810
|
+
const el = this.fieldRef()?.nativeElement;
|
|
811
|
+
if (!el)
|
|
812
|
+
return;
|
|
813
|
+
const v = this.value();
|
|
814
|
+
el.value = v === null ? '' : this.format(v);
|
|
815
|
+
}
|
|
816
|
+
// --- Formatting / parsing helpers -----------------------------------------
|
|
817
|
+
/** Format a committed value with the configured min/max fraction digits (used at rest and on blur). */
|
|
818
|
+
format(n) {
|
|
819
|
+
try {
|
|
820
|
+
const body = new Intl.NumberFormat(this.locale() || undefined, this.committedOptions()).format(n);
|
|
821
|
+
return `${this.prefix()}${body}${this.suffix()}`;
|
|
822
|
+
}
|
|
823
|
+
catch {
|
|
824
|
+
return `${this.prefix()}${n}${this.suffix()}`;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Format a partially-typed value, keeping exactly the fraction digits the user has entered (so
|
|
829
|
+
* trailing zeros and a lone decimal separator survive) while still grouping the integer part and
|
|
830
|
+
* showing the currency symbol / affixes.
|
|
831
|
+
*/
|
|
832
|
+
formatLive(p) {
|
|
833
|
+
const locale = this.locale() || undefined;
|
|
834
|
+
const fracCount = p.fracDigits.length;
|
|
835
|
+
const num = this.toNumber(p) ?? 0;
|
|
836
|
+
if (fracCount > 0 || !p.hadDecimal) {
|
|
837
|
+
try {
|
|
838
|
+
const body = new Intl.NumberFormat(locale, {
|
|
839
|
+
...this.baseOptions(),
|
|
840
|
+
minimumFractionDigits: fracCount,
|
|
841
|
+
maximumFractionDigits: fracCount,
|
|
842
|
+
}).format(num);
|
|
843
|
+
return `${this.prefix()}${body}${this.suffix()}`;
|
|
844
|
+
}
|
|
845
|
+
catch {
|
|
846
|
+
return `${this.prefix()}${num}${this.suffix()}`;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
// A lone decimal separator was just typed ("1234,"): format the integer, then splice the separator
|
|
850
|
+
// in right after the integer digits — which, for a trailing-symbol currency, is NOT the string end.
|
|
851
|
+
const dec = this.separators().decimal;
|
|
852
|
+
let out = '';
|
|
853
|
+
let inserted = false;
|
|
854
|
+
try {
|
|
855
|
+
const parts = new Intl.NumberFormat(locale, {
|
|
856
|
+
...this.baseOptions(),
|
|
857
|
+
maximumFractionDigits: 0,
|
|
858
|
+
}).formatToParts(num);
|
|
859
|
+
parts.forEach((part, i) => {
|
|
860
|
+
out += part.value;
|
|
861
|
+
const here = part.type === 'integer' || part.type === 'group';
|
|
862
|
+
const next = parts[i + 1];
|
|
863
|
+
const nextHere = next && (next.type === 'integer' || next.type === 'group');
|
|
864
|
+
if (here && !nextHere && !inserted) {
|
|
865
|
+
out += dec;
|
|
866
|
+
inserted = true;
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
catch {
|
|
871
|
+
out = `${num}${dec}`;
|
|
872
|
+
inserted = true;
|
|
873
|
+
}
|
|
874
|
+
if (!inserted)
|
|
875
|
+
out += dec;
|
|
876
|
+
return `${this.prefix()}${out}${this.suffix()}`;
|
|
877
|
+
}
|
|
878
|
+
toNumber(p) {
|
|
879
|
+
const s = `${p.negative ? '-' : ''}${p.intDigits || '0'}${p.fracDigits ? '.' + p.fracDigits : ''}`;
|
|
880
|
+
const n = Number(s);
|
|
881
|
+
return Number.isFinite(n) ? n : null;
|
|
882
|
+
}
|
|
883
|
+
/** Pull the sign / integer / fraction fragments out of a formatted, partially-typed string. */
|
|
884
|
+
parseInput(raw) {
|
|
885
|
+
const { group, decimal } = this.separators();
|
|
886
|
+
let t = raw;
|
|
887
|
+
const prefix = this.prefix();
|
|
888
|
+
const suffix = this.suffix();
|
|
889
|
+
if (prefix && t.startsWith(prefix))
|
|
890
|
+
t = t.slice(prefix.length);
|
|
891
|
+
if (suffix && t.endsWith(suffix))
|
|
892
|
+
t = t.slice(0, t.length - suffix.length);
|
|
893
|
+
if (group)
|
|
894
|
+
t = t.split(group).join('');
|
|
895
|
+
if (decimal && decimal !== '.')
|
|
896
|
+
t = t.split(decimal).join('.');
|
|
897
|
+
t = t.replace(/[^0-9.\-]/g, '');
|
|
898
|
+
const negative = t.includes('-');
|
|
899
|
+
t = t.replace(/-/g, '');
|
|
900
|
+
const dot = t.indexOf('.');
|
|
901
|
+
const hadDecimal = dot !== -1 && this.maxFrac() > 0;
|
|
902
|
+
let intDigits = (hadDecimal ? t.slice(0, dot) : t.replace(/\./g, '')).replace(/\D/g, '');
|
|
903
|
+
let fracDigits = hadDecimal ? t.slice(dot + 1).replace(/\D/g, '') : '';
|
|
904
|
+
// Strip a leading run of zeros ("007" -> "7") but keep a lone "0".
|
|
905
|
+
intDigits = intDigits.replace(/^0+(?=\d)/, '');
|
|
906
|
+
fracDigits = fracDigits.slice(0, this.maxFrac());
|
|
907
|
+
return { negative, intDigits, fracDigits, hadDecimal };
|
|
908
|
+
}
|
|
909
|
+
/** Characters that "carry meaning" for caret placement (digits, minus, the decimal separator). */
|
|
910
|
+
isSignificant(ch) {
|
|
911
|
+
return (ch >= '0' && ch <= '9') || ch === '-' || ch === this.separators().decimal;
|
|
912
|
+
}
|
|
913
|
+
significantBefore(s, upto) {
|
|
914
|
+
let n = 0;
|
|
915
|
+
for (let i = 0; i < upto && i < s.length; i++)
|
|
916
|
+
if (this.isSignificant(s[i]))
|
|
917
|
+
n++;
|
|
918
|
+
return n;
|
|
919
|
+
}
|
|
920
|
+
/** The caret index in `formatted` that sits just after `target` significant characters. */
|
|
921
|
+
caretAfter(formatted, target) {
|
|
922
|
+
if (target <= 0) {
|
|
923
|
+
for (let i = 0; i < formatted.length; i++)
|
|
924
|
+
if (this.isSignificant(formatted[i]))
|
|
925
|
+
return i;
|
|
926
|
+
return formatted.length;
|
|
927
|
+
}
|
|
928
|
+
let n = 0;
|
|
929
|
+
for (let i = 0; i < formatted.length; i++) {
|
|
930
|
+
if (this.isSignificant(formatted[i]) && ++n === target)
|
|
931
|
+
return i + 1;
|
|
932
|
+
}
|
|
933
|
+
return formatted.length;
|
|
934
|
+
}
|
|
935
|
+
clamp(n) {
|
|
936
|
+
const min = this.min();
|
|
937
|
+
const max = this.max();
|
|
938
|
+
if (min !== null && n < min)
|
|
939
|
+
n = min;
|
|
940
|
+
if (max !== null && n > max)
|
|
941
|
+
n = max;
|
|
942
|
+
return n;
|
|
943
|
+
}
|
|
944
|
+
/** Trim binary-float drift (0.1 + 0.2) back to a clean decimal. */
|
|
945
|
+
tidy(n) {
|
|
946
|
+
return Math.round(n * 1e10) / 1e10;
|
|
947
|
+
}
|
|
948
|
+
// --- Interaction ----------------------------------------------------------
|
|
949
|
+
onInput(event) {
|
|
950
|
+
const el = event.target;
|
|
951
|
+
const raw = el.value;
|
|
952
|
+
const caret = el.selectionStart ?? raw.length;
|
|
953
|
+
const target = this.significantBefore(raw, caret);
|
|
954
|
+
const parsed = this.parseInput(raw);
|
|
955
|
+
if (parsed.intDigits === '' && parsed.fracDigits === '' && !parsed.hadDecimal) {
|
|
956
|
+
// Nothing numeric left — allow a lone "-" mid-entry, otherwise empty.
|
|
957
|
+
const text = parsed.negative ? '-' : '';
|
|
958
|
+
el.value = text;
|
|
959
|
+
el.setSelectionRange(text.length, text.length);
|
|
960
|
+
this.value.set(null);
|
|
961
|
+
this.onChange(null);
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
const num = this.toNumber(parsed);
|
|
965
|
+
const formatted = this.formatLive(parsed);
|
|
966
|
+
el.value = formatted;
|
|
967
|
+
const next = this.caretAfter(formatted, target);
|
|
968
|
+
el.setSelectionRange(next, next);
|
|
969
|
+
this.value.set(num);
|
|
970
|
+
this.onChange(num);
|
|
971
|
+
}
|
|
972
|
+
onKeydown(event) {
|
|
973
|
+
if (event.key === 'ArrowUp') {
|
|
974
|
+
event.preventDefault();
|
|
975
|
+
this.spin(1);
|
|
976
|
+
}
|
|
977
|
+
else if (event.key === 'ArrowDown') {
|
|
978
|
+
event.preventDefault();
|
|
979
|
+
this.spin(-1);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
onBlur() {
|
|
983
|
+
let v = this.value();
|
|
984
|
+
if (v === null && !this.allowEmpty())
|
|
985
|
+
v = this.min() ?? 0;
|
|
986
|
+
if (v !== null)
|
|
987
|
+
v = this.clamp(this.tidy(v));
|
|
988
|
+
// Re-render through the committed formatter (applies min-fraction padding + clamping).
|
|
989
|
+
this.value.set(v);
|
|
990
|
+
this.paintCommitted();
|
|
991
|
+
this.onChange(v);
|
|
992
|
+
this.onTouched();
|
|
993
|
+
}
|
|
994
|
+
spin(direction) {
|
|
995
|
+
if (this.isDisabled() || this.readonly())
|
|
996
|
+
return;
|
|
997
|
+
const base = this.value() ?? this.min() ?? 0;
|
|
998
|
+
const nextValue = this.clamp(this.tidy(base + direction * this.step()));
|
|
999
|
+
this.value.set(nextValue);
|
|
1000
|
+
this.onChange(nextValue);
|
|
1001
|
+
this.onTouched();
|
|
1002
|
+
// Paint the stepped value ourselves — the effect stands down while the field is focused.
|
|
1003
|
+
const el = this.fieldRef()?.nativeElement;
|
|
1004
|
+
if (el) {
|
|
1005
|
+
const text = this.format(nextValue);
|
|
1006
|
+
el.value = text;
|
|
1007
|
+
if (this.isActive(el))
|
|
1008
|
+
el.setSelectionRange(text.length, text.length);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
clear() {
|
|
1012
|
+
this.value.set(null);
|
|
1013
|
+
this.onChange(null);
|
|
1014
|
+
this.onTouched();
|
|
1015
|
+
const el = this.fieldRef()?.nativeElement;
|
|
1016
|
+
if (el)
|
|
1017
|
+
el.value = '';
|
|
1018
|
+
}
|
|
1019
|
+
// --- ControlValueAccessor -------------------------------------------------
|
|
1020
|
+
onChange = () => { };
|
|
1021
|
+
onTouched = () => { };
|
|
1022
|
+
writeValue(value) {
|
|
1023
|
+
this.value.set(typeof value === 'number' && Number.isFinite(value) ? value : null);
|
|
1024
|
+
}
|
|
1025
|
+
registerOnChange(fn) {
|
|
1026
|
+
this.onChange = fn;
|
|
1027
|
+
}
|
|
1028
|
+
registerOnTouched(fn) {
|
|
1029
|
+
this.onTouched = fn;
|
|
1030
|
+
}
|
|
1031
|
+
setDisabledState(isDisabled) {
|
|
1032
|
+
this.cvaDisabled.set(isDisabled);
|
|
1033
|
+
}
|
|
1034
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsInputNumber, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1035
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsInputNumber, isStandalone: true, selector: "sds-input-number", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null }, currencyDisplay: { classPropertyName: "currencyDisplay", publicName: "currencyDisplay", isSignal: true, isRequired: false, transformFunction: null }, useGrouping: { classPropertyName: "useGrouping", publicName: "useGrouping", isSignal: true, isRequired: false, transformFunction: null }, minFractionDigits: { classPropertyName: "minFractionDigits", publicName: "minFractionDigits", isSignal: true, isRequired: false, transformFunction: null }, maxFractionDigits: { classPropertyName: "maxFractionDigits", publicName: "maxFractionDigits", isSignal: true, isRequired: false, transformFunction: null }, prefix: { classPropertyName: "prefix", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, showButtons: { classPropertyName: "showButtons", publicName: "showButtons", isSignal: true, isRequired: false, transformFunction: null }, buttonLayout: { classPropertyName: "buttonLayout", publicName: "buttonLayout", isSignal: true, isRequired: false, transformFunction: null }, incrementIcon: { classPropertyName: "incrementIcon", publicName: "incrementIcon", isSignal: true, isRequired: false, transformFunction: null }, decrementIcon: { classPropertyName: "decrementIcon", publicName: "decrementIcon", isSignal: true, isRequired: false, transformFunction: null }, allowEmpty: { classPropertyName: "allowEmpty", publicName: "allowEmpty", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, clearLabel: { classPropertyName: "clearLabel", publicName: "clearLabel", isSignal: true, isRequired: false, transformFunction: null }, incrementLabel: { classPropertyName: "incrementLabel", publicName: "incrementLabel", isSignal: true, isRequired: false, transformFunction: null }, decrementLabel: { classPropertyName: "decrementLabel", publicName: "decrementLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, providers: [
|
|
1036
|
+
{
|
|
1037
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1038
|
+
useExisting: forwardRef(() => SdsInputNumber),
|
|
1039
|
+
multi: true,
|
|
1040
|
+
},
|
|
1041
|
+
], viewQueries: [{ propertyName: "fieldRef", first: true, predicate: ["field"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1042
|
+
@if (label()) {
|
|
1043
|
+
<label [for]="inputId" class="mb-1.5 block text-sm font-medium text-ink">{{ label() }}</label>
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
<div class="relative border transition-colors" [class]="wrapperClasses()">
|
|
1047
|
+
<!-- Horizontal: minus before the field. Vertical: plus above it. -->
|
|
1048
|
+
@if (showButtons() && buttonLayout() === 'horizontal') {
|
|
1049
|
+
<button
|
|
1050
|
+
type="button"
|
|
1051
|
+
tabindex="-1"
|
|
1052
|
+
[attr.aria-label]="decrementLabel()"
|
|
1053
|
+
[disabled]="!canDecrement()"
|
|
1054
|
+
class="border-r"
|
|
1055
|
+
[class]="edgeButtonClasses()"
|
|
1056
|
+
(mousedown)="$event.preventDefault()"
|
|
1057
|
+
(click)="spin(-1)"
|
|
1058
|
+
>
|
|
1059
|
+
<sds-icon [name]="decrementIconName()" [size]="16" />
|
|
1060
|
+
</button>
|
|
1061
|
+
}
|
|
1062
|
+
@if (showButtons() && buttonLayout() === 'vertical') {
|
|
1063
|
+
<button
|
|
1064
|
+
type="button"
|
|
1065
|
+
tabindex="-1"
|
|
1066
|
+
[attr.aria-label]="incrementLabel()"
|
|
1067
|
+
[disabled]="!canIncrement()"
|
|
1068
|
+
class="w-full border-b"
|
|
1069
|
+
[class]="edgeButtonClasses()"
|
|
1070
|
+
(mousedown)="$event.preventDefault()"
|
|
1071
|
+
(click)="spin(1)"
|
|
1072
|
+
>
|
|
1073
|
+
<sds-icon [name]="incrementIconName()" [size]="16" />
|
|
1074
|
+
</button>
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
<div class="relative flex min-w-0 flex-1 items-center" [class]="fieldCellClasses()">
|
|
1078
|
+
<input
|
|
1079
|
+
#field
|
|
1080
|
+
[id]="inputId"
|
|
1081
|
+
type="text"
|
|
1082
|
+
inputmode="decimal"
|
|
1083
|
+
role="spinbutton"
|
|
1084
|
+
[placeholder]="placeholder()"
|
|
1085
|
+
[disabled]="isDisabled()"
|
|
1086
|
+
[readOnly]="readonly()"
|
|
1087
|
+
[attr.aria-label]="!label() && ariaLabel() ? ariaLabel() : null"
|
|
1088
|
+
[attr.aria-invalid]="error() ? 'true' : null"
|
|
1089
|
+
[attr.aria-describedby]="describedBy()"
|
|
1090
|
+
[attr.aria-valuenow]="value()"
|
|
1091
|
+
[attr.aria-valuemin]="min()"
|
|
1092
|
+
[attr.aria-valuemax]="max()"
|
|
1093
|
+
class="w-full bg-transparent outline-none placeholder:text-current/50 disabled:cursor-not-allowed"
|
|
1094
|
+
[class]="inputClasses()"
|
|
1095
|
+
(input)="onInput($event)"
|
|
1096
|
+
(keydown)="onKeydown($event)"
|
|
1097
|
+
(blur)="onBlur()"
|
|
1098
|
+
/>
|
|
1099
|
+
|
|
1100
|
+
@if (clearVisible()) {
|
|
1101
|
+
<button
|
|
1102
|
+
type="button"
|
|
1103
|
+
tabindex="-1"
|
|
1104
|
+
[attr.aria-label]="clearLabel()"
|
|
1105
|
+
class="absolute right-1.5 inline-flex h-6 w-6 items-center justify-center rounded-full opacity-60 hover:bg-black/5 hover:opacity-100"
|
|
1106
|
+
(mousedown)="$event.preventDefault()"
|
|
1107
|
+
(click)="clear()"
|
|
1108
|
+
>
|
|
1109
|
+
<sds-icon name="x" [size]="15" />
|
|
1110
|
+
</button>
|
|
1111
|
+
}
|
|
1112
|
+
</div>
|
|
1113
|
+
|
|
1114
|
+
<!-- Horizontal: plus after the field. Vertical: minus below it. -->
|
|
1115
|
+
@if (showButtons() && buttonLayout() === 'horizontal') {
|
|
1116
|
+
<button
|
|
1117
|
+
type="button"
|
|
1118
|
+
tabindex="-1"
|
|
1119
|
+
[attr.aria-label]="incrementLabel()"
|
|
1120
|
+
[disabled]="!canIncrement()"
|
|
1121
|
+
class="border-l"
|
|
1122
|
+
[class]="edgeButtonClasses()"
|
|
1123
|
+
(mousedown)="$event.preventDefault()"
|
|
1124
|
+
(click)="spin(1)"
|
|
1125
|
+
>
|
|
1126
|
+
<sds-icon [name]="incrementIconName()" [size]="16" />
|
|
1127
|
+
</button>
|
|
1128
|
+
}
|
|
1129
|
+
@if (showButtons() && buttonLayout() === 'vertical') {
|
|
1130
|
+
<button
|
|
1131
|
+
type="button"
|
|
1132
|
+
tabindex="-1"
|
|
1133
|
+
[attr.aria-label]="decrementLabel()"
|
|
1134
|
+
[disabled]="!canDecrement()"
|
|
1135
|
+
class="w-full border-t"
|
|
1136
|
+
[class]="edgeButtonClasses()"
|
|
1137
|
+
(mousedown)="$event.preventDefault()"
|
|
1138
|
+
(click)="spin(-1)"
|
|
1139
|
+
>
|
|
1140
|
+
<sds-icon [name]="decrementIconName()" [size]="16" />
|
|
1141
|
+
</button>
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
<!-- Stacked: a chevron column at the trailing edge. -->
|
|
1145
|
+
@if (showButtons() && buttonLayout() === 'stacked') {
|
|
1146
|
+
<div class="flex w-9 shrink-0 flex-col divide-y divide-border border-l border-border">
|
|
1147
|
+
<button
|
|
1148
|
+
type="button"
|
|
1149
|
+
tabindex="-1"
|
|
1150
|
+
[attr.aria-label]="incrementLabel()"
|
|
1151
|
+
[disabled]="!canIncrement()"
|
|
1152
|
+
class="min-h-0 flex-1"
|
|
1153
|
+
[class]="stackedButtonClasses()"
|
|
1154
|
+
(mousedown)="$event.preventDefault()"
|
|
1155
|
+
(click)="spin(1)"
|
|
1156
|
+
>
|
|
1157
|
+
<sds-icon [name]="incrementIconName()" [size]="14" />
|
|
1158
|
+
</button>
|
|
1159
|
+
<button
|
|
1160
|
+
type="button"
|
|
1161
|
+
tabindex="-1"
|
|
1162
|
+
[attr.aria-label]="decrementLabel()"
|
|
1163
|
+
[disabled]="!canDecrement()"
|
|
1164
|
+
class="min-h-0 flex-1"
|
|
1165
|
+
[class]="stackedButtonClasses()"
|
|
1166
|
+
(mousedown)="$event.preventDefault()"
|
|
1167
|
+
(click)="spin(-1)"
|
|
1168
|
+
>
|
|
1169
|
+
<sds-icon [name]="decrementIconName()" [size]="14" />
|
|
1170
|
+
</button>
|
|
1171
|
+
</div>
|
|
1172
|
+
}
|
|
1173
|
+
</div>
|
|
1174
|
+
|
|
1175
|
+
@if (error()) {
|
|
1176
|
+
<p [id]="inputId + '-err'" class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
|
|
1177
|
+
} @else if (hint()) {
|
|
1178
|
+
<p [id]="inputId + '-hint'" class="mt-1.5 text-xs text-ink-faint">{{ hint() }}</p>
|
|
1179
|
+
}
|
|
1180
|
+
`, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1181
|
+
}
|
|
1182
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsInputNumber, decorators: [{
|
|
1183
|
+
type: Component,
|
|
1184
|
+
args: [{
|
|
1185
|
+
selector: 'sds-input-number',
|
|
1186
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1187
|
+
imports: [SdsIcon],
|
|
1188
|
+
providers: [
|
|
1189
|
+
{
|
|
1190
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1191
|
+
useExisting: forwardRef(() => SdsInputNumber),
|
|
1192
|
+
multi: true,
|
|
1193
|
+
},
|
|
1194
|
+
],
|
|
1195
|
+
host: { class: 'block' },
|
|
1196
|
+
template: `
|
|
1197
|
+
@if (label()) {
|
|
1198
|
+
<label [for]="inputId" class="mb-1.5 block text-sm font-medium text-ink">{{ label() }}</label>
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
<div class="relative border transition-colors" [class]="wrapperClasses()">
|
|
1202
|
+
<!-- Horizontal: minus before the field. Vertical: plus above it. -->
|
|
1203
|
+
@if (showButtons() && buttonLayout() === 'horizontal') {
|
|
1204
|
+
<button
|
|
1205
|
+
type="button"
|
|
1206
|
+
tabindex="-1"
|
|
1207
|
+
[attr.aria-label]="decrementLabel()"
|
|
1208
|
+
[disabled]="!canDecrement()"
|
|
1209
|
+
class="border-r"
|
|
1210
|
+
[class]="edgeButtonClasses()"
|
|
1211
|
+
(mousedown)="$event.preventDefault()"
|
|
1212
|
+
(click)="spin(-1)"
|
|
1213
|
+
>
|
|
1214
|
+
<sds-icon [name]="decrementIconName()" [size]="16" />
|
|
1215
|
+
</button>
|
|
1216
|
+
}
|
|
1217
|
+
@if (showButtons() && buttonLayout() === 'vertical') {
|
|
1218
|
+
<button
|
|
1219
|
+
type="button"
|
|
1220
|
+
tabindex="-1"
|
|
1221
|
+
[attr.aria-label]="incrementLabel()"
|
|
1222
|
+
[disabled]="!canIncrement()"
|
|
1223
|
+
class="w-full border-b"
|
|
1224
|
+
[class]="edgeButtonClasses()"
|
|
1225
|
+
(mousedown)="$event.preventDefault()"
|
|
1226
|
+
(click)="spin(1)"
|
|
1227
|
+
>
|
|
1228
|
+
<sds-icon [name]="incrementIconName()" [size]="16" />
|
|
1229
|
+
</button>
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
<div class="relative flex min-w-0 flex-1 items-center" [class]="fieldCellClasses()">
|
|
1233
|
+
<input
|
|
1234
|
+
#field
|
|
1235
|
+
[id]="inputId"
|
|
1236
|
+
type="text"
|
|
1237
|
+
inputmode="decimal"
|
|
1238
|
+
role="spinbutton"
|
|
1239
|
+
[placeholder]="placeholder()"
|
|
1240
|
+
[disabled]="isDisabled()"
|
|
1241
|
+
[readOnly]="readonly()"
|
|
1242
|
+
[attr.aria-label]="!label() && ariaLabel() ? ariaLabel() : null"
|
|
1243
|
+
[attr.aria-invalid]="error() ? 'true' : null"
|
|
1244
|
+
[attr.aria-describedby]="describedBy()"
|
|
1245
|
+
[attr.aria-valuenow]="value()"
|
|
1246
|
+
[attr.aria-valuemin]="min()"
|
|
1247
|
+
[attr.aria-valuemax]="max()"
|
|
1248
|
+
class="w-full bg-transparent outline-none placeholder:text-current/50 disabled:cursor-not-allowed"
|
|
1249
|
+
[class]="inputClasses()"
|
|
1250
|
+
(input)="onInput($event)"
|
|
1251
|
+
(keydown)="onKeydown($event)"
|
|
1252
|
+
(blur)="onBlur()"
|
|
1253
|
+
/>
|
|
1254
|
+
|
|
1255
|
+
@if (clearVisible()) {
|
|
1256
|
+
<button
|
|
1257
|
+
type="button"
|
|
1258
|
+
tabindex="-1"
|
|
1259
|
+
[attr.aria-label]="clearLabel()"
|
|
1260
|
+
class="absolute right-1.5 inline-flex h-6 w-6 items-center justify-center rounded-full opacity-60 hover:bg-black/5 hover:opacity-100"
|
|
1261
|
+
(mousedown)="$event.preventDefault()"
|
|
1262
|
+
(click)="clear()"
|
|
1263
|
+
>
|
|
1264
|
+
<sds-icon name="x" [size]="15" />
|
|
1265
|
+
</button>
|
|
1266
|
+
}
|
|
1267
|
+
</div>
|
|
1268
|
+
|
|
1269
|
+
<!-- Horizontal: plus after the field. Vertical: minus below it. -->
|
|
1270
|
+
@if (showButtons() && buttonLayout() === 'horizontal') {
|
|
1271
|
+
<button
|
|
1272
|
+
type="button"
|
|
1273
|
+
tabindex="-1"
|
|
1274
|
+
[attr.aria-label]="incrementLabel()"
|
|
1275
|
+
[disabled]="!canIncrement()"
|
|
1276
|
+
class="border-l"
|
|
1277
|
+
[class]="edgeButtonClasses()"
|
|
1278
|
+
(mousedown)="$event.preventDefault()"
|
|
1279
|
+
(click)="spin(1)"
|
|
1280
|
+
>
|
|
1281
|
+
<sds-icon [name]="incrementIconName()" [size]="16" />
|
|
1282
|
+
</button>
|
|
1283
|
+
}
|
|
1284
|
+
@if (showButtons() && buttonLayout() === 'vertical') {
|
|
1285
|
+
<button
|
|
1286
|
+
type="button"
|
|
1287
|
+
tabindex="-1"
|
|
1288
|
+
[attr.aria-label]="decrementLabel()"
|
|
1289
|
+
[disabled]="!canDecrement()"
|
|
1290
|
+
class="w-full border-t"
|
|
1291
|
+
[class]="edgeButtonClasses()"
|
|
1292
|
+
(mousedown)="$event.preventDefault()"
|
|
1293
|
+
(click)="spin(-1)"
|
|
1294
|
+
>
|
|
1295
|
+
<sds-icon [name]="decrementIconName()" [size]="16" />
|
|
1296
|
+
</button>
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
<!-- Stacked: a chevron column at the trailing edge. -->
|
|
1300
|
+
@if (showButtons() && buttonLayout() === 'stacked') {
|
|
1301
|
+
<div class="flex w-9 shrink-0 flex-col divide-y divide-border border-l border-border">
|
|
1302
|
+
<button
|
|
1303
|
+
type="button"
|
|
1304
|
+
tabindex="-1"
|
|
1305
|
+
[attr.aria-label]="incrementLabel()"
|
|
1306
|
+
[disabled]="!canIncrement()"
|
|
1307
|
+
class="min-h-0 flex-1"
|
|
1308
|
+
[class]="stackedButtonClasses()"
|
|
1309
|
+
(mousedown)="$event.preventDefault()"
|
|
1310
|
+
(click)="spin(1)"
|
|
1311
|
+
>
|
|
1312
|
+
<sds-icon [name]="incrementIconName()" [size]="14" />
|
|
1313
|
+
</button>
|
|
1314
|
+
<button
|
|
1315
|
+
type="button"
|
|
1316
|
+
tabindex="-1"
|
|
1317
|
+
[attr.aria-label]="decrementLabel()"
|
|
1318
|
+
[disabled]="!canDecrement()"
|
|
1319
|
+
class="min-h-0 flex-1"
|
|
1320
|
+
[class]="stackedButtonClasses()"
|
|
1321
|
+
(mousedown)="$event.preventDefault()"
|
|
1322
|
+
(click)="spin(-1)"
|
|
1323
|
+
>
|
|
1324
|
+
<sds-icon [name]="decrementIconName()" [size]="14" />
|
|
1325
|
+
</button>
|
|
1326
|
+
</div>
|
|
1327
|
+
}
|
|
1328
|
+
</div>
|
|
1329
|
+
|
|
1330
|
+
@if (error()) {
|
|
1331
|
+
<p [id]="inputId + '-err'" class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
|
|
1332
|
+
} @else if (hint()) {
|
|
1333
|
+
<p [id]="inputId + '-hint'" class="mt-1.5 text-xs text-ink-faint">{{ hint() }}</p>
|
|
1334
|
+
}
|
|
1335
|
+
`,
|
|
1336
|
+
}]
|
|
1337
|
+
}], ctorParameters: () => [], propDecorators: { fieldRef: [{ type: i0.ViewChild, args: ['field', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], currencyDisplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "currencyDisplay", required: false }] }], useGrouping: [{ type: i0.Input, args: [{ isSignal: true, alias: "useGrouping", required: false }] }], minFractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "minFractionDigits", required: false }] }], maxFractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFractionDigits", required: false }] }], prefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefix", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], showButtons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showButtons", required: false }] }], buttonLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonLayout", required: false }] }], incrementIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "incrementIcon", required: false }] }], decrementIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "decrementIcon", required: false }] }], allowEmpty: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowEmpty", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], incrementLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "incrementLabel", required: false }] }], decrementLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "decrementLabel", required: false }] }] } });
|
|
1338
|
+
|
|
604
1339
|
let nextId$b = 0;
|
|
605
1340
|
const SIZES$9 = {
|
|
606
1341
|
sm: 'min-h-9 text-sm',
|
|
@@ -8640,5 +9375,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
8640
9375
|
* Generated bundle index. Do not edit.
|
|
8641
9376
|
*/
|
|
8642
9377
|
|
|
8643
|
-
export { SDS_ICONS, SDS_PHONE_COUNTRIES, SdsAccordion, SdsAccordionContent, SdsAccordionHeader, SdsAccordionPanel, SdsAccordionStore, SdsAvatar, SdsBadge, SdsButton, SdsCalendar, SdsCard, SdsCheckbox, SdsDatePicker, SdsIcon, SdsInput, SdsLocalizedInput, SdsMultiSelect, SdsNavGroup, SdsNavItem, SdsOtpInput, SdsPaginator, SdsPhoneInput, SdsSelect, SdsShell, SdsSidebar, SdsTab, SdsTabList, SdsTabPanel, SdsTabPanels, SdsTable, SdsTableCell, SdsTableRowExpansion, SdsTabs, SdsTabsStore, SdsTextarea, SdsToastHost, SdsToastService, SdsToggleSwitch, SdsTopbar, SidebarStore, addDays, addMonths, addMonthsKeepDay, formatDdMmYyyy, formatHhMm, isOutOfRange, isSameDay, isoDay, pad2, sdsFormatNational, sdsMatchDialCode, sdsNormalizeSignificant, startOfDay, startOfMonth };
|
|
9378
|
+
export { SDS_ICONS, SDS_PHONE_COUNTRIES, SdsAccordion, SdsAccordionContent, SdsAccordionHeader, SdsAccordionPanel, SdsAccordionStore, SdsAvatar, SdsBadge, SdsButton, SdsCalendar, SdsCard, SdsCheckbox, SdsDatePicker, SdsIcon, SdsInput, SdsInputNumber, SdsLocalizedInput, SdsMultiSelect, SdsNavGroup, SdsNavItem, SdsOtpInput, SdsPaginator, SdsPhoneInput, SdsSelect, SdsShell, SdsSidebar, SdsTab, SdsTabList, SdsTabPanel, SdsTabPanels, SdsTable, SdsTableCell, SdsTableRowExpansion, SdsTabs, SdsTabsStore, SdsTextarea, SdsToastHost, SdsToastService, SdsToggleSwitch, SdsTopbar, SidebarStore, addDays, addMonths, addMonthsKeepDay, formatDdMmYyyy, formatHhMm, isOutOfRange, isSameDay, isoDay, pad2, sdsFormatNational, sdsMatchDialCode, sdsNormalizeSignificant, startOfDay, startOfMonth };
|
|
8644
9379
|
//# sourceMappingURL=besaitech-ng-design-system.mjs.map
|