More fixing of missing search options / tags

Fixes new free entries don't show up after removing a free entry
This commit is contained in:
nathan 2022-09-22 11:15:25 -06:00
parent 0424836ba8
commit 5a8f7c3c70
2 changed files with 78 additions and 15 deletions

View File

@ -8,7 +8,7 @@
*/ */
import {Et2InputWidget, Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget"; import {Et2InputWidget, Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget";
import {html, LitElement, PropertyValues, render, repeat, TemplateResult} from "@lion/core"; import {html, LitElement, PropertyValues, render, TemplateResult} from "@lion/core";
import {et2_readAttrWithDefault} from "../et2_core_xml"; import {et2_readAttrWithDefault} from "../et2_core_xml";
import {cleanSelectOptions, find_select_options, SelectOption} from "./FindSelectOptions"; import {cleanSelectOptions, find_select_options, SelectOption} from "./FindSelectOptions";
import {SearchMixinInterface} from "./SearchMixin"; import {SearchMixinInterface} from "./SearchMixin";
@ -134,13 +134,39 @@ export const Et2widgetWithSelectMixin = <T extends Constructor<LitElement>>(supe
protected _renderOptions() protected _renderOptions()
{ {
// Add in options as children to the target node // Add in options as children to the target node
if(this._optionTargetNode) if(!this._optionTargetNode)
{ {
render(html`${this._emptyLabelTemplate()} return Promise.resolve();
${repeat(<SelectOption[]>this.select_options, (option : SelectOption) => option.value, this._optionTemplate.bind(this))}`,
this._optionTargetNode
);
} }
/**
* 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, 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");
let options = html`${this._emptyLabelTemplate()}${this.select_options.map(this._optionTemplate.bind(this))}`;
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(!this._optionTargetNode.querySelector("[value='" + item.value + "']"))
{
this._optionTargetNode.appendChild(item);
}
});
if(typeof this.handleMenuSlotChange == "function")
{
this.handleMenuSlotChange();
}
});
} }
/** /**

View File

@ -437,11 +437,46 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
return this.querySelectorAll(this.optionTag + ":not(.remote)"); return this.querySelectorAll(this.optionTag + ":not(.remote)");
} }
/**
* Only remote options from search results
* @returns {NodeList}
* @protected
*/
protected get remoteItems() : NodeList protected get remoteItems() : NodeList
{ {
return this.querySelectorAll(this.optionTag + ".remote"); return this.querySelectorAll(this.optionTag + ".remote");
} }
/**
* Only free entries
* @returns {NodeList}
* @protected
*/
protected get freeEntries() : NodeList
{
return this.querySelectorAll(this.optionTag + ".freeEntry");
}
get search_options() : SelectOption[]
{
let options = [];
if(this.allowFreeEntries)
{
this.freeEntries.forEach((item) =>
{
options.push({value: item.value, label: item.textContent, class: item.classList.toString()});
})
}
// Any provided options
options = options.concat(this.__search_options);
// Any kept remote options
options = options.concat(this._selected_remote);
return options;
}
get value() get value()
{ {
return super.value; return super.value;
@ -897,6 +932,11 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
// Remove "no suggestions" // Remove "no suggestions"
target.querySelector(".no-results")?.remove(); target.querySelector(".no-results")?.remove();
// Remove any previously selected remote options that aren't used anymore
this._selected_remote = this._selected_remote.filter((option) =>
{
return this.value.indexOf(option.value) != -1;
});
// Remove remote options that aren't used // Remove remote options that aren't used
let keepers = this._selected_remote.reduce((prev, current) => let keepers = this._selected_remote.reduce((prev, current) =>
{ {
@ -1005,9 +1045,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
let target = this._optionTargetNode || this; let target = this._optionTargetNode || this;
if(target) if(target)
{ {
// Keep local options first, add in remote options // Add in remote options, avoiding duplicates
// Include already selected remote entries, or they will be removed and we lose icon/class this.select_options.filter(function(item)
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)
@ -1020,11 +1059,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
let options = html`${entries.map(this._optionTemplate.bind(this))}`; let options = html`${entries.map(this._optionTemplate.bind(this))}`;
/** /**
* Doing all this garbage to get the options to always show up. * Add in new options.
* If we just do `render(options, target)`, they only show up in the DOM the first time. If the * Rendering directly into target will remove existing options, which we don't need to do
* 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"); let temp_target = document.createElement("div");
@ -1087,7 +1123,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
{ {
this.__select_options.push(<SelectOption>{ this.__select_options.push(<SelectOption>{
value: text, value: text,
label: text label: text,
class: "freeEntry"
}); });
this.requestUpdate('select_options'); this.requestUpdate('select_options');
} }