Building responsive Cytoscape graphs

Starting from v0.2.0, you can make your cytoscape graph responsive:

cyto.Cytoscape(
    id='cytoscape',
    ...,
    responsive=True
)

The following app shows this new feature in action:

"""
Original Demo: <a href="http://js.cytoscape.org/demos/cose-layout/">http://js.cytoscape.org/demos/cose-layout/</a>

Note: This implementation looks different from the original implementation,
although the input parameters are exactly the same.
"""
import requests
import json

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

def load_json(st):
    if 'http' in st:
        return requests.get(st).json()
    else:
        with open(st, 'rb') as f:
            x = json.load(f)
        return x

app = Dash(__name__)
server = app.server

# Load Data
elements = load_json('https://js.cytoscape.org/demos/colajs-graph/data.json')
stylesheet = load_json('https://js.cytoscape.org/demos/colajs-graph/cy-style.json')

styles = {
    'container': {
        'position': 'fixed',
        'display': 'flex',
        'flex-direction': 'column',
        'height': '100%',
        'width': '100%'
    },
    'cy-container': {
        'flex': '1',
        'position': 'relative'
    },
    'cytoscape': {
        'position': 'absolute',
        'width': '100%',
        'height': '100%',
        'z-index': 999
    }
}

# App
app.layout = html.Div(style=styles['container'], children=[
    html.Div([
        html.Button("Responsive Toggle", id='toggle-button'),
        html.Div(id='toggle-text')
    ]),
    html.Div(className='cy-container', style=styles['cy-container'], children=[
        cyto.Cytoscape(
            id='cytoscape-responsive-layout',
            elements=elements,
            stylesheet=stylesheet,
            style=styles['cytoscape'],
            layout={
                'name': 'cose',
                'idealEdgeLength': 100,
                'nodeOverlap': 20,
                'refresh': 20,
                'fit': True,
                'padding': 30,
                'randomize': False,
                'componentSpacing': 100,
                'nodeRepulsion': 400000,
                'edgeElasticity': 100,
                'nestingFactor': 5,
                'gravity': 80,
                'numIter': 1000,
                'initialTemp': 200,
                'coolingFactor': 0.95,
                'minTemp': 1.0
            },
            responsive=True
        )
    ])
])

@callback(Output('cytoscape', 'responsive'), Input('toggle-button', 'n_clicks'))
def toggle_responsive(n_clicks):
    n_clicks = 2 if n_clicks is None else n_clicks
    toggle_on = n_clicks % 2 == 0
    return toggle_on

@callback(Output('toggle-text', 'children'), Input('cytoscape', 'responsive'))
def update_toggle_text(responsive):
    return '\t' + 'Responsive ' + ('On' if responsive else 'Off')

if __name__ == '__main__':
    app.run(debug=True)