jsplumb / community-edition

The community edition of jsPlumb, versions 1.x - 6.x
https://jsplumbtoolkit.com
Other
225 stars 18 forks source link

Feature request: allow updating of target selectors list during a drag #82

Open panayotisk opened 2 years ago

panayotisk commented 2 years ago

version: jsplumb 5.8.3 description: This request is about allowing recalculation of target selectors (through a function call) during a drag. The reason is to avoid always rendering DOM elements for connection handlers and thus having a very reduced DOM size. If we could somehow create DOM elements for connection handlers only when hovering over a node, we would drastically reduce the number of DOM nodes on the page. In our case this means 4 connection handlers (left, right, up, down) per node. We made performance benchmarks on our page with and without the rendering of connection handlers and there is significant difference on large DOMs. I do not know if it is possible at all to modify the jsplumb setup in order to support this request, however if it can be done it would improve the performance of our application a lot. initial (relevant) issue: jsplumb/community-edition#253 I think this request is important enough to deserve a separate issue submission.

sporritt commented 2 years ago

I am a bit wary of this idea. Do you think you could achieve what you want with the concept of "position finders":

https://blog.jsplumbtoolkit.com/2022/02/10/anchor-position-finder

panayotisk commented 2 years ago

Hmm maybe. I did not know of this feature! It might solve our issue with the DOM elements. In this case I do not need the handler elements, and write the appropriate position finder code. What confuses me is that during drag I would like to highlight the side (face) that will host the anchor and, as described in jsplumb/community-edition#253, I do not know what mouse event to use in order to achieve it. Maybe you have a tip on this? This is important for UX because the user should know where the drop will position their connection.

sporritt commented 2 years ago

I probably wouldnt do the hover with jsplumb, it could be done with a couple of event listeners that you manage:

https://jsfiddle.net/8zq5ohb7/1/

<div id="container">
   <div id="aDiv" class="el"></div>
   <div id="anotherDiv" class="el"></div>
</div>
// remove al classes
function reset(e) {
    e.target.classList.remove("north", "south", "east", "west")
}

// calculate distance between two points
function dist(x1, y1, x2, y2) {
    return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
}

container.addEventListener("mousemove", (e) => {
  if (e.target.classList.contains("el")) {
        reset(e)
    const b = e.target.getBoundingClientRect(),
// prepare an array of midpoints for each face
        faces = [
            [ "north", b.x + (b.width/ 2), b.y ],
            [ "south", b.x + (b.width / 2), b.y + b.height ],
            [ "east", b.x + b.width, b.y + (b.height / 2) ],          
            [ "west", b.x, b.y + (b.height / 2) ],      
        ]

// sort midpoints by distance from event's page location
   faces.sort((a,b) => {
    return dist(a[1], a[2], e.pageX, e.pageY) - dist(b[1], b[2], e.pageX, e.pageY)
   })     

// add the class corresponding to the closest midpoint to the element
  e.target.classList.add(faces[0][0])
  }
})

container.addEventListener("mouseout", (e) => {
  if (e.target.classList.contains("el")) {
        reset(e)
  }
})
sporritt commented 2 years ago

i should point out that in this case the event listeners are on the container and they're checking for an "el" class on the elements. This might be a little simplified from a real world use case because here the nodes dont have any child elements; in the real world this would probably need tweaking, in particular the mousemove listener, as the events won't necessarily be coming from the node element. The mouseout event would probably be fired by the node elements, though, regardless of their content.

You may need to attach a delegated event handler, or of course you could attach this listener to every node. but i know you want to render some large datasets and your focus is on performance.

panayotisk commented 2 years ago

I will be trying your suggestions and get back to you as soon as I manage to have something that is working for our case.

panayotisk commented 2 years ago

I finally got the time to test your suggestion of showing the desirable handler by using simple javascript. For determining the source handler (left-right-top-bottom) this works pretty well. For determining the target handler (while dragging the connection endpoint) there is the problem that mouse events are consumed by jsplumb so the mousemove event stops working. This was described here Maybe you have some tip what to do for determining the handler while dragging?

A code sandbox that uses your code above with jsplumb is here My issue is how to display handlers on target nodes, since all I got is the jtk-drag-hover class.