mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-03 04:29:28 +01:00
Et2Select: Fix/re-add hidden tag flag when multiple,readonly & rows=1
This commit is contained in:
parent
b4936c07af
commit
38dcda2a01
@ -19,6 +19,7 @@ import {property} from "lit/decorators/property.js";
|
|||||||
import {SlChangeEvent, SlOption, SlSelect} from "@shoelace-style/shoelace";
|
import {SlChangeEvent, SlOption, SlSelect} from "@shoelace-style/shoelace";
|
||||||
import {repeat} from "lit/directives/repeat.js";
|
import {repeat} from "lit/directives/repeat.js";
|
||||||
import {classMap} from "lit/directives/class-map.js";
|
import {classMap} from "lit/directives/class-map.js";
|
||||||
|
import {state} from "lit/decorators/state.js";
|
||||||
|
|
||||||
// export Et2WidgetWithSelect which is used as type in other modules
|
// export Et2WidgetWithSelect which is used as type in other modules
|
||||||
export class Et2WidgetWithSelect extends RowLimitedMixin(Et2WidgetWithSelectMixin(LitElement))
|
export class Et2WidgetWithSelect extends RowLimitedMixin(Et2WidgetWithSelectMixin(LitElement))
|
||||||
@ -128,10 +129,12 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Keep overflow tag right-aligned. It's the only sl-tag. */
|
/* No rows set, default height limit about 5 rows */
|
||||||
|
|
||||||
::part(tags) sl-tag {
|
:host(:not([rows])) ::part(tags) {
|
||||||
margin-left: auto;
|
min-height: inherit;
|
||||||
|
max-height: 11em;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
select:hover {
|
select:hover {
|
||||||
@ -140,20 +143,22 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
|
|
||||||
/* Hide dropdown trigger when multiple & readonly */
|
/* Hide dropdown trigger when multiple & readonly */
|
||||||
|
|
||||||
:host([readonly][multiple])::part(expand-icon) {
|
:host([readonly][multiple]):not([rows="1"])::part(expand-icon) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Style for tag count if rows=1 */
|
/* Style for tag count if rows=1 */
|
||||||
|
|
||||||
:host([readonly][multiple][rows])::part(tags) {
|
.tag_limit {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
top: 1px;
|
top: 0px;
|
||||||
|
bottom: 0px;
|
||||||
box-shadow: rgb(0 0 0/50%) -1.5ex 0px 1ex -1ex, rgb(0 0 0 / 0%) 0px 0px 0px 0px;
|
box-shadow: rgb(0 0 0/50%) -1.5ex 0px 1ex -1ex, rgb(0 0 0 / 0%) 0px 0px 0px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([readonly][multiple][rows]) .select__tags sl-tag::part(base) {
|
.tag_limit::part(base) {
|
||||||
|
height: 100%;
|
||||||
background-color: var(--sl-input-background-color);
|
background-color: var(--sl-input-background-color);
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
@ -164,12 +169,16 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
|
|
||||||
/* Show all rows on hover if rows=1 */
|
/* Show all rows on hover if rows=1 */
|
||||||
|
|
||||||
:host([readonly][multiple][rows]):hover .select__tags {
|
:host([ readonly ][ multiple ][ rows ]) .hover__popup {
|
||||||
width: -webkit-fill-available;
|
width: -webkit-fill-available;
|
||||||
width: -moz-fill-available;
|
width: -moz-fill-available;
|
||||||
width: fill-available;
|
width: fill-available;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host([ readonly ][ multiple ][ rows ]) .hover__popup .select__tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
::part(listbox) {
|
::part(listbox) {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
background: var(--sl-input-background-color);
|
background: var(--sl-input-background-color);
|
||||||
@ -254,15 +263,23 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
/** The select's required attribute. */
|
/** The select's required attribute. */
|
||||||
@property({type: Boolean, reflect: true}) required = false;
|
@property({type: Boolean, reflect: true}) required = false;
|
||||||
|
|
||||||
|
/** If the select is limited to 1 row, we show the number of tags not visible */
|
||||||
|
@state()
|
||||||
|
protected _tagsHidden = 0;
|
||||||
|
|
||||||
private __value : string | string[] = "";
|
private __value : string | string[] = "";
|
||||||
|
|
||||||
|
protected tagOverflowObserver : IntersectionObserver = null;
|
||||||
|
|
||||||
constructor()
|
constructor()
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
this.hoist = true;
|
this.hoist = true;
|
||||||
|
|
||||||
this._tagTemplate = this._tagTemplate.bind(this);
|
this._tagTemplate = this._tagTemplate.bind(this);
|
||||||
|
this._handleMouseEnter = this._handleMouseEnter.bind(this);
|
||||||
|
this._handleMouseLeave = this._handleMouseLeave.bind(this);
|
||||||
|
this._handleTagOverflow = this._handleTagOverflow.bind(this);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* List of properties that get translated
|
* List of properties that get translated
|
||||||
@ -287,6 +304,9 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
this.select?.requestUpdate("value");
|
this.select?.requestUpdate("value");
|
||||||
// Fixes incorrect opening position
|
// Fixes incorrect opening position
|
||||||
this.select?.popup?.handleAnchorChange();
|
this.select?.popup?.handleAnchorChange();
|
||||||
|
|
||||||
|
// requestUpdate("value") above means we need to check tags again
|
||||||
|
this.select.updateComplete.then(() => {this.checkTagOverflow(); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,6 +531,49 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After render, DOM nodes are there
|
||||||
|
*
|
||||||
|
* Check to see if tags overflow, set the counter flag
|
||||||
|
*
|
||||||
|
* @param {PropertyValues} changedProperties
|
||||||
|
*/
|
||||||
|
updated(changedProperties : PropertyValues)
|
||||||
|
{
|
||||||
|
super.updated(changedProperties);
|
||||||
|
|
||||||
|
this.checkTagOverflow();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected checkTagOverflow()
|
||||||
|
{
|
||||||
|
// Create / destroy intersection observer
|
||||||
|
if(this.readonly && this.rows == "1" && this.multiple && this.tagOverflowObserver == null)
|
||||||
|
{
|
||||||
|
this.tagOverflowObserver = new IntersectionObserver(this._handleTagOverflow, {
|
||||||
|
root: this.select.shadowRoot.querySelector(".select__tags"),
|
||||||
|
threshold: 0.1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if((!this.readonly || this.rows !== "1" || !this.multiple) && this.tagOverflowObserver !== null)
|
||||||
|
{
|
||||||
|
this.tagOverflowObserver.disconnect();
|
||||||
|
this.tagOverflowObserver = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.tagOverflowObserver)
|
||||||
|
{
|
||||||
|
this.select.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
for(const tag of this.select.shadowRoot.querySelectorAll(".select__tags *:not(div):not(sl-tag)"))
|
||||||
|
{
|
||||||
|
this.tagOverflowObserver.observe(tag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag used for rendering tags when multiple=true
|
* Tag used for rendering tags when multiple=true
|
||||||
* Used for creating, finding & filtering options.
|
* Used for creating, finding & filtering options.
|
||||||
@ -602,6 +665,111 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for the intersection observer so we know when tags don't fit
|
||||||
|
*
|
||||||
|
* Here we set the flag to show how many more tags are hidden, but this only happens
|
||||||
|
* when there are more tags than space.
|
||||||
|
*
|
||||||
|
* @param entries
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
protected _handleTagOverflow(entries : IntersectionObserverEntry[])
|
||||||
|
{
|
||||||
|
const oldCount = this._tagsHidden;
|
||||||
|
let visibleTagCount = this.value.length - this._tagsHidden;
|
||||||
|
let update = false;
|
||||||
|
// If we have all tags, start from 0, otherwise it's just a change
|
||||||
|
if(entries.length == this.value.length)
|
||||||
|
{
|
||||||
|
visibleTagCount = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
for(const tag of entries)
|
||||||
|
{
|
||||||
|
if(tag.isIntersecting)
|
||||||
|
{
|
||||||
|
visibleTagCount++;
|
||||||
|
}
|
||||||
|
else if(update && !tag.isIntersecting)
|
||||||
|
{
|
||||||
|
visibleTagCount--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(visibleTagCount && visibleTagCount < this.value.length)
|
||||||
|
{
|
||||||
|
this._tagsHidden = this.value.length - visibleTagCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._tagsHidden = 0;
|
||||||
|
}
|
||||||
|
this.requestUpdate("_tagsHidden", oldCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If rows=1 and multiple=true, when they put the mouse over the widget show all tags
|
||||||
|
* @param {MouseEvent} e
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
protected _handleMouseEnter(e : MouseEvent)
|
||||||
|
{
|
||||||
|
if(this.rows == 1 && this.multiple == true && this.value.length > 1)
|
||||||
|
{
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
let distance = (-1 * parseInt(getComputedStyle(this).height));
|
||||||
|
|
||||||
|
// Bind to turn this all off
|
||||||
|
this.addEventListener("mouseleave", this._handleMouseLeave);
|
||||||
|
|
||||||
|
// Popup - this might get wiped out next render(), might not
|
||||||
|
this.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
let tags = this.select.shadowRoot.querySelector(".select__tags");
|
||||||
|
let popup = document.createElement("sl-popup");
|
||||||
|
popup.anchor = this;
|
||||||
|
popup.distance = distance;
|
||||||
|
popup.placement = "bottom";
|
||||||
|
popup.strategy = "fixed";
|
||||||
|
popup.active = true;
|
||||||
|
popup.sync = "width";
|
||||||
|
popup.setAttribute("exportparts", "tags");
|
||||||
|
popup.classList.add("hover__popup", "details", "hoist", "details__body");
|
||||||
|
this.shadowRoot.append(popup);
|
||||||
|
popup.appendChild(tags);
|
||||||
|
tags.style.width = getComputedStyle(this).width;
|
||||||
|
tags.style.margin = 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we're showing all rows because of _handleMouseEnter, reset when mouse leaves
|
||||||
|
* @param {MouseEvent} e
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
protected _handleMouseLeave(e : MouseEvent)
|
||||||
|
{
|
||||||
|
let popup = this.shadowRoot.querySelector("sl-popup");
|
||||||
|
if(popup)
|
||||||
|
{
|
||||||
|
// Popup still here. Remove it
|
||||||
|
let tags = popup.firstChild;
|
||||||
|
this.select.shadowRoot.querySelector(".select__combobox").append(tags);
|
||||||
|
popup.remove();
|
||||||
|
}
|
||||||
|
this.removeEventListener("mouseleave", this._handleMouseLeave);
|
||||||
|
this.select.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
/** Shows the listbox. */
|
/** Shows the listbox. */
|
||||||
async show()
|
async show()
|
||||||
{
|
{
|
||||||
@ -781,6 +949,21 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected _tagLimitTemplate() : TemplateResult | typeof nothing
|
||||||
|
{
|
||||||
|
if(this._tagsHidden == 0)
|
||||||
|
{
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<sl-tag
|
||||||
|
part="tag__limit"
|
||||||
|
class="tag_limit"
|
||||||
|
slot="expand-icon"
|
||||||
|
>+${this._tagsHidden}
|
||||||
|
</sl-tag>`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional customisation template
|
* Additional customisation template
|
||||||
* Override if needed. Added after select options.
|
* Override if needed. Added after select options.
|
||||||
@ -835,6 +1018,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
.maxOptionsVisible=${0}
|
.maxOptionsVisible=${0}
|
||||||
.value=${value}
|
.value=${value}
|
||||||
@sl-change=${this.handleValueChange}
|
@sl-change=${this.handleValueChange}
|
||||||
|
@mouseenter=${this._handleMouseEnter}
|
||||||
@mouseup=${this.handleOptionClick}
|
@mouseup=${this.handleOptionClick}
|
||||||
@mousewheel=${
|
@mousewheel=${
|
||||||
// Grab & stop mousewheel to prevent scrolling sidemenu when scrolling through options
|
// Grab & stop mousewheel to prevent scrolling sidemenu when scrolling through options
|
||||||
@ -845,6 +1029,7 @@ export class Et2Select extends Et2WithSearchMixin(Et2WidgetWithSelect)
|
|||||||
${icon}
|
${icon}
|
||||||
${this._emptyLabelTemplate()}
|
${this._emptyLabelTemplate()}
|
||||||
${this._optionsTemplate()}
|
${this._optionsTemplate()}
|
||||||
|
${this._tagLimitTemplate()}
|
||||||
${this._extraTemplate()}
|
${this._extraTemplate()}
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<div slot="help-text">
|
<div slot="help-text">
|
||||||
|
@ -116,6 +116,8 @@ export class Et2SelectEmail extends Et2Select
|
|||||||
|
|
||||||
updated(changedProperties : Map<string, any>)
|
updated(changedProperties : Map<string, any>)
|
||||||
{
|
{
|
||||||
|
super.updated(changedProperties);
|
||||||
|
|
||||||
// Make tags draggable
|
// Make tags draggable
|
||||||
if(!this.readonly && this.allowFreeEntries && this.allowDragAndDrop)
|
if(!this.readonly && this.allowFreeEntries && this.allowDragAndDrop)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user