mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-26 00:29:38 +01:00
Select improvements
- Add _styleTemplate for easier extension - re-organize file - Fix weird tag style - some cleanup
This commit is contained in:
parent
01797f071d
commit
f75567a863
@ -17,7 +17,7 @@ import {RowLimitedMixin} from "../Layout/RowLimitedMixin";
|
||||
import {Et2Tag} from "./Tag/Et2Tag";
|
||||
import {Et2WithSearchMixin} from "./SearchMixin";
|
||||
import {property} from "lit/decorators/property.js";
|
||||
import {SlChangeEvent, SlSelect} from "@shoelace-style/shoelace";
|
||||
import {SlChangeEvent, SlOption, SlSelect} from "@shoelace-style/shoelace";
|
||||
import {repeat} from "lit/directives/repeat.js";
|
||||
|
||||
// export Et2WidgetWithSelect which is used as type in other modules
|
||||
@ -556,59 +556,6 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
||||
}
|
||||
*/
|
||||
|
||||
_emptyLabelTemplate() : TemplateResult
|
||||
{
|
||||
if(!this.emptyLabel || this.multiple)
|
||||
{
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<sl-option value=""
|
||||
.selected=${this.getValueAsArray().some(v => v == "")}
|
||||
>
|
||||
${this.emptyLabel}
|
||||
</sl-option>`;
|
||||
}
|
||||
|
||||
protected _optionsTemplate() : TemplateResult
|
||||
{
|
||||
return html`${repeat(this.select_options
|
||||
// Filter out empty values if we have empty label to avoid duplicates
|
||||
.filter(o => this.emptyLabel ? o.value !== '' : o), this._groupTemplate.bind(this))
|
||||
}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to render each option into the select
|
||||
*
|
||||
* @param {SelectOption} option
|
||||
* @returns {TemplateResult}
|
||||
*/
|
||||
protected _optionTemplate(option : SelectOption) : TemplateResult
|
||||
{
|
||||
// Exclude non-matches when searching
|
||||
if(typeof option.isMatch == "boolean" && !option.isMatch)
|
||||
{
|
||||
return html``;
|
||||
}
|
||||
|
||||
// Tag used must match this.optionTag, but you can't use the variable directly.
|
||||
// Pass option along so SearchMixin can grab it if needed
|
||||
const value = (<string>option.value).replaceAll(" ", "___");
|
||||
return html`
|
||||
<sl-option
|
||||
part="option"
|
||||
value="${value}"
|
||||
title="${!option.title || this.noLang ? option.title : this.egw().lang(option.title)}"
|
||||
class="${option.class}" .option=${option}
|
||||
.selected=${this.getValueAsArray().some(v => v == value)}
|
||||
?disabled=${option.disabled}
|
||||
>
|
||||
${this._iconTemplate(option)}
|
||||
${this.noLang ? option.label : this.egw().lang(option.label)}
|
||||
</sl-option>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag used for rendering tags when multiple=true
|
||||
* Used for creating, finding & filtering options.
|
||||
@ -620,54 +567,6 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
||||
return literal`et2-tag`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom tag
|
||||
* @param {Et2Option} option
|
||||
* @param {number} index
|
||||
* @returns {TemplateResult}
|
||||
* @protected
|
||||
*/
|
||||
protected _tagTemplate(option : Et2Option, index : number) : TemplateResult
|
||||
{
|
||||
const readonly = (this.readonly || option && typeof (option.disabled) != "undefined" && option.disabled);
|
||||
const isEditable = this.editModeEnabled && !readonly;
|
||||
const image = this._createImage(option);
|
||||
const tagName = this.tagTag;
|
||||
return html`
|
||||
<${tagName}
|
||||
part="tag"
|
||||
exportparts="
|
||||
base:tag__base,
|
||||
content:tag__content,
|
||||
remove-button:tag__remove-button,
|
||||
remove-button__base:tag__remove-button__base,
|
||||
icon:icon
|
||||
"
|
||||
class=${"search_tag " + option.classList.value}
|
||||
?pill=${this.pill}
|
||||
size=${this.size}
|
||||
?removable=${!readonly}
|
||||
?readonly=${readonly}
|
||||
?editable=${isEditable}
|
||||
.value=${option.value.replaceAll("___", " ")}
|
||||
@dblclick=${this._handleDoubleClick}
|
||||
@click=${typeof this.onTagClick == "function" ? (e) => this.onTagClick(e, e.target) : nothing}
|
||||
>
|
||||
${image ?? nothing}
|
||||
${option.getTextLabel().trim()}
|
||||
</${tagName}>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional customisation template
|
||||
* @returns {*}
|
||||
* @protected
|
||||
*/
|
||||
protected _extraTemplate()
|
||||
{
|
||||
return typeof super._extraTemplate == "function" ? super._extraTemplate() : nothing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customise how tags are rendered. This overrides what SlSelect
|
||||
@ -867,6 +766,141 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
||||
return this.shadowRoot?.querySelector("sl-select");
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom, dynamic styling
|
||||
*
|
||||
* Put as much as you can in static styles for performance reasons
|
||||
* Override this for custom dynamic styles
|
||||
*
|
||||
* @returns {TemplateResult}
|
||||
* @protected
|
||||
*/
|
||||
protected _styleTemplate() : TemplateResult
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for the "no value" option for single select
|
||||
* Placeholder is used for multi-select with no value
|
||||
*
|
||||
* @returns {TemplateResult}
|
||||
*/
|
||||
_emptyLabelTemplate() : TemplateResult
|
||||
{
|
||||
if(!this.emptyLabel || this.multiple)
|
||||
{
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<sl-option value=""
|
||||
.selected=${this.getValueAsArray().some(v => v == "")}
|
||||
>
|
||||
${this.emptyLabel}
|
||||
</sl-option>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all the options
|
||||
* @returns {TemplateResult}
|
||||
* @protected
|
||||
*/
|
||||
protected _optionsTemplate() : TemplateResult
|
||||
{
|
||||
return html`${repeat(this.select_options
|
||||
// Filter out empty values if we have empty label to avoid duplicates
|
||||
.filter(o => this.emptyLabel ? o.value !== '' : o), this._groupTemplate.bind(this))
|
||||
}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to render each option into the select
|
||||
* Override for custom select options. Note that spaces are not allowed in option values,
|
||||
* and sl-select _requires_ options to be <sl-option>
|
||||
*
|
||||
* @param {SelectOption} option
|
||||
* @returns {TemplateResult}
|
||||
*/
|
||||
protected _optionTemplate(option : SelectOption) : TemplateResult
|
||||
{
|
||||
// Exclude non-matches when searching
|
||||
if(typeof option.isMatch == "boolean" && !option.isMatch)
|
||||
{
|
||||
return html``;
|
||||
}
|
||||
|
||||
// Tag used must match this.optionTag, but you can't use the variable directly.
|
||||
// Pass option along so SearchMixin can grab it if needed
|
||||
const value = (<string>option.value).replaceAll(" ", "___");
|
||||
return html`
|
||||
<sl-option
|
||||
part="option"
|
||||
value="${value}"
|
||||
title="${!option.title || this.noLang ? option.title : this.egw().lang(option.title)}"
|
||||
class="${option.class}" .option=${option}
|
||||
.selected=${this.getValueAsArray().some(v => v == value)}
|
||||
?disabled=${option.disabled}
|
||||
>
|
||||
${this._iconTemplate(option)}
|
||||
${this.noLang ? option.label : this.egw().lang(option.label)}
|
||||
</sl-option>`;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Custom tag
|
||||
*
|
||||
* Override this to customise display when multiple=true.
|
||||
* There is no restriction on the tag used, unlike _optionTemplate()
|
||||
*
|
||||
* @param {Et2Option} option
|
||||
* @param {number} index
|
||||
* @returns {TemplateResult}
|
||||
* @protected
|
||||
*/
|
||||
protected _tagTemplate(option : SlOption, index : number) : TemplateResult
|
||||
{
|
||||
const readonly = (this.readonly || option && typeof (option.disabled) != "undefined" && option.disabled);
|
||||
const isEditable = this.editModeEnabled && !readonly;
|
||||
const image = this._createImage(option);
|
||||
const tagName = this.tagTag;
|
||||
return html`
|
||||
<${tagName}
|
||||
part="tag"
|
||||
exportparts="
|
||||
base:tag__base,
|
||||
content:tag__content,
|
||||
remove-button:tag__remove-button,
|
||||
remove-button__base:tag__remove-button__base,
|
||||
icon:icon
|
||||
"
|
||||
class=${"search_tag " + option.classList.value}
|
||||
?pill=${this.pill}
|
||||
size=${this.size || "medium"}
|
||||
?removable=${!readonly}
|
||||
?readonly=${readonly}
|
||||
?editable=${isEditable}
|
||||
.value=${option.value.replaceAll("___", " ")}
|
||||
@dblclick=${this._handleDoubleClick}
|
||||
@click=${typeof this.onTagClick == "function" ? (e) => this.onTagClick(e, e.target) : nothing}
|
||||
>
|
||||
${image ?? nothing}
|
||||
${option.getTextLabel().trim()}
|
||||
</${tagName}>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional customisation template
|
||||
* Override if needed. Added after select options.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
protected _extraTemplate() : TemplateResult | typeof nothing
|
||||
{
|
||||
return typeof super._extraTemplate == "function" ? super._extraTemplate() : nothing;
|
||||
}
|
||||
|
||||
public render()
|
||||
{
|
||||
const value = Array.isArray(this.value) ?
|
||||
@ -883,12 +917,13 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
||||
}
|
||||
}
|
||||
return html`
|
||||
${this._styleTemplate()}
|
||||
<sl-select
|
||||
exportparts="prefix, tags, display-input, expand-icon, combobox, listbox, option"
|
||||
label=${this.label}
|
||||
placeholder=${this.placeholder}
|
||||
?multiple=${this.multiple}
|
||||
?disabled=${this.disabled}
|
||||
?disabled=${this.disabled || this.readonly}
|
||||
?clearable=${this.clearable}
|
||||
?required=${this.required}
|
||||
helpText=${this.helpText}
|
||||
|
@ -71,7 +71,7 @@ export declare class SearchMixinInterface
|
||||
*
|
||||
* @type {TemplateResult}
|
||||
*/
|
||||
_extraTemplate : TemplateResult
|
||||
_extraTemplate : TemplateResult | typeof nothing
|
||||
}
|
||||
|
||||
/**
|
||||
@ -409,7 +409,7 @@ export const Et2WithSearchMixin = dedupeMixin(<T extends Constructor<LitElement>
|
||||
}
|
||||
}
|
||||
|
||||
protected _extraTemplate()
|
||||
protected _extraTemplate() : TemplateResult | typeof nothing
|
||||
{
|
||||
if(!this.searchEnabled && !this.editModeEnabled && !this.allowFreeEntries || this.readonly)
|
||||
{
|
||||
|
@ -70,14 +70,6 @@ export class Et2SelectAccount extends SelectAccountMixin(Et2StaticSelectMixin(Et
|
||||
this.fetchComplete = Promise.all(fetch);
|
||||
}
|
||||
|
||||
|
||||
firstUpdated(changedProperties?)
|
||||
{
|
||||
super.firstUpdated(changedProperties);
|
||||
// Due to the different way Et2SelectAccount handles options, we call this explicitly
|
||||
this._renderOptions();
|
||||
}
|
||||
|
||||
set accountType(type : AccountType)
|
||||
{
|
||||
this.__accountType = type;
|
||||
|
@ -105,15 +105,15 @@ export class Et2SelectCategory extends Et2StaticSelectMixin(Et2Select)
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to render each option into the select
|
||||
* Overridden for colors
|
||||
* Custom, dynamic styling
|
||||
*
|
||||
* CSS variables are not making it through to options, re-declaring them here works
|
||||
*
|
||||
* @param {SelectOption} option
|
||||
* @returns {TemplateResult}
|
||||
* @protected
|
||||
*/
|
||||
public render() : TemplateResult
|
||||
protected _styleTemplate() : TemplateResult
|
||||
{
|
||||
/** CSS variables are not making it through to options, re-declaring them here works */
|
||||
return html`
|
||||
<style>
|
||||
${repeat(this.select_options, (option) =>
|
||||
@ -128,7 +128,6 @@ export class Et2SelectCategory extends Et2StaticSelectMixin(Et2Select)
|
||||
);
|
||||
})}
|
||||
</style>
|
||||
${super.render()}
|
||||
`;
|
||||
}
|
||||
|
||||
@ -141,20 +140,6 @@ export class Et2SelectCategory extends Et2StaticSelectMixin(Et2Select)
|
||||
{
|
||||
return literal`et2-category-tag`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customise how tags are rendered.
|
||||
* This overrides parent to set application
|
||||
*
|
||||
* @param item
|
||||
* @protected
|
||||
*/
|
||||
protected _createTagNode(item)
|
||||
{
|
||||
let tag = super._createTagNode(item);
|
||||
tag.application = this.application;
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("et2-select-cat", Et2SelectCategory);
|
@ -243,16 +243,6 @@ export class Et2SelectEmail extends Et2Select
|
||||
</et2-lavatar>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override image to skip it, we add images in Et2EmailTag using CSS
|
||||
* @param item
|
||||
* @protected
|
||||
*/
|
||||
protected _createImage(item)
|
||||
{
|
||||
return this.multiple ? "" : super._createImage(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten to NOT split RFC822 addresses containing a comma in quoted name part
|
||||
*
|
||||
|
@ -35,6 +35,9 @@ export class Et2Tag extends Et2Widget(SlTag)
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.tag__prefix {
|
||||
line-height: normal;
|
||||
}
|
||||
.tag__content {
|
||||
padding: 0px 0.2rem;
|
||||
flex: 1 2 auto;
|
||||
|
Loading…
Reference in New Issue
Block a user