@fuse_ui/select 0.0.1
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/README.md +7 -0
- package/fesm2022/fuse_ui-select.mjs +188 -0
- package/fesm2022/fuse_ui-select.mjs.map +1 -0
- package/package.json +61 -0
- package/types/fuse_ui-select.d.ts +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, output, viewChild, ElementRef, signal, inject, DestroyRef, computed, forwardRef, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
5
|
+
import * as i1 from '@angular/cdk/overlay';
|
|
6
|
+
import { CdkConnectedOverlay, OverlayModule } from '@angular/cdk/overlay';
|
|
7
|
+
import { take } from 'rxjs';
|
|
8
|
+
|
|
9
|
+
class FuseSelectComponent {
|
|
10
|
+
// ─── Public @Input() API ────────────────────────────────────────────────────
|
|
11
|
+
options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
12
|
+
placeholder = input('Select an option', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
13
|
+
multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
|
|
14
|
+
searchable = input(false, ...(ngDevMode ? [{ debugName: "searchable" }] : /* istanbul ignore next */ []));
|
|
15
|
+
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
16
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
17
|
+
hasError = input(false, ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
|
|
18
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
19
|
+
// ─── Public @Output() API ───────────────────────────────────────────────────
|
|
20
|
+
selectionChange = output();
|
|
21
|
+
// ─── View queries ────────────────────────────────────────────────────────────
|
|
22
|
+
overlayDir = viewChild(CdkConnectedOverlay, ...(ngDevMode ? [{ debugName: "overlayDir" }] : /* istanbul ignore next */ []));
|
|
23
|
+
triggerRef = viewChild.required('triggerRef', { read: ElementRef });
|
|
24
|
+
// ─── Internal signal state ──────────────────────────────────────────────────
|
|
25
|
+
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
|
|
26
|
+
filterText = signal('', ...(ngDevMode ? [{ debugName: "filterText" }] : /* istanbul ignore next */ []));
|
|
27
|
+
highlightedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "highlightedIndex" }] : /* istanbul ignore next */ []));
|
|
28
|
+
_value = signal(null, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
|
|
29
|
+
_formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_formDisabled" }] : /* istanbul ignore next */ []));
|
|
30
|
+
// ─── Injections ─────────────────────────────────────────────────────────────
|
|
31
|
+
destroyRef = inject(DestroyRef);
|
|
32
|
+
// ─── Derived ────────────────────────────────────────────────────────────────
|
|
33
|
+
isDisabled = computed(() => this.disabled() || this._formDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
34
|
+
filteredOptions = computed(() => this.options().filter(o => o.label.toLowerCase().includes(this.filterText().toLowerCase())), ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
|
|
35
|
+
displayLabel = computed(() => {
|
|
36
|
+
const val = this._value();
|
|
37
|
+
if (this.multiple()) {
|
|
38
|
+
const vals = val ?? [];
|
|
39
|
+
return this.options()
|
|
40
|
+
.filter(o => vals.includes(o.value))
|
|
41
|
+
.map(o => o.label)
|
|
42
|
+
.join(', ');
|
|
43
|
+
}
|
|
44
|
+
return this.options().find(o => o.value === val)?.label ?? '';
|
|
45
|
+
}, ...(ngDevMode ? [{ debugName: "displayLabel" }] : /* istanbul ignore next */ []));
|
|
46
|
+
hasValue = computed(() => {
|
|
47
|
+
const val = this._value();
|
|
48
|
+
if (this.multiple())
|
|
49
|
+
return (val ?? []).length > 0;
|
|
50
|
+
return val !== null && val !== undefined && val !== '';
|
|
51
|
+
}, ...(ngDevMode ? [{ debugName: "hasValue" }] : /* istanbul ignore next */ []));
|
|
52
|
+
// ─── CDK overlay positions (below, fallback above) ──────────────────────────
|
|
53
|
+
positions = [
|
|
54
|
+
{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
|
|
55
|
+
{ originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
|
|
56
|
+
];
|
|
57
|
+
panelWidth = 0;
|
|
58
|
+
// ─── CVA callbacks ──────────────────────────────────────────────────────────
|
|
59
|
+
onChange = () => { };
|
|
60
|
+
onTouched = () => { };
|
|
61
|
+
writeValue(v) {
|
|
62
|
+
this._value.set(v ?? (this.multiple() ? [] : null));
|
|
63
|
+
}
|
|
64
|
+
registerOnChange(fn) {
|
|
65
|
+
this.onChange = fn;
|
|
66
|
+
}
|
|
67
|
+
registerOnTouched(fn) {
|
|
68
|
+
this.onTouched = fn;
|
|
69
|
+
}
|
|
70
|
+
setDisabledState(disabled) {
|
|
71
|
+
this._formDisabled.set(disabled);
|
|
72
|
+
}
|
|
73
|
+
// ─── Open / Close ────────────────────────────────────────────────────────────
|
|
74
|
+
open() {
|
|
75
|
+
if (this.isDisabled())
|
|
76
|
+
return;
|
|
77
|
+
this.panelWidth = this.triggerRef().nativeElement.offsetWidth ?? 0;
|
|
78
|
+
this.isOpen.set(true);
|
|
79
|
+
this.highlightedIndex.set(-1);
|
|
80
|
+
this.filterText.set('');
|
|
81
|
+
}
|
|
82
|
+
close() {
|
|
83
|
+
this.isOpen.set(false);
|
|
84
|
+
this.onTouched();
|
|
85
|
+
}
|
|
86
|
+
toggleOpen() {
|
|
87
|
+
this.isOpen() ? this.close() : this.open();
|
|
88
|
+
}
|
|
89
|
+
// Called by (attach) on CdkConnectedOverlay — sets up backdrop-click close
|
|
90
|
+
onPanelAttach() {
|
|
91
|
+
this.overlayDir()?.overlayRef
|
|
92
|
+
.backdropClick()
|
|
93
|
+
.pipe(take(1), takeUntilDestroyed(this.destroyRef))
|
|
94
|
+
.subscribe(() => this.close());
|
|
95
|
+
}
|
|
96
|
+
// ─── Option selection ────────────────────────────────────────────────────────
|
|
97
|
+
isSelected(opt) {
|
|
98
|
+
if (this.multiple()) {
|
|
99
|
+
return (this._value() ?? []).includes(opt.value);
|
|
100
|
+
}
|
|
101
|
+
return this._value() === opt.value;
|
|
102
|
+
}
|
|
103
|
+
selectOption(opt) {
|
|
104
|
+
if (opt.disabled)
|
|
105
|
+
return;
|
|
106
|
+
if (this.multiple()) {
|
|
107
|
+
const current = [...(this._value() ?? [])];
|
|
108
|
+
const idx = current.indexOf(opt.value);
|
|
109
|
+
if (idx >= 0) {
|
|
110
|
+
current.splice(idx, 1);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
current.push(opt.value);
|
|
114
|
+
}
|
|
115
|
+
this._value.set(current);
|
|
116
|
+
this.onChange(current);
|
|
117
|
+
this.selectionChange.emit(current);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this._value.set(opt.value);
|
|
121
|
+
this.onChange(opt.value);
|
|
122
|
+
this.selectionChange.emit(opt.value);
|
|
123
|
+
this.close();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// ─── Keyboard navigation ─────────────────────────────────────────────────────
|
|
127
|
+
handleKeydown(event) {
|
|
128
|
+
if (!this.isOpen()) {
|
|
129
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
this.open();
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
switch (event.key) {
|
|
136
|
+
case 'ArrowDown':
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
this.highlightedIndex.set(Math.min(this.highlightedIndex() + 1, this.filteredOptions().length - 1));
|
|
139
|
+
break;
|
|
140
|
+
case 'ArrowUp':
|
|
141
|
+
event.preventDefault();
|
|
142
|
+
this.highlightedIndex.set(Math.max(this.highlightedIndex() - 1, 0));
|
|
143
|
+
break;
|
|
144
|
+
case 'Enter': {
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
const idx = this.highlightedIndex();
|
|
147
|
+
if (idx >= 0 && idx < this.filteredOptions().length) {
|
|
148
|
+
this.selectOption(this.filteredOptions()[idx]);
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case 'Escape':
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
this.close();
|
|
155
|
+
break;
|
|
156
|
+
case 'Tab':
|
|
157
|
+
this.close();
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuseSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
162
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuseSelectComponent, isStandalone: true, selector: "fuse-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, hasError: { classPropertyName: "hasError", publicName: "hasError", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, host: { listeners: { "keydown": "handleKeydown($event)" } }, providers: [
|
|
163
|
+
{
|
|
164
|
+
provide: NG_VALUE_ACCESSOR,
|
|
165
|
+
useExisting: forwardRef(() => FuseSelectComponent),
|
|
166
|
+
multi: true,
|
|
167
|
+
},
|
|
168
|
+
], viewQueries: [{ propertyName: "overlayDir", first: true, predicate: CdkConnectedOverlay, descendants: true, isSignal: true }, { propertyName: "triggerRef", first: true, predicate: ["triggerRef"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<div\n class=\"fuse-select\"\n [class.fuse-select--sm]=\"size() === 'sm'\"\n [class.fuse-select--lg]=\"size() === 'lg'\"\n [class.fuse-select--open]=\"isOpen()\"\n [class.fuse-select--error]=\"hasError()\"\n [class.fuse-select--disabled]=\"isDisabled()\">\n\n @if (label()) {\n <!-- eslint-disable-next-line @angular-eslint/template/label-has-associated-control -->\n <label class=\"fuse-select__label\">{{ label() }}</label>\n }\n\n <!-- \u2500\u2500\u2500 Trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button\n #triggerRef\n type=\"button\"\n class=\"fuse-select__trigger\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggleOpen()\"\n role=\"combobox\"\n aria-controls=\"fuse-select-panel\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-haspopup=\"listbox\">\n\n <span\n class=\"fuse-select__value\"\n [class.fuse-select__value--placeholder]=\"!hasValue()\">\n {{ hasValue() ? displayLabel() : placeholder() }}\n </span>\n\n <svg\n class=\"fuse-select__arrow\"\n [class.fuse-select__arrow--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\" height=\"16\"\n viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <polyline points=\"6 9 12 15 18 9\"></polyline>\n </svg>\n </button>\n\n <!-- \u2500\u2500\u2500 CDK Dropdown Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayWidth]=\"panelWidth\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (attach)=\"onPanelAttach()\">\n\n <div class=\"fuse-select__panel\" role=\"listbox\" [attr.aria-multiselectable]=\"multiple()\">\n\n <!-- Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (searchable()) {\n <div class=\"fuse-select__search\">\n <input\n class=\"fuse-select__search-input\"\n type=\"text\"\n placeholder=\"Search\u2026\"\n [value]=\"filterText()\"\n (input)=\"filterText.set($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n autocomplete=\"off\" />\n </div>\n }\n\n <!-- Empty state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (filteredOptions().length === 0) {\n <div class=\"fuse-select__empty\">\n No options found\n </div>\n }\n\n <!-- Options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @for (opt of filteredOptions(); track opt.value; let i = $index) {\n <button\n type=\"button\"\n class=\"fuse-select__option\"\n [class.fuse-select__option--selected]=\"isSelected(opt)\"\n [class.fuse-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.fuse-select__option--disabled]=\"opt.disabled\"\n [disabled]=\"opt.disabled\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(opt)\"\n (click)=\"selectOption(opt)\"\n (mouseenter)=\"highlightedIndex.set(i)\">\n\n <!-- Multiple: checkbox indicator -->\n @if (multiple()) {\n <span class=\"fuse-select__checkbox\" aria-hidden=\"true\">\n @if (isSelected(opt)) {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\"\n viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"3\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n }\n </span>\n }\n\n <span class=\"fuse-select__option-label\">{{ opt.label }}</span>\n\n <!-- Single: check indicator -->\n @if (!multiple() && isSelected(opt)) {\n <svg\n class=\"fuse-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\"\n viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n }\n </button>\n }\n </div>\n </ng-template>\n</div>\n", styles: [":host{--fuse-select-bg: var(--fuse-color-bg-surface);--fuse-select-border: var(--fuse-color-border-default);--fuse-select-border-focus: var(--fuse-color-border-focus);--fuse-select-border-error: var(--fuse-color-border-error);--fuse-select-radius: var(--fuse-radius-md);--fuse-select-color: var(--fuse-color-text-primary);--fuse-select-placeholder: var(--fuse-color-text-disabled);--fuse-select-font-size: var(--fuse-font-size-md);--fuse-select-py: var(--fuse-spacing-2);--fuse-select-px: var(--fuse-spacing-3);--fuse-select-panel-bg: var(--fuse-color-bg-surface);--fuse-select-panel-shadow: var(--fuse-shadow-lg);--fuse-select-option-hover: var(--fuse-color-bg-subtle, var(--fuse-color-bg-surface));display:block}:host-context(.ios){--fuse-select-radius: var(--fuse-radius-lg)}:host-context(.md){--fuse-select-radius: var(--fuse-radius-sm)}.fuse-select{display:flex;flex-direction:column;gap:var(--fuse-spacing-1)}.fuse-select__label{font-size:var(--fuse-font-size-sm);font-weight:var(--fuse-font-weight-medium);color:var(--fuse-select-color);line-height:1.25}.fuse-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--fuse-spacing-2);width:100%;padding:var(--fuse-select-py) var(--fuse-select-px);font-size:var(--fuse-select-font-size);color:var(--fuse-select-color);background:var(--fuse-select-bg);border:1.5px solid var(--fuse-select-border);border-radius:var(--fuse-select-radius);cursor:pointer;font-family:inherit;text-align:left;transition:border-color .15s,box-shadow .15s}.fuse-select__trigger:focus-visible{outline:none;border-color:var(--fuse-select-border-focus);box-shadow:0 0 0 3px color-mix(in srgb,var(--fuse-color-primary) 25%,transparent)}.fuse-select__trigger:disabled{opacity:.5;cursor:not-allowed}.fuse-select--open .fuse-select__trigger{border-color:var(--fuse-select-border-focus);box-shadow:0 0 0 3px color-mix(in srgb,var(--fuse-color-primary) 25%,transparent)}.fuse-select--error .fuse-select__trigger{border-color:var(--fuse-select-border-error)}.fuse-select--error .fuse-select__trigger:focus-visible,.fuse-select--open .fuse-select--error .fuse-select__trigger{box-shadow:0 0 0 3px color-mix(in srgb,var(--fuse-color-danger) 25%,transparent)}.fuse-select--sm .fuse-select__trigger{--fuse-select-py: var(--fuse-spacing-1);--fuse-select-px: var(--fuse-spacing-2);--fuse-select-font-size: var(--fuse-font-size-sm)}.fuse-select--lg .fuse-select__trigger{--fuse-select-py: var(--fuse-spacing-3);--fuse-select-px: var(--fuse-spacing-4);--fuse-select-font-size: var(--fuse-font-size-lg)}.fuse-select__value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fuse-select__value--placeholder{color:var(--fuse-select-placeholder)}.fuse-select__arrow{flex-shrink:0;transition:transform .2s;color:var(--fuse-color-text-secondary, var(--fuse-select-placeholder))}.fuse-select__arrow--open{transform:rotate(180deg)}.fuse-select__panel{background:var(--fuse-select-panel-bg);border:1.5px solid var(--fuse-select-border);border-radius:var(--fuse-select-radius);box-shadow:var(--fuse-select-panel-shadow);overflow:hidden;max-height:240px;overflow-y:auto;outline:none}.fuse-select__search{padding:var(--fuse-spacing-2);border-bottom:1px solid var(--fuse-select-border);position:sticky;top:0;background:var(--fuse-select-panel-bg);z-index:1}.fuse-select__search-input{width:100%;padding:var(--fuse-spacing-1) var(--fuse-spacing-2);font-size:var(--fuse-font-size-sm);color:var(--fuse-select-color);background:transparent;border:1.5px solid var(--fuse-select-border);border-radius:var(--fuse-radius-sm);outline:none;font-family:inherit;box-sizing:border-box}.fuse-select__search-input::placeholder{color:var(--fuse-select-placeholder)}.fuse-select__search-input:focus{border-color:var(--fuse-select-border-focus)}.fuse-select__empty{padding:var(--fuse-spacing-3) var(--fuse-spacing-4);font-size:var(--fuse-font-size-sm);color:var(--fuse-select-placeholder);text-align:center}.fuse-select__option{display:flex;align-items:center;gap:var(--fuse-spacing-2);width:100%;padding:var(--fuse-spacing-2) var(--fuse-spacing-3);font-size:var(--fuse-font-size-md);color:var(--fuse-select-color);background:transparent;border:none;cursor:pointer;text-align:left;font-family:inherit;transition:background .1s}.fuse-select__option:hover:not(:disabled),.fuse-select__option--highlighted:not(:disabled){background:var(--fuse-select-option-hover)}.fuse-select__option--selected{color:var(--fuse-color-primary);font-weight:var(--fuse-font-weight-medium)}.fuse-select__option--disabled{opacity:.4;cursor:not-allowed}.fuse-select__option-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fuse-select__checkbox{display:flex;align-items:center;justify-content:center;width:16px;height:16px;border:1.5px solid var(--fuse-select-border);border-radius:var(--fuse-radius-sm);flex-shrink:0;color:var(--fuse-color-primary)}.fuse-select__option--selected .fuse-select__checkbox{background:var(--fuse-color-primary);border-color:var(--fuse-color-primary);color:var(--fuse-color-on-primary)}.fuse-select__check{flex-shrink:0;color:var(--fuse-color-primary)}\n"], dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
169
|
+
}
|
|
170
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuseSelectComponent, decorators: [{
|
|
171
|
+
type: Component,
|
|
172
|
+
args: [{ selector: 'fuse-select', standalone: true, imports: [OverlayModule], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
173
|
+
'(keydown)': 'handleKeydown($event)',
|
|
174
|
+
}, providers: [
|
|
175
|
+
{
|
|
176
|
+
provide: NG_VALUE_ACCESSOR,
|
|
177
|
+
useExisting: forwardRef(() => FuseSelectComponent),
|
|
178
|
+
multi: true,
|
|
179
|
+
},
|
|
180
|
+
], template: "<div\n class=\"fuse-select\"\n [class.fuse-select--sm]=\"size() === 'sm'\"\n [class.fuse-select--lg]=\"size() === 'lg'\"\n [class.fuse-select--open]=\"isOpen()\"\n [class.fuse-select--error]=\"hasError()\"\n [class.fuse-select--disabled]=\"isDisabled()\">\n\n @if (label()) {\n <!-- eslint-disable-next-line @angular-eslint/template/label-has-associated-control -->\n <label class=\"fuse-select__label\">{{ label() }}</label>\n }\n\n <!-- \u2500\u2500\u2500 Trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <button\n #triggerRef\n type=\"button\"\n class=\"fuse-select__trigger\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggleOpen()\"\n role=\"combobox\"\n aria-controls=\"fuse-select-panel\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-haspopup=\"listbox\">\n\n <span\n class=\"fuse-select__value\"\n [class.fuse-select__value--placeholder]=\"!hasValue()\">\n {{ hasValue() ? displayLabel() : placeholder() }}\n </span>\n\n <svg\n class=\"fuse-select__arrow\"\n [class.fuse-select__arrow--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\" height=\"16\"\n viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <polyline points=\"6 9 12 15 18 9\"></polyline>\n </svg>\n </button>\n\n <!-- \u2500\u2500\u2500 CDK Dropdown Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayWidth]=\"panelWidth\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (attach)=\"onPanelAttach()\">\n\n <div class=\"fuse-select__panel\" role=\"listbox\" [attr.aria-multiselectable]=\"multiple()\">\n\n <!-- Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (searchable()) {\n <div class=\"fuse-select__search\">\n <input\n class=\"fuse-select__search-input\"\n type=\"text\"\n placeholder=\"Search\u2026\"\n [value]=\"filterText()\"\n (input)=\"filterText.set($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n autocomplete=\"off\" />\n </div>\n }\n\n <!-- Empty state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (filteredOptions().length === 0) {\n <div class=\"fuse-select__empty\">\n No options found\n </div>\n }\n\n <!-- Options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @for (opt of filteredOptions(); track opt.value; let i = $index) {\n <button\n type=\"button\"\n class=\"fuse-select__option\"\n [class.fuse-select__option--selected]=\"isSelected(opt)\"\n [class.fuse-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.fuse-select__option--disabled]=\"opt.disabled\"\n [disabled]=\"opt.disabled\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(opt)\"\n (click)=\"selectOption(opt)\"\n (mouseenter)=\"highlightedIndex.set(i)\">\n\n <!-- Multiple: checkbox indicator -->\n @if (multiple()) {\n <span class=\"fuse-select__checkbox\" aria-hidden=\"true\">\n @if (isSelected(opt)) {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\"\n viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"3\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n }\n </span>\n }\n\n <span class=\"fuse-select__option-label\">{{ opt.label }}</span>\n\n <!-- Single: check indicator -->\n @if (!multiple() && isSelected(opt)) {\n <svg\n class=\"fuse-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\"\n viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n }\n </button>\n }\n </div>\n </ng-template>\n</div>\n", styles: [":host{--fuse-select-bg: var(--fuse-color-bg-surface);--fuse-select-border: var(--fuse-color-border-default);--fuse-select-border-focus: var(--fuse-color-border-focus);--fuse-select-border-error: var(--fuse-color-border-error);--fuse-select-radius: var(--fuse-radius-md);--fuse-select-color: var(--fuse-color-text-primary);--fuse-select-placeholder: var(--fuse-color-text-disabled);--fuse-select-font-size: var(--fuse-font-size-md);--fuse-select-py: var(--fuse-spacing-2);--fuse-select-px: var(--fuse-spacing-3);--fuse-select-panel-bg: var(--fuse-color-bg-surface);--fuse-select-panel-shadow: var(--fuse-shadow-lg);--fuse-select-option-hover: var(--fuse-color-bg-subtle, var(--fuse-color-bg-surface));display:block}:host-context(.ios){--fuse-select-radius: var(--fuse-radius-lg)}:host-context(.md){--fuse-select-radius: var(--fuse-radius-sm)}.fuse-select{display:flex;flex-direction:column;gap:var(--fuse-spacing-1)}.fuse-select__label{font-size:var(--fuse-font-size-sm);font-weight:var(--fuse-font-weight-medium);color:var(--fuse-select-color);line-height:1.25}.fuse-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--fuse-spacing-2);width:100%;padding:var(--fuse-select-py) var(--fuse-select-px);font-size:var(--fuse-select-font-size);color:var(--fuse-select-color);background:var(--fuse-select-bg);border:1.5px solid var(--fuse-select-border);border-radius:var(--fuse-select-radius);cursor:pointer;font-family:inherit;text-align:left;transition:border-color .15s,box-shadow .15s}.fuse-select__trigger:focus-visible{outline:none;border-color:var(--fuse-select-border-focus);box-shadow:0 0 0 3px color-mix(in srgb,var(--fuse-color-primary) 25%,transparent)}.fuse-select__trigger:disabled{opacity:.5;cursor:not-allowed}.fuse-select--open .fuse-select__trigger{border-color:var(--fuse-select-border-focus);box-shadow:0 0 0 3px color-mix(in srgb,var(--fuse-color-primary) 25%,transparent)}.fuse-select--error .fuse-select__trigger{border-color:var(--fuse-select-border-error)}.fuse-select--error .fuse-select__trigger:focus-visible,.fuse-select--open .fuse-select--error .fuse-select__trigger{box-shadow:0 0 0 3px color-mix(in srgb,var(--fuse-color-danger) 25%,transparent)}.fuse-select--sm .fuse-select__trigger{--fuse-select-py: var(--fuse-spacing-1);--fuse-select-px: var(--fuse-spacing-2);--fuse-select-font-size: var(--fuse-font-size-sm)}.fuse-select--lg .fuse-select__trigger{--fuse-select-py: var(--fuse-spacing-3);--fuse-select-px: var(--fuse-spacing-4);--fuse-select-font-size: var(--fuse-font-size-lg)}.fuse-select__value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fuse-select__value--placeholder{color:var(--fuse-select-placeholder)}.fuse-select__arrow{flex-shrink:0;transition:transform .2s;color:var(--fuse-color-text-secondary, var(--fuse-select-placeholder))}.fuse-select__arrow--open{transform:rotate(180deg)}.fuse-select__panel{background:var(--fuse-select-panel-bg);border:1.5px solid var(--fuse-select-border);border-radius:var(--fuse-select-radius);box-shadow:var(--fuse-select-panel-shadow);overflow:hidden;max-height:240px;overflow-y:auto;outline:none}.fuse-select__search{padding:var(--fuse-spacing-2);border-bottom:1px solid var(--fuse-select-border);position:sticky;top:0;background:var(--fuse-select-panel-bg);z-index:1}.fuse-select__search-input{width:100%;padding:var(--fuse-spacing-1) var(--fuse-spacing-2);font-size:var(--fuse-font-size-sm);color:var(--fuse-select-color);background:transparent;border:1.5px solid var(--fuse-select-border);border-radius:var(--fuse-radius-sm);outline:none;font-family:inherit;box-sizing:border-box}.fuse-select__search-input::placeholder{color:var(--fuse-select-placeholder)}.fuse-select__search-input:focus{border-color:var(--fuse-select-border-focus)}.fuse-select__empty{padding:var(--fuse-spacing-3) var(--fuse-spacing-4);font-size:var(--fuse-font-size-sm);color:var(--fuse-select-placeholder);text-align:center}.fuse-select__option{display:flex;align-items:center;gap:var(--fuse-spacing-2);width:100%;padding:var(--fuse-spacing-2) var(--fuse-spacing-3);font-size:var(--fuse-font-size-md);color:var(--fuse-select-color);background:transparent;border:none;cursor:pointer;text-align:left;font-family:inherit;transition:background .1s}.fuse-select__option:hover:not(:disabled),.fuse-select__option--highlighted:not(:disabled){background:var(--fuse-select-option-hover)}.fuse-select__option--selected{color:var(--fuse-color-primary);font-weight:var(--fuse-font-weight-medium)}.fuse-select__option--disabled{opacity:.4;cursor:not-allowed}.fuse-select__option-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fuse-select__checkbox{display:flex;align-items:center;justify-content:center;width:16px;height:16px;border:1.5px solid var(--fuse-select-border);border-radius:var(--fuse-radius-sm);flex-shrink:0;color:var(--fuse-color-primary)}.fuse-select__option--selected .fuse-select__checkbox{background:var(--fuse-color-primary);border-color:var(--fuse-color-primary);color:var(--fuse-color-on-primary)}.fuse-select__check{flex-shrink:0;color:var(--fuse-color-primary)}\n"] }]
|
|
181
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], hasError: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasError", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], overlayDir: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkConnectedOverlay), { isSignal: true }] }], triggerRef: [{ type: i0.ViewChild, args: ['triggerRef', { ...{ read: ElementRef }, isSignal: true }] }] } });
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Generated bundle index. Do not edit.
|
|
185
|
+
*/
|
|
186
|
+
|
|
187
|
+
export { FuseSelectComponent };
|
|
188
|
+
//# sourceMappingURL=fuse_ui-select.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuse_ui-select.mjs","sources":["../../../../packages/select/src/lib/select/fuse-select.component.ts","../../../../packages/select/src/lib/select/fuse-select.component.html","../../../../packages/select/src/fuse_ui-select.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ElementRef,\n computed,\n forwardRef,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { OverlayModule, CdkConnectedOverlay, ConnectedPosition } from '@angular/cdk/overlay';\nimport { take } from 'rxjs';\n\nexport interface FuseSelectOption {\n value: any;\n label: string;\n disabled?: boolean;\n}\n\n@Component({\n selector: 'fuse-select',\n standalone: true,\n imports: [OverlayModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './fuse-select.component.html',\n styleUrl: './fuse-select.component.scss',\n host: {\n '(keydown)': 'handleKeydown($event)',\n },\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => FuseSelectComponent),\n multi: true,\n },\n ],\n})\nexport class FuseSelectComponent implements ControlValueAccessor {\n // ─── Public @Input() API ────────────────────────────────────────────────────\n\n readonly options = input<FuseSelectOption[]>([]);\n readonly placeholder = input('Select an option');\n readonly multiple = input(false);\n readonly searchable = input(false);\n readonly label = input('');\n readonly size = input<'sm' | 'md' | 'lg'>('md');\n readonly hasError = input(false);\n readonly disabled = input(false);\n\n // ─── Public @Output() API ───────────────────────────────────────────────────\n\n readonly selectionChange = output<any | any[]>();\n\n // ─── View queries ────────────────────────────────────────────────────────────\n\n private readonly overlayDir = viewChild(CdkConnectedOverlay);\n private readonly triggerRef = viewChild.required('triggerRef', { read: ElementRef });\n\n // ─── Internal signal state ──────────────────────────────────────────────────\n\n protected readonly isOpen = signal(false);\n protected readonly filterText = signal('');\n protected readonly highlightedIndex = signal(-1);\n private readonly _value = signal<any>(null);\n\n private readonly _formDisabled = signal(false);\n\n // ─── Injections ─────────────────────────────────────────────────────────────\n\n private readonly destroyRef = inject(DestroyRef);\n\n // ─── Derived ────────────────────────────────────────────────────────────────\n\n protected readonly isDisabled = computed(() => this.disabled() || this._formDisabled());\n\n protected readonly filteredOptions = computed(() =>\n this.options().filter(o =>\n o.label.toLowerCase().includes(this.filterText().toLowerCase())\n )\n );\n\n protected readonly displayLabel = computed(() => {\n const val = this._value();\n if (this.multiple()) {\n const vals: any[] = val ?? [];\n return this.options()\n .filter(o => vals.includes(o.value))\n .map(o => o.label)\n .join(', ');\n }\n return this.options().find(o => o.value === val)?.label ?? '';\n });\n\n protected readonly hasValue = computed(() => {\n const val = this._value();\n if (this.multiple()) return ((val as any[]) ?? []).length > 0;\n return val !== null && val !== undefined && val !== '';\n });\n\n // ─── CDK overlay positions (below, fallback above) ──────────────────────────\n\n protected readonly positions: ConnectedPosition[] = [\n { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },\n { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },\n ];\n\n protected panelWidth = 0;\n\n // ─── CVA callbacks ──────────────────────────────────────────────────────────\n\n private onChange: (v: any) => void = () => {};\n private onTouched: () => void = () => {};\n\n writeValue(v: any): void {\n this._value.set(v ?? (this.multiple() ? [] : null));\n }\n\n registerOnChange(fn: (v: any) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(disabled: boolean): void {\n this._formDisabled.set(disabled);\n }\n\n // ─── Open / Close ────────────────────────────────────────────────────────────\n\n open(): void {\n if (this.isDisabled()) return;\n this.panelWidth = this.triggerRef().nativeElement.offsetWidth ?? 0;\n this.isOpen.set(true);\n this.highlightedIndex.set(-1);\n this.filterText.set('');\n }\n\n close(): void {\n this.isOpen.set(false);\n this.onTouched();\n }\n\n toggleOpen(): void {\n this.isOpen() ? this.close() : this.open();\n }\n\n // Called by (attach) on CdkConnectedOverlay — sets up backdrop-click close\n protected onPanelAttach(): void {\n this.overlayDir()?.overlayRef\n .backdropClick()\n .pipe(take(1), takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this.close());\n }\n\n // ─── Option selection ────────────────────────────────────────────────────────\n\n isSelected(opt: FuseSelectOption): boolean {\n if (this.multiple()) {\n return ((this._value() as any[]) ?? []).includes(opt.value);\n }\n return this._value() === opt.value;\n }\n\n selectOption(opt: FuseSelectOption): void {\n if (opt.disabled) return;\n if (this.multiple()) {\n const current: any[] = [...((this._value() as any[]) ?? [])];\n const idx = current.indexOf(opt.value);\n if (idx >= 0) {\n current.splice(idx, 1);\n } else {\n current.push(opt.value);\n }\n this._value.set(current);\n this.onChange(current);\n this.selectionChange.emit(current);\n } else {\n this._value.set(opt.value);\n this.onChange(opt.value);\n this.selectionChange.emit(opt.value);\n this.close();\n }\n }\n\n // ─── Keyboard navigation ─────────────────────────────────────────────────────\n\n handleKeydown(event: KeyboardEvent): void {\n if (!this.isOpen()) {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n this.open();\n }\n return;\n }\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.highlightedIndex.set(\n Math.min(this.highlightedIndex() + 1, this.filteredOptions().length - 1)\n );\n break;\n case 'ArrowUp':\n event.preventDefault();\n this.highlightedIndex.set(Math.max(this.highlightedIndex() - 1, 0));\n break;\n case 'Enter': {\n event.preventDefault();\n const idx = this.highlightedIndex();\n if (idx >= 0 && idx < this.filteredOptions().length) {\n this.selectOption(this.filteredOptions()[idx]);\n }\n break;\n }\n case 'Escape':\n event.preventDefault();\n this.close();\n break;\n case 'Tab':\n this.close();\n break;\n }\n }\n}\n","<div\n class=\"fuse-select\"\n [class.fuse-select--sm]=\"size() === 'sm'\"\n [class.fuse-select--lg]=\"size() === 'lg'\"\n [class.fuse-select--open]=\"isOpen()\"\n [class.fuse-select--error]=\"hasError()\"\n [class.fuse-select--disabled]=\"isDisabled()\">\n\n @if (label()) {\n <!-- eslint-disable-next-line @angular-eslint/template/label-has-associated-control -->\n <label class=\"fuse-select__label\">{{ label() }}</label>\n }\n\n <!-- ─── Trigger ─────────────────────────────────────────────────────────── -->\n <button\n #triggerRef\n type=\"button\"\n class=\"fuse-select__trigger\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggleOpen()\"\n role=\"combobox\"\n aria-controls=\"fuse-select-panel\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-haspopup=\"listbox\">\n\n <span\n class=\"fuse-select__value\"\n [class.fuse-select__value--placeholder]=\"!hasValue()\">\n {{ hasValue() ? displayLabel() : placeholder() }}\n </span>\n\n <svg\n class=\"fuse-select__arrow\"\n [class.fuse-select__arrow--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\" height=\"16\"\n viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <polyline points=\"6 9 12 15 18 9\"></polyline>\n </svg>\n </button>\n\n <!-- ─── CDK Dropdown Panel ──────────────────────────────────────────────── -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayWidth]=\"panelWidth\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (attach)=\"onPanelAttach()\">\n\n <div class=\"fuse-select__panel\" role=\"listbox\" [attr.aria-multiselectable]=\"multiple()\">\n\n <!-- Search ──────────────────────────────────────────────────────────── -->\n @if (searchable()) {\n <div class=\"fuse-select__search\">\n <input\n class=\"fuse-select__search-input\"\n type=\"text\"\n placeholder=\"Search…\"\n [value]=\"filterText()\"\n (input)=\"filterText.set($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n autocomplete=\"off\" />\n </div>\n }\n\n <!-- Empty state ─────────────────────────────────────────────────────── -->\n @if (filteredOptions().length === 0) {\n <div class=\"fuse-select__empty\">\n No options found\n </div>\n }\n\n <!-- Options ─────────────────────────────────────────────────────────── -->\n @for (opt of filteredOptions(); track opt.value; let i = $index) {\n <button\n type=\"button\"\n class=\"fuse-select__option\"\n [class.fuse-select__option--selected]=\"isSelected(opt)\"\n [class.fuse-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.fuse-select__option--disabled]=\"opt.disabled\"\n [disabled]=\"opt.disabled\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(opt)\"\n (click)=\"selectOption(opt)\"\n (mouseenter)=\"highlightedIndex.set(i)\">\n\n <!-- Multiple: checkbox indicator -->\n @if (multiple()) {\n <span class=\"fuse-select__checkbox\" aria-hidden=\"true\">\n @if (isSelected(opt)) {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\"\n viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"3\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n }\n </span>\n }\n\n <span class=\"fuse-select__option-label\">{{ opt.label }}</span>\n\n <!-- Single: check indicator -->\n @if (!multiple() && isSelected(opt)) {\n <svg\n class=\"fuse-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\"\n viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n }\n </button>\n }\n </div>\n </ng-template>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fuse_ui/select",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"fuse-ui",
|
|
10
|
+
"angular",
|
|
11
|
+
"ionic",
|
|
12
|
+
"ionic8",
|
|
13
|
+
"angular18",
|
|
14
|
+
"angular19",
|
|
15
|
+
"angular20",
|
|
16
|
+
"angular21",
|
|
17
|
+
"ui-components",
|
|
18
|
+
"design-system",
|
|
19
|
+
"css-variables",
|
|
20
|
+
"signals",
|
|
21
|
+
"standalone",
|
|
22
|
+
"multi-theme",
|
|
23
|
+
"dark-mode",
|
|
24
|
+
"fluid-typography",
|
|
25
|
+
"animated"
|
|
26
|
+
],
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@angular/core": ">=18.0.0",
|
|
29
|
+
"@angular/common": ">=18.0.0",
|
|
30
|
+
"rxjs": ">=7.4.0",
|
|
31
|
+
"@angular/cdk": ">=18.0.0",
|
|
32
|
+
"@angular/forms": "21.1.1"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"@ionic/angular": {
|
|
36
|
+
"optional": true
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"sideEffects": [
|
|
40
|
+
"*.css",
|
|
41
|
+
"**/*.scss"
|
|
42
|
+
],
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20.0.0"
|
|
45
|
+
},
|
|
46
|
+
"module": "fesm2022/fuse_ui-select.mjs",
|
|
47
|
+
"typings": "types/fuse_ui-select.d.ts",
|
|
48
|
+
"exports": {
|
|
49
|
+
"./package.json": {
|
|
50
|
+
"default": "./package.json"
|
|
51
|
+
},
|
|
52
|
+
".": {
|
|
53
|
+
"types": "./types/fuse_ui-select.d.ts",
|
|
54
|
+
"default": "./fesm2022/fuse_ui-select.mjs"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"type": "module",
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"tslib": "^2.3.0"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { ControlValueAccessor } from '@angular/forms';
|
|
3
|
+
import { ConnectedPosition } from '@angular/cdk/overlay';
|
|
4
|
+
|
|
5
|
+
interface FuseSelectOption {
|
|
6
|
+
value: any;
|
|
7
|
+
label: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare class FuseSelectComponent implements ControlValueAccessor {
|
|
11
|
+
readonly options: _angular_core.InputSignal<FuseSelectOption[]>;
|
|
12
|
+
readonly placeholder: _angular_core.InputSignal<string>;
|
|
13
|
+
readonly multiple: _angular_core.InputSignal<boolean>;
|
|
14
|
+
readonly searchable: _angular_core.InputSignal<boolean>;
|
|
15
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
16
|
+
readonly size: _angular_core.InputSignal<"sm" | "md" | "lg">;
|
|
17
|
+
readonly hasError: _angular_core.InputSignal<boolean>;
|
|
18
|
+
readonly disabled: _angular_core.InputSignal<boolean>;
|
|
19
|
+
readonly selectionChange: _angular_core.OutputEmitterRef<any>;
|
|
20
|
+
private readonly overlayDir;
|
|
21
|
+
private readonly triggerRef;
|
|
22
|
+
protected readonly isOpen: _angular_core.WritableSignal<boolean>;
|
|
23
|
+
protected readonly filterText: _angular_core.WritableSignal<string>;
|
|
24
|
+
protected readonly highlightedIndex: _angular_core.WritableSignal<number>;
|
|
25
|
+
private readonly _value;
|
|
26
|
+
private readonly _formDisabled;
|
|
27
|
+
private readonly destroyRef;
|
|
28
|
+
protected readonly isDisabled: _angular_core.Signal<boolean>;
|
|
29
|
+
protected readonly filteredOptions: _angular_core.Signal<FuseSelectOption[]>;
|
|
30
|
+
protected readonly displayLabel: _angular_core.Signal<string>;
|
|
31
|
+
protected readonly hasValue: _angular_core.Signal<boolean>;
|
|
32
|
+
protected readonly positions: ConnectedPosition[];
|
|
33
|
+
protected panelWidth: number;
|
|
34
|
+
private onChange;
|
|
35
|
+
private onTouched;
|
|
36
|
+
writeValue(v: any): void;
|
|
37
|
+
registerOnChange(fn: (v: any) => void): void;
|
|
38
|
+
registerOnTouched(fn: () => void): void;
|
|
39
|
+
setDisabledState(disabled: boolean): void;
|
|
40
|
+
open(): void;
|
|
41
|
+
close(): void;
|
|
42
|
+
toggleOpen(): void;
|
|
43
|
+
protected onPanelAttach(): void;
|
|
44
|
+
isSelected(opt: FuseSelectOption): boolean;
|
|
45
|
+
selectOption(opt: FuseSelectOption): void;
|
|
46
|
+
handleKeydown(event: KeyboardEvent): void;
|
|
47
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FuseSelectComponent, never>;
|
|
48
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FuseSelectComponent, "fuse-select", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "hasError": { "alias": "hasError"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "selectionChange": "selectionChange"; }, never, never, true, never>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { FuseSelectComponent };
|
|
52
|
+
export type { FuseSelectOption };
|