mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-07 16:44:20 +01:00
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:
parent
2e5cc0de10
commit
a3d2674757
@ -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(','));
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user