Et2TreeDropdown improvements

- multiple=false rendering
- fix some display search vs tree weirdness
- fix some keyboard actions
- translate placeholder
- blur widget after option selected when multiple=false
- implement clearable attribute
This commit is contained in:
nathan 2024-09-18 08:11:25 -06:00
parent 2e0010d138
commit 2da2ac81fa
5 changed files with 333 additions and 70 deletions

View File

@ -1043,7 +1043,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
> >
<sl-icon name="chevron-right" slot="expand-icon"></sl-icon> <sl-icon name="chevron-right" slot="expand-icon"></sl-icon>
<sl-icon name="chevron-down" slot="collapse-icon"></sl-icon> <sl-icon name="chevron-down" slot="collapse-icon"></sl-icon>
${repeat(this._selectOptions, this._optionTemplate)} ${repeat(this._selectOptions, (o) => o.value, this._optionTemplate)}
</sl-tree> </sl-tree>
`; `;
} }

View File

@ -9,6 +9,10 @@ export default css`
display: none; display: none;
} }
.form-control-input {
display: flex;
}
/* Label */ /* Label */
.form-control--has-label .form-control__label { .form-control--has-label .form-control__label {
@ -37,14 +41,26 @@ export default css`
margin-top: var(--sl-spacing-3x-small); margin-top: var(--sl-spacing-3x-small);
} }
.tree-dropdown__value-input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
opacity: 0;
z-index: -1;
}
.tree-dropdown__combobox { .tree-dropdown__combobox {
min-height: calc(var(--sl-input-height-medium) - 2 * var(--sl-input-border-width)); min-height: calc(var(--sl-input-height-medium) - 2 * var(--sl-input-border-width));
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: nowrap; flex-wrap: nowrap;
align-items: flex-start; align-items: center;
justify-content: space-between; justify-content: space-between;
vertical-align: middle;
background-color: var(--sl-input-background-color); background-color: var(--sl-input-background-color);
border: solid var(--sl-input-border-width) var(--sl-input-border-color); border: solid var(--sl-input-border-width) var(--sl-input-border-color);
@ -58,6 +74,12 @@ export default css`
transition: var(--sl-transition-fast) color, var(--sl-transition-fast) border, var(--sl-transition-fast) box-shadow, transition: var(--sl-transition-fast) color, var(--sl-transition-fast) border, var(--sl-transition-fast) box-shadow,
var(--sl-transition-fast) background-color; var(--sl-transition-fast) background-color;
cursor: pointer;
}
:host([multiple]) .tree-dropdown__combobox {
align-items: flex-start
} }
.tree-dropdown--disabled { .tree-dropdown--disabled {
@ -91,6 +113,7 @@ export default css`
transition: var(--sl-transition-medium) rotate ease; transition: var(--sl-transition-medium) rotate ease;
rotate: 0; rotate: 0;
margin-inline-start: var(--sl-spacing-small); margin-inline-start: var(--sl-spacing-small);
order: 99;
} }
.tree-dropdown--open .tree-dropdown__expand-icon { .tree-dropdown--open .tree-dropdown__expand-icon {
@ -103,9 +126,18 @@ export default css`
order: 1; order: 1;
} }
/* Single */
/* End single */
/* Tags */ /* Tags */
.tree-dropdown__tags { .tree-dropdown__tags {
display: none;
}
.tree-dropdown--multiple.tree-dropdown--has-value:not(.tree-dropdown--placeholder-visible) .tree-dropdown__tags {
display: flex; display: flex;
flex: 2 1 auto; flex: 2 1 auto;
flex-wrap: wrap; flex-wrap: wrap;
@ -116,6 +148,7 @@ export default css`
min-width: 0px; min-width: 0px;
} }
/* Limit tag size */ /* Limit tag size */
.tree_tag { .tree_tag {
@ -132,10 +165,6 @@ export default css`
/* Search box */ /* Search box */
:host([readonly]) .tree-dropdown__search {
display: none;
}
.tree-dropdown__search { .tree-dropdown__search {
flex: 1 1 7em; flex: 1 1 7em;
order: 10; order: 10;
@ -143,9 +172,10 @@ export default css`
border: none; border: none;
outline: none; outline: none;
color: var(--sl-input-color);
font-size: var(--sl-input-font-size-medium); font-size: var(--sl-input-font-size-medium);
padding-block: 0; padding-block: 0;
padding-inline: var(--sl-input-spacing-medium); cursor: inherit;
} }
.form-control--medium .tree-dropdown__search { .form-control--medium .tree-dropdown__search {
@ -153,6 +183,14 @@ export default css`
height: calc(var(--sl-input-height-medium) * 0.8); height: calc(var(--sl-input-height-medium) * 0.8);
} }
:host([open]) .tree-dropdown__search {
cursor: text;
}
:host(:not([open])) .tree-dropdown--has-value.tree-dropdown--multiple .tree-dropdown__search {
visibility: hidden;
}
.tree-dropdown--disabled .tree-dropdown__search { .tree-dropdown--disabled .tree-dropdown__search {
cursor: not-allowed; cursor: not-allowed;
} }
@ -161,17 +199,6 @@ export default css`
cursor: default; cursor: default;
} }
/* tag takes full width when widget is not multiple and has value and does not have focus */
:host(:not([multiple])) .tree-dropdown--has-value .tree-dropdown__search {
display: none;
}
:host(:not([multiple])) .tree-dropdown--focused .tree-dropdown__search,
:host(:not([multiple])) .tree-dropdown--open .tree-dropdown__search {
display: initial;
}
.tree-dropdown__suffix { .tree-dropdown__suffix {
order: 20; order: 20;
} }

View File

@ -11,9 +11,10 @@ import {SlPopup, SlRemoveEvent, SlTreeItem} from "@shoelace-style/shoelace";
import shoelace from "../Styles/shoelace"; import shoelace from "../Styles/shoelace";
import styles from "./Et2TreeDropdown.styles"; import styles from "./Et2TreeDropdown.styles";
import {Et2Tag} from "../Et2Select/Tag/Et2Tag"; import {Et2Tag} from "../Et2Select/Tag/Et2Tag";
import {SearchMixin, SearchResult, SearchResultsInterface} from "../Et2Widget/SearchMixin"; import {SearchMixin, SearchResult, SearchResultElement, SearchResultsInterface} from "../Et2Widget/SearchMixin";
import {Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget"; import {Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget";
import {Required} from "../Validators/Required"; import {Required} from "../Validators/Required";
import {SelectOption} from "../Et2Select/FindSelectOptions";
interface TreeSearchResults extends SearchResultsInterface<TreeItemData> interface TreeSearchResults extends SearchResultsInterface<TreeItemData>
@ -56,11 +57,26 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
]; ];
} }
/**
* List of properties that get translated
* @returns object
*/
static get translate()
{
return {
...super.translate,
placeholder: true,
}
}
/** Placeholder text to show as a hint when the select is empty. */ /** Placeholder text to show as a hint when the select is empty. */
@property() placeholder = ""; @property() placeholder = "";
@property({type: Boolean, reflect: true}) multiple = false; @property({type: Boolean, reflect: true}) multiple = false;
/** Adds a clear button when the dropdown is not empty. */
@property({type: Boolean}) clearable = false;
/** The component's help text. If you need to display HTML, use the `help-text` slot instead. */ /** The component's help text. If you need to display HTML, use the `help-text` slot instead. */
@property({attribute: 'help-text'}) helpText = ""; @property({attribute: 'help-text'}) helpText = "";
@ -122,6 +138,8 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
protected readonly hasSlotController = new HasSlotController(<LitElement><unknown>this, "help-text", "label"); protected readonly hasSlotController = new HasSlotController(<LitElement><unknown>this, "help-text", "label");
private __value : string[]; private __value : string[];
protected displayLabel = '';
constructor() constructor()
{ {
super(); super();
@ -133,11 +151,30 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
connectedCallback() connectedCallback()
{ {
super.connectedCallback(); super.connectedCallback();
document.addEventListener("click", this.handleDocumentClick);
} }
disconnectedCallback() disconnectedCallback()
{ {
super.disconnectedCallback(); super.disconnectedCallback();
document.removeEventListener("click", this.handleDocumentClick);
}
willUpdate(changedProperties)
{
super.willUpdate(changedProperties);
// Child tree not updating when our emptyLabel changes
if(this._tree && (changedProperties.has("select_options") || changedProperties.has("emptyLabel")))
{
let options = this.multiple || !this.emptyLabel ? this.select_options : [{
value: "",
label: this.emptyLabel
}, ...this.select_options];
this._tree._selectOptions = <TreeItemData[]>options;
this._tree.requestUpdate("_selectOptions");
}
} }
updated(changedProperties : PropertyValues) updated(changedProperties : PropertyValues)
@ -188,6 +225,17 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
const oldValue = this.__value; const oldValue = this.__value;
// Filter to make sure there are no trailing commas or duplicates // Filter to make sure there are no trailing commas or duplicates
this.__value = Array.from(new Set(<string[]>new_value.filter(v => v))); this.__value = Array.from(new Set(<string[]>new_value.filter(v => v)));
this.displayLabel = "";
if(!this.multiple)
{
const option = this.optionSearch(this.__value[0], this.select_options, 'value', 'children');
if(option)
{
this.displayLabel = option.label;
}
}
this.requestUpdate("value", oldValue); this.requestUpdate("value", oldValue);
} }
@ -199,47 +247,49 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
} }
get select_options() : SelectOption[]
{
return super.select_options;
}
set select_options(new_options : SelectOption[])
{
super.select_options = new_options;
// Overridden so we can update displayLabel in the case where value got set before selectOptions
if(this.value && !this.multiple)
{
const option = this.optionSearch(typeof this.value == "string" ? this.value : this.value[0], this.select_options, 'value', 'children');
if(option)
{
this.displayLabel = option.label;
}
}
}
/** Sets focus on the control. */ /** Sets focus on the control. */
focus(options? : FocusOptions) focus(options? : FocusOptions)
{ {
this.hasFocus = true; this.handleFocus();
// Should not be needed, but not firing the update
this.requestUpdate("hasFocus");
if(this._searchNode)
{
this._searchNode.focus(options);
}
} }
/** Removes focus from the control. */ /** Removes focus from the control. */
blur() blur()
{ {
this.open = false; this.handleBlur();
this.treeOrSearch = "tree";
this.hasFocus = false;
this._popup.active = false;
// Should not be needed, but not firing the update
this.requestUpdate("open");
this.requestUpdate("hasFocus");
this._searchNode.blur();
clearTimeout(this._searchTimeout);
} }
/** Shows the tree. */ /** Shows the tree. */
async show() async show()
{ {
if(this.open || this.disabled) if(this.readonly || this.disabled)
{ {
this.open = false; this.open = false;
this.requestUpdate("open", true); this.requestUpdate("open", true);
return undefined; return this.updateComplete;
} }
document.addEventListener("click", this.handleDocumentClick);
this.open = true; this.open = true;
this.requestUpdate("open", false) this.requestUpdate("open", false)
return this.updateComplete return this.updateComplete
@ -253,8 +303,6 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
return undefined; return undefined;
} }
document.removeEventListener("click", this.handleDocumentClick);
this.open = false; this.open = false;
this._popup.active = false; this._popup.active = false;
this._searchNode.value = ""; this._searchNode.value = "";
@ -311,10 +359,25 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
return super.localSearch(search, searchOptions, this.select_options); return super.localSearch(search, searchOptions, this.select_options);
} }
/**
* Toggles a search result's selected state
* Overridden to handle multiple attribute so only 1 result is selected
*/
protected toggleResultSelection(result : HTMLElement & SearchResultElement, force? : boolean)
{
if(!this.multiple)
{
this._resultNodes.forEach(t => t.selected = false);
}
super.toggleResultSelection(result, force);
}
protected searchResultSelected() protected searchResultSelected()
{ {
super.searchResultSelected(); super.searchResultSelected();
const oldValue = [...this.value];
if(this.multiple && typeof this.value !== "undefined") if(this.multiple && typeof this.value !== "undefined")
{ {
// Add in the new result(s), no duplicates // Add in the new result(s), no duplicates
@ -328,9 +391,50 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
// Done with search, show the tree // Done with search, show the tree
this.treeOrSearch = "tree"; this.treeOrSearch = "tree";
// Close the dropdown
this.hide(); // Close the dropdown, move on
this.requestUpdate("value"); if(!this.multiple || this.egw().preference("select_multiple_close") == "close")
{
this.blur();
}
else
{
this._tree.value = <string[]>this.value;
}
// Update values
this.updateComplete.then(() =>
{
this.dispatchEvent(new Event("change", {bubbles: true}));
});
this._tree.requestUpdate("value", oldValue);
this.requestUpdate("value", oldValue);
}
private handleClearClick(event : MouseEvent)
{
event.stopPropagation();
if(this.value.length > 0)
{
this.value = [];
this.displayInput.focus({preventScroll: true});
// Emit after update
this.updateComplete.then(() =>
{
this.emit('sl-clear');
this.emit('sl-input');
this.emit('sl-change');
});
}
}
private handleClearMouseDown(event : MouseEvent)
{
// Don't lose focus or propagate events when clicking the clear button
event.stopPropagation();
event.preventDefault();
} }
/** /**
@ -403,11 +507,60 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
event.preventDefault(); event.preventDefault();
this.hide() this.hide()
} }
else this.blur();
{ }
this.blur();
}
private handleFocus()
{
this.hasFocus = true;
// Should not be needed, but not firing the update
this.requestUpdate("hasFocus");
this.updateComplete.then(() =>
{
if(this._searchNode)
{
this._searchNode.focus();
}
else
{
this._tree.focus();
}
this.dispatchEvent(new Event("sl-focus"));
})
}
private handleBlur()
{
this.open = false;
this.treeOrSearch = "tree";
this.hasFocus = false;
this.resultsOpen = false;
this._popup.active = false;
// Should not be needed, but not firing the update
this.requestUpdate("resultsOpen");
this.requestUpdate("open");
this.requestUpdate("hasFocus");
this._searchNode?.blur();
clearTimeout(this._searchTimeout);
this.updateComplete.then(() =>
{
this.dispatchEvent(new Event("sl-blur"));
})
}
protected handleClick(event)
{
// Open if clicking somewhere in the widget
if(event.target.classList.contains("tree-dropdown__combobox"))
{
event.stopPropagation();
this.show();
this.handleFocus();
}
} }
private handleSearchFocus() private handleSearchFocus()
@ -419,12 +572,28 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
// Reset tags to not take focus // Reset tags to not take focus
this.setCurrentTag(null); this.setCurrentTag(null);
this.show();
}
private handleSearchBlur(event)
{
// Focus lost to some other internal component - ignore it
if(event.composedPath().includes(this.shadowRoot))
{
return;
}
this.handleBlur();
} }
handleSearchKeyDown(event) handleSearchKeyDown(event)
{ {
super.handleSearchKeyDown(event); super.handleSearchKeyDown(event);
if(event.key == "ArrowDown" && !this.open && !this.resultsOpen)
{
this.show();
}
// Left at beginning goes to tags // Left at beginning goes to tags
if(this._searchNode.selectionStart == 0 && event.key == "ArrowLeft") if(this._searchNode.selectionStart == 0 && event.key == "ArrowLeft")
{ {
@ -492,30 +661,31 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
this.value = [...this.value, id]; this.value = [...this.value, id];
} }
} }
this.requestUpdate("value", oldValue);
this.updateComplete.then(() => this.updateComplete.then(() =>
{ {
this.dispatchEvent(new Event("change", {bubbles: true})); this.dispatchEvent(new Event("change", {bubbles: true}));
}); });
if(!this.multiple) if(!this.multiple || this.egw().preference("select_multiple_close") == "close")
{ {
this.hide(); this.blur();
} }
} }
handleTriggerClick() handleTriggerClick(event)
{ {
event.stopPropagation();
this.hasFocus = true; this.hasFocus = true;
if(this.open) if(this.open)
{ {
this._popup.active = false; this._popup.active = false;
this._searchNode.value = ""; this._searchNode.value = "";
document.removeEventListener("click", this.handleDocumentClick);
} }
else else
{ {
this._popup.active = true; this._popup.active = true;
document.addEventListener("click", this.handleDocumentClick);
} }
this.open = this._popup.active; this.open = this._popup.active;
this.treeOrSearch = "tree"; this.treeOrSearch = "tree";
@ -523,7 +693,7 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
this.updateComplete.then(() => this.updateComplete.then(() =>
{ {
this._tree.style.minWidth = getComputedStyle(this).width; this._tree.style.minWidth = getComputedStyle(this).width;
this._tree.focus(); this.focus();
}) })
} }
@ -546,18 +716,49 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
inputTemplate() inputTemplate()
{ {
let placeholder = this.egw().lang("search");
if(this.disabled || this.readonly || (this.open && this.value))
{
placeholder = "";
}
else
{
placeholder = this.emptyLabel || this.placeholder;
}
return html` return html`
<input id="search" type="text" part="input" <input id="search" type="text" part="input"
class="tree-dropdown__search search__input" class="tree-dropdown__search search__input"
autocomplete="off" autocomplete="off"
spellcheck="false"
autocapitalize="off"
aria-controls="listbox"
aria-expanded=${this.open ? 'true' : 'false'}
aria-haspopup="listbox"
aria-labelledby="label"
aria-disabled=${this.disabled ? 'true' : 'false'}
aria-describedby="help-text"
role="combobox"
?disabled=${this.disabled} ?disabled=${this.disabled}
?readonly=${this.readonly} ?readonly=${this.readonly}
placeholder="${this.hasFocus || this.value.length > 0 || this.disabled || this.readonly ? "" : this.egw().lang(this.placeholder || this.emptyLabel)}" placeholder="${placeholder}"
tabindex="0" tabindex="0"
.value=${this.hasFocus ? "" : this.displayLabel}
@keydown=${this.handleSearchKeyDown} @keydown=${this.handleSearchKeyDown}
@blur=${() => {this.hasFocus = false;}} @blur=${this.handleSearchBlur}
@focus=${this.handleSearchFocus} @focus=${this.handleSearchFocus}
@paste=${this.handlePaste} @paste=${this.handlePaste}
/>
<input
class="tree-dropdown__value-input"
type="text"
?disabled=${this.disabled}
?required=${this.required}
.value=${Array.isArray(this.value) ? this.value.join(', ') : this.value}
tabindex="-1"
aria-hidden="true"
@focus=${this.handleFocus}
@blur=${this.handleBlur}
/> />
`; `;
} }
@ -578,15 +779,23 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
return literal`et2-tag`; return literal`et2-tag`;
} }
/**
* Shows the currently selected values as tags when multiple=true
*
* @returns {TemplateResult}
*/
tagsTemplate() tagsTemplate()
{ {
const value = this.getValueAsArray(); const value = this.getValueAsArray();
return html`${map(value, (value, index) => return html`
{ <div part="tags" class="tree-dropdown__tags">
// Deal with value that is not in options ${map(value, (value, index) =>
const option = this.optionSearch(value, this.select_options, 'value', 'children'); {
return option ? this.tagTemplate(option) : nothing; // Deal with value that is not in options
})}`; const option = this.optionSearch(value, this.select_options, 'value', 'children');
return option ? this.tagTemplate(option) : nothing;
})}
</div>`;
} }
tagTemplate(option : TreeItemData) tagTemplate(option : TreeItemData)
@ -633,8 +842,12 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
const hasLabel = this.label ? true : !!hasLabelSlot; const hasLabel = this.label ? true : !!hasLabelSlot;
const hasValue = this.value && this.value.length > 0; const hasValue = this.value && this.value.length > 0;
const hasHelpText = this.helpText ? true : !!hasHelpTextSlot; const hasHelpText = this.helpText ? true : !!hasHelpTextSlot;
const hasClearIcon = this.clearable && !this.disabled && this.value.length > 0;
const isPlaceholderVisible = (this.placeholder || this.emptyLabel) && this.value.length === 0 && !this.disabled && !this.readonly; const isPlaceholderVisible = (this.placeholder || this.emptyLabel) && this.value.length === 0 && !this.disabled && !this.readonly;
let options = this.multiple || !this.emptyLabel ? this.select_options : [{
value: "",
label: this.emptyLabel
}, ...this.select_options];
return html` return html`
<div <div
part="form-control" part="form-control"
@ -662,6 +875,7 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
'tree-dropdown--disabled': this.disabled, 'tree-dropdown--disabled': this.disabled,
'tree-dropdown--readonly': this.readonly, 'tree-dropdown--readonly': this.readonly,
'tree-dropdown--focused': this.hasFocus, 'tree-dropdown--focused': this.hasFocus,
'tree-dropdown--multiple': this.multiple,
'tree-dropdown--placeholder-visible': isPlaceholderVisible, 'tree-dropdown--placeholder-visible': isPlaceholderVisible,
'tree-dropdown--searching': this.treeOrSearch == "search", 'tree-dropdown--searching': this.treeOrSearch == "search",
'tree-dropdown--has-value': hasValue 'tree-dropdown--has-value': hasValue
@ -672,7 +886,6 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
auto-size-padding="10" auto-size-padding="10"
?active=${this.open} ?active=${this.open}
placement=${this.placement || "bottom"} placement=${this.placement || "bottom"}
stay-open-on-select
strategy="fixed" strategy="fixed"
?disabled=${this.disabled} ?disabled=${this.disabled}
> >
@ -681,12 +894,28 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
class="tree-dropdown__combobox" class="tree-dropdown__combobox"
slot="anchor" slot="anchor"
@keydown=${this.handleComboboxKeyDown} @keydown=${this.handleComboboxKeyDown}
@click=${this.handleClick}
> >
<slot part="prefix" name="prefix" class="tree-dropdown__prefix"></slot> <slot part="prefix" name="prefix" class="tree-dropdown__prefix"></slot>
<div part="tags" class="tree-dropdown__tags"> ${this.multiple ? this.tagsTemplate() : nothing}
${this.tagsTemplate()} ${this.inputTemplate()}
${this.inputTemplate()} ${hasClearIcon
</div> ? html`
<button
part="clear-button"
class="select__clear"
type="button"
aria-label=${this.localize.term('clearEntry')}
@mousedown=${this.handleClearMouseDown}
@click=${this.handleClearClick}
tabindex="-1"
>
<slot name="clear-icon">
<sl-icon name="x-circle-fill" library="system"></sl-icon>
</slot>
</button>
`
: ''}
<slot part="suffix" name="suffix" class="tree-dropdown__suffix"></slot> <slot part="suffix" name="suffix" class="tree-dropdown__suffix"></slot>
<slot name="expand-icon" part="expand-icon" class="tree-dropdown__expand-icon" <slot name="expand-icon" part="expand-icon" class="tree-dropdown__expand-icon"
@click=${this.handleTriggerClick}> @click=${this.handleTriggerClick}>
@ -702,7 +931,7 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
?readonly=${this.readonly} ?readonly=${this.readonly}
?disabled=${this.disabled} ?disabled=${this.disabled}
value=${this.multiple ? nothing : this.value} value=${this.multiple ? nothing : this.value}
._selectOptions=${this.select_options} ._selectOptions=${options}
.actions=${this.actions} .actions=${this.actions}
.styleTemplate=${() => this.styleTemplate()} .styleTemplate=${() => this.styleTemplate()}
.autoloading="${this.autoloading}" .autoloading="${this.autoloading}"

View File

@ -505,6 +505,11 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
{ {
this.value = [this.selectedResults[0]?.value] ?? []; this.value = [this.selectedResults[0]?.value] ?? [];
} }
// Dispatch the change event
this.updateComplete.then(() =>
{
this.dispatchEvent(new Event("change", {bubbles: true}));
});
*/ */
this.updateComplete.then(() => this.updateComplete.then(() =>

View File

@ -3797,6 +3797,8 @@ export class et2_nextmatch_header_bar extends et2_DOMWidget implements et2_INext
!select.select_options.filter(option => option.value === '').length) !select.select_options.filter(option => option.value === '').length)
{ {
select.emptyLabel = this.egw().lang('All categories'); select.emptyLabel = this.egw().lang('All categories');
// requestUpdate because widget is not firing update itself
select.requestUpdate("emptyLabel");
} }
select.requestUpdate("value"); select.requestUpdate("value");
}) })