Break out SearchMixin.searchMatch() to allow for easy overriding of how local search determines a match.

Currently we search value, label & title
This commit is contained in:
nathan 2024-02-26 15:15:17 -07:00
parent 11f9196b42
commit 824f6416ca

View File

@ -73,6 +73,15 @@ export declare class SearchMixinInterface<DataType extends SearchResult, Results
*/ */
startSearch() : Promise<void> startSearch() : Promise<void>
/**
* Check to see if a local search result matches the search string
*
* @param {string} search
* @param {DataType} result
* @returns {boolean}
*/
searchMatch<DataType>(search : string, result : DataType) : boolean
/** /**
* Search local options * Search local options
*/ */
@ -214,7 +223,7 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
// Element where we render the search results // Element where we render the search results
protected get _listNode() : HTMLElement { return this.shadowRoot.querySelector("#listbox");} protected get _listNode() : HTMLElement { return this.shadowRoot.querySelector("#listbox");}
protected get _resultNodes() : (LitElement & SearchResultElement)[] { return this._listNode ? Array.from(this._listNode.querySelectorAll("*:not(div)")) : [];} protected get _resultNodes() : (LitElement & SearchResultElement)[] { return this._listNode ? Array.from(this._listNode.querySelectorAll(":scope > :not(div)")) : [];}
constructor(...args : any[]) constructor(...args : any[])
{ {
@ -277,6 +286,35 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
return [this.value]; return [this.value];
} }
/**
* Check if one of our [local] items matches the search
*
* @param search
* @param item
* @returns {boolean}
* @protected
*/
public searchMatch<DataType extends SearchResult>(search : string, option : DataType) : boolean
{
if(!option || !option.value)
{
return false;
}
// Search all string fields
let searchString = search.toLowerCase();
const searchFields = ["label", "value", "title"]
for(let i = 0; i < searchFields.length; i++)
{
let field = searchFields[i];
if(option[field]?.toLowerCase().includes(searchString))
{
return true;
}
}
return false;
}
/** /**
* If you have a local list of options, you can search through them on the client and include them in the results. * If you have a local list of options, you can search through them on the client and include them in the results.
* *
@ -293,7 +331,7 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
results: <DataType[]>[], results: <DataType[]>[],
total: 0 total: 0
} }
let doSearch = function <DataType extends SearchResult>(options : DataType[], value : string) let doSearch = <DataType extends SearchResult>(options : DataType[], value : string) =>
{ {
options.forEach((option) => options.forEach((option) =>
{ {
@ -301,7 +339,7 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
{ {
return; return;
} }
if(option.label?.includes(value) || option.value?.includes(value)) if(this.searchMatch<DataType>(value, option))
{ {
local.results.push(option); local.results.push(option);
local.total++; local.total++;
@ -311,7 +349,7 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
return doSearch(option.children, value); return doSearch(option.children, value);
} }
}); });
} };
doSearch(localOptions, search); doSearch(localOptions, search);
return Promise.resolve(this.processLocalResults(local)); return Promise.resolve(this.processLocalResults(local));
@ -520,6 +558,12 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
this.resultsOpen = false; this.resultsOpen = false;
this._searchNode.focus(); this._searchNode.focus();
} }
else if([" ", "Enter"].includes(event.key) && this.currentResult)
{
event.preventDefault();
this.currentResult.selected = true;
this.searchResultSelected();
}
} }