UPDATED: Implemented functionality to hide the dropdown when clicking outside of it.
This is a simplified implementation of the "searchable combobox" concept suggested by Damecek in the comments.
It utilizes the standard lightning-input component (set to type "search") and the SLDS combobox options blueprint to mimic the standard appearance. It includes the basic functionality of displaying all options when the input is focused (as the default combobox does) and filters picklist options based on the user's input.
HTML
<template>
<div class="slds-card slds-p-around_medium">
<div class="slds-form-element">
<div class="slds-form-element__control">
<lightning-input
label="Searchable Combobox"
onchange={search}
onfocus={showPickListOptions}
type="search"
value={selectedValue}
>
</lightning-input>
<div
if:true={searchResults}
class="slds-dropdown slds-dropdown_length-5 slds-dropdown_fluid"
role="listbox"
>
<ul class="slds-listbox slds-listbox_vertical" role="presentation">
<template for:each={searchResults} for:item="searchResult">
<li
key={searchResult.value}
data-value={searchResult.value}
role="presentation"
onclick={selectSearchResult}
class="slds-listbox__item"
>
<div
class="slds-media slds-listbox__option slds-listbox__option_plain slds-media_small"
role="option"
>
<span class="slds-media__body">
<span
class="slds-truncate"
title={searchResult.label}
>
{searchResult.label}
</span>
</span>
</div>
</li>
</template>
</ul>
</div>
</div>
</div>
</div>
</template>
JavaScript
import { LightningElement } from "lwc";
import getAccounts from "@salesforce/apex/SearchableComboboxController.getAccounts";
export default class SearchableCombobox extends LightningElement {
isListening = false;
pickListOrdered;
searchResults;
selectedSearchResult;
get selectedValue() {
return this.selectedSearchResult?.label ?? null;
}
connectedCallback() {
getAccounts().then((result) => {
this.pickListOrdered = result.sort((a, b) =>
a.label.localeCompare(b.label)
);
});
}
renderedCallback() {
if (this.isListening) return;
window.addEventListener("click", (event) => {
this.hideDropdown(event);
});
this.isListening = true;
}
/**
* This function compares the name of the component (`cmpName`) with the name of the clicked element (`clickedElementSrcName`).
* If the clicked element is outside the component, the dropdown (search results) is hidden by calling `clearSearchResults()`.
*
* - `cmpName` is the tag name of the host element of this component (e.g., 'C-SEARCHABLE-COMBOBOX').
* - `clickedElementSrcName` is the tag name of the element that was clicked on the page.
* - `isClickedOutside` is a boolean that is true if the clicked element is outside the component.
*/
hideDropdown(event) {
const cmpName = this.template.host.tagName;
const clickedElementSrcName = event.target.tagName;
const isClickedOutside = cmpName !== clickedElementSrcName;
if (this.searchResults && isClickedOutside) {
this.clearSearchResults();
}
}
search(event) {
const input = event.detail.value.toLowerCase();
const result = this.pickListOrdered.filter((pickListOption) =>
pickListOption.label.toLowerCase().includes(input)
);
this.searchResults = result;
}
selectSearchResult(event) {
const selectedValue = event.currentTarget.dataset.value;
this.selectedSearchResult = this.pickListOrdered.find(
(pickListOption) => pickListOption.value === selectedValue
);
this.clearSearchResults();
}
clearSearchResults() {
this.searchResults = null;
}
showPickListOptions() {
if (!this.searchResults) {
this.searchResults = this.pickListOrdered;
}
}
}
Demo and source code
Source Code