mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
ffcc8eee98
Several issues in the shared docs `Select` component (used by the API reference package filter): - The options list never scrolled: its element used a class with no styles, so the intended max-height/overflow rule was dead. Point it at the styled class so long lists scroll within the popover. - Selecting an option now closes the popup instead of leaving it open. - Clip the trigger and popover corners (overflow: hidden) so their rounded borders render cleanly, and align the popover width with the trigger. - Drop the selected-option checkmark. The component is single-select and already marks the selection with a highlight, so the tick was misleading. - Remove the unused `disabled` input.
103 lines
2.6 KiB
TypeScript
103 lines
2.6 KiB
TypeScript
/**
|
|
* @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
|
|
*/
|
|
|
|
import {Combobox, ComboboxPopup, ComboboxWidget} from '@angular/aria/combobox';
|
|
import {Listbox, Option} from '@angular/aria/listbox';
|
|
import {OverlayModule} from '@angular/cdk/overlay';
|
|
import {
|
|
afterRenderEffect,
|
|
Component,
|
|
computed,
|
|
input,
|
|
model,
|
|
signal,
|
|
viewChild,
|
|
} from '@angular/core';
|
|
import {FormValueControl} from '@angular/forms/signals';
|
|
|
|
type SelectOptionValue = string;
|
|
|
|
export interface SelectOption {
|
|
label: string;
|
|
value: SelectOptionValue;
|
|
}
|
|
|
|
@Component({
|
|
selector: 'docs-select',
|
|
templateUrl: 'select.component.html',
|
|
styleUrl: 'select.component.css',
|
|
imports: [Combobox, ComboboxPopup, ComboboxWidget, Listbox, Option, OverlayModule],
|
|
})
|
|
export class Select implements FormValueControl<string | null> {
|
|
readonly value = model<string | null>(null);
|
|
|
|
readonly id = input.required<string>({alias: 'selectId'});
|
|
readonly name = input.required<string>();
|
|
readonly options = input.required<SelectOption[]>();
|
|
|
|
readonly listbox = viewChild(Listbox);
|
|
readonly combobox = viewChild(Combobox);
|
|
|
|
readonly searchString = signal('');
|
|
|
|
readonly popupExpanded = signal(false);
|
|
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(() => {
|
|
this.listbox()?.scrollActiveItemIntoView();
|
|
});
|
|
}
|
|
|
|
onCommit() {
|
|
const values = this.selectedValues();
|
|
if (values.length) {
|
|
this.value.set(values[0]);
|
|
this.popupExpanded.set(false);
|
|
}
|
|
}
|
|
|
|
/** Dismisses the dialog overlay on Escape key. */
|
|
onSearchEscape(event: Event) {
|
|
this.popupExpanded.set(false);
|
|
this.combobox()?.element.focus();
|
|
}
|
|
|
|
/** Handles keydown events on the clear button. */
|
|
onKeydown(event: KeyboardEvent): void {
|
|
if (event.key === 'Enter') {
|
|
this.clear();
|
|
this.popupExpanded.set(false);
|
|
event.stopPropagation();
|
|
}
|
|
}
|
|
|
|
/** Clears the search query and all selected options. */
|
|
clear(): void {
|
|
this.searchString.set('');
|
|
this.value.set(null);
|
|
}
|
|
}
|