lidorsystems / integralui-web-angular

IntegralUI Web - Advanced UI Components for Angular 9
https://www.lidorsystems.com/products/web/studio/
Other
10 stars 8 forks source link

[dragLeave] event didn't fire when mouse move out of tree-view component #21

Closed mm-ryo closed 3 years ago

mm-ryo commented 3 years ago

If your mouse moves out of the tree-view component slowly. The event didn't fire sometimes.

here is a demo, you can try to drag the top tree of the last item to the below tree. https://stackblitz.com/edit/integralui-treeview-drag-drop-qz8rta

I have changed below codes: .trw-dd-normal { float: left; margin-right: 20px; **width: 100%;** height: 300px; }

Lidor-Systems commented 3 years ago

We are not sure exactly why dragLeave event doesn't fire sometimes. We suspect may be because of interference with view scrolling when mouse is close to the bottom border.

Anyway, we will check it out and make sure the fix is available in coming update, in few weeks.

Lidor-Systems commented 3 years ago

As you may know standard dragleave event is fired whenever mouse cursor leave the specified element. This happens also when that element contains child elements and the mouse cursor enters the child element space, even if the mouse cursor is still within the parent element space.

The TreeView dragLeave event is fired only when mouse cursor leaves the TreeView space, regardless of child item elements, scrollbar or any other custom content within. However, we have found out that the dragLeave event will not fire if mouse cursor is on TreeView border (any side), when leaving its space.

We have found what causes this and now is fixed. The fix will be available in coming update, v21.1.

mm-ryo commented 3 years ago

As you may know standard dragleave event is fired whenever mouse cursor leave the specified element. This happens also when that element contains child elements and the mouse cursor enters the child element space, even if the mouse cursor is still within the parent element space.

The TreeView dragLeave event is fired only when mouse cursor leaves the TreeView space, regardless of child item elements, scrollbar or any other custom content within. However, we have found out that the dragLeave event will not fire if mouse cursor is on TreeView border (any side), when leaving its space.

We have found what causes this and now is fixed. The fix will be available in coming update, v21.1.

Hi, I have tested with the hotfix. the [dragLeave] issue is fixed, but now the [dragEnd] event not fired 😭

Steps:

  1. drag item inside tree and not move out of tree component
  2. the target item has children and not expanded
Lidor-Systems commented 3 years ago

We cannot reproduce the problem using your steps. We also checked the code, and whenever standard HTML5 dragend event is fired from the TreeView component, the dragEnd event is also fired.

There is no condition that prevents firing of this event, so it should happen always whenever the standard HTML5 dragend event fires.

Do you have something in your code that cancels the drag and drop process?

mm-ryo commented 3 years ago

Hi,

Steps:

  1. select 'Watches' node
  2. drag&drop onto 'Sports' node

it won't work if drag position is center.

here is the demo https://stackblitz.com/edit/integralui-treeview-drag-drop-9typuu i already subscribed 'dragEnd' event in codes and printed console log.

Lidor-Systems commented 3 years ago

In HTML set virtual mode active:

[virtualMode]="true"

When not in virtual mode, the drag and drop is handled differently. The item is not created directly, but instead with a treeitem directive. Probably this prevents dragEnd event to fire when item is dropped over some target item.

Anyway, the non-virtual mode should be used only for simple tree hierarchies, it is not suitable to use in most cases. We highly recommend to use virtual mode.

mm-ryo commented 3 years ago

the code works only when node is collapsed, but it doesn't work in auto expand mode. the dragEnd event is still not fired.

Steps:

  1. collapse all nodes
  2. drag 'Watches' node to 'Sports' and wait for expanding
  3. drop item

[autoExpand]="true"

Updated demo: https://stackblitz.com/edit/integralui-treeview-drag-drop-9typuu

Lidor-Systems commented 3 years ago

Upon checking what is causing this, we notice that even the standard dragend event is not fired, when TreeView is updated during expanding of targeted item. We suspect because of this update to the DOM the dragend event was detached and that's the reason it doesn't fire. The dragend event is related with dragstart event, meaning if the start item is no longer present or recreated in the DOM, the dragend event cannot fire.

Whenever parent item is auto-expanded, the view is changed and probably because of it, the dragend event and subsequently the dragEnd event doesn't fire. We have tried also to handle dragend event from the , but still it doesn't fire.

Further we have checked this behavior for standard dragend event by starting to drag an item which is above or below the current view. Then by dragging that item close to the top or bottom order to activate the scrolling and change the view so that its no longer visible, we notice that even if it is dropped on target item without children or collapsed, the dragend event still doesn't fire.

As a conclusion: the standard dragend event fires only if the starting element from which the drag starts is still present in the DOM. Otherwise, it will not fire. If the starting element somehow changes, by re-recreating it, the connection with dragend event is lost.

Can you tell us why do you need to handle dragEnd event?

If you want to add custom code when item is dropped (you can call here dragDrop event) and to be sure that drop process is fully completed, you can use dragDropComplete event instead.

mm-ryo commented 3 years ago

Hi, so when we expand a node and tree-view component will recreate everything on the current viewport? if yes, I think the parent of expanded node shouldn't be recreated.

I have an idea, is it possible to fire 'dragend' event while 'mouseup' ?

I want to show a highlight color on the nodes which are not acceptable to the drag node. The below image: I drag node '6' and move on the node '5' and '4'. the color has changed to grey which means you can't drop node '6' on them, but you can drop onto node '1' or '2' or '3' image

could you provide some sample codes to implement it with dragDropComplete event or any other possible solution?

Lidor-Systems commented 3 years ago

so when we expand a node and tree-view component will recreate everything on the current viewport?

Yes. Because of virtualization, only tiny part of the tree data is displayed, depending on tree height.

I think the parent of expanded node shouldn't be recreated.

That's very hard to do. Because Angular auto-updates the DOM based on provided data.

I have an idea, is it possible to fire 'dragend' event while 'mouseup' ?

Mouse events are suppressed during drag and drop, this is standard HTML5 drag drop behavior. Its not possible to detect mouseup event when drag and drop ends.

Lidor-Systems commented 3 years ago

I want to show a highlight color on the nodes which are not acceptable to the drag node.

Yes, you can do this. Follow these steps:

  1. In item template use ngStyle directive to change the text color:
    <ng-template let-item [iuiTemplate]="{ type: 'item' }">
        <div [ngStyle]="getItemStyle(item)">
            {{item.text}}
        </div>
    </ng-template>
  1. Depending on whether there is active drag and drop and item, you can change the color:
    private isDropAllowed(item){
        return item && (item.id === 2 || item.id === 21 || item.id === 211 || item.id === 212) ? false : true;
    }

    getItemStyle(item){
        if (this.currentDragItem && !this.isDropAllowed(item))
            return { color: 'gray' }

        return null;
    }

In this case, if the item id is one of set values: 2, 21, 211 or 212 the drop is not allowed. In this case these items will appear in gray color.

  1. Make sure this happens only when there is an item dragged. For this you need to handle dragOver, dragDrop and dragEnd events:
    <iui-treeview . . .  (dragOver)="onDragOver($event)" (dragDrop)="onDragDrop($event)" (dragEnd)="onDragEnd($event)"></iui-treeview>

    private currentDragItem: any = null;

    onDragOver(e){
        this.currentDragItem = e.dragItem;

        if (!this.isDropAllowed(e.targetItem))
            e.cancel = true;
    }

    onDragDrop(e){
        this.currentDragItem = null;
    }

    onDragEnd(e){
        this.currentDragItem = null;
    }
  1. Also make sure that if drop is not allowed to actually stop the drag and drop operation over that item. This is done in dragOver event handler, by setting the event cancel field to true.
        if (!this.isDropAllowed(e.targetItem))
            e.cancel = true;

could you provide some sample codes to implement it with dragDropComplete event or any other possible solution?

dragDrop and dragDropComplete events both fire when the item is dropped in TreeView. The difference is that you can cancel the dragDrop event by setting the cancel field to true and prevent built-in drop item functionality to take place. In this way you can set your own custom actions when item is dropped.

On the other hand dragDropComplete is fired after the dragDrop event and when the whole drag and drop process is fully complete. You cannot cancel this event, but you can add your own custom actions when drop completes.

mm-ryo commented 3 years ago

I think this doesn't work for me. we already implemented the above codes. Now the problem is that we don't know 'dragEnd' event whether is complete. and we don't know when we can clear the highlight style.

did you suppress event or called StopPropagation() in your component? I subscribed 'dragEnd' or 'mouseup' event on the container div of tree-view component but the event didn't bubble to parent element.

Lidor-Systems commented 3 years ago

No, the dragEnd is not suppressed. You can still add a handler to standard dragend event in any element above the TreeView. But as we mentioned earlier, this event is not fired if the starting drag element is not present in the DOM (that is an item from where the drag starts somewhere outside of the current view).

Did you try to handle the dragDrop event?

    onDragDrop(e){
        this.currentDragItem = null;
    }

This event is fired whenever an item is dropped over some other item or in empty space in TreeView.

In above code the only situation that is not covered if you have starting drag item not present in the view (that is you scroll the view during drag drop) and you release the mouse button outside the treeview, In that case, you can clear the currentDragItem by handling the dragLeave event.

    onDragLeave(e){
        this.currentDragItem = null;
    }

The Solution is:

  1. If you drag an item and drop it in the TreeView without scrolling, then clearing in dragDrop event will do the work
  2. If you drag an item and drop it in the TreeView with scrolling, then clearing in dragDrop event will also do the work
  3. If you drag an item and drop it outside of the TreeView, then clearing in dragLeave event will do the work

In the last case #3, you also need to handle dragEnter or dragOver event (if you didn't release the mouse button), so that the specified items will change the color again when entering theTreeView. In this case you will update the currentDragItem from the event data,

Here is the code that you need:

    onDragEnter(e){
        this.currentDragItem = e.dragItem;
    }

    onDragOver(e){
        this.currentDragItem = e.dragItem;
    }

    onDragLeave(e){
        this.currentDragItem = null;
    }

    onDragDrop(e){
        this.currentDragItem = null;
    }

As you can see, you don't need the dragEnd event at all. We have tried this in all situations and it works.

mm-ryo commented 3 years ago

I forgot the 'dragDrop' event. it seems working fine. thanks a lot.