2026-05-10 09:33:13 +00:00
|
|
|
/**
|
2024-07-23 16:50:12 +00:00
|
|
|
* @license
|
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
|
* found in the LICENSE file at https://angular.dev/license
|
|
|
|
|
*/
|
|
|
|
|
|
2026-05-10 09:33:13 +00:00
|
|
|
import {Combobox, ComboboxPopup, ComboboxWidget} from '@angular/aria/combobox';
|
2026-01-26 19:42:36 +00:00
|
|
|
import {Listbox, Option} from '@angular/aria/listbox';
|
2026-05-10 09:33:13 +00:00
|
|
|
import {OverlayModule} from '@angular/cdk/overlay';
|
2026-01-26 19:42:36 +00:00
|
|
|
import {
|
|
|
|
|
afterRenderEffect,
|
|
|
|
|
Component,
|
|
|
|
|
computed,
|
|
|
|
|
input,
|
|
|
|
|
model,
|
|
|
|
|
signal,
|
|
|
|
|
viewChild,
|
|
|
|
|
} from '@angular/core';
|
2026-03-25 11:19:57 +00:00
|
|
|
import {FormValueControl} from '@angular/forms/signals';
|
2024-07-23 16:50:12 +00:00
|
|
|
|
2026-05-10 09:33:13 +00:00
|
|
|
type SelectOptionValue = string;
|
2024-07-23 16:50:12 +00:00
|
|
|
|
|
|
|
|
export interface SelectOption {
|
|
|
|
|
label: string;
|
|
|
|
|
value: SelectOptionValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'docs-select',
|
2026-05-10 09:33:13 +00:00
|
|
|
templateUrl: 'select.component.html',
|
|
|
|
|
styleUrl: 'select.component.css',
|
|
|
|
|
imports: [Combobox, ComboboxPopup, ComboboxWidget, Listbox, Option, OverlayModule],
|
2024-07-23 16:50:12 +00:00
|
|
|
})
|
2026-01-04 22:27:51 +00:00
|
|
|
export class Select implements FormValueControl<string | null> {
|
|
|
|
|
readonly value = model<string | null>(null);
|
|
|
|
|
|
2024-10-30 05:24:44 +00:00
|
|
|
readonly id = input.required<string>({alias: 'selectId'});
|
|
|
|
|
readonly name = input.required<string>();
|
|
|
|
|
readonly options = input.required<SelectOption[]>();
|
2026-01-04 22:27:51 +00:00
|
|
|
readonly disabled = input(false);
|
2024-07-23 16:50:12 +00:00
|
|
|
|
2026-05-10 09:33:13 +00:00
|
|
|
readonly listbox = viewChild(Listbox);
|
|
|
|
|
readonly combobox = viewChild(Combobox);
|
2026-01-26 19:42:36 +00:00
|
|
|
|
|
|
|
|
readonly searchString = signal('');
|
|
|
|
|
|
2026-05-10 09:33:13 +00:00
|
|
|
readonly popupExpanded = signal(false);
|
2026-01-26 19:42:36 +00:00
|
|
|
readonly filteredOptions = computed(() => {
|
|
|
|
|
const search = this.searchString().toLowerCase();
|
|
|
|
|
if (!search) {
|
|
|
|
|
return this.options();
|
|
|
|
|
}
|
|
|
|
|
return this.options().filter((option) => option.label.toLowerCase().includes(search));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
readonly displayValue = computed(() => {
|
|
|
|
|
const currentValue = this.value();
|
|
|
|
|
if (currentValue === null) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
const option = this.options().find((opt) => opt.value === currentValue);
|
|
|
|
|
return option ? option.label : '';
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
readonly selectedValues = signal<SelectOptionValue[]>([]);
|
|
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
|
afterRenderEffect(() => {
|
2026-05-10 09:33:13 +00:00
|
|
|
this.listbox()?.scrollActiveItemIntoView();
|
2026-01-26 19:42:36 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 09:33:13 +00:00
|
|
|
onCommit() {
|
|
|
|
|
const values = this.selectedValues();
|
|
|
|
|
if (values.length) {
|
|
|
|
|
this.value.set(values[0]);
|
|
|
|
|
//this.popupExpanded.set(false);
|
2024-07-23 16:50:12 +00:00
|
|
|
}
|
2026-05-10 09:33:13 +00:00
|
|
|
}
|
2024-07-23 16:50:12 +00:00
|
|
|
|
2026-05-10 09:33:13 +00:00
|
|
|
/** Dismisses the dialog overlay on Escape key. */
|
|
|
|
|
onSearchEscape(event: Event) {
|
|
|
|
|
this.popupExpanded.set(false);
|
|
|
|
|
this.combobox()?.element.focus();
|
|
|
|
|
}
|
2026-01-26 19:42:36 +00:00
|
|
|
|
2026-05-10 09:33:13 +00:00
|
|
|
/** Handles keydown events on the clear button. */
|
|
|
|
|
onKeydown(event: KeyboardEvent): void {
|
|
|
|
|
if (event.key === 'Enter') {
|
|
|
|
|
this.clear();
|
|
|
|
|
this.popupExpanded.set(false);
|
|
|
|
|
event.stopPropagation();
|
2026-01-26 19:42:36 +00:00
|
|
|
}
|
2024-07-23 16:50:12 +00:00
|
|
|
}
|
2026-05-10 09:33:13 +00:00
|
|
|
|
|
|
|
|
/** Clears the search query and all selected options. */
|
|
|
|
|
clear(): void {
|
|
|
|
|
this.searchString.set('');
|
|
|
|
|
this.value.set(null);
|
|
|
|
|
}
|
2024-07-23 16:50:12 +00:00
|
|
|
}
|