mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-30 20:04:30 +01:00
Et2Email: WIP - Most interactions done
This commit is contained in:
parent
580466f9b8
commit
a60844d45a
@ -1,6 +1,18 @@
|
|||||||
import {css} from 'lit';
|
import {css} from 'lit';
|
||||||
|
|
||||||
export default css`
|
export default css`
|
||||||
|
:host([open]) {
|
||||||
|
/* Handles z-index issues with toolbar of html editor on the page*/
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control-input {
|
||||||
|
/* This allows the dropdown to show over other inputs */
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.email .email__combobox {
|
.email .email__combobox {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -39,8 +51,13 @@ export default css`
|
|||||||
|
|
||||||
/* Tags */
|
/* Tags */
|
||||||
|
|
||||||
|
.email .email__combobox > div {
|
||||||
|
margin: auto 0px;
|
||||||
|
}
|
||||||
.email et2-email-tag {
|
.email et2-email-tag {
|
||||||
--icon-width: 1.8em;
|
--icon-width: 1.8em;
|
||||||
|
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search box */
|
/* Search box */
|
||||||
@ -78,7 +95,8 @@ export default css`
|
|||||||
max-width: var(--auto-size-available-width);
|
max-width: var(--auto-size-available-width);
|
||||||
max-height: var(--auto-size-available-height);
|
max-height: var(--auto-size-available-height);
|
||||||
|
|
||||||
--icon-width: 1.8em;
|
/* This doesn't work for some reason, it's overwritten somewhere */
|
||||||
|
--size: 1.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.email__listbox ::slotted(sl-divider) {
|
.email__listbox ::slotted(sl-divider) {
|
||||||
|
@ -19,6 +19,10 @@ import {Et2EmailTag} from "../Et2Select/Tag/Et2EmailTag";
|
|||||||
import {waitForEvent} from "../Et2Widget/event";
|
import {waitForEvent} from "../Et2Widget/event";
|
||||||
import styles from "./Et2Email.styles";
|
import styles from "./Et2Email.styles";
|
||||||
import {SelectOption} from "../Et2Select/FindSelectOptions";
|
import {SelectOption} from "../Et2Select/FindSelectOptions";
|
||||||
|
import {SearchMixinInterface} from "../Et2Select/SearchMixin";
|
||||||
|
import {IsEmail} from "../Validators/IsEmail";
|
||||||
|
import {Validator} from "@lion/form-core";
|
||||||
|
import Sortable from "sortablejs/modular/sortable.complete.esm.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Enter email addresses, offering suggestions from contacts
|
* @summary Enter email addresses, offering suggestions from contacts
|
||||||
@ -58,7 +62,7 @@ import {SelectOption} from "../Et2Select/FindSelectOptions";
|
|||||||
* @csspart tag__remove-button - The tag's remove button.
|
* @csspart tag__remove-button - The tag's remove button.
|
||||||
* @csspart tag__remove-button__base - The tag's remove button base part.
|
* @csspart tag__remove-button__base - The tag's remove button base part.
|
||||||
*/
|
*/
|
||||||
export class Et2Email extends Et2InputWidget(LitElement)
|
export class Et2Email extends Et2InputWidget(LitElement) implements SearchMixinInterface
|
||||||
{
|
{
|
||||||
static shadowRootOptions = {...LitElement.shadowRootOptions, delegatesFocus: true};
|
static shadowRootOptions = {...LitElement.shadowRootOptions, delegatesFocus: true};
|
||||||
|
|
||||||
@ -159,7 +163,13 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
* @type {number}
|
* @type {number}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected static SEARCH_TIMEOUT = 500;
|
public static SEARCH_TIMEOUT = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Typing these characters will end the email address and start a new one
|
||||||
|
* @type {string[]}
|
||||||
|
*/
|
||||||
|
public static TAG_BREAK : string[] = ["Tab", "Enter", ","];
|
||||||
|
|
||||||
protected readonly hasSlotController = new HasSlotController(this, 'help-text', 'label');
|
protected readonly hasSlotController = new HasSlotController(this, 'help-text', 'label');
|
||||||
|
|
||||||
@ -170,12 +180,15 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
protected _searchPromise : Promise<SelectOption[]> = Promise.resolve([]);
|
protected _searchPromise : Promise<SelectOption[]> = Promise.resolve([]);
|
||||||
protected _selectOptions : SelectOption[] = [];
|
protected _selectOptions : SelectOption[] = [];
|
||||||
|
|
||||||
|
protected _sortable : Sortable;
|
||||||
|
|
||||||
constructor(...args : any[])
|
constructor(...args : any[])
|
||||||
{
|
{
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
super(...args);
|
super(...args);
|
||||||
|
|
||||||
|
this.defaultValidators.push(new IsEmail(this.allowPlaceholder));
|
||||||
|
|
||||||
// Additional option for select email, per ticket #79694
|
// Additional option for select email, per ticket #79694
|
||||||
this._close_on_select = this.egw().preference("select_multiple_close") != "open";
|
this._close_on_select = this.egw().preference("select_multiple_close") != "open";
|
||||||
|
|
||||||
@ -189,6 +202,17 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
this.open = false;
|
this.open = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
willUpdate(changedProperties : PropertyValues)
|
||||||
|
{
|
||||||
|
super.willUpdate(changedProperties);
|
||||||
|
|
||||||
|
if(changedProperties.has('allowPlaceholder'))
|
||||||
|
{
|
||||||
|
this.defaultValidators = (<Array<Validator>>this.defaultValidators).filter(v => !(v instanceof IsEmail));
|
||||||
|
this.defaultValidators.push(new IsEmail(this.allowPlaceholder));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
update(changedProperties : PropertyValues)
|
update(changedProperties : PropertyValues)
|
||||||
{
|
{
|
||||||
super.update(changedProperties)
|
super.update(changedProperties)
|
||||||
@ -199,6 +223,17 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updated(changedProperties : PropertyValues)
|
||||||
|
{
|
||||||
|
super.updated(changedProperties);
|
||||||
|
|
||||||
|
// Re-set sorting / drag & drop
|
||||||
|
if(changedProperties.has("value"))
|
||||||
|
{
|
||||||
|
this.makeSortable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private addOpenListeners()
|
private addOpenListeners()
|
||||||
{
|
{
|
||||||
document.addEventListener('focusin', this.handleLostFocus);
|
document.addEventListener('focusin', this.handleLostFocus);
|
||||||
@ -211,6 +246,93 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
document.removeEventListener('mousedown', this.handleLostFocus);
|
document.removeEventListener('mousedown', this.handleLostFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected makeSortable()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current suggestion, which is the option the user is currently interacting with (e.g. via keyboard).
|
||||||
|
* Only one option may be "current" at a time.
|
||||||
|
*/
|
||||||
|
private setCurrentOption(option : SlOption | null)
|
||||||
|
{
|
||||||
|
// Clear selection
|
||||||
|
this._suggestions.forEach(el =>
|
||||||
|
{
|
||||||
|
el.current = false;
|
||||||
|
el.tabIndex = -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select the target option
|
||||||
|
if(option)
|
||||||
|
{
|
||||||
|
this.currentOption = option;
|
||||||
|
option.current = true;
|
||||||
|
option.tabIndex = 0;
|
||||||
|
option.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setCurrentTag(tag : Et2EmailTag)
|
||||||
|
{
|
||||||
|
this._tags.forEach(t =>
|
||||||
|
{
|
||||||
|
t.tabIndex = -1;
|
||||||
|
if(t.current)
|
||||||
|
{
|
||||||
|
t.current = false;
|
||||||
|
t.requestUpdate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.currentTag = tag;
|
||||||
|
if(tag)
|
||||||
|
{
|
||||||
|
this.currentTag.tabIndex = 0;
|
||||||
|
this.currentTag.current = true;
|
||||||
|
this.currentTag.requestUpdate();
|
||||||
|
this.currentTag.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an entry that is not in the options and add it to the value
|
||||||
|
*
|
||||||
|
* @param {string} text Used as both value and label
|
||||||
|
*/
|
||||||
|
public addAddress(text : string) : boolean
|
||||||
|
{
|
||||||
|
if(!text || !this.validateAddress(text))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Make sure not to double-add
|
||||||
|
if(!this.value.includes(text.replace(/'/g, "\\\'")))
|
||||||
|
{
|
||||||
|
this.value.push(text.trim());
|
||||||
|
this.requestUpdate('value');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatchEvent(new Event("change", {bubbles: true}));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a free entry value is acceptable.
|
||||||
|
* We use validators directly using the proposed value
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
public validateAddress(text) : boolean
|
||||||
|
{
|
||||||
|
let validators = [...this.validators, ...this.defaultValidators];
|
||||||
|
let result = validators.filter(v =>
|
||||||
|
v.execute(text, v.param, {node: this}),
|
||||||
|
);
|
||||||
|
return validators.length > 0 && result.length == 0 || validators.length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** Sets focus on the control. */
|
/** Sets focus on the control. */
|
||||||
focus(options? : FocusOptions)
|
focus(options? : FocusOptions)
|
||||||
@ -286,7 +408,7 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
return this._searchPromise.then(async() =>
|
return this._searchPromise.then(async() =>
|
||||||
{
|
{
|
||||||
this.searching = false;
|
this.searching = false;
|
||||||
if(!this.open)
|
if(!this.open && this.hasFocus)
|
||||||
{
|
{
|
||||||
this.show();
|
this.show();
|
||||||
}
|
}
|
||||||
@ -334,12 +456,11 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
*/
|
*/
|
||||||
protected processRemoteResults(entries)
|
protected processRemoteResults(entries)
|
||||||
{
|
{
|
||||||
if(!entries?.length)
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
this._selectOptions = entries;
|
this._selectOptions = entries;
|
||||||
|
this.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
this.currentOption = this._suggestions[0];
|
||||||
|
});
|
||||||
|
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
|
|
||||||
@ -366,8 +487,7 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
if(this.open && !this.disabled)
|
if(this.open && !this.disabled)
|
||||||
{
|
{
|
||||||
// Reset the current option
|
// Reset the current option
|
||||||
// TODO
|
this.setCurrentOption(this._suggestions[0]);
|
||||||
//this.setCurrentOption(this._suggestions[0]);
|
|
||||||
|
|
||||||
// Show
|
// Show
|
||||||
this.dispatchEvent(new CustomEvent('sl-show', {bubbles: true}));
|
this.dispatchEvent(new CustomEvent('sl-show', {bubbles: true}));
|
||||||
@ -385,8 +505,7 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
// Make sure the current option is scrolled into view (required for Safari)
|
// Make sure the current option is scrolled into view (required for Safari)
|
||||||
if(this.currentOption)
|
if(this.currentOption)
|
||||||
{
|
{
|
||||||
// TODO
|
this.currentOption.scrollIntoView();
|
||||||
//scrollIntoView(this.currentOption, this._listbox, 'vertical', 'auto');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchEvent(new CustomEvent('sl-after-show', {bubbles: true}));
|
this.dispatchEvent(new CustomEvent('sl-after-show', {bubbles: true}));
|
||||||
@ -411,8 +530,7 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
this.requestUpdate("hasFocus");
|
this.requestUpdate("hasFocus");
|
||||||
|
|
||||||
// Reset tags to not take focus
|
// Reset tags to not take focus
|
||||||
this._tags.forEach(t => t.tabIndex = -1);
|
this.setCurrentTag(null);
|
||||||
this.currentTag = null;
|
|
||||||
|
|
||||||
this._search.setSelectionRange(0, 0);
|
this._search.setSelectionRange(0, 0);
|
||||||
}
|
}
|
||||||
@ -433,8 +551,10 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
{
|
{
|
||||||
this.hide();
|
this.hide();
|
||||||
this._tags.forEach(t => t.tabIndex = 0);
|
this._tags.forEach(t => t.tabIndex = 0);
|
||||||
this.currentTag = this._tags[this._tags.length - 1];
|
if(this._tags.length > 0)
|
||||||
this.currentTag.focus();
|
{
|
||||||
|
this.setCurrentTag(this._tags[this._tags.length - 1]);
|
||||||
|
}
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -446,13 +566,34 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Up / Down navigates options
|
// Up / Down navigates options
|
||||||
if(['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key))
|
if(['ArrowDown', 'ArrowUp'].includes(event.key) && this._suggestions.length)
|
||||||
{
|
{
|
||||||
// TODO - pass focus to list
|
if(!this.open)
|
||||||
this.show();
|
{
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
this.setCurrentOption(this._suggestions[0]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Tab or enter checks current value
|
// Tab or enter checks current value
|
||||||
|
else if(Et2Email.TAG_BREAK.indexOf(event.key) !== -1)
|
||||||
|
{
|
||||||
|
if(!this.validateAddress(this._search.value.trim()) && this.currentOption)
|
||||||
|
{
|
||||||
|
this._search.value = this.currentOption.value.replaceAll("___", " ");
|
||||||
|
}
|
||||||
|
if(this.addAddress(this._search.value.trim()))
|
||||||
|
{
|
||||||
|
this.open = false;
|
||||||
|
this._search.value = "";
|
||||||
|
}
|
||||||
|
if(event.key == "Tab")
|
||||||
|
{
|
||||||
|
this.blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Start search immediately
|
||||||
else if(event.key == "Enter")
|
else if(event.key == "Enter")
|
||||||
{
|
{
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -461,7 +602,7 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
}
|
}
|
||||||
else if(event.key == "Escape")
|
else if(event.key == "Escape")
|
||||||
{
|
{
|
||||||
this.handleSearchAbort(event);
|
this._selectOptions = [];
|
||||||
this.hide();
|
this.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -510,10 +651,7 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
nextTagIndex = Math.max(0, nextTagIndex);
|
nextTagIndex = Math.max(0, nextTagIndex);
|
||||||
if(nextTagIndex < tagCount && this._tags[nextTagIndex])
|
if(nextTagIndex < tagCount && this._tags[nextTagIndex])
|
||||||
{
|
{
|
||||||
this._tags.forEach(t => t.tabIndex = -1);
|
this.setCurrentTag(this._tags[nextTagIndex]);
|
||||||
this.currentTag = this._tags[nextTagIndex];
|
|
||||||
this.currentTag.tabIndex = 0;
|
|
||||||
this.currentTag.focus();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -526,7 +664,18 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
// Remove tag
|
// Remove tag
|
||||||
if(event.target instanceof Et2EmailTag && ["Delete", "Backspace"].includes(event.key))
|
if(event.target instanceof Et2EmailTag && ["Delete", "Backspace"].includes(event.key))
|
||||||
{
|
{
|
||||||
|
const tags = this._tags;
|
||||||
|
let index = tags.indexOf(event.target);
|
||||||
event.target.dispatchEvent(new CustomEvent('sl-remove', {bubbles: true}));
|
event.target.dispatchEvent(new CustomEvent('sl-remove', {bubbles: true}));
|
||||||
|
index += event.key == "Delete" ? 1 : -1;
|
||||||
|
if(index >= 0 && index < tags.length)
|
||||||
|
{
|
||||||
|
this.setCurrentTag(this._tags[index]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._search.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Edit tag
|
// Edit tag
|
||||||
else if(event.target instanceof Et2EmailTag && ["Enter"].includes(event.key))
|
else if(event.target instanceof Et2EmailTag && ["Enter"].includes(event.key))
|
||||||
@ -535,6 +684,92 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keyboard events from the suggestion list
|
||||||
|
*
|
||||||
|
* @param {KeyboardEvent} event
|
||||||
|
*/
|
||||||
|
handleSuggestionsKeyDown(event : KeyboardEvent)
|
||||||
|
{
|
||||||
|
// Select the option
|
||||||
|
const value = (<string>this.currentOption.value).replaceAll("___", " ");
|
||||||
|
if(this.currentOption && ["ArrowRight", " ", ...Et2Email.TAG_BREAK].includes(event.key) && this.addAddress(value))
|
||||||
|
{
|
||||||
|
event.preventDefault();
|
||||||
|
this._search.value = "";
|
||||||
|
this.open = false;
|
||||||
|
if(this._close_on_select)
|
||||||
|
{
|
||||||
|
this.blur();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._search.focus();
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Navigate options
|
||||||
|
if(["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key))
|
||||||
|
{
|
||||||
|
event.stopPropagation()
|
||||||
|
const suggestions = this._suggestions;
|
||||||
|
const currentIndex = suggestions.indexOf(this.currentOption);
|
||||||
|
let newIndex = Math.max(0, currentIndex);
|
||||||
|
|
||||||
|
// Prevent scrolling
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if(event.key === "ArrowDown")
|
||||||
|
{
|
||||||
|
newIndex = currentIndex + 1;
|
||||||
|
if(newIndex > suggestions.length - 1)
|
||||||
|
{
|
||||||
|
newIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(event.key === "ArrowUp")
|
||||||
|
{
|
||||||
|
newIndex = currentIndex - 1;
|
||||||
|
if(newIndex < 0)
|
||||||
|
{
|
||||||
|
newIndex = suggestions.length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(event.key === "Home")
|
||||||
|
{
|
||||||
|
newIndex = 0;
|
||||||
|
}
|
||||||
|
else if(event.key === "End")
|
||||||
|
{
|
||||||
|
newIndex = suggestions.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setCurrentOption(suggestions[newIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mouse up from the suggestion list
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
handleSuggestionsMouseUp(event : MouseEvent)
|
||||||
|
{
|
||||||
|
const value = ((<SlOption>event.target).value).replaceAll("___", " ");
|
||||||
|
this.value.push(value);
|
||||||
|
this.open = false;
|
||||||
|
this._search.value = "";
|
||||||
|
this.requestUpdate("value");
|
||||||
|
if(this._close_on_select)
|
||||||
|
{
|
||||||
|
this.blur();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._search.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleTagChange(event)
|
handleTagChange(event)
|
||||||
{
|
{
|
||||||
// Need to update our value, or it will just redo the tag with the old value
|
// Need to update our value, or it will just redo the tag with the old value
|
||||||
@ -545,12 +780,16 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
this.value[index] = event.target.value;
|
this.value[index] = event.target.value;
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
if(event.target.current)
|
||||||
|
{
|
||||||
|
this.setCurrentTag(event.target);
|
||||||
|
;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTagRemove(event : SlRemoveEvent, value : string)
|
handleTagRemove(event : SlRemoveEvent, value : string)
|
||||||
{
|
{
|
||||||
// Find the tag value and remove it from current value
|
// Find the tag value and remove it from current value
|
||||||
debugger;
|
|
||||||
const index = this.value.indexOf(value);
|
const index = this.value.indexOf(value);
|
||||||
this.value.splice(index, 1);
|
this.value.splice(index, 1);
|
||||||
this.requestUpdate("value");
|
this.requestUpdate("value");
|
||||||
@ -585,6 +824,7 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
?readonly=${readonly}
|
?readonly=${readonly}
|
||||||
?editable=${isEditable}
|
?editable=${isEditable}
|
||||||
@mousedown=${(e) => {this._cancelOpen = true;}}
|
@mousedown=${(e) => {this._cancelOpen = true;}}
|
||||||
|
@dblclick=${(e) => {e.target.startEdit();}}
|
||||||
@change=${this.handleTagChange}
|
@change=${this.handleTagChange}
|
||||||
>
|
>
|
||||||
</et2-email-tag>`;
|
</et2-email-tag>`;
|
||||||
@ -597,7 +837,8 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
class="email__search"
|
class="email__search"
|
||||||
exportparts="base:search__base"
|
exportparts="base:search__base"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
placeholder="${this.hasFocus ? "" : this.placeholder}"
|
placeholder="${this.hasFocus || this.value.length > 0 ? "" : this.placeholder}"
|
||||||
|
tabindex="0"
|
||||||
@keydown=${this.handleSearchKeyDown}
|
@keydown=${this.handleSearchKeyDown}
|
||||||
@blur=${this.handleSearchBlur}
|
@blur=${this.handleSearchBlur}
|
||||||
@focus=${this.handleSearchFocus}
|
@focus=${this.handleSearchFocus}
|
||||||
@ -633,7 +874,7 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
.option=${option}
|
.option=${option}
|
||||||
?disabled=${option.disabled}
|
?disabled=${option.disabled}
|
||||||
>
|
>
|
||||||
<et2-lavatar slot="prefix" part="icon"
|
<et2-lavatar slot="prefix" part="icon" size="1.8em"
|
||||||
lname=${option.lname || nothing}
|
lname=${option.lname || nothing}
|
||||||
fname=${option.fname || nothing}
|
fname=${option.fname || nothing}
|
||||||
image=${option.icon || nothing}
|
image=${option.icon || nothing}
|
||||||
@ -699,7 +940,6 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
class="email__combobox"
|
class="email__combobox"
|
||||||
slot="anchor"
|
slot="anchor"
|
||||||
@keydown=${this.handleComboboxKeyDown}
|
@keydown=${this.handleComboboxKeyDown}
|
||||||
@mousedown=${this.handleComboboxMouseDown}
|
|
||||||
>
|
>
|
||||||
<slot part="prefix" name="prefix" class="email__prefix"></slot>
|
<slot part="prefix" name="prefix" class="email__prefix"></slot>
|
||||||
${this.tagsTemplate()}
|
${this.tagsTemplate()}
|
||||||
@ -714,7 +954,8 @@ export class Et2Email extends Et2InputWidget(LitElement)
|
|||||||
part="listbox"
|
part="listbox"
|
||||||
class="email__listbox"
|
class="email__listbox"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
@mouseup=${this.handleOptionClick}
|
@keydown=${this.handleSuggestionsKeyDown}
|
||||||
|
@mouseup=${this.handleSuggestionsMouseUp}
|
||||||
>
|
>
|
||||||
${this.suggestionsTemplate()}
|
${this.suggestionsTemplate()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,7 @@ import {SlTag} from "@shoelace-style/shoelace";
|
|||||||
import {css, html, TemplateResult} from "lit";
|
import {css, html, TemplateResult} from "lit";
|
||||||
import {classMap} from "lit/directives/class-map.js";
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
import shoelace from "../../Styles/shoelace";
|
import shoelace from "../../Styles/shoelace";
|
||||||
|
import {state} from "lit/decorators/state.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag is usually used in a Select with multiple=true, but there's no reason it can't go anywhere
|
* Tag is usually used in a Select with multiple=true, but there's no reason it can't go anywhere
|
||||||
@ -77,6 +78,8 @@ export class Et2Tag extends Et2Widget(SlTag)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@state() current = false; // the user has keyed into the tag (focused), but hasn't done anything yet (shows a highlight)
|
||||||
|
|
||||||
constructor(...args : [])
|
constructor(...args : [])
|
||||||
{
|
{
|
||||||
super(...args);
|
super(...args);
|
||||||
@ -137,9 +140,9 @@ export class Et2Tag extends Et2Widget(SlTag)
|
|||||||
'tag--editable': this.editable,
|
'tag--editable': this.editable,
|
||||||
'tag--editing': this.isEditing,
|
'tag--editing': this.isEditing,
|
||||||
// Types
|
// Types
|
||||||
'tag--primary': this.variant === 'primary',
|
'tag--primary': this.variant === 'primary' || this.current,
|
||||||
'tag--success': this.variant === 'success',
|
'tag--success': this.variant === 'success',
|
||||||
'tag--neutral': this.variant === 'neutral',
|
'tag--neutral': this.variant === 'neutral' && !this.current,
|
||||||
'tag--warning': this.variant === 'warning',
|
'tag--warning': this.variant === 'warning',
|
||||||
'tag--danger': this.variant === 'danger',
|
'tag--danger': this.variant === 'danger',
|
||||||
'tag--text': this.variant === 'text',
|
'tag--text': this.variant === 'text',
|
||||||
|
Loading…
Reference in New Issue
Block a user