Skip to content

Commit

Permalink
Make it easier to subclass components (#7730)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored Feb 25, 2025
1 parent a48b5fc commit 2da70d6
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 22 deletions.
14 changes: 8 additions & 6 deletions panel/chat/feed.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ class ChatFeed(ListPanel):
_disabled_stack = param.List(doc="""
The previous disabled state of the feed.""")

_card_type: ClassVar[type[Card]] = Card
_message_type: ClassVar[type[ChatMessage]] = ChatMessage
_stylesheets: ClassVar[list[str]] = [f"{CDN_DIST}css/chat_feed.css"]

def __init__(self, *objects, **params):
Expand All @@ -238,15 +240,15 @@ def __init__(self, *objects, **params):
# forward message params to ChatMessage for convenience
message_params = params.get("message_params", {})
for param_key in params.copy():
if param_key not in self.param and param_key in ChatMessage.param:
if param_key not in self.param and param_key in self._message_type.param:
message_params[param_key] = params.pop(param_key)
params["message_params"] = message_params

super().__init__(*objects, **params)

if self.help_text:
self.objects = [
ChatMessage(
self._message_type(
self.help_text,
user="Help",
show_edit_icon=False,
Expand Down Expand Up @@ -303,7 +305,7 @@ def __init__(self, *objects, **params):
card_params.update(card_overrides)
self.link(self._chat_log, objects='objects', bidirectional=True)
# we have a card for the title
self._card = Card(
self._card = self._card_type(
self._chat_log,
VSpacer(),
**card_params
Expand Down Expand Up @@ -359,7 +361,7 @@ def _update_placeholder(self):
PLACEHOLDER_SVG, sizing_mode="fixed", width=35, height=35,
css_classes=["rotating-placeholder"]
)
self._placeholder = ChatMessage(
self._placeholder = self._message_type(
self.placeholder_text,
avatar=loading_avatar,
css_classes=["message"],
Expand Down Expand Up @@ -436,7 +438,7 @@ def _build_message(
(isinstance(user, str) and user.lower() not in (self.callback_user.lower(), "help"))
)

message = ChatMessage(**message_params)
message = self._message_type(**message_params)
message.param.watch(self._on_edit_message, "edited")
return message

Expand Down Expand Up @@ -863,7 +865,7 @@ def add_step(
if default_layout == "column":
layout = Column
elif default_layout == "card":
layout = Card
layout = self._card_type
input_layout_params["header_css_classes"] = ["card-header"]
title = layout_params.pop("title", None)
input_layout_params["header"] = HTML(
Expand Down
28 changes: 15 additions & 13 deletions panel/chat/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,12 @@ class ChatInterface(ChatFeed):
The rendered buttons.""")

_stylesheets: ClassVar[list[str]] = [f"{CDN_DIST}css/chat_interface.css"]
_input_type: ClassVar[type[WidgetBase]] = ChatAreaInput

def __init__(self, *objects, **params):
widgets = params.get("widgets")
if widgets is None:
params["widgets"] = [ChatAreaInput(placeholder="Send a message")]
params["widgets"] = [self._input_type(placeholder="Send a message")]
elif not isinstance(widgets, list):
params["widgets"] = [widgets]
active = params.pop("active", None)
Expand Down Expand Up @@ -204,15 +205,7 @@ def _update_input_width(self):
if self.show_button_name is None:
self.show_button_name = self.width is None or self.width >= 400

@param.depends("widgets", "button_properties", watch=True)
def _init_widgets(self):
"""
Initialize the input widgets.
Returns
-------
The input widgets.
"""
def _init_button_data(self):
default_button_properties = {
"send": {"icon": "send", "_default_callback": self._click_send},
"stop": {"icon": "player-stop", "_default_callback": self._click_stop},
Expand All @@ -221,7 +214,6 @@ def _init_widgets(self):
"clear": {"icon": "trash", "_default_callback": self._click_clear},
}
self._allow_revert = len(self.button_properties) == 0

button_properties = {**default_button_properties, **self.button_properties}
for index, (name, properties) in enumerate(button_properties.items()):
name = name.lower()
Expand Down Expand Up @@ -256,6 +248,16 @@ def _init_widgets(self):
js_on_click=js_on_click,
)

@param.depends("widgets", "button_properties", watch=True)
def _init_widgets(self):
"""
Initialize the input widgets.
Returns
-------
The input widgets.
"""
self._init_button_data()
widgets = self.widgets
if isinstance(self.widgets, WidgetBase):
widgets = [self.widgets]
Expand Down Expand Up @@ -290,7 +292,7 @@ def _init_widgets(self):
# TextAreaInput will trigger auto send!
auto_send = (
isinstance(widget, tuple(self.auto_send_types)) or
type(widget) in (TextInput, ChatAreaInput)
type(widget) in (TextInput, self._input_type)
)
if auto_send and widget in new_widgets:
callback = partial(self._button_data["send"].callback, self)
Expand All @@ -299,7 +301,7 @@ def _init_widgets(self):
sizing_mode="stretch_width",
css_classes=["chat-interface-input-widget"]
)
if isinstance(widget, ChatAreaInput):
if isinstance(widget, self._input_type):
self.link(widget, disabled="disabled_enter")

self._buttons = {}
Expand Down
2 changes: 1 addition & 1 deletion panel/reactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -1618,7 +1618,7 @@ def _set_on_model(self, msg: Mapping[str, Any], root: Model, model: Model) -> No
try:
model.update(**transformed)
finally:
if old:
if prev_changing:
self._changing[root.ref['id']] = prev_changing
else:
del self._changing[root.ref['id']]
Expand Down
2 changes: 1 addition & 1 deletion panel/viewable.py
Original file line number Diff line number Diff line change
Expand Up @@ -1261,7 +1261,7 @@ def is_viewable_param(parameter: param.Parameter) -> bool:
"""
if isinstance(parameter, (Child, Children)):
return True
if isinstance(parameter, param.ClassSelector) and _is_viewable_class_selector(parameter):
if isinstance(parameter, param.ClassSelector) and _is_viewable_class_selector(parameter) and parameter.is_instance:
return True
if isinstance(parameter, param.List) and _is_viewable_list(parameter):
return True
Expand Down
2 changes: 1 addition & 1 deletion panel/widgets/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def _validate_options_groups(self, *events):
)

def _process_param_change(self, msg: dict[str, Any]) -> dict[str, Any]:
groups_provided = 'groups' in msg
groups_provided = msg.get('groups') is not None
msg = super()._process_param_change(msg)
if groups_provided or 'options' in msg and self.groups:
groups: dict[str, list[str | tuple[str, str]]] = self.groups
Expand Down

0 comments on commit 2da70d6

Please sign in to comment.