Et2Select + SearchMixin: Fix some bugs with display and selecting entries

- Fix clicking on text while editing triggered re-display
- Avoid unneeded node re-creation when editing free entry, handle empty value as removal
multiple=true:
- Fix clearing search removed previously selected remote results
- Fix adding a free entry did not move search down, covering the new value
multiple=false:
- Fix search & edit inputs were shown at the same time
- Fix edit free entry always took over making it impossible to search
This commit is contained in:
nathan 2022-09-21 17:13:29 -06:00
parent e6ce33b153
commit 2952df2799

View File

@ -348,6 +348,12 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
*/ */
protected _addNodes() protected _addNodes()
{ {
if(this._activeControls)
{
// Already there
this._activeControls.remove();
}
const div = document.createElement("div"); const div = document.createElement("div");
div.classList.add("search_input"); div.classList.add("search_input");
render(this._searchInputTemplate(), div); render(this._searchInputTemplate(), div);
@ -372,6 +378,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
{ {
edit = html`<input id="edit" type="text" part="input" style="width:100%" edit = html`<input id="edit" type="text" part="input" style="width:100%"
@keydown=${this._handleEditKeyDown} @keydown=${this._handleEditKeyDown}
@click=${(e) => e.stopPropagation()}
@blur=${this.stopEdit.bind(this)} @blur=${this.stopEdit.bind(this)}
/>`; />`;
} }
@ -534,9 +541,14 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
this._activeControls?.classList.add("active"); this._activeControls?.classList.add("active");
this._searchInputNode.focus(); this._searchInputNode.focus();
this._searchInputNode.select(); this._searchInputNode.select();
// Hide edit explicitly since it's so hard via CSS
if(this._editInputNode)
{
this._editInputNode.style.display = "none";
}
} }
if(this.editModeEnabled && this.allowFreeEntries && !this.multiple) if(this.editModeEnabled && this.allowFreeEntries && !this.multiple && this.value)
{ {
this.startEdit(); this.startEdit();
this._editInputNode.select(); this._editInputNode.select();
@ -588,6 +600,17 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
return; return;
} }
super.handleMenuHide(); super.handleMenuHide();
// Reset display
if(this._searchInputNode)
{
this._searchInputNode.style.display = "";
}
if(this._editInputNode)
{
this._editInputNode.style.display = "";
}
if(this.searchEnabled || this.allowFreeEntries) if(this.searchEnabled || this.allowFreeEntries)
{ {
this._activeControls?.classList.remove("active"); this._activeControls?.classList.remove("active");
@ -657,6 +680,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
if(this._activeControls.classList.contains("novalue")) if(this._activeControls.classList.contains("novalue"))
{ {
this.handleMenuShow(); this.handleMenuShow();
this._handleAfterShow();
} }
// Scroll the new tag into view // Scroll the new tag into view
@ -682,7 +706,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
*/ */
_handleClear(e) _handleClear(e)
{ {
this._selected_remote = []; // Only keep remote options that are still used
this._selected_remote = this._selected_remote.filter((option) => this.getValueAsArray().indexOf(option.value) !== -1);
if(!this.multiple && this.searchEnabled) if(!this.multiple && this.searchEnabled)
{ {
@ -709,7 +734,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
if(event.relatedTarget && this !== (<Element>event.relatedTarget).parentElement) if(event.relatedTarget && this !== (<Element>event.relatedTarget).parentElement)
{ {
await this.dropdown.hide(); await this.dropdown.hide();
if(event.relatedTarget) if(event.relatedTarget && event.relatedTarget !== this)
{ {
event.relatedTarget.focus(); event.relatedTarget.focus();
} }
@ -739,14 +764,15 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
{ {
event.preventDefault(); event.preventDefault();
this._searchInputNode.value = ""; this._searchInputNode.value = "";
if(!this.multiple) this.dropdown.hide().then(async() =>
{ {
this.dropdown.hide(); // update sizing / position before getting ready for another one
} if(this.multiple)
else {
{ await this.dropdown.show();
this._searchInputNode.focus(); this._searchInputNode.focus();
} }
});
} }
else if(event.key == "Enter") else if(event.key == "Enter")
{ {
@ -1057,7 +1083,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
return false; return false;
} }
// Make sure not to double-add // Make sure not to double-add
if(!this.select_options.find(o => o.value == text) && !this.__select_options.find(o => o.value == text)) if(!this.querySelector("[value='" + text + "']") && !this.__select_options.find(o => o.value == text))
{ {
this.__select_options.push(<SelectOption>{ this.__select_options.push(<SelectOption>{
value: text, value: text,
@ -1079,7 +1105,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
// If we were overlapping edit inputbox with the value display, reset // If we were overlapping edit inputbox with the value display, reset
if(!this.readonly && this._activeControls?.classList.contains("novalue")) if(!this.readonly && this._activeControls?.classList.contains("novalue"))
{ {
this.handleMenuShow(); this._searchInputNode.style.display = "";
} }
return true; return true;
} }
@ -1116,17 +1142,10 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
this._activeControls.classList.add("editing", "active"); this._activeControls.classList.add("editing", "active");
// Pre-set value to tag value // Pre-set value to tag value
this._editInputNode.style.display = "";
this._editInputNode.value = tag_value this._editInputNode.value = tag_value
this._editInputNode.focus(); this._editInputNode.focus();
// Remove from value & DOM. If they finish the edit, the new one will be added.
if(this.multiple)
{
this.value = this.value.filter(v => v !== tag_value);
this.querySelector("[value='" + tag_value + "']").remove();
}
this.select_options = this.select_options.filter(v => v.value !== tag_value);
this.querySelector("[value='" + tag_value + "']")?.remove();
if(tag) if(tag)
{ {
tag.remove(); tag.remove();
@ -1149,15 +1168,49 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
abort = false; abort = false;
} }
let value = abort ? this._editInputNode.dataset.initial : this._editInputNode.value; const original = this._editInputNode.dataset.initial;
this.createFreeEntry(value);
delete this._editInputNode.dataset.initial; delete this._editInputNode.dataset.initial;
let value = abort ? original : this._editInputNode.value;
this._editInputNode.value = "";
if(value && value != original)
{
this.createFreeEntry(value);
this.updateComplete.then(() =>
{
const item = this.querySelector("[value='" + value + "']");
item.dispatchEvent(new CustomEvent("sl-select", {detail: {item}}));
})
}
// Remove original from value & DOM
if(value != original)
{
if(this.multiple)
{
this.value = this.value.filter(v => v !== original);
this.querySelector("[value='" + original + "']")?.remove();
}
else
{
this.value = value;
}
this.select_options = this.select_options.filter(v => v.value !== original);
}
this._activeControls.classList.remove("editing", "active"); this._activeControls.classList.remove("editing", "active");
if(!this.multiple) if(!this.multiple)
{ {
this.dropdown.hide(); this.updateComplete.then(async() =>
{
// Don't know why, but this doesn't always work leaving the value hidden by prefix
await this.dropdown.hide();
this.dropdown.classList.remove("select--open");
this.dropdown.panel.setAttribute("hidden", "");
});
} }
} }