Et2Select + search: Fix some bugs

- Select a searched value didn't validate due to different attribute name
- et2-searchbox inside et2-select threw an additional change event, needed to catch that
- fix_bad_value() needs different handling when you can search, otherwise it just gets cleared again
This commit is contained in:
nathan 2022-08-31 13:27:10 -06:00
parent 2e5cc0de10
commit a3d2674757
3 changed files with 74 additions and 7 deletions

View File

@ -193,7 +193,10 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
_triggerChange(e)
{
this.dispatchEvent(new Event("change"));
if(super._triggerChange(e))
{
this.dispatchEvent(new Event("change"));
}
}
/**
@ -234,6 +237,11 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
// Nothing to do here
return;
}
// See if parent (search / free entry) is OK with it
if(super.fix_bad_value())
{
return;
}
// If no value is set, choose the first option
// Only do this on once during initial setup, or it can be impossible to clear the value
const valueArray = Array.isArray(this.value) ? this.value : (!this.value ? [] : this.value.toString().split(','));

View File

@ -140,6 +140,10 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
flex: 1 1 auto;
width: 100%;
}
/* Full width search textbox covers loading spinner, lift it up */
::slotted(sl-spinner) {
z-index: 2;
}
/* Don't show the current value while searching for single, we want the space
This lets the current value shrink to nothing so the input can expand
*/
@ -265,6 +269,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
this._handleClear = this._handleClear.bind(this);
this._handleDoubleClick = this._handleDoubleClick.bind(this);
this._handleSearchAbort = this._handleSearchAbort.bind(this);
this._handleSearchChange = this._handleSearchChange.bind(this);
this._handleSearchKeyDown = this._handleSearchKeyDown.bind(this);
this._handleEditKeyDown = this._handleEditKeyDown.bind(this);
}
@ -393,12 +398,12 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
protected get _searchInputNode() : HTMLInputElement
{
return this._activeControls.querySelector("#search");
return this._activeControls?.querySelector("#search");
}
protected get _editInputNode() : HTMLInputElement
{
return this._activeControls.querySelector("input#edit");
return this._activeControls?.querySelector("input#edit");
}
protected get _activeControls()
@ -454,6 +459,25 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
}
}
protected fix_bad_value()
{
if(!this.allowFreeEntries && !this.searchEnabled)
{
// Let regular select deal with it
return false;
}
const valueArray = Array.isArray(this.value) ? this.value : (!this.value ? [] : this.value.toString().split(','));
// Check any already found options
if(Object.values(this.menuItems).filter((option) => valueArray.find(val => val == option.value)).length === 0)
{
return false;
}
return true;
// TODO? Should we check the server, or just be OK with it? Passing the "current" value in sel_options makes sure the value is there
}
protected _bindListeners()
{
this.addEventListener("sl-select", this._handleSelect);
@ -468,6 +492,9 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
// selecting an option fires 2 change events - 1 before the widget is finished adjusting, losing the value
// We catch all change events, then call this._oldChange only when value changes
this.removeEventListener("change", this._oldChange);
this._searchInputNode.removeEventListener("change", this._searchInputNode.handleChange);
this._searchInputNode.addEventListener("change", this._handleSearchChange);
});
}
@ -476,6 +503,8 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
this.removeEventListener("sl-select", this._handleSelect);
this.removeEventListener("sl-clear", this._handleClear)
this.removeEventListener("change", this._handleChange);
this._searchInputNode?.removeEventListener("change", this._handleSearchChange);
}
handleMenuShow()
@ -523,6 +552,18 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
}
}
_triggerChange(event)
{
// Don't want searchbox events to trigger change event
if(event.target == this._searchInputNode)
{
event.stopImmediatePropagation();
event.preventDefault();
return false;
}
return true;
}
_handleChange(event)
{
if(event.target == this._searchInputNode)
@ -792,6 +833,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
render(html`${repeat(<SelectOption[]>entries, (option : SelectOption) => option.value, this._optionTemplate.bind(this))}`,
target
);
this.handleMenuSlotChange();
}
}
@ -951,6 +993,19 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
})
this.syncItemsFromValue();
}
/**
* et2-searchbox (SlInput) sends out an event on change.
* We don't care, and if we let it bubble it'll get in the way.
* @param e
* @protected
*/
protected _handleSearchChange(e)
{
e.stopImmediatePropagation();
e.preventDefault();
return false;
}
}
return Et2WidgetWithSearch as unknown as Constructor<SearchMixinInterface> & T;

View File

@ -152,19 +152,23 @@ class Taglist extends Etemplate\Widget
$type == 2 && $account_type == 'users' ||
in_array($account_type, array('owngroups', 'memberships')) &&
!in_array($val, $GLOBALS['egw']->accounts->memberships(
$GLOBALS['egw_info']['user']['account_id'], true))
$GLOBALS['egw_info']['user']['account_id'], true
)
)
)
{
self::set_validation_error($form_name, lang("'%1' is NOT allowed ('%2')!", $val,
!$type?'not found' : ($type == 1 ? 'user' : 'group')),'');
!$type ? 'not found' : ($type == 1 ? 'user' : 'group')
), ''
);
$value = '';
break;
}
continue;
}
if(count($allowed) && !$this->attrs['allowFreeEntries'] && empty($this->attrs['autocomplete_url']) && !array_key_exists($val,$allowed))
if(count($allowed) && !$this->attrs['allowFreeEntries'] && empty($this->attrs['searchUrl']) && !array_key_exists($val, $allowed))
{
self::set_validation_error($form_name,lang("'%1' is NOT allowed ('%2')!",$val,implode("','",array_keys($allowed))),'');
self::set_validation_error($form_name, lang("'%1' is NOT allowed ('%2')!", $val, implode("','", array_keys($allowed))), '');
unset($value[$key]);
}
if(str_contains($this->type, 'email') && $this->attrs['include_lists'] && is_numeric($val))