mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-29 01:59:39 +01:00
Et2Search: Fix some UI bugs
- Fix missing loading spinner while searching Clear button was in the way, now hiding clear button when displaying the spinner - Fix searching for the same thing twice displays no results the second time repeat() and map() directives were not putting the DOM nodes back in, worked around by using a temp div to render into. There were some lifecycle mistakes as well leading to collisions & re-doing.
This commit is contained in:
parent
f9ced2c834
commit
56eb4d64ac
@ -118,6 +118,8 @@ export const Et2widgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
|
|||||||
{
|
{
|
||||||
// Add in options as children to the target node
|
// Add in options as children to the target node
|
||||||
this._renderOptions();
|
this._renderOptions();
|
||||||
|
|
||||||
|
// This is needed to display initial load value in some cases, like infolog nm header filters
|
||||||
if(this.handleMenuSlotChange)
|
if(this.handleMenuSlotChange)
|
||||||
{
|
{
|
||||||
this.handleMenuSlotChange();
|
this.handleMenuSlotChange();
|
||||||
|
@ -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";
|
||||||
@ -268,7 +268,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
*/
|
*/
|
||||||
this.defaultValidators = [];
|
this.defaultValidators = [];
|
||||||
|
|
||||||
this._handleSelect = this._handleSelect.bind(this);
|
this.handleMenuSelect = this.handleMenuSelect.bind(this);
|
||||||
this._handleChange = this._handleChange.bind(this);
|
this._handleChange = this._handleChange.bind(this);
|
||||||
this._handleSearchBlur = this._handleSearchBlur.bind(this);
|
this._handleSearchBlur = this._handleSearchBlur.bind(this);
|
||||||
this._handleClear = this._handleClear.bind(this);
|
this._handleClear = this._handleClear.bind(this);
|
||||||
@ -450,16 +450,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
{
|
{
|
||||||
this.createFreeEntry(this.value);
|
this.createFreeEntry(this.value);
|
||||||
}
|
}
|
||||||
else if(this.multiple)
|
|
||||||
{
|
|
||||||
this.value.forEach((e) =>
|
|
||||||
{
|
|
||||||
if(!this._menuItems.find(o => o.value == e && !o.classList.contains('remote')))
|
|
||||||
{
|
|
||||||
this.createFreeEntry(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fix_bad_value()
|
protected fix_bad_value()
|
||||||
@ -483,7 +473,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
|
|
||||||
protected _bindListeners()
|
protected _bindListeners()
|
||||||
{
|
{
|
||||||
this.addEventListener("sl-select", this._handleSelect);
|
|
||||||
this.addEventListener("sl-clear", this._handleClear)
|
this.addEventListener("sl-clear", this._handleClear)
|
||||||
|
|
||||||
// Need our own change to catch the change event from search input
|
// Need our own change to catch the change event from search input
|
||||||
@ -606,41 +595,45 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
/**
|
/**
|
||||||
* An option was selected
|
* An option was selected
|
||||||
*/
|
*/
|
||||||
_handleSelect(event)
|
handleMenuSelect(event)
|
||||||
{
|
{
|
||||||
// Need to keep the remote option - only if selected
|
// 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))
|
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});
|
this._selected_remote.push({...event.detail.item.option});
|
||||||
}
|
}
|
||||||
|
super.handleMenuSelect(event);
|
||||||
|
|
||||||
// If they just chose one from the list, re-focus the search
|
this.updateComplete.then(() =>
|
||||||
if(this.multiple && this.searchEnabled)
|
|
||||||
{
|
{
|
||||||
this._searchInputNode.focus();
|
// If they just chose one from the list, re-focus the search
|
||||||
this._searchInputNode.select();
|
if(this.multiple && this.searchEnabled)
|
||||||
|
|
||||||
// If we were overlapping, reset
|
|
||||||
if(this._activeControls.classList.contains("novalue"))
|
|
||||||
{
|
{
|
||||||
this.handleMenuShow();
|
this._searchInputNode.focus();
|
||||||
}
|
this._searchInputNode.select();
|
||||||
|
|
||||||
// Scroll the new tag into view
|
// If we were overlapping, reset
|
||||||
if(event.detail && event.detail.item)
|
if(this._activeControls.classList.contains("novalue"))
|
||||||
{
|
|
||||||
this.updateComplete.then(() =>
|
|
||||||
{
|
{
|
||||||
this.shadowRoot.querySelector("et2-tag[value='" + event.detail.item.value + "']")?.scrollIntoView();
|
this.handleMenuShow();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Scroll the new tag into view
|
||||||
|
if(event.detail && event.detail.item)
|
||||||
|
{
|
||||||
|
this.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
this.shadowRoot.querySelector("et2-tag[value='" + event.detail.item.value + "']")?.scrollIntoView();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else if(!this.multiple && this.searchEnabled)
|
||||||
else if(!this.multiple && this.searchEnabled)
|
{
|
||||||
{
|
// Stop all the search stuff when they select an option
|
||||||
// Stop all the search stuff when they select an option
|
// this shows all non-matching options again
|
||||||
// this shows all non-matching options again
|
this._handleSearchAbort(event);
|
||||||
this._handleSearchAbort(event);
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -675,7 +668,10 @@ 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();
|
||||||
event.relatedTarget.focus();
|
if(event.relatedTarget)
|
||||||
|
{
|
||||||
|
event.relatedTarget.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -764,6 +760,13 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
spinner.slot = "suffix";
|
spinner.slot = "suffix";
|
||||||
this.appendChild(spinner);
|
this.appendChild(spinner);
|
||||||
|
|
||||||
|
// Hide clear button
|
||||||
|
let clear_button = <HTMLElement>this._searchInputNode.shadowRoot.querySelector(".input__clear")
|
||||||
|
if(clear_button)
|
||||||
|
{
|
||||||
|
clear_button.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
// Start the searches
|
// Start the searches
|
||||||
Promise.all([
|
Promise.all([
|
||||||
this.localSearch(this._searchInputNode.value, this.searchOptions),
|
this.localSearch(this._searchInputNode.value, this.searchOptions),
|
||||||
@ -771,6 +774,11 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
]).then(() =>
|
]).then(() =>
|
||||||
{
|
{
|
||||||
spinner.remove();
|
spinner.remove();
|
||||||
|
// Restore clear button
|
||||||
|
if(clear_button)
|
||||||
|
{
|
||||||
|
clear_button.style.display = "";
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -791,6 +799,13 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
return ":not([value='" + current.value + "'])";
|
return ":not([value='" + current.value + "'])";
|
||||||
}, "");
|
}, "");
|
||||||
target.querySelectorAll(".remote" + keepers).forEach(o => o.remove());
|
target.querySelectorAll(".remote" + keepers).forEach(o => o.remove());
|
||||||
|
target.childNodes.forEach((n) =>
|
||||||
|
{
|
||||||
|
if(n.nodeType == Node.COMMENT_NODE)
|
||||||
|
{
|
||||||
|
n.remove();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Reset remaining options. It might be faster to re-create instead.
|
// Reset remaining options. It might be faster to re-create instead.
|
||||||
this._menuItems.forEach((item) =>
|
this._menuItems.forEach((item) =>
|
||||||
@ -899,10 +914,32 @@ 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))}`,
|
let options = html`${entries.map(this._optionTemplate.bind(this))}`;
|
||||||
target
|
|
||||||
);
|
/**
|
||||||
this.handleMenuSlotChange();
|
* Doing all this garbage to get the options to always show up.
|
||||||
|
* If we just do `render(options, target)`, they only show up in the DOM the first time. If the
|
||||||
|
* same option comes back in a subsequent search, it map() does not put it into the DOM.
|
||||||
|
* If we render into a new target, the options get rendered, but we have to wait for them to be
|
||||||
|
* rendered before we can do anything else with them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let temp_target = document.createElement("div");
|
||||||
|
|
||||||
|
render(options, temp_target);
|
||||||
|
return Promise.all(([...temp_target.querySelectorAll(":scope > *")].map(item => item.render)))
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
|
temp_target.querySelectorAll(":scope > *").forEach((item) =>
|
||||||
|
{
|
||||||
|
// Avoid duplicate error
|
||||||
|
if(!target.querySelector("[value='" + item.value + "']"))
|
||||||
|
{
|
||||||
|
target.appendChild(item);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.handleMenuSlotChange();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -962,11 +999,6 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
this.value = text;
|
this.value = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once added to options, add to value / tags
|
|
||||||
this.updateComplete.then(() =>
|
|
||||||
{
|
|
||||||
this.handleMenuSlotChange();
|
|
||||||
});
|
|
||||||
// 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"))
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user