diff --git a/src/app/components/dropdown/dropdown.html b/src/app/components/dropdown/dropdown.html index 976eb58..60d7cb6 100644 --- a/src/app/components/dropdown/dropdown.html +++ b/src/app/components/dropdown/dropdown.html @@ -4,8 +4,9 @@ } + contenteditable="true" role="textbox" aria-multiline="false" [aria-placeholder]="translation.common.selectOption" + [attr.placeholder]="translation.common.selectOption" tabindex="-1" spellcheck="false" + (input)="onSearch($event)" (click)="handleInputClick($event)" (mousedown)="handleInputMouseDown()"> @if (!isOpen()) { {{ selectedLabel() }} diff --git a/src/app/components/dropdown/dropdown.scss b/src/app/components/dropdown/dropdown.scss index e38f992..57288f0 100644 --- a/src/app/components/dropdown/dropdown.scss +++ b/src/app/components/dropdown/dropdown.scss @@ -75,7 +75,6 @@ $default_animation_duration: 0.2s; .dropdown-input:not(.focus-mode) { caret-color: transparent; user-select: none; - cursor: text; } .dropdown-input.hidden { @@ -87,6 +86,10 @@ $default_animation_duration: 0.2s; visibility: hidden; } + .dropdown-input { + cursor: text; + } + .placeholder, .dropdown-input { flex-grow: 1; font-size: 18px; diff --git a/src/app/components/dropdown/dropdown.ts b/src/app/components/dropdown/dropdown.ts index 1c5faf1..1941097 100644 --- a/src/app/components/dropdown/dropdown.ts +++ b/src/app/components/dropdown/dropdown.ts @@ -38,6 +38,7 @@ export interface DropdownOption { '[class.open]': 'isOpen()', tabindex: '0', '(click)': 'toggle()', + '(focus)': 'onFocus($event)', '(mousedown)': 'onMouseDown($event)', '(keydown)': 'onKeyDown($event)', '(keydown.enter)': 'onEnter($event)', @@ -65,8 +66,9 @@ export class Dropdown implements OnInit { searchQuery = signal(''); private static nextId = 0; + private isLatestMouseDownInsideInput = false; dropdownId = `dropdown-${Dropdown.nextId++}`; - private wasInside = false; + private isLatestMouseDownInsideContainer = false; constructor(private element: ElementRef) { effect(() => this.filteredOptions()); @@ -104,16 +106,21 @@ export class Dropdown implements OnInit { }); filteredOptions = computed(() => { - const query = this.searchQuery().toLowerCase().trim(); + const query = this.searchQuery().toLowerCase().trim().normalize("NFKD"); const opts = this.normalizedOptions(); if (!this.inputFocusMode()) return opts; if (!query) return opts; - return opts.filter((o) => o.label.toLowerCase().includes(query)); + return opts.filter((o) => o.label.toLowerCase().normalize('NFKD').includes(query)); }); toggle() { + if (this.isLatestMouseDownInsideInput) { + this.isLatestMouseDownInsideInput = false; + return; + } + this.isOpen.update((v) => !v); if (this.isOpen()) { this.searchQuery.set(''); @@ -128,6 +135,15 @@ export class Dropdown implements OnInit { } } + onFocus(event: Event) { + if (this.isOpen()) { + const searchInputElement = this.searchInput()!.nativeElement; + + searchInputElement.focus(); + this.setCursorToEnd(searchInputElement); + } + } + private checkIfInputFocus(event: Event) { let triggerInputFocus = this.searchInput()!.nativeElement.contains( event.target! as HTMLElement, @@ -209,8 +225,12 @@ export class Dropdown implements OnInit { this.inputFocusMode.set(true); } + handleInputMouseDown() { + this.isLatestMouseDownInsideInput = true; + } + onMouseDown(event: MouseEvent) { - this.wasInside = this.element.nativeElement.contains(event.target); + this.isLatestMouseDownInsideContainer = this.element.nativeElement.contains(event.target); if (this.checkIfInputFocus(event)) { if (!this.inputFocusMode()) this.inputFocusMode.set(true); @@ -219,16 +239,21 @@ export class Dropdown implements OnInit { @HostListener('document:click', ['$event']) onDocumentClick(event: MouseEvent) { - if (!this.element.nativeElement.contains(event.target) && !this.wasInside) { + if ( + !this.element.nativeElement.contains(event.target) && + !this.isLatestMouseDownInsideContainer + ) { this.close(); } - this.wasInside = false; + this.isLatestMouseDownInsideInput = false; + this.isLatestMouseDownInsideContainer = false; } handleInputClick(event: MouseEvent) { event.stopPropagation(); - this.wasInside = false; + this.isLatestMouseDownInsideInput = false; + this.isLatestMouseDownInsideContainer = false; } onSearch(event: Event) {