Skip to content

Commit b034fcf

Browse files
committed
fix: move focusout handling into ComboboxController
1 parent dcad921 commit b034fcf

File tree

2 files changed

+38
-42
lines changed

2 files changed

+38
-42
lines changed

core/pfe-core/controllers/combobox-controller.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ export class ComboboxController<
188188

189189
private static langsRE = new RegExp(ComboboxController.langs.join('|'));
190190

191+
private static instances = new WeakMap<ReactiveControllerHost, ComboboxController<HTMLElement>>();
192+
193+
private static hosts = new Set<ReactiveControllerHost>();
194+
191195
static {
192196
// apply visually-hidden styles
193197
this.#alertTemplate.innerHTML = `
@@ -205,6 +209,19 @@ export class ComboboxController<
205209
`;
206210
}
207211

212+
// Hide listbox on focusout
213+
static {
214+
document.addEventListener('focusout', event => {
215+
const target = event.target as HTMLElement;
216+
for (const host of ComboboxController.hosts) {
217+
if (host instanceof Node && host.contains(target)) {
218+
const instance = ComboboxController.instances.get(host);
219+
instance?._onFocusoutElement();
220+
}
221+
}
222+
});
223+
}
224+
208225
private options: RequireProps<ComboboxControllerOptions<Item>,
209226
| 'isItemDisabled'
210227
| 'isItem'
@@ -333,6 +350,8 @@ export class ComboboxController<
333350
isItemDisabled: this.options.isItemDisabled,
334351
setItemSelected: this.options.setItemSelected,
335352
});
353+
ComboboxController.instances.set(host, this);
354+
ComboboxController.hosts.add(host);
336355
}
337356

338357
async hostConnected(): Promise<void> {
@@ -359,6 +378,24 @@ export class ComboboxController<
359378
this.#fc?.hostDisconnected();
360379
}
361380

381+
disconnect(): void {
382+
ComboboxController.instances.delete(this.host);
383+
ComboboxController.hosts.delete(this.host);
384+
}
385+
386+
async _onFocusoutElement(): Promise<void> {
387+
if (this.#hasTextInput && this.options.isExpanded()) {
388+
const root = this.#element?.getRootNode();
389+
await new Promise(requestAnimationFrame);
390+
if (root instanceof ShadowRoot || root instanceof Document) {
391+
const { activeElement } = root;
392+
if (!this.#element?.contains(activeElement)) {
393+
this.#hide();
394+
}
395+
}
396+
}
397+
}
398+
362399
/**
363400
* Order of operations is important
364401
*/

elements/pf-search-input/pf-search-input.ts

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,6 @@ export class PfSearchInput extends LitElement {
5151
delegatesFocus: true,
5252
};
5353

54-
static instances: Set<PfSearchInput> = new Set<PfSearchInput>();
55-
56-
static {
57-
if (!isServer) {
58-
document.addEventListener('click', event => {
59-
for (const instance of PfSearchInput.instances) {
60-
instance._onOutsideClick(event);
61-
}
62-
});
63-
document.addEventListener('focusout', () => {
64-
for (const instance of PfSearchInput.instances) {
65-
instance._onFocusOut();
66-
}
67-
});
68-
}
69-
}
70-
7154
/** Accessible label for the search input */
7255
@property({ attribute: 'accessible-label' }) accessibleLabel?: string;
7356

@@ -77,11 +60,6 @@ export class PfSearchInput extends LitElement {
7760
/** Whether the search input's listbox is expanded */
7861
@property({ type: Boolean, reflect: true }) expanded = false;
7962

80-
/**
81-
* Enable to flip listbox when it reaches boundary
82-
*/
83-
@property({ attribute: 'enable-flip', type: Boolean }) enableFlip = false;
84-
8563
/** Current form value */
8664
@property() value?: string;
8765

@@ -133,29 +111,10 @@ export class PfSearchInput extends LitElement {
133111

134112
connectedCallback(): void {
135113
super.connectedCallback();
136-
PfSearchInput.instances.add(this);
137114
}
138115

139116
disconnectedCallback(): void {
140117
super.disconnectedCallback();
141-
PfSearchInput.instances.delete(this);
142-
}
143-
144-
// Function to handle the closing of popover on outside click
145-
@bound private _onOutsideClick(event: MouseEvent) {
146-
const path = event.composedPath();
147-
if (!path.includes(this._searchInputContainer)) {
148-
if (this.expanded) {
149-
this.expanded = false;
150-
}
151-
}
152-
}
153-
154-
// Function to handle the closing of popover on focus out
155-
@bound private _onFocusOut() {
156-
if (this.expanded) {
157-
this.expanded = false;
158-
}
159118
}
160119

161120
/** List of options */
@@ -243,7 +202,7 @@ export class PfSearchInput extends LitElement {
243202

244203
async #doExpand() {
245204
try {
246-
await this.#float.show({ placement: this.position || 'bottom', flip: !!this.enableFlip });
205+
await this.#float.show({ placement: this.position || 'bottom', flip: true });
247206
return true;
248207
} catch {
249208
return false;

0 commit comments

Comments
 (0)