mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-02-08 14:30:05 +01:00
Further fixes on tree actions
- Fix expand on hover - Fix trouble with dragging over item children
This commit is contained in:
parent
2ed29d1557
commit
8613ed823b
@ -25,7 +25,8 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
|
||||
// Reference to the widget that's handling actions for us
|
||||
public findActionTargetHandler : FindActionTarget;
|
||||
|
||||
private timeout : ReturnType<typeof setTimeout>;
|
||||
// List of timeouts indexed by ID because drag out doesn't always happen before drag in
|
||||
private timeouts : { [key : string] : ReturnType<typeof setTimeout> };
|
||||
|
||||
constructor(_tree : Et2Tree)
|
||||
{
|
||||
@ -33,16 +34,17 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
|
||||
super();
|
||||
this.tree = _tree
|
||||
this.findActionTargetHandler = _tree;
|
||||
this.timeouts = {};
|
||||
}
|
||||
|
||||
public doTriggerEvent(egw_event : number, data : any)
|
||||
{
|
||||
let dom_event = data.event ?? data;
|
||||
const target = this.findActionTargetHandler.findActionTarget(dom_event);
|
||||
if(egw_event == EGW_AI_DRAG_ENTER)
|
||||
if(egw_event == EGW_AI_DRAG_ENTER && !target.target.classList.contains("draggedOver"))
|
||||
{
|
||||
target.target.classList.add("draggedOver", "drop-hover");
|
||||
this.timeout = setTimeout(() =>
|
||||
this.timeouts[target.target.id] = setTimeout(() =>
|
||||
{
|
||||
if(target.target.classList.contains("draggedOver"))
|
||||
{
|
||||
@ -53,7 +55,7 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
|
||||
else if(egw_event == EGW_AI_DRAG_OUT)
|
||||
{
|
||||
target.target.classList.remove("draggedOver", "drop-hover");
|
||||
clearTimeout(this.timeout)
|
||||
clearTimeout(this.timeouts[target.target.id])
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -20,12 +20,13 @@ export type TreeItemData = SelectOption & {
|
||||
// Has children, but they may not be provided in item
|
||||
child: Boolean | 1,
|
||||
data?: Object,//{sieve:true,...} or {acl:true} or other
|
||||
id: string,
|
||||
//this is coming from SelectOption
|
||||
value: string,
|
||||
im0: String,
|
||||
im1: String,
|
||||
im2: String,
|
||||
// Child items
|
||||
item: TreeItemData[],
|
||||
children: TreeItemData[],
|
||||
checked?: Boolean,
|
||||
nocheckbox: number | Boolean,
|
||||
open: 0 | 1,
|
||||
@ -74,6 +75,8 @@ export const composedPathContains = (_ev: any, tag?: string, className?: string)
|
||||
* @event {{id: String, item:SlTreeItem}} sl-expand emmited when tree item expands
|
||||
* //TODO add for other events
|
||||
* @since 23.1.x
|
||||
*
|
||||
* @event et2-click Emitted when a tree item is clicked. Clicks on the expand / collapse button and other slotted contents are excluded
|
||||
*/
|
||||
export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements FindActionTarget
|
||||
{
|
||||
@ -99,6 +102,11 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
||||
openIcon: String;
|
||||
@property({type: Function})
|
||||
onclick;// description: "JS code which gets executed when clicks on text of a node"
|
||||
/**
|
||||
* If true, only leafs (NOT folders) are selectable
|
||||
*/
|
||||
@property({type:Boolean})
|
||||
leafOnly = false
|
||||
|
||||
|
||||
//onselect and oncheck only appear in multiselectTree
|
||||
@ -224,25 +232,31 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
||||
css`
|
||||
:host {
|
||||
--sl-spacing-large: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
::part(expand-button) {
|
||||
rotate: none;
|
||||
padding: 0 0.2em 0 5em;
|
||||
margin-left: -5em;
|
||||
|
||||
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 sl-icon {
|
||||
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 {
|
||||
@ -259,6 +273,14 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
||||
background-color: var(--highlight-background-color);
|
||||
}
|
||||
|
||||
sl-tree-item.drop-hover > *:not(sl-tree-item) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -416,7 +438,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
||||
/** Sets focus on the control. */
|
||||
focus(options? : FocusOptions)
|
||||
{
|
||||
this._tree.focus();
|
||||
this._tree?.focus();
|
||||
}
|
||||
|
||||
/** Removes focus from the control. */
|
||||
@ -829,8 +851,52 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
||||
{
|
||||
return;
|
||||
}
|
||||
let id = option.value ?? (typeof option.id == 'number' ? String(option.id) : option.id);
|
||||
console.log(event.type, id);
|
||||
|
||||
// Remove drop hover from any parent nodes
|
||||
if(event.type == "dragenter")
|
||||
{
|
||||
event.stopPropagation();
|
||||
let current = option.parentElement;
|
||||
while(current)
|
||||
{
|
||||
current.classList.remove("draggedOver", "drop-hover");
|
||||
current = current.parentElement;
|
||||
}
|
||||
}
|
||||
// Ignore/stop events from child nodes, unless it's dragenter and the parent sl-tree-item isn't hovered yet
|
||||
if(["dragenter", "dragleave"].includes(event.type) && event.target != option && event.composedPath().includes(option))
|
||||
{
|
||||
event.stopPropagation();
|
||||
if(event.type != "dragenter" || option.classList.contains("drop-hover"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
//let id = option.value ?? (typeof option.id == 'number' ? String(option.id) : option.id);
|
||||
//console.log(event.type, id, event.target);
|
||||
|
||||
// Remove drop hover from any parent nodes
|
||||
if(event.type == "dragenter")
|
||||
{
|
||||
event.stopPropagation();
|
||||
let current = option.parentElement;
|
||||
while(current)
|
||||
{
|
||||
current.classList.remove("draggedOver", "drop-hover");
|
||||
current = current.parentElement;
|
||||
}
|
||||
}
|
||||
// Ignore/stop events from child nodes, unless it's dragenter and the parent sl-tree-item isn't hovered yet
|
||||
if(["dragenter", "dragleave"].includes(event.type) && event.target != option && event.composedPath().includes(option))
|
||||
{
|
||||
event.stopPropagation();
|
||||
if(event.type != "dragenter" || option.classList.contains("drop-hover"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
//let id = option.value ?? (typeof option.id == 'number' ? String(option.id) : option.id);
|
||||
//console.log(event.type, id, event.target);
|
||||
|
||||
const typeMap = {
|
||||
dragenter: EGW_AI_DRAG_ENTER,
|
||||
@ -878,16 +944,6 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
||||
}
|
||||
//fallback to try and set icon if everything else failed
|
||||
if (!img) img = selectOption.icon ?? selectOption.im0 ?? selectOption.im1 ?? selectOption.im2;
|
||||
if (img?.endsWith(".png"))
|
||||
{
|
||||
//sl-icon images need to be svgs if there is a png try to find the corresponding svg
|
||||
img = img.replace(".png", ".svg");
|
||||
}
|
||||
//append image path, if img is only relative (does not start with / or https://)
|
||||
if (img && typeof img === "string" && !img.match(/^(\/|https?:\/\/)/))
|
||||
{
|
||||
img = this.imagePath + img;
|
||||
}
|
||||
|
||||
// lazy iff "child" is set and "item" is empty or item does not exist in the first place
|
||||
const lazy = (selectOption.item?.length === 0 && selectOption.child) || (selectOption.child && !selectOption.item)
|
||||
@ -899,18 +955,34 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
||||
})
|
||||
}
|
||||
const value = selectOption.value ?? selectOption.id;
|
||||
const selected = typeof this.value == "string" && this.value == value || Array.isArray(this.value) && this.value.includes(value);
|
||||
|
||||
return html`
|
||||
<sl-tree-item
|
||||
part="item"
|
||||
exportparts="checkbox, label, item:item-item"
|
||||
id=${selectOption.id}
|
||||
title=${selectOption.tooltip || nothing}
|
||||
id=${value}
|
||||
title=${selectOption.tooltip ||selectOption.title || nothing}
|
||||
class=${selectOption.class || nothing}
|
||||
?selected=${typeof this.value == "string" && this.value == value || Array.isArray(this.value) && this.value.includes(value)}
|
||||
?selected=${selected}
|
||||
?expanded=${expandState}
|
||||
?disabled=${selectOption.disabled}
|
||||
?lazy=${lazy}
|
||||
?focused=${selectOption.focused || nothing}
|
||||
@click=${async(event) =>
|
||||
{
|
||||
// Don't react to expand or children
|
||||
if(event.target.hasAttribute("slot") || !event.target?.closest("sl-tree-item"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
await this.updateComplete;
|
||||
event.target?.closest("sl-tree-item").dispatchEvent(new CustomEvent("et2-click", {
|
||||
detail: {item: event.target?.closest("sl-tree-item")},
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
}}
|
||||
@sl-lazy-load=${(event) => {
|
||||
// No need for this to bubble up, we'll handle it (otherwise the parent leaf will load too)
|
||||
event.stopPropagation();
|
||||
@ -948,8 +1020,8 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
||||
>
|
||||
|
||||
|
||||
<sl-icon src="${img ?? nothing}"></sl-icon>
|
||||
<span class="tree-item__label">
|
||||
<et2-image src="${img ?? nothing}"></et2-image>
|
||||
<span part="label_text" class="tree-item__label">
|
||||
${selectOption.label ?? selectOption.text}
|
||||
</span>
|
||||
${(selectOption.badge) ?
|
||||
@ -968,7 +1040,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
||||
${this.styleTemplate()}
|
||||
<sl-tree
|
||||
part="tree"
|
||||
.selection=${/* implement unlinked multiple: this.multiple ? "multiple" :*/ "single"}
|
||||
.selection=${this.leafOnly?"leaf":"single"}
|
||||
@sl-selection-change=${
|
||||
(event: any) => {
|
||||
this._previousOption = this._currentOption ?? (this.value.length ? this.getNode(this.value[0]) : null);
|
||||
@ -1051,7 +1123,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
|
||||
>
|
||||
<sl-icon name="chevron-right" slot="expand-icon"></sl-icon>
|
||||
<sl-icon name="chevron-down" slot="collapse-icon"></sl-icon>
|
||||
${repeat(this._selectOptions, this._optionTemplate)}
|
||||
${repeat(this._selectOptions, (o) => o.value, this._optionTemplate)}
|
||||
</sl-tree>
|
||||
`;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user