Below, I use {{ index }}
and I understand it can automatically update with ${index}, but I was wondering if there’s a way to manually trigger it in _log
or _scripts
from panel.reactive import ReactiveHTML
import param
class JSSlideshow(ReactiveHTML):
index = param.Integer(default=0)
_template = """<img id="slideshow" src="https://picsum.photos/800/300?image={{ index }}" onclick="${script('click')}"></img>"""
_scripts = {'click': 'data.index += 1'}
@param.depends("index", watch=True)
def _log(self):
print(self.index)
ss = JSSlideshow(width=800, height=300)
ss
Specifically, this works…
from panel.reactive import ReactiveHTML
import param
import panel as pn
pn.extension()
class Test(ReactiveHTML):
value = param.List(doc="The selected reactions.")
options = param.Dict(default={"favorite": "heart"}, doc="""
A key-value pair of reaction values and their corresponding tabler icon names
found on https://tabler-icons.io.""")
_template = """
<div id="reactions" class="reactions">
{% for reaction, icon_name in options.items() %}
<div id="v-{{ loop.index0 }}">${icon_name}</div>
{% endfor %}
</div>
"""
test = Test()
test
But not:
from panel.reactive import ReactiveHTML
import param
import panel as pn
pn.extension()
class Test(ReactiveHTML):
value = param.List(doc="The selected reactions.")
options = param.Dict(default={"favorite": "heart"}, doc="""
A key-value pair of reaction values and their corresponding tabler icon names
found on https://tabler-icons.io.""")
_template = """
<div id="reactions" class="reactions">
{% for reaction, icon_name in options.items() %}
<img id="reaction-{{ loop.index0 }}" src="https://tabler-icons.io/icons/static/tabler-icons/{{ icon_name }}.svg" alt="${reaction}"></img>
{% endfor %}
</div>
"""
test = Test()
test
So I am planning to use {{ }} instead as a work around.
Okay, I think rebuilding the HTML with Python param.depends works:
from panel.reactive import ReactiveHTML
import param
import panel as pn
pn.extension()
class Test(ReactiveHTML):
value = param.List(doc="The selected reactions.")
options = param.Dict(
default={"favorite": "heart"},
doc="""
A key-value pair of reaction values and their corresponding tabler icon names
found on https://tabler-icons.io.""",
)
_icons_html = param.String()
_template = """
<div id="reactions" class="reactions">
${_icons_html}
</div>
"""
@param.depends("options", watch=True, on_init=True)
def _update_icons_html(self):
self._icons_html = "\n".join(
f"""
<img id="reaction-{reaction}"
src="https://tabler-icons.io/static/tabler-icons/icons/{icon_name}.svg"
alt="{reaction}">
</img>
"""
for reaction, icon_name in self.options.items()
)
test = Test()
test
I guess it’s not able to attach scripts…
I finally found an elegant solution that is completely reactive after discovering how to loop properly:
class ReactionIcons(ReactiveHTML):
value = param.List()
options = param.Dict(default={})
_reactions = param.List()
_icons = param.List()
_base_url = param.String("https://tabler-icons.io/static/tabler-icons/icons/")
_template = """
<div id="reaction-icons">
{% for option in options %}
<span id="reaction-{{ loop.index0 }}" onclick=${_update_value} style="cursor: pointer;">${_icons[{{ loop.index0 }}]}</div>
{% endfor %}
</div>
"""
def _update_value(self, event):
reaction_index = int(event.node.split("-")[1])
reaction = self._reactions[reaction_index]
if reaction in self.value:
self.value = [r for r in self.value if r != reaction]
else:
self.value = [*self.value, reaction]
@pn.depends("value", "options", watch=True, on_init=True)
def _update_icons(self):
self._reactions = list(self.options.keys())
icons = []
for reaction, icon in self.options.items():
src = f"{self._base_url}{icon}.svg"
if reaction in self.value:
src = src.replace(".svg", "-filled.svg")
icons.append(src)
self._icons = icons
ri = ReactionIcons(
options={"like": "thumb-up", "dislike": "thumb-down"}, value=["like"]
)
ri
For a single icon:
import param
import panel as pn
import requests
pn.extension()
class ToggleIcon(pn.reactive.ReactiveHTML):
icon = param.String(default="thumb-up")
active = param.Boolean(default=False)
_svg = param.String()
_template = """
<div id="icon" onclick=${_click_icon} style="cursor: pointer;">${_svg}</div>
"""
def _click_icon(self, event):
self.active = not self.active
@pn.cache
def _fetch_svg(self, icon, active):
filled = "-filled" if active else ""
response = requests.get(
f"https://tabler-icons.io/static/tabler-icons/icons/" f"{icon}{filled}.svg"
)
svg = response.text
return svg
@pn.depends("icon", "active", watch=True, on_init=True)
def _update_icon(self):
svg = self._fetch_svg(self.icon, self.active)
if self.width:
svg = svg.replace('width="24"', f'width="{self.width}"')
if self.height:
svg = svg.replace('height="24"', f'height="{self.height}"')
self._svg = svg
ToggleIcon(active=True, width=200, height=200)