ReactiveHTML - Make Custom Components Easily

Panel 0.12.0 introduces the ReactiveHTML component which makes it easy to create custom components.

Check it out and please share your comments, questions and examples. Thanks.

Reference: Custom Components — Panel 0.12.0 documentation

Examples: Gallery — Panel 0.12.0 documentation

import panel as pn
import param
class Slideshow(pn.reactive.ReactiveHTML):

    index = param.Integer(default=0)

    _template = '<img id="img" src="https://picsum.photos/800/300?image=${index}" onclick="${_img_click}"></img>'

    def _img_click(self, event):
        self.index += 1

Slideshow(width=800, height=300)

The code to create the app in the video is

import panel as pn
import param

pn.extension("ace", sizing_mode="stretch_width")



accent_base_color = "#00A170" # "#F08080"
_LOGOS = {
    "default": "https://panel.holoviz.org/_static/logo_stacked.png",
    "dark": "https://raw.githubusercontent.com/holoviz/panel/98389a8dead125bcb7c60dc2c1564e112d89d3fa/doc/_static/logo_stacked_dark_theme.png",
}
template = pn.template.FastListTemplate(
    title="Panel 0.12 - ReactiveHTML - Custom Components made Easy!",
    accent_base_color=accent_base_color,
    header_background=accent_base_color,
    header_accent_base_color="white",
)
theme = "dark" if template.theme == pn.template.DarkTheme else "default"
logo=_LOGOS[theme]
template.logo=logo
_ACE_THEMES={
    "default": "chrome",
    "dark": "tomorrow_night_eighties"
}
ace_theme=_ACE_THEMES[theme]

def get_logo_pane():
    announcement_pane = """\
# `ReactiveHTML` - Custom Components made Easy!

The [`ReactiveHTML`](https://panel.holoviz.org/user_guide/Custom_Components.html#reactivehtml-components) enables you to **make custom components easily** to power up your #DataScience and #MachineLearning apps.

## `ReactiveHTML` Reference

The `ReactiveHTML` provides **bi-directional syncing** of arbitrary HTML attributes and DOM properties with parameters on the subclass.

This kind of component must declare a HTML template written using Javascript template variables (`${}`) and optionally *Jinja2* syntax:

- `_template`: The HTML template to render declaring how to link parameters on the class to HTML attributes.

Additionally the component may declare some additional attributes providing further functionality

- `_child_config` (optional): Optional mapping that controls how children are rendered.

- `_dom_events` (optional): Optional mapping of named nodes to DOM events to add event listeners to.

- `_scripts` (optional): Optional mapping of Javascript to execute on specific parameter changes.

For more check out the [REFERENCE GUIDE](https://panel.holoviz.org/user_guide/Custom_Components.html#reactivehtml-components) on the Panel website.
"""
    logo_pane = pn.pane.PNG(
        logo,
        link_url="https://panel.holoviz.org",
        embed=False,
        sizing_mode="fixed",
        align="start",
        margin=(10,5,10,50),
        name="LOGO",
    )
    return pn.Row(announcement_pane, logo_pane, min_height=400, sizing_mode="stretch_both")

def get_example_pane():
    class Slideshow(pn.reactive.ReactiveHTML):

        index = param.Integer(default=0)

        _template = '<img id="img" src="https://picsum.photos/800/300?image=${index}" onclick="${_img_click}"></img>'

        def _img_click(self, event):
            self.index += 1

    example=Slideshow(height=300, width=300)
    return pn.Column(
        "# Example - SlideShow with `ReactiveHTML`", example, "Changes the slide when clicked. The code is simple."
    )

def get_code_pane():
    code = pn.widgets.Ace(
        value="""\
import panel as pn
import param
class Slideshow(pn.reactive.ReactiveHTML):

    index = param.Integer(default=0)

    _template = '<img id="img" src="https://picsum.photos/800/300?image=${index}" onclick="${_img_click}"></img>'

    def _img_click(self, event):
        self.index += 1

Slideshow(width=800, height=300)
""", name="CODE", sizing_mode="stretch_width", height=260, language="python", theme=ace_theme
    )
    return code


template.main[:]=[
    get_logo_pane, pn.Column(get_example_pane, get_code_pane, sizing_mode="stretch_both"),
]
template.servable()