mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-28 19:03:14 +01:00
Et2Tree: Add "unselectable" attribute to TreeItemData
unselectable=true is like disabled, in that the item cannot be selected, but if it has children it can still be expanded so children can be selected. disabled=true cannot be expanded. Also some restructuring due to method size
This commit is contained in:
parent
9f90aaf085
commit
0add086707
101
api/js/etemplate/Et2Tree/Et2Tree.styles.ts
Normal file
101
api/js/etemplate/Et2Tree/Et2Tree.styles.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import {css} from 'lit';
|
||||||
|
|
||||||
|
export default css`
|
||||||
|
:host {
|
||||||
|
--sl-spacing-large: 1rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
::part(expand-button) {
|
||||||
|
rotate: none;
|
||||||
|
padding: 0 var(--sl-spacing-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop icon from shrinking if there's not enough space */
|
||||||
|
/* increase font size by 2px this was previously done in pixelegg css but document css can not reach shadow root*/
|
||||||
|
|
||||||
|
sl-tree-item et2-image {
|
||||||
|
flex: 0 0 1em;
|
||||||
|
font-size: calc(100% + 2px);
|
||||||
|
line-height: calc(100% - 2px);
|
||||||
|
padding-right: .4em;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
::part(label) {
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
::part(label):hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-item__label {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-tree-item.drop-hover {
|
||||||
|
background-color: var(--highlight-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-tree-item.drop-hover > *:not(sl-tree-item) {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Mail specific style TODO move it out of the component*/
|
||||||
|
|
||||||
|
sl-tree-item.unread > .tree-item__label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-tree-item.mailAccount > .tree-item__label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-tree > sl-tree-item:nth-of-type(n+2) {
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End Mail specific style*/
|
||||||
|
|
||||||
|
sl-tree-item.drop-hover sl-tree-item {
|
||||||
|
background-color: var(--sl-color-neutral-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-tree-item[unselectable]::part(item) {
|
||||||
|
outline: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*TODO color of selected marker in front should be #006699 same as border top color*/
|
||||||
|
|
||||||
|
sl-badge::part(base) {
|
||||||
|
|
||||||
|
background-color: var(--badge-color); /* This is the same color as app color mail */
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 900;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0.5em;
|
||||||
|
line-height: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-device-width: 768px) {
|
||||||
|
:host {
|
||||||
|
--sl-font-size-medium: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-tree-item {
|
||||||
|
padding: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
`;
|
@ -2,7 +2,7 @@ import {SlTreeItem} from "@shoelace-style/shoelace";
|
|||||||
import {egw} from "../../jsapi/egw_global";
|
import {egw} from "../../jsapi/egw_global";
|
||||||
import {find_select_options, SelectOption} from "../Et2Select/FindSelectOptions";
|
import {find_select_options, SelectOption} from "../Et2Select/FindSelectOptions";
|
||||||
import {Et2WidgetWithSelectMixin} from "../Et2Select/Et2WidgetWithSelectMixin";
|
import {Et2WidgetWithSelectMixin} from "../Et2Select/Et2WidgetWithSelectMixin";
|
||||||
import {css, html, LitElement, nothing, PropertyValues, TemplateResult} from "lit";
|
import {html, LitElement, nothing, PropertyValues, TemplateResult} from "lit";
|
||||||
import {repeat} from "lit/directives/repeat.js";
|
import {repeat} from "lit/directives/repeat.js";
|
||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
import {property} from "lit/decorators/property.js";
|
import {property} from "lit/decorators/property.js";
|
||||||
@ -14,6 +14,7 @@ import {EgwAction} from "../../egw_action/EgwAction";
|
|||||||
import {EgwDragDropShoelaceTree} from "../../egw_action/EgwDragDropShoelaceTree";
|
import {EgwDragDropShoelaceTree} from "../../egw_action/EgwDragDropShoelaceTree";
|
||||||
import {FindActionTarget} from "../FindActionTarget";
|
import {FindActionTarget} from "../FindActionTarget";
|
||||||
import {EGW_AI_DRAG_ENTER, EGW_AI_DRAG_OUT, EGW_AO_FLAG_IS_CONTAINER} from "../../egw_action/egw_action_constants";
|
import {EGW_AI_DRAG_ENTER, EGW_AI_DRAG_OUT, EGW_AO_FLAG_IS_CONTAINER} from "../../egw_action/egw_action_constants";
|
||||||
|
import styles from "./Et2Tree.styles";
|
||||||
|
|
||||||
export type TreeItemData = SelectOption & {
|
export type TreeItemData = SelectOption & {
|
||||||
focused?: boolean;
|
focused?: boolean;
|
||||||
@ -28,6 +29,12 @@ export type TreeItemData = SelectOption & {
|
|||||||
// Child items
|
// Child items
|
||||||
children: TreeItemData[],
|
children: TreeItemData[],
|
||||||
checked?: Boolean,
|
checked?: Boolean,
|
||||||
|
|
||||||
|
// For items with children, "disabled" will make the item not expandable.
|
||||||
|
// unselectable=true is like disabled=true, but will still allow the item to expand
|
||||||
|
// and show its children
|
||||||
|
unselectable? : boolean,
|
||||||
|
|
||||||
nocheckbox: number | Boolean,
|
nocheckbox: number | Boolean,
|
||||||
open: 0 | 1,
|
open: 0 | 1,
|
||||||
parent: String,
|
parent: String,
|
||||||
@ -227,101 +234,8 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
shoelace,
|
shoelace,
|
||||||
// @ts-ignore
|
super.styles,
|
||||||
...super.styles,
|
styles
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
--sl-spacing-large: 1rem;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
::part(expand-button) {
|
|
||||||
rotate: none;
|
|
||||||
padding: 0 var(--sl-spacing-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop icon from shrinking if there's not enough space */
|
|
||||||
/* increase font size by 2px this was previously done in pixelegg css but document css can not reach shadow root*/
|
|
||||||
|
|
||||||
sl-tree-item et2-image {
|
|
||||||
flex: 0 0 1em;
|
|
||||||
font-size: calc(100% + 2px);
|
|
||||||
line-height: calc(100% - 2px);
|
|
||||||
padding-right: .4em;
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
::part(label) {
|
|
||||||
overflow: hidden;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
::part(label):hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-item__label {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
sl-tree-item.drop-hover {
|
|
||||||
background-color: var(--highlight-background-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
sl-tree-item.drop-hover > *:not(sl-tree-item) {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Mail specific style TODO move it out of the component*/
|
|
||||||
sl-tree-item.unread > .tree-item__label {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
sl-tree-item.mailAccount > .tree-item__label {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
sl-tree > sl-tree-item:nth-of-type(n+2){
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
/* End Mail specific style*/
|
|
||||||
|
|
||||||
sl-tree-item.drop-hover sl-tree-item {
|
|
||||||
background-color: var(--sl-color-neutral-0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*TODO color of selected marker in front should be #006699 same as border top color*/
|
|
||||||
|
|
||||||
sl-badge::part(base) {
|
|
||||||
|
|
||||||
background-color: var(--badge-color); /* This is the same color as app color mail */
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 900;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0.5em;
|
|
||||||
line-height: 60%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media only screen and (max-device-width: 768px) {
|
|
||||||
:host {
|
|
||||||
--sl-font-size-medium: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
sl-tree-item {
|
|
||||||
padding: 0.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,6 +792,87 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
|||||||
this.widget_object.iface.triggerEvent(typeMap[event.type] ?? event.type, event);
|
this.widget_object.iface.triggerEvent(typeMap[event.type] ?? event.type, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a change in selected items
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
protected handleSelectionChange(event)
|
||||||
|
{
|
||||||
|
// Filter out unselectable nodes
|
||||||
|
let nodes = event.detail.selection.filter(node => !node.hasAttribute("unselectable"));
|
||||||
|
if(nodes.length != event.detail.selection.length)
|
||||||
|
{
|
||||||
|
event.detail.selection.forEach(n =>
|
||||||
|
{
|
||||||
|
if(!n.hasAttribute("unselectable"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
n.removeAttribute("selected");
|
||||||
|
if(n.querySelectorAll(":scope > sl-tree-item").length > 0)
|
||||||
|
{
|
||||||
|
n.toggleAttribute("expanded");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
event.stopPropagation();
|
||||||
|
this.requestUpdate("value");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._previousOption = this._currentOption ?? (this.value.length ? this.getNode(this.value[0]) : null);
|
||||||
|
this._currentOption = this.getNode(nodes[0].id) ?? this.optionSearch(nodes[0].id, this._selectOptions, 'id', 'item');
|
||||||
|
const ids = event.detail.selection.map(i => i.id);
|
||||||
|
// implemented unlinked multiple
|
||||||
|
if(this.multiple)
|
||||||
|
{
|
||||||
|
const idx = this.value.indexOf(ids[0]);
|
||||||
|
if(idx < 0)
|
||||||
|
{
|
||||||
|
this.value.push(ids[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.value.splice(idx, 1);
|
||||||
|
}
|
||||||
|
// sync tree-items selected attribute with this.value
|
||||||
|
this.selectedNodes = [];
|
||||||
|
Array.from(this._tree.querySelectorAll('sl-tree-item')).forEach((item : SlTreeItem) =>
|
||||||
|
{
|
||||||
|
if(this.value.includes(item.id))
|
||||||
|
{
|
||||||
|
item.setAttribute("selected", "");
|
||||||
|
this.selectedNodes.push(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.removeAttribute("selected");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._tree.requestUpdate();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.value = this.multiple ? ids ?? [] : ids[0] ?? "";
|
||||||
|
}
|
||||||
|
event.detail.previous = this._previousOption?.id;
|
||||||
|
this._currentSlTreeItem = nodes[0];
|
||||||
|
/* implemented unlinked-multiple
|
||||||
|
if(this.multiple)
|
||||||
|
{
|
||||||
|
this.selectedNodes = event.detail.selection
|
||||||
|
}*/
|
||||||
|
if(typeof this.onclick == "function")
|
||||||
|
{
|
||||||
|
// wait for the update, so app founds DOM in the expected state
|
||||||
|
this._tree.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
this.onclick(nodes[0].id, this, event.detail.previous)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected async finishedLazyLoading()
|
protected async finishedLazyLoading()
|
||||||
{
|
{
|
||||||
await this.lazyLoading;
|
await this.lazyLoading;
|
||||||
@ -899,7 +894,6 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
|||||||
//this.selectOptions = find_select_options(this)[1];
|
//this.selectOptions = find_select_options(this)[1];
|
||||||
_optionTemplate(selectOption: TreeItemData): TemplateResult<1>
|
_optionTemplate(selectOption: TreeItemData): TemplateResult<1>
|
||||||
{
|
{
|
||||||
|
|
||||||
// Check to see if node is marked as open with no children. If autoloadable, load the children
|
// Check to see if node is marked as open with no children. If autoloadable, load the children
|
||||||
const expandState = (this.calculateExpandState(selectOption));
|
const expandState = (this.calculateExpandState(selectOption));
|
||||||
|
|
||||||
@ -937,7 +931,8 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
|||||||
id=${value}
|
id=${value}
|
||||||
title=${selectOption.tooltip ||selectOption.title || nothing}
|
title=${selectOption.tooltip ||selectOption.title || nothing}
|
||||||
class=${selectOption.class || nothing}
|
class=${selectOption.class || nothing}
|
||||||
?selected=${selected}
|
?selected=${selected && !selectOption.unselectable}
|
||||||
|
?unselectable=${selectOption.unselectable}
|
||||||
?expanded=${expandState}
|
?expanded=${expandState}
|
||||||
?disabled=${selectOption.disabled}
|
?disabled=${selectOption.disabled}
|
||||||
?lazy=${lazy}
|
?lazy=${lazy}
|
||||||
@ -1014,59 +1009,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
|||||||
<sl-tree
|
<sl-tree
|
||||||
part="tree"
|
part="tree"
|
||||||
.selection=${this.leafOnly?"leaf":"single"}
|
.selection=${this.leafOnly?"leaf":"single"}
|
||||||
@sl-selection-change=${
|
@sl-selection-change=${this.handleSelectionChange}
|
||||||
(event: any) => {
|
|
||||||
this._previousOption = this._currentOption ?? (this.value.length ? this.getNode(this.value[0]) : null);
|
|
||||||
this._currentOption = this.getNode(event.detail.selection[0].id) ?? this.optionSearch(event.detail.selection[0].id, this._selectOptions, 'id', 'item');
|
|
||||||
const ids = event.detail.selection.map(i => i.id);
|
|
||||||
// implemented unlinked multiple
|
|
||||||
if (this.multiple)
|
|
||||||
{
|
|
||||||
const idx = this.value.indexOf(ids[0]);
|
|
||||||
if (idx < 0)
|
|
||||||
{
|
|
||||||
this.value.push(ids[0]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.value.splice(idx, 1);
|
|
||||||
}
|
|
||||||
// sync tree-items selected attribute with this.value
|
|
||||||
this.selectedNodes = [];
|
|
||||||
Array.from(this._tree.querySelectorAll('sl-tree-item')).forEach((item : SlTreeItem) =>
|
|
||||||
{
|
|
||||||
if(this.value.includes(item.id))
|
|
||||||
{
|
|
||||||
item.setAttribute("selected", "");
|
|
||||||
this.selectedNodes.push(item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
item.removeAttribute("selected");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._tree.requestUpdate();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.value = this.multiple ? ids ?? [] : ids[0] ?? "";
|
|
||||||
}
|
|
||||||
event.detail.previous = this._previousOption?.id;
|
|
||||||
this._currentSlTreeItem = event.detail.selection[0];
|
|
||||||
/* implemented unlinked-multiple
|
|
||||||
if(this.multiple)
|
|
||||||
{
|
|
||||||
this.selectedNodes = event.detail.selection
|
|
||||||
}*/
|
|
||||||
if(typeof this.onclick == "function")
|
|
||||||
{
|
|
||||||
// wait for the update, so app founds DOM in the expected state
|
|
||||||
this._tree.updateComplete.then(() => {
|
|
||||||
this.onclick(event.detail.selection[0].id, this, event.detail.previous)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@sl-expand=${
|
@sl-expand=${
|
||||||
(event) => {
|
(event) => {
|
||||||
event.detail.id = event.target.id
|
event.detail.id = event.target.id
|
||||||
|
@ -984,7 +984,7 @@ export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidg
|
|||||||
@et2-click=${(e) =>
|
@et2-click=${(e) =>
|
||||||
{
|
{
|
||||||
// Always hide the popup when a tree item is clicked
|
// Always hide the popup when a tree item is clicked
|
||||||
this.hide();
|
// this.hide();
|
||||||
}}
|
}}
|
||||||
@keydown=${this.handleComboboxKeyDown}
|
@keydown=${this.handleComboboxKeyDown}
|
||||||
@sl-selection-change=${this.handleTreeChange}
|
@sl-selection-change=${this.handleTreeChange}
|
||||||
|
Loading…
Reference in New Issue
Block a user