Polish Mouse Click Response

This commit is contained in:
copecone committed 2026-04-19 19:55:12 +09:00
1 parent 3f33c0dfa5
commit 3a415a2525
3 files changed
+49 -10

No files matched your search

+2 -2
View File
@@ -3,9 +3,9 @@
<span class="material-symbols-outlined icon-24 prefix-icon">{{ icon() }}</span> <span class="material-symbols-outlined icon-24 prefix-icon">{{ icon() }}</span>
} }
<div #searchInput class="dropdown-input" [class.hidden]="!isOpen()" <div #searchInput class="dropdown-input" [class.hidden]="!isOpen()" [class.focus-mode]="inputFocusMode()"
contenteditable="true" role="textbox" aria-multiline="false" [aria-placeholder]="translation.common.selectOption" tabindex="-1" spellcheck="false" contenteditable="true" role="textbox" aria-multiline="false" [aria-placeholder]="translation.common.selectOption" tabindex="-1" spellcheck="false"
(input)="onSearch($event)"></div> (input)="onSearch($event)" (click)="handleInputClick($event)" [attr.placeholder]="translation.common.selectOption"></div>
@if (!isOpen()) { @if (!isOpen()) {
<span class="placeholder">{{ selectedLabel() }}</span> <span class="placeholder">{{ selectedLabel() }}</span>
+11
View File
@@ -67,6 +67,17 @@ $default_animation_duration: 0.2s;
outline: none; outline: none;
} }
.dropdown-input:empty:before {
content: attr(placeholder);
opacity: 50%;
}
.dropdown-input:not(.focus-mode) {
caret-color: transparent;
user-select: none;
cursor: text;
}
.dropdown-input.hidden { .dropdown-input.hidden {
position: absolute; position: absolute;
+36 -8
View File
@@ -38,6 +38,7 @@ export interface DropdownOption {
'[class.open]': 'isOpen()', '[class.open]': 'isOpen()',
tabindex: '0', tabindex: '0',
'(click)': 'toggle()', '(click)': 'toggle()',
'(mousedown)': 'onMouseDown($event)',
'(keydown)': 'onKeyDown($event)', '(keydown)': 'onKeyDown($event)',
'(keydown.enter)': 'onEnter($event)', '(keydown.enter)': 'onEnter($event)',
'(keydown.space)': 'onSpaceDown($event)', '(keydown.space)': 'onSpaceDown($event)',
@@ -65,9 +66,10 @@ export class Dropdown implements OnInit {
private static nextId = 0; private static nextId = 0;
dropdownId = `dropdown-${Dropdown.nextId++}`; dropdownId = `dropdown-${Dropdown.nextId++}`;
private wasInside = false;
constructor(private element: ElementRef) { constructor(private element: ElementRef) {
effect(() => this.filteredOptions()) effect(() => this.filteredOptions());
effect(() => { effect(() => {
const isDropdownOpen = this.isOpen(); const isDropdownOpen = this.isOpen();
@@ -122,10 +124,17 @@ export class Dropdown implements OnInit {
searchInputElement.focus(); searchInputElement.focus();
this.setCursorToEnd(searchInputElement); this.setCursorToEnd(searchInputElement);
}) });
} }
} }
private checkIfInputFocus(event: Event) {
let triggerInputFocus = this.searchInput()!.nativeElement.contains(
event.target! as HTMLElement,
);
return this.isOpen() && triggerInputFocus;
}
close() { close() {
this.isOpen.set(false); this.isOpen.set(false);
this.focusedIndex.set(-1); this.focusedIndex.set(-1);
@@ -138,7 +147,7 @@ export class Dropdown implements OnInit {
} }
onConfirm(event: Event) { onConfirm(event: Event) {
event.preventDefault() event.preventDefault();
if (this.isOpen()) { if (this.isOpen()) {
const currentIndex = this.focusedIndex(); const currentIndex = this.focusedIndex();
@@ -151,11 +160,13 @@ export class Dropdown implements OnInit {
} }
} }
onEnter(event: Event) { this.onConfirm(event) } onEnter(event: Event) {
this.onConfirm(event);
}
onSpaceDown(event: Event) { onSpaceDown(event: Event) {
if (this.isOpen() && this.inputFocusMode()) return; if (this.isOpen() && this.inputFocusMode()) return;
this.onConfirm(event) this.onConfirm(event);
} }
onArrowDown(event: Event) { onArrowDown(event: Event) {
@@ -191,22 +202,39 @@ export class Dropdown implements OnInit {
} }
onKeyDown(event: KeyboardEvent) { onKeyDown(event: KeyboardEvent) {
if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey || event.key == 'Tab') return; if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey || event.key == 'Tab')
return;
if (!this.isOpen() || this.inputFocusMode()) return; if (!this.isOpen() || this.inputFocusMode()) return;
this.inputFocusMode.set(true); this.inputFocusMode.set(true);
} }
onMouseDown(event: MouseEvent) {
this.wasInside = this.element.nativeElement.contains(event.target);
if (this.checkIfInputFocus(event)) {
if (!this.inputFocusMode()) this.inputFocusMode.set(true);
}
}
@HostListener('document:click', ['$event']) @HostListener('document:click', ['$event'])
onDocumentClick(event: MouseEvent) { onDocumentClick(event: MouseEvent) {
if (!this.element.nativeElement.contains(event.target)) { if (!this.element.nativeElement.contains(event.target) && !this.wasInside) {
this.close(); this.close();
} }
this.wasInside = false;
}
handleInputClick(event: MouseEvent) {
event.stopPropagation();
this.wasInside = false;
} }
onSearch(event: Event) { onSearch(event: Event) {
const text = (event.target as HTMLElement).innerText; const element = event.target as HTMLElement;
const text = element.innerText;
if (text.trim().length <= 0) element.innerHTML = '';
this.focusedIndex.set(0); this.focusedIndex.set(0);
this.searchQuery.set(text); this.searchQuery.set(text);
} }