Select / Search bugs:

- search result would only be shown once even if next search included it
- selected values disappear when starting a new search
- loading spinner was not shown
This commit is contained in:
nathan 2022-06-16 11:02:16 -06:00
parent 6b950900ee
commit 851a054599
2 changed files with 29 additions and 17 deletions

View File

@ -351,9 +351,10 @@ export class Et2Select extends Et2WithSearchMixin(Et2InvokerMixin(Et2WidgetWithS
<et2-image slot="prefix" part="icon" style="width: var(--icon-width)" <et2-image slot="prefix" part="icon" style="width: var(--icon-width)"
src="${option.icon}"></et2-image>` : ""; src="${option.icon}"></et2-image>` : "";
// Tag used must match this.optionTag, but you can't use the variable directly // Tag used must match this.optionTag, but you can't use the variable directly.
// Pass option along so SearchMixin can grab it if needed
return html` return html`
<sl-menu-item value="${option.value}" title="${option.title}" class="${option.class}"> <sl-menu-item value="${option.value}" title="${option.title}" class="${option.class}" .option=${option}>
${icon} ${icon}
${option.label} ${option.label}
</sl-menu-item>`; </sl-menu-item>`;

View File

@ -8,7 +8,7 @@
*/ */
import {css, html, LitElement, render, repeat, SlotMixin} from "@lion/core"; import {css, html, LitElement, render, SlotMixin} from "@lion/core";
import {cleanSelectOptions, SelectOption} from "./FindSelectOptions"; import {cleanSelectOptions, SelectOption} from "./FindSelectOptions";
import {Validator} from "@lion/form-core"; import {Validator} from "@lion/form-core";
import {Et2Tag} from "./Tag/Et2Tag"; import {Et2Tag} from "./Tag/Et2Tag";
@ -117,10 +117,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
// @ts-ignore // @ts-ignore
...(super.styles ? (Symbol.iterator in Object(super.styles) ? super.styles : [super.styles]) : []), ...(super.styles ? (Symbol.iterator in Object(super.styles) ? super.styles : [super.styles]) : []),
css` css`
/* Show / hide SlSelect icons - dropdown arrow, etc */
::slotted([slot="suffix"]) {
display: none;
}
:host([search]) ::slotted([slot="suffix"]) { :host([search]) ::slotted([slot="suffix"]) {
display: initial; display: initial;
} }
@ -139,7 +135,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
.select--standard.select--focused:not(.select--disabled) .select__control { .select--standard.select--focused:not(.select--disabled) .select__control {
box-shadow: initial; box-shadow: initial;
} }
:host([allowFreeEntries]) ::slotted([slot="suffix"]) { /* Show / hide SlSelect icons - dropdown arrow, etc but not loading spinner */
:host([allowFreeEntries]) ::slotted(sl-icon[slot="suffix"]) {
display: none; display: none;
} }
/* Make search textbox take full width */ /* Make search textbox take full width */
@ -206,6 +203,9 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
private _searchTimeout : number; private _searchTimeout : number;
protected static SEARCH_TIMEOUT = 500; protected static SEARCH_TIMEOUT = 500;
protected static MIN_CHARS = 2; protected static MIN_CHARS = 2;
// Hold the original option data from earlier search results, since we discard on subsequent search
private _selected_remote = <SelectOption[]>[];
/** /**
* These characters will end a free tag * These characters will end a free tag
* @type {string[]} * @type {string[]}
@ -367,12 +367,12 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
* *
* @protected * @protected
*/ */
protected get localItems() protected get localItems() : NodeList
{ {
return this.querySelectorAll(this.optionTag + ":not(.remote)"); return this.querySelectorAll(this.optionTag + ":not(.remote)");
} }
protected get remoteItems() protected get remoteItems() : NodeList
{ {
return this.querySelectorAll(this.optionTag + ".remote"); return this.querySelectorAll(this.optionTag + ".remote");
} }
@ -476,6 +476,12 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
*/ */
_handleSelect(event) _handleSelect(event)
{ {
// Need to keep the remote option - only if selected
if(event.detail.item.classList.contains("remote") && !this._selected_remote.find(o => o.value == event.detail.item.value))
{
this._selected_remote.push({...event.detail.item.option});
}
// If they just chose one from the list, re-focus the search // If they just chose one from the list, re-focus the search
if(this.multiple && this.searchEnabled) if(this.multiple && this.searchEnabled)
{ {
@ -497,6 +503,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
*/ */
_handleClear() _handleClear()
{ {
this._selected_remote = [];
if(!this.multiple && this.searchEnabled) if(!this.multiple && this.searchEnabled)
{ {
// Restore label styling // Restore label styling
@ -514,6 +522,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
*/ */
protected _handleSearchKeyDown(event : KeyboardEvent) protected _handleSearchKeyDown(event : KeyboardEvent)
{ {
clearTimeout(this._searchTimeout);
this._activeControls?.classList.add("active"); this._activeControls?.classList.add("active");
this.dropdown.show(); this.dropdown.show();
@ -533,20 +542,20 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
{ {
this.dropdown.hide(); this.dropdown.hide();
} }
} }
else if(event.key == "Enter") else if(event.key == "Enter")
{ {
event.preventDefault(); event.preventDefault();
this.startSearch(); this.startSearch();
return;
} }
else if(event.key == "Escape") else if(event.key == "Escape")
{ {
this._handleSearchAbort(event); this._handleSearchAbort(event);
return;
} }
// Start the search automatically if they have enough letters // Start the search automatically if they have enough letters
clearTimeout(this._searchTimeout);
if(this._searchInputNode.value.length >= Et2WidgetWithSearch.MIN_CHARS) if(this._searchInputNode.value.length >= Et2WidgetWithSearch.MIN_CHARS)
{ {
this._searchTimeout = window.setTimeout(() => {this.startSearch()}, Et2WidgetWithSearch.SEARCH_TIMEOUT); this._searchTimeout = window.setTimeout(() => {this.startSearch()}, Et2WidgetWithSearch.SEARCH_TIMEOUT);
@ -577,6 +586,9 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
*/ */
public startSearch() public startSearch()
{ {
// Stop timeout timer
clearTimeout(this._searchTimeout);
// Show a spinner instead of search button // Show a spinner instead of search button
this._searchButtonNode.style.display = "hidden"; this._searchButtonNode.style.display = "hidden";
let spinner = document.createElement("sl-spinner"); let spinner = document.createElement("sl-spinner");
@ -622,9 +634,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
*/ */
protected remoteSearch(search : string, options : object) protected remoteSearch(search : string, options : object)
{ {
// Remove existing remote items
this.remoteItems.forEach(i => i.remove());
if(!this.searchUrl) if(!this.searchUrl)
{ {
return Promise.resolve(); return Promise.resolve();
@ -670,7 +679,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
if(target) if(target)
{ {
// Keep local options first, add in remote options // Keep local options first, add in remote options
this.select_options.filter(function(item) // Include already selected remote entries, or they will be removed and we lose icon/class
this.select_options.concat(this._selected_remote).filter(function(item)
{ {
let i = entries.findIndex(x => (x.value == item.value)); let i = entries.findIndex(x => (x.value == item.value));
if(i <= -1) if(i <= -1)
@ -680,7 +690,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
return null; return null;
}); });
render(html`${repeat(<SelectOption[]>entries, (option : SelectOption) => option.value, this._optionTemplate.bind(this))}`, //render(html`${repeat(<SelectOption[]>entries, (option : SelectOption) => option.value, this._optionTemplate.bind(this))}`,
render(entries.map((option) => this._optionTemplate(option)),
target target
); );
} }