How do I use jsPanel with Panel and ReactiveHTML?

Ok. So here is something nice to get started :slight_smile:

floating-panel-speedup

import panel as pn
from panel import config
import param
from panel.layout.base import ListLike

RAW_CSS = """
body {
    margin: 0px;}
"""

if not RAW_CSS in pn.config.raw_css:
    pn.config.raw_css.append(RAW_CSS)
pn.extension(sizing_mode="stretch_width")

POSITIONS = [
    'center',
    'left-top',
    'center-top',
    'right-top',
    'right-center',
    'right-bottom',
    'center-bottom',
    'left-bottom',
    'left-center',
]


class JsPanel(ListLike, pn.reactive.ReactiveHTML):
    """A list-like floating panel for Panel :-)"""
    position = param.Selector(default='right-top', objects=POSITIONS)
    offsetx = param.Integer(0)
    offsety = param.Integer(0)
    theme = param.String(default="primary")

    config = param.Dict({}, doc="Additional configuration. C.f. the jsPanel docs. Takes precedence over parameter values")

    _template = """
<div id="element" class="bk-root" style="padding:8px;padding-right:30px">
{% for obj in objects %}
<div id="flex-item-{{ loop.index0 }}">
    ${objects[{{ loop.index0 }}]}
</div>
{% endfor %}
</div>
"""

    _scripts = {
        "render": """
console.log(data.config)
var config = {
    headerTitle: data.name,
    content: element,
    theme: data.theme,
    position: {
        at: data.position,
        my: data.position,
        offsetX: data.offsetx,
        offsetY: data.offsety,
    },
    contentSize: `${model.width} ${model.height}`,

}
config = {...config, ...data.config}
console.log(config)
jsPanel.create(config);
"""
}

    __css__ = ["https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel.css"]

    __javascript__ = [
        "https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel.js",
        "https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js",
        "https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js",
        "https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js",
        "https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js",
        "https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js",
        "https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js",
    ]

    def __init__(self, *objects, **params):
        super().__init__(objects=list(objects), **params)

from bokeh.plotting import figure

import holoviews as hv
import panel as pn
import panel.widgets as pnw

from bokeh.sampledata.autompg import autompg

df = autompg.copy()

ORIGINS = ['North America', 'Europe', 'Asia']

# data cleanup
df.origin = [ORIGINS[x-1] for x in df.origin]

df['mfr'] = [x.split()[0] for x in df.name]
df.loc[df.mfr=='chevy', 'mfr'] = 'chevrolet'
df.loc[df.mfr=='chevroelt', 'mfr'] = 'chevrolet'
df.loc[df.mfr=='maxda', 'mfr'] = 'mazda'
df.loc[df.mfr=='mercedes-benz', 'mfr'] = 'mercedes'
df.loc[df.mfr=='toyouta', 'mfr'] = 'toyota'
df.loc[df.mfr=='vokswagen', 'mfr'] = 'volkswagen'
df.loc[df.mfr=='vw', 'mfr'] = 'volkswagen'
del df['name']

columns = sorted(df.columns)
discrete = [x for x in columns if df[x].dtype == object]
continuous = [x for x in columns if x not in discrete]
quantileable = [x for x in continuous if len(df[x].unique()) > 20]

x = pnw.Select(name='X-Axis', value='mpg', options=quantileable)
y = pnw.Select(name='Y-Axis', value='hp', options=quantileable)
size = pnw.Select(name='Size', value='accel', options=['None'] + quantileable)
color = pnw.Select(name='Color', value='accel', options=['None'] + quantileable)

@pn.depends(x.param.value, y.param.value, color.param.value, size.param.value)
def create_figure(x, y, color, size):
    opts = dict(cmap='rainbow', responsive=True, min_width=1800, min_height=800, line_color='black')
    if color != 'None':
        opts['color'] = color
    if size != 'None':
        opts['size'] = hv.dim(size).norm()*20
    return hv.Points(df, [x, y], label="%s vs %s" % (x.title(), y.title())).opts(**opts)

widgets = JsPanel(x, y, color, size, name="Settings", theme="crimson", offsety=150, offsetx=-100, config={"headerControls": {"close": 'remove',},})
header = pn.pane.Markdown("   Awesome Panel - Floating Panel :-)", background="rgb(219, 20, 60)", margin=0, style={"color": "white", "padding-left": "25px", "font-weight": "bold", "font-size": "25px"})

pn.Column(header, pn.panel(create_figure, sizing_mode="stretch_both"), widgets).servable()

Thanks @nghenzi for helping me make this possible.

4 Likes