@besaitech/ng-design-system 0.0.41 → 0.0.44
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/besaitech-ng-design-system.mjs +1054 -36
- package/fesm2022/besaitech-ng-design-system.mjs.map +1 -1
- package/index.d.ts +311 -14
- package/package.json +1 -1
|
@@ -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, TemplateRef, Directive, contentChild, Injectable, contentChildren } 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',
|
|
@@ -3395,6 +4130,83 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
3395
4130
|
}], 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 }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTime", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], timeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeLabel", required: false }] }], hoursLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoursLabel", required: false }] }], minutesLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "minutesLabel", required: false }] }], doneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "doneLabel", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], openCalendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "openCalendarLabel", required: false }] }], closeCalendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeCalendarLabel", required: false }] }], panelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelLabel", required: false }] }], calendarLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "calendarLabel", required: false }] }], previousLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], todayActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "todayActionLabel", required: false }] }], clearActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearActionLabel", required: false }] }], calendar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SdsCalendar), { isSignal: true }] }], trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }] } });
|
|
3396
4131
|
let nextId$6 = 0;
|
|
3397
4132
|
|
|
4133
|
+
/**
|
|
4134
|
+
* Supplies a custom template for each option row (and the selected value shown
|
|
4135
|
+
* in the trigger) of an `<sds-select>` / `<sds-multi-select>`. Use as a
|
|
4136
|
+
* structural template projected into the select:
|
|
4137
|
+
*
|
|
4138
|
+
* @example
|
|
4139
|
+
* <sds-select [options]="categories" optionLabel="name" optionValue="id" [(ngModel)]="catId">
|
|
4140
|
+
* <ng-template sdsSelectOption let-option let-selected="selected">
|
|
4141
|
+
* <span class="dot" [style.background]="option.color"></span>
|
|
4142
|
+
* <span class="truncate">{{ option.name }}</span>
|
|
4143
|
+
* </ng-template>
|
|
4144
|
+
* </sds-select>
|
|
4145
|
+
*/
|
|
4146
|
+
class SdsSelectOption {
|
|
4147
|
+
/** The wrapped template, rendered by the host select via ngTemplateOutlet. */
|
|
4148
|
+
template = inject(TemplateRef);
|
|
4149
|
+
/** Lets the compiler type-check `let-*` bindings against SdsSelectOptionContext. */
|
|
4150
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
4151
|
+
return true;
|
|
4152
|
+
}
|
|
4153
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsSelectOption, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4154
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: SdsSelectOption, isStandalone: true, selector: "ng-template[sdsSelectOption]", ngImport: i0 });
|
|
4155
|
+
}
|
|
4156
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsSelectOption, decorators: [{
|
|
4157
|
+
type: Directive,
|
|
4158
|
+
args: [{
|
|
4159
|
+
selector: 'ng-template[sdsSelectOption]',
|
|
4160
|
+
standalone: true,
|
|
4161
|
+
}]
|
|
4162
|
+
}] });
|
|
4163
|
+
/**
|
|
4164
|
+
* Supplies a custom template shown in place of the default "no results" message
|
|
4165
|
+
* when a select has no (matching) options.
|
|
4166
|
+
*
|
|
4167
|
+
* @example
|
|
4168
|
+
* <ng-template sdsSelectEmpty let-q>No category matches "{{ q }}".</ng-template>
|
|
4169
|
+
*/
|
|
4170
|
+
class SdsSelectEmpty {
|
|
4171
|
+
template = inject(TemplateRef);
|
|
4172
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
4173
|
+
return true;
|
|
4174
|
+
}
|
|
4175
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsSelectEmpty, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4176
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: SdsSelectEmpty, isStandalone: true, selector: "ng-template[sdsSelectEmpty]", ngImport: i0 });
|
|
4177
|
+
}
|
|
4178
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsSelectEmpty, decorators: [{
|
|
4179
|
+
type: Directive,
|
|
4180
|
+
args: [{
|
|
4181
|
+
selector: 'ng-template[sdsSelectEmpty]',
|
|
4182
|
+
standalone: true,
|
|
4183
|
+
}]
|
|
4184
|
+
}] });
|
|
4185
|
+
/**
|
|
4186
|
+
* Supplies a custom sticky footer rendered below the option list — e.g. a
|
|
4187
|
+
* "+ Create new" action. The `close` context callback dismisses the panel.
|
|
4188
|
+
*
|
|
4189
|
+
* @example
|
|
4190
|
+
* <ng-template sdsSelectFooter let-close="close">
|
|
4191
|
+
* <button (click)="createCategory(); close()">+ New category</button>
|
|
4192
|
+
* </ng-template>
|
|
4193
|
+
*/
|
|
4194
|
+
class SdsSelectFooter {
|
|
4195
|
+
template = inject(TemplateRef);
|
|
4196
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
4197
|
+
return true;
|
|
4198
|
+
}
|
|
4199
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsSelectFooter, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4200
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: SdsSelectFooter, isStandalone: true, selector: "ng-template[sdsSelectFooter]", ngImport: i0 });
|
|
4201
|
+
}
|
|
4202
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsSelectFooter, decorators: [{
|
|
4203
|
+
type: Directive,
|
|
4204
|
+
args: [{
|
|
4205
|
+
selector: 'ng-template[sdsSelectFooter]',
|
|
4206
|
+
standalone: true,
|
|
4207
|
+
}]
|
|
4208
|
+
}] });
|
|
4209
|
+
|
|
3398
4210
|
const SIZES$4 = {
|
|
3399
4211
|
sm: 'h-9 text-sm',
|
|
3400
4212
|
md: 'h-10 text-sm',
|
|
@@ -3411,9 +4223,25 @@ let nextId$5 = 0;
|
|
|
3411
4223
|
* key; otherwise selection matching falls back to reference equality (a written
|
|
3412
4224
|
* copy of an option will not match).
|
|
3413
4225
|
*
|
|
4226
|
+
* Options render as plain text by default. To render richer options (an icon,
|
|
4227
|
+
* an avatar, a two-line row…) project template directives — the same content
|
|
4228
|
+
* also renders in the trigger for the selected value:
|
|
4229
|
+
* - `<ng-template sdsSelectOption let-option>` — one option row (see {@link SdsSelectOption});
|
|
4230
|
+
* context: the raw `option` (implicit), plus `label`, `value`, `selected`, `disabled`, `index`.
|
|
4231
|
+
* - `<ng-template sdsSelectEmpty let-query>` — replaces the "no results" message (see {@link SdsSelectEmpty}).
|
|
4232
|
+
* - `<ng-template sdsSelectFooter let-close="close">` — a sticky footer action, e.g. "+ New…"
|
|
4233
|
+
* (see {@link SdsSelectFooter}); `close()` dismisses the panel.
|
|
4234
|
+
*
|
|
3414
4235
|
* @example
|
|
3415
4236
|
* <sds-select [options]="services" optionLabel="name" optionValue="id"
|
|
3416
4237
|
* placeholder="Choisir un service" [(ngModel)]="serviceId" filter clearable />
|
|
4238
|
+
*
|
|
4239
|
+
* @example
|
|
4240
|
+
* <sds-select [options]="categories" optionLabel="name" optionValue="id" [(ngModel)]="categoryId">
|
|
4241
|
+
* <ng-template sdsSelectOption let-option>
|
|
4242
|
+
* <span class="dot" [style.background]="option.color"></span> {{ option.name }}
|
|
4243
|
+
* </ng-template>
|
|
4244
|
+
* </sds-select>
|
|
3417
4245
|
*/
|
|
3418
4246
|
class SdsSelect {
|
|
3419
4247
|
host = inject(ElementRef);
|
|
@@ -3438,6 +4266,12 @@ class SdsSelect {
|
|
|
3438
4266
|
filter = input(false, ...(ngDevMode ? [{ debugName: "filter", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
3439
4267
|
filterPlaceholder = input('Rechercher…', ...(ngDevMode ? [{ debugName: "filterPlaceholder" }] : []));
|
|
3440
4268
|
emptyMessage = input('Aucun résultat', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : []));
|
|
4269
|
+
/** Optional projected `<ng-template sdsSelectOption>` — custom option + selected-value rendering. */
|
|
4270
|
+
optionTpl = contentChild(SdsSelectOption, ...(ngDevMode ? [{ debugName: "optionTpl" }] : []));
|
|
4271
|
+
/** Optional projected `<ng-template sdsSelectEmpty>` — custom no-results content. */
|
|
4272
|
+
emptyTpl = contentChild(SdsSelectEmpty, ...(ngDevMode ? [{ debugName: "emptyTpl" }] : []));
|
|
4273
|
+
/** Optional projected `<ng-template sdsSelectFooter>` — a sticky footer action. */
|
|
4274
|
+
footerTpl = contentChild(SdsSelectFooter, ...(ngDevMode ? [{ debugName: "footerTpl" }] : []));
|
|
3441
4275
|
value = signal(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
3442
4276
|
open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
|
|
3443
4277
|
filterText = signal('', ...(ngDevMode ? [{ debugName: "filterText" }] : []));
|
|
@@ -3494,9 +4328,30 @@ class SdsSelect {
|
|
|
3494
4328
|
const label = labelKey && labelKey in o ? String(o[labelKey]) : String(o['label'] ?? raw);
|
|
3495
4329
|
const value = this.optionValue() ? o[this.optionValue()] : raw;
|
|
3496
4330
|
const disabled = this.optionDisabled() ? !!o[this.optionDisabled()] : !!o['disabled'];
|
|
3497
|
-
return { label, value, disabled };
|
|
4331
|
+
return { label, value, disabled, raw };
|
|
3498
4332
|
}
|
|
3499
|
-
return { label: String(raw), value: raw, disabled: false };
|
|
4333
|
+
return { label: String(raw), value: raw, disabled: false, raw };
|
|
4334
|
+
}
|
|
4335
|
+
/** Context for a projected option template (an option row). */
|
|
4336
|
+
optionContext(opt, index) {
|
|
4337
|
+
return {
|
|
4338
|
+
$implicit: opt.raw,
|
|
4339
|
+
option: opt.raw,
|
|
4340
|
+
label: opt.label,
|
|
4341
|
+
value: opt.value,
|
|
4342
|
+
selected: this.isSelected(opt.value),
|
|
4343
|
+
disabled: opt.disabled,
|
|
4344
|
+
index,
|
|
4345
|
+
};
|
|
4346
|
+
}
|
|
4347
|
+
/** Context for the option template reused in the trigger to render the selected value. */
|
|
4348
|
+
triggerContext = computed(() => {
|
|
4349
|
+
const sel = this.selectedOption();
|
|
4350
|
+
return sel ? this.optionContext(sel, -1) : null;
|
|
4351
|
+
}, ...(ngDevMode ? [{ debugName: "triggerContext" }] : []));
|
|
4352
|
+
/** Context for a projected footer template. */
|
|
4353
|
+
footerContext() {
|
|
4354
|
+
return { $implicit: this.filterText(), filterText: this.filterText(), close: () => this.close(true) };
|
|
3500
4355
|
}
|
|
3501
4356
|
equals(a, b) {
|
|
3502
4357
|
const k = this.dataKey();
|
|
@@ -3768,7 +4623,7 @@ class SdsSelect {
|
|
|
3768
4623
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3769
4624
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsSelect, isStandalone: true, selector: "sds-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, optionDisabled: { classPropertyName: "optionDisabled", publicName: "optionDisabled", isSignal: true, isRequired: false, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", 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 }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, filterPlaceholder: { classPropertyName: "filterPlaceholder", publicName: "filterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:click": "onDocumentClick($event)" }, classAttribute: "block" }, providers: [
|
|
3770
4625
|
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsSelect), multi: true },
|
|
3771
|
-
], viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
4626
|
+
], queries: [{ propertyName: "optionTpl", first: true, predicate: SdsSelectOption, descendants: true, isSignal: true }, { propertyName: "emptyTpl", first: true, predicate: SdsSelectEmpty, descendants: true, isSignal: true }, { propertyName: "footerTpl", first: true, predicate: SdsSelectFooter, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
3772
4627
|
@if (label()) {
|
|
3773
4628
|
<label [for]="selectId" class="mb-1.5 block text-sm font-medium text-ink">
|
|
3774
4629
|
{{ label() }}
|
|
@@ -3791,12 +4646,21 @@ class SdsSelect {
|
|
|
3791
4646
|
(click)="toggle()"
|
|
3792
4647
|
(keydown)="onTriggerKeydown($event)"
|
|
3793
4648
|
>
|
|
3794
|
-
|
|
3795
|
-
class="flex-1 truncate pl-3.5"
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
4649
|
+
@if (optionTpl() && selectedOption()) {
|
|
4650
|
+
<span class="flex flex-1 items-center gap-2 truncate pl-3.5 text-ink">
|
|
4651
|
+
<ng-container
|
|
4652
|
+
[ngTemplateOutlet]="optionTpl()!.template"
|
|
4653
|
+
[ngTemplateOutletContext]="triggerContext()!"
|
|
4654
|
+
/>
|
|
4655
|
+
</span>
|
|
4656
|
+
} @else {
|
|
4657
|
+
<span
|
|
4658
|
+
class="flex-1 truncate pl-3.5"
|
|
4659
|
+
[class]="selectedOption() ? 'text-ink' : 'text-ink-faint'"
|
|
4660
|
+
>
|
|
4661
|
+
{{ selectedOption() ? selectedOption()!.label : placeholder() }}
|
|
4662
|
+
</span>
|
|
4663
|
+
}
|
|
3800
4664
|
|
|
3801
4665
|
@if (showClear()) {
|
|
3802
4666
|
<button
|
|
@@ -3854,15 +4718,42 @@ class SdsSelect {
|
|
|
3854
4718
|
(click)="opt.disabled ? null : selectOption(opt)"
|
|
3855
4719
|
(mouseenter)="opt.disabled ? null : highlightedIndex.set(i)"
|
|
3856
4720
|
>
|
|
3857
|
-
|
|
4721
|
+
@if (optionTpl()) {
|
|
4722
|
+
<span class="flex min-w-0 flex-1 items-center gap-2">
|
|
4723
|
+
<ng-container
|
|
4724
|
+
[ngTemplateOutlet]="optionTpl()!.template"
|
|
4725
|
+
[ngTemplateOutletContext]="optionContext(opt, i)"
|
|
4726
|
+
/>
|
|
4727
|
+
</span>
|
|
4728
|
+
} @else {
|
|
4729
|
+
<span class="truncate">{{ opt.label }}</span>
|
|
4730
|
+
}
|
|
3858
4731
|
@if (isSelected(opt.value)) {
|
|
3859
4732
|
<sds-icon name="check" [size]="16" class="shrink-0 text-brand-600" />
|
|
3860
4733
|
}
|
|
3861
4734
|
</li>
|
|
3862
4735
|
} @empty {
|
|
3863
|
-
|
|
4736
|
+
@if (emptyTpl()) {
|
|
4737
|
+
<li class="px-3 py-2 text-sm text-ink-faint">
|
|
4738
|
+
<ng-container
|
|
4739
|
+
[ngTemplateOutlet]="emptyTpl()!.template"
|
|
4740
|
+
[ngTemplateOutletContext]="{ $implicit: filterText(), filterText: filterText() }"
|
|
4741
|
+
/>
|
|
4742
|
+
</li>
|
|
4743
|
+
} @else {
|
|
4744
|
+
<li class="px-3 py-2 text-sm text-ink-faint">{{ emptyMessage() }}</li>
|
|
4745
|
+
}
|
|
3864
4746
|
}
|
|
3865
4747
|
</ul>
|
|
4748
|
+
|
|
4749
|
+
@if (footerTpl()) {
|
|
4750
|
+
<div class="border-t border-border">
|
|
4751
|
+
<ng-container
|
|
4752
|
+
[ngTemplateOutlet]="footerTpl()!.template"
|
|
4753
|
+
[ngTemplateOutletContext]="footerContext()"
|
|
4754
|
+
/>
|
|
4755
|
+
</div>
|
|
4756
|
+
}
|
|
3866
4757
|
</div>
|
|
3867
4758
|
}
|
|
3868
4759
|
</div>
|
|
@@ -3873,14 +4764,14 @@ class SdsSelect {
|
|
|
3873
4764
|
@if (error()) {
|
|
3874
4765
|
<p class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
|
|
3875
4766
|
}
|
|
3876
|
-
`, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4767
|
+
`, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3877
4768
|
}
|
|
3878
4769
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsSelect, decorators: [{
|
|
3879
4770
|
type: Component,
|
|
3880
4771
|
args: [{
|
|
3881
4772
|
selector: 'sds-select',
|
|
3882
4773
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3883
|
-
imports: [SdsIcon],
|
|
4774
|
+
imports: [SdsIcon, NgTemplateOutlet],
|
|
3884
4775
|
providers: [
|
|
3885
4776
|
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsSelect), multi: true },
|
|
3886
4777
|
],
|
|
@@ -3911,12 +4802,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
3911
4802
|
(click)="toggle()"
|
|
3912
4803
|
(keydown)="onTriggerKeydown($event)"
|
|
3913
4804
|
>
|
|
3914
|
-
|
|
3915
|
-
class="flex-1 truncate pl-3.5"
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
4805
|
+
@if (optionTpl() && selectedOption()) {
|
|
4806
|
+
<span class="flex flex-1 items-center gap-2 truncate pl-3.5 text-ink">
|
|
4807
|
+
<ng-container
|
|
4808
|
+
[ngTemplateOutlet]="optionTpl()!.template"
|
|
4809
|
+
[ngTemplateOutletContext]="triggerContext()!"
|
|
4810
|
+
/>
|
|
4811
|
+
</span>
|
|
4812
|
+
} @else {
|
|
4813
|
+
<span
|
|
4814
|
+
class="flex-1 truncate pl-3.5"
|
|
4815
|
+
[class]="selectedOption() ? 'text-ink' : 'text-ink-faint'"
|
|
4816
|
+
>
|
|
4817
|
+
{{ selectedOption() ? selectedOption()!.label : placeholder() }}
|
|
4818
|
+
</span>
|
|
4819
|
+
}
|
|
3920
4820
|
|
|
3921
4821
|
@if (showClear()) {
|
|
3922
4822
|
<button
|
|
@@ -3974,15 +4874,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
3974
4874
|
(click)="opt.disabled ? null : selectOption(opt)"
|
|
3975
4875
|
(mouseenter)="opt.disabled ? null : highlightedIndex.set(i)"
|
|
3976
4876
|
>
|
|
3977
|
-
|
|
4877
|
+
@if (optionTpl()) {
|
|
4878
|
+
<span class="flex min-w-0 flex-1 items-center gap-2">
|
|
4879
|
+
<ng-container
|
|
4880
|
+
[ngTemplateOutlet]="optionTpl()!.template"
|
|
4881
|
+
[ngTemplateOutletContext]="optionContext(opt, i)"
|
|
4882
|
+
/>
|
|
4883
|
+
</span>
|
|
4884
|
+
} @else {
|
|
4885
|
+
<span class="truncate">{{ opt.label }}</span>
|
|
4886
|
+
}
|
|
3978
4887
|
@if (isSelected(opt.value)) {
|
|
3979
4888
|
<sds-icon name="check" [size]="16" class="shrink-0 text-brand-600" />
|
|
3980
4889
|
}
|
|
3981
4890
|
</li>
|
|
3982
4891
|
} @empty {
|
|
3983
|
-
|
|
4892
|
+
@if (emptyTpl()) {
|
|
4893
|
+
<li class="px-3 py-2 text-sm text-ink-faint">
|
|
4894
|
+
<ng-container
|
|
4895
|
+
[ngTemplateOutlet]="emptyTpl()!.template"
|
|
4896
|
+
[ngTemplateOutletContext]="{ $implicit: filterText(), filterText: filterText() }"
|
|
4897
|
+
/>
|
|
4898
|
+
</li>
|
|
4899
|
+
} @else {
|
|
4900
|
+
<li class="px-3 py-2 text-sm text-ink-faint">{{ emptyMessage() }}</li>
|
|
4901
|
+
}
|
|
3984
4902
|
}
|
|
3985
4903
|
</ul>
|
|
4904
|
+
|
|
4905
|
+
@if (footerTpl()) {
|
|
4906
|
+
<div class="border-t border-border">
|
|
4907
|
+
<ng-container
|
|
4908
|
+
[ngTemplateOutlet]="footerTpl()!.template"
|
|
4909
|
+
[ngTemplateOutletContext]="footerContext()"
|
|
4910
|
+
/>
|
|
4911
|
+
</div>
|
|
4912
|
+
}
|
|
3986
4913
|
</div>
|
|
3987
4914
|
}
|
|
3988
4915
|
</div>
|
|
@@ -3995,7 +4922,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
3995
4922
|
}
|
|
3996
4923
|
`,
|
|
3997
4924
|
}]
|
|
3998
|
-
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], optionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionDisabled", required: false }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", 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 }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }] } });
|
|
4925
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], optionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionDisabled", required: false }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", 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 }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], optionTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsSelectOption), { isSignal: true }] }], emptyTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsSelectEmpty), { isSignal: true }] }], footerTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsSelectFooter), { isSignal: true }] }], trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }] } });
|
|
3999
4926
|
|
|
4000
4927
|
const SIZES$3 = {
|
|
4001
4928
|
sm: 'min-h-9 text-sm',
|
|
@@ -4013,9 +4940,24 @@ let nextId$4 = 0;
|
|
|
4013
4940
|
* For object options, set [optionValue] or [dataKey] so written values match by
|
|
4014
4941
|
* key; otherwise matching falls back to reference equality.
|
|
4015
4942
|
*
|
|
4943
|
+
* Project template directives to customise the panel:
|
|
4944
|
+
* - `<ng-template sdsSelectOption let-option>` — the content of each option row, after its
|
|
4945
|
+
* checkbox (see {@link SdsSelectOption}); context: the raw `option` (implicit), plus `label`,
|
|
4946
|
+
* `value`, `selected`, `disabled`, `index`.
|
|
4947
|
+
* - `<ng-template sdsSelectEmpty let-query>` — replaces the "no results" message (see {@link SdsSelectEmpty}).
|
|
4948
|
+
* - `<ng-template sdsSelectFooter let-close="close">` — a sticky footer action (see {@link SdsSelectFooter});
|
|
4949
|
+
* `close()` dismisses the panel.
|
|
4950
|
+
*
|
|
4016
4951
|
* @example
|
|
4017
4952
|
* <sds-multi-select [options]="services" optionLabel="name" optionValue="code"
|
|
4018
4953
|
* display="chip" filter [(ngModel)]="codes" />
|
|
4954
|
+
*
|
|
4955
|
+
* @example
|
|
4956
|
+
* <sds-multi-select [options]="tags" optionLabel="name" optionValue="id" [(ngModel)]="tagIds">
|
|
4957
|
+
* <ng-template sdsSelectOption let-option>
|
|
4958
|
+
* <span class="dot" [style.background]="option.color"></span> {{ option.name }}
|
|
4959
|
+
* </ng-template>
|
|
4960
|
+
* </sds-multi-select>
|
|
4019
4961
|
*/
|
|
4020
4962
|
class SdsMultiSelect {
|
|
4021
4963
|
host = inject(ElementRef);
|
|
@@ -4043,6 +4985,12 @@ class SdsMultiSelect {
|
|
|
4043
4985
|
filter = input(false, ...(ngDevMode ? [{ debugName: "filter", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4044
4986
|
filterPlaceholder = input('Rechercher…', ...(ngDevMode ? [{ debugName: "filterPlaceholder" }] : []));
|
|
4045
4987
|
emptyMessage = input('Aucun résultat', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : []));
|
|
4988
|
+
/** Optional projected `<ng-template sdsSelectOption>` — custom option-row rendering. */
|
|
4989
|
+
optionTpl = contentChild(SdsSelectOption, ...(ngDevMode ? [{ debugName: "optionTpl" }] : []));
|
|
4990
|
+
/** Optional projected `<ng-template sdsSelectEmpty>` — custom no-results content. */
|
|
4991
|
+
emptyTpl = contentChild(SdsSelectEmpty, ...(ngDevMode ? [{ debugName: "emptyTpl" }] : []));
|
|
4992
|
+
/** Optional projected `<ng-template sdsSelectFooter>` — a sticky footer action. */
|
|
4993
|
+
footerTpl = contentChild(SdsSelectFooter, ...(ngDevMode ? [{ debugName: "footerTpl" }] : []));
|
|
4046
4994
|
value = signal([], ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
4047
4995
|
open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
|
|
4048
4996
|
filterText = signal('', ...(ngDevMode ? [{ debugName: "filterText" }] : []));
|
|
@@ -4108,9 +5056,25 @@ class SdsMultiSelect {
|
|
|
4108
5056
|
const label = labelKey && labelKey in o ? String(o[labelKey]) : String(o['label'] ?? raw);
|
|
4109
5057
|
const value = this.optionValue() ? o[this.optionValue()] : raw;
|
|
4110
5058
|
const disabled = this.optionDisabled() ? !!o[this.optionDisabled()] : !!o['disabled'];
|
|
4111
|
-
return { label, value, disabled };
|
|
5059
|
+
return { label, value, disabled, raw };
|
|
4112
5060
|
}
|
|
4113
|
-
return { label: String(raw), value: raw, disabled: false };
|
|
5061
|
+
return { label: String(raw), value: raw, disabled: false, raw };
|
|
5062
|
+
}
|
|
5063
|
+
/** Context for a projected option template (an option row). */
|
|
5064
|
+
optionContext(opt, index) {
|
|
5065
|
+
return {
|
|
5066
|
+
$implicit: opt.raw,
|
|
5067
|
+
option: opt.raw,
|
|
5068
|
+
label: opt.label,
|
|
5069
|
+
value: opt.value,
|
|
5070
|
+
selected: this.isSelected(opt.value),
|
|
5071
|
+
disabled: opt.disabled,
|
|
5072
|
+
index,
|
|
5073
|
+
};
|
|
5074
|
+
}
|
|
5075
|
+
/** Context for a projected footer template. */
|
|
5076
|
+
footerContext() {
|
|
5077
|
+
return { $implicit: this.filterText(), filterText: this.filterText(), close: () => this.close(true) };
|
|
4114
5078
|
}
|
|
4115
5079
|
equals(a, b) {
|
|
4116
5080
|
const k = this.dataKey();
|
|
@@ -4422,7 +5386,7 @@ class SdsMultiSelect {
|
|
|
4422
5386
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsMultiSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4423
5387
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdsMultiSelect, isStandalone: true, selector: "sds-multi-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, optionDisabled: { classPropertyName: "optionDisabled", publicName: "optionDisabled", isSignal: true, isRequired: false, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", 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 }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, maxSelectedLabels: { classPropertyName: "maxSelectedLabels", publicName: "maxSelectedLabels", isSignal: true, isRequired: false, transformFunction: null }, selectedItemsLabel: { classPropertyName: "selectedItemsLabel", publicName: "selectedItemsLabel", isSignal: true, isRequired: false, transformFunction: null }, showToggleAll: { classPropertyName: "showToggleAll", publicName: "showToggleAll", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, filterPlaceholder: { classPropertyName: "filterPlaceholder", publicName: "filterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:click": "onDocumentClick($event)" }, classAttribute: "block" }, providers: [
|
|
4424
5388
|
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsMultiSelect), multi: true },
|
|
4425
|
-
], viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
5389
|
+
], queries: [{ propertyName: "optionTpl", first: true, predicate: SdsSelectOption, descendants: true, isSignal: true }, { propertyName: "emptyTpl", first: true, predicate: SdsSelectEmpty, descendants: true, isSignal: true }, { propertyName: "footerTpl", first: true, predicate: SdsSelectFooter, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
4426
5390
|
@if (label()) {
|
|
4427
5391
|
<label [for]="selectId" class="mb-1.5 block text-sm font-medium text-ink">
|
|
4428
5392
|
{{ label() }}
|
|
@@ -4555,12 +5519,39 @@ class SdsMultiSelect {
|
|
|
4555
5519
|
<sds-icon name="check" [size]="12" />
|
|
4556
5520
|
}
|
|
4557
5521
|
</span>
|
|
4558
|
-
|
|
5522
|
+
@if (optionTpl()) {
|
|
5523
|
+
<span class="flex min-w-0 flex-1 items-center gap-2">
|
|
5524
|
+
<ng-container
|
|
5525
|
+
[ngTemplateOutlet]="optionTpl()!.template"
|
|
5526
|
+
[ngTemplateOutletContext]="optionContext(opt, i)"
|
|
5527
|
+
/>
|
|
5528
|
+
</span>
|
|
5529
|
+
} @else {
|
|
5530
|
+
<span class="truncate">{{ opt.label }}</span>
|
|
5531
|
+
}
|
|
4559
5532
|
</li>
|
|
4560
5533
|
} @empty {
|
|
4561
|
-
|
|
5534
|
+
@if (emptyTpl()) {
|
|
5535
|
+
<li class="px-3 py-2 text-sm text-ink-faint">
|
|
5536
|
+
<ng-container
|
|
5537
|
+
[ngTemplateOutlet]="emptyTpl()!.template"
|
|
5538
|
+
[ngTemplateOutletContext]="{ $implicit: filterText(), filterText: filterText() }"
|
|
5539
|
+
/>
|
|
5540
|
+
</li>
|
|
5541
|
+
} @else {
|
|
5542
|
+
<li class="px-3 py-2 text-sm text-ink-faint">{{ emptyMessage() }}</li>
|
|
5543
|
+
}
|
|
4562
5544
|
}
|
|
4563
5545
|
</ul>
|
|
5546
|
+
|
|
5547
|
+
@if (footerTpl()) {
|
|
5548
|
+
<div class="border-t border-border">
|
|
5549
|
+
<ng-container
|
|
5550
|
+
[ngTemplateOutlet]="footerTpl()!.template"
|
|
5551
|
+
[ngTemplateOutletContext]="footerContext()"
|
|
5552
|
+
/>
|
|
5553
|
+
</div>
|
|
5554
|
+
}
|
|
4564
5555
|
</div>
|
|
4565
5556
|
}
|
|
4566
5557
|
</div>
|
|
@@ -4571,14 +5562,14 @@ class SdsMultiSelect {
|
|
|
4571
5562
|
@if (error()) {
|
|
4572
5563
|
<p class="mt-1.5 text-xs font-medium text-danger">{{ error() }}</p>
|
|
4573
5564
|
}
|
|
4574
|
-
`, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5565
|
+
`, isInline: true, dependencies: [{ kind: "component", type: SdsIcon, selector: "sds-icon", inputs: ["name", "size", "strokeWidth"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4575
5566
|
}
|
|
4576
5567
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdsMultiSelect, decorators: [{
|
|
4577
5568
|
type: Component,
|
|
4578
5569
|
args: [{
|
|
4579
5570
|
selector: 'sds-multi-select',
|
|
4580
5571
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4581
|
-
imports: [SdsIcon],
|
|
5572
|
+
imports: [SdsIcon, NgTemplateOutlet],
|
|
4582
5573
|
providers: [
|
|
4583
5574
|
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SdsMultiSelect), multi: true },
|
|
4584
5575
|
],
|
|
@@ -4719,12 +5710,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
4719
5710
|
<sds-icon name="check" [size]="12" />
|
|
4720
5711
|
}
|
|
4721
5712
|
</span>
|
|
4722
|
-
|
|
5713
|
+
@if (optionTpl()) {
|
|
5714
|
+
<span class="flex min-w-0 flex-1 items-center gap-2">
|
|
5715
|
+
<ng-container
|
|
5716
|
+
[ngTemplateOutlet]="optionTpl()!.template"
|
|
5717
|
+
[ngTemplateOutletContext]="optionContext(opt, i)"
|
|
5718
|
+
/>
|
|
5719
|
+
</span>
|
|
5720
|
+
} @else {
|
|
5721
|
+
<span class="truncate">{{ opt.label }}</span>
|
|
5722
|
+
}
|
|
4723
5723
|
</li>
|
|
4724
5724
|
} @empty {
|
|
4725
|
-
|
|
5725
|
+
@if (emptyTpl()) {
|
|
5726
|
+
<li class="px-3 py-2 text-sm text-ink-faint">
|
|
5727
|
+
<ng-container
|
|
5728
|
+
[ngTemplateOutlet]="emptyTpl()!.template"
|
|
5729
|
+
[ngTemplateOutletContext]="{ $implicit: filterText(), filterText: filterText() }"
|
|
5730
|
+
/>
|
|
5731
|
+
</li>
|
|
5732
|
+
} @else {
|
|
5733
|
+
<li class="px-3 py-2 text-sm text-ink-faint">{{ emptyMessage() }}</li>
|
|
5734
|
+
}
|
|
4726
5735
|
}
|
|
4727
5736
|
</ul>
|
|
5737
|
+
|
|
5738
|
+
@if (footerTpl()) {
|
|
5739
|
+
<div class="border-t border-border">
|
|
5740
|
+
<ng-container
|
|
5741
|
+
[ngTemplateOutlet]="footerTpl()!.template"
|
|
5742
|
+
[ngTemplateOutletContext]="footerContext()"
|
|
5743
|
+
/>
|
|
5744
|
+
</div>
|
|
5745
|
+
}
|
|
4728
5746
|
</div>
|
|
4729
5747
|
}
|
|
4730
5748
|
</div>
|
|
@@ -4737,7 +5755,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
4737
5755
|
}
|
|
4738
5756
|
`,
|
|
4739
5757
|
}]
|
|
4740
|
-
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], optionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionDisabled", required: false }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", 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 }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], maxSelectedLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelectedLabels", required: false }] }], selectedItemsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedItemsLabel", required: false }] }], showToggleAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToggleAll", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }] } });
|
|
5758
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], optionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionDisabled", required: false }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", 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 }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], maxSelectedLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelectedLabels", required: false }] }], selectedItemsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedItemsLabel", required: false }] }], showToggleAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToggleAll", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], optionTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsSelectOption), { isSignal: true }] }], emptyTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsSelectEmpty), { isSignal: true }] }], footerTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdsSelectFooter), { isSignal: true }] }], trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }] } });
|
|
4741
5759
|
|
|
4742
5760
|
const SDS_PHONE_COUNTRIES = [
|
|
4743
5761
|
{ iso2: 'BE', name: 'Belgique', dialCode: '32', lengths: [9], format: '### ## ## ##', example: '470123456' },
|
|
@@ -8640,5 +9658,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
8640
9658
|
* Generated bundle index. Do not edit.
|
|
8641
9659
|
*/
|
|
8642
9660
|
|
|
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 };
|
|
9661
|
+
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, SdsSelectEmpty, SdsSelectFooter, SdsSelectOption, 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
9662
|
//# sourceMappingURL=besaitech-ng-design-system.mjs.map
|