Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] CyLeaflet: Click position mismatch #212

Open
emilykl opened this issue Mar 15, 2024 · 1 comment
Open

[BUG] CyLeaflet: Click position mismatch #212

emilykl opened this issue Mar 15, 2024 · 1 comment

Comments

@emilykl
Copy link
Contributor

emilykl commented Mar 15, 2024

Description

Under some conditions, the click position of the user does not match the click position interpreted by Cytoscape. This results in, for example, the user clicking on a node but Cytoscape interpreting the click as below the node. The issue disappears after the browser window is resized.

Suspect this is caused by some nuance of CSS styling messing up the position of the Cytoscape canvas, but difficult to debug as I have not been able to reproduce on my machine.

Screen Recording

click.position.mismatch.mov

Steps/Code to Reproduce

import dash
from dash import html, dcc, callback, Input, Output
import dash_cytoscape as cyto
import dash_leaflet as dl


ELEMENTS = [
    {"data": {"id": "a", "label": "Node A", "lat": 45.519, "lon": -73.576}},
    {"data": {"id": "b", "label": "Node B", "lat": 45.521, "lon": -73.574}},
    {"data": {"id": "c", "label": "Node C", "lat": 45.520, "lon": -73.572}},
    {"data": {"id": "ab", "source": "a", "target": "b"}},
]

def serve_layout():
    return html.Div(
        children=[
            html.H1("CyLeaflet: Initial click position mismatch / Resize issue"),
            html.Hr(),
            dcc.Markdown("""
            * Make sure you refresh the browser (F5)
            * After the initial load, try to click in the center of a Node B.
            * Notice the grey circle displayed BELOW your click location, resulting in a pan insted of a node selection or a node drag.
            * To set things straight, here are 2 ways i found:
                * Resizing the browser window
                * Modifify the application file (.py), save, and let it reload by itself
            * But then if whenever you refresh the browser, the problem comes up again.
            """),
            cyto.CyLeaflet(
                id="cyleaflet",
                cytoscape_props=dict(
                    elements=ELEMENTS,
                ),
            ),
        ],
    )


app = dash.Dash(__name__)
server = app.server
app.layout = serve_layout


if __name__ == "__main__":
    app.run_server(debug=True)

Versions

OS: Windows
Browser: Chrome

@Farkites
Copy link
Contributor

This issue doesn't only apply to CyLeaflet AIO component. It is caused when the Cytoscape component is loaded and then its position on the DOM changes. I have been able to reproduce this behaviour by inputing a large number of elements to Cytoscape and having a dcc.Markdown above it. For some reason, the content in the dcc.Markdown loads after the Cytoscape so this is pushed down.

Easier ways to reproduce this would be to change the style of the elements around the Cytoscape component via callback. Any changes that result in modifying the position of the Cytoscape component will cause this mouse mismatch. For example, if you look closely at the video attached in the description it's possible to see that the mismatch is only vertical and of the exact height of the text between the title and the Cytoscape component.

The issue is solved by calling the cytoscape function cy.resize(), which is equivalent to resizing the window manually (as shown in the video).

The problem is where do we call this function since it can't run automatically when the DOM changes?
A partial solution would be to add it on a viewport event and on ('dragfree add remove') so we can make sure that only the first click is mismatching.

This code can be used to replicate the issue.

import dash
from dash import html, dcc, callback, Input, Output
import dash_cytoscape as cyto

lat_init = 45.5
lon_init = -73.576

elements = [
    {"data": {"id": str(x), "lat": lat_init + x / 1000, "lon": lon_init}}
    for x in range(10)
]

stylesheet = [
    {
        "selector": "node",
        "style": {
            "width": 1000,
            "height": 100,
            "background-color": "red",
        },
    },
]


def serve_layout():
    return html.Div(
        children=[
            html.H1("DASH CYTOSCAPE"),
            html.Hr(),
            dcc.Markdown(
                """
                    * Several
                    * lines
                    * to
                    * show
                    * mismatch
                """,
                id="text",
            ),
            cyto.CyLeaflet(
                id="cyleaflet",
                cytoscape_props=dict(
                    elements=elements,
                    stylesheet=stylesheet,   
                )
            ),
            html.Button("Show/hide text", id="btn")
        ],
    )

@callback(
    Output("text", "style"),
    Input("btn", "n_clicks")
)
def click(click):
    if not click or click % 2 == 0:
        return {"display": "block"}
    return {"display": "none"}

app = dash.Dash(__name__)
server = app.server
app.layout = serve_layout


if __name__ == "__main__":
    app.run_server(debug=True)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants