Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* `playwright.controller.InputActionButton` gains a `expect_icon()` method. As a result, the already existing `expect_label()` no longer includes the icon. (#2020)

* `ui.sidebar()` gains a `fillable` argument to support vertical fill behavior in sidebars. (#2077)

### Changes

* `express.ui.insert_accordion_panel()`'s function signature has changed to be more ergonomic. Now you can pass the `panel_title` and `panel_contents` directly instead of `ui.hold()`ing the `ui.accordion_panel()` context manager. (#2042)
Expand Down
6 changes: 6 additions & 0 deletions shiny/express/ui/_cm_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def sidebar(
max_height_mobile: Optional[str | float] = None,
gap: Optional[CssUnit] = None,
padding: Optional[CssUnit | list[CssUnit]] = None,
fillable: bool = False,
**kwargs: TagAttrValue,
) -> RecallContextManager[ui.Sidebar]:
"""
Expand Down Expand Up @@ -122,6 +123,10 @@ def sidebar(
and right, and the third will be bottom.
* If four, then the values will be interpreted as top, right, bottom, and left
respectively.
fillable
Whether or not the sidebar should be considered a fillable container.
When `True`, the sidebar and its content can use `fill` to consume
available vertical space.
**kwargs
Named attributes are supplied to the sidebar content container.
"""
Expand All @@ -139,6 +144,7 @@ def sidebar(
max_height_mobile=max_height_mobile,
gap=gap,
padding=padding,
fillable=fillable,
**kwargs,
),
)
Expand Down
53 changes: 40 additions & 13 deletions shiny/ui/_sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ class directly. Instead, supply the :func:`~shiny.ui.sidebar` object to
and right, and the third will be bottom.
* If four, then the values will be interpreted as top, right, bottom, and left
respectively.
fillable
Whether or not the sidebar should be considered a fillable container.
When `True`, the sidebar and its content can use `fill` to consume
available vertical space.

Parameters
----------
Expand Down Expand Up @@ -283,6 +287,7 @@ def __init__(
max_height_mobile: Optional[str | float] = None,
gap: Optional[CssUnit] = None,
padding: Optional[CssUnit | list[CssUnit]] = None,
fillable: bool = False,
):
if isinstance(title, (str, int, float)):
title = tags.header(str(title), class_="sidebar-title")
Expand All @@ -292,6 +297,7 @@ def __init__(
self.class_ = class_
self.gap = as_css_unit(gap)
self.padding = as_css_padding(padding)
self.fillable = fillable
# User-provided initial open state
self._open: SidebarOpen | None = self._as_open(open)
# Shiny or consumer-provided default open state, change with `_set_default_open()`
Expand Down Expand Up @@ -403,29 +409,44 @@ def _sidebar_tag(self, id: str | None) -> Tag:
self.open().desktop == "closed" or self.open().mobile == "closed"
)

return tags.aside(
# Create the sidebar content div
sidebar_content = div(
{
"class": "sidebar-content bslib-gap-spacing",
"style": css(
gap=self.gap,
padding=self.padding,
),
},
self.title,
*self.children,
self.attrs,
)

# Apply fill_item to the content div if fillable
if self.fillable:
sidebar_content = as_fill_item(sidebar_content)
sidebar_content = as_fillable_container(sidebar_content)

# Create the sidebar tag
sidebar_tag = tags.aside(
{
"id": id,
"class": "sidebar",
"hidden": "true" if is_hidden_initially else None,
},
# If the user provided an id, we make the sidebar an input to report state
{"class": "bslib-sidebar-input"} if self.id is not None else None,
div(
{
"class": "sidebar-content bslib-gap-spacing",
"style": css(
gap=self.gap,
padding=self.padding,
),
},
self.title,
*self.children,
self.attrs,
),
sidebar_content,
class_=self.class_,
)

# Apply fillable container to the sidebar if needed
if self.fillable:
sidebar_tag = as_fillable_container(sidebar_tag)

return sidebar_tag

def tagify(self) -> TagList:
id = self._get_sidebar_id()
taglist = TagList(self._sidebar_tag(id), self._collapse_tag(id))
Expand All @@ -446,6 +467,7 @@ def sidebar(
max_height_mobile: Optional[str | float] = None,
gap: Optional[CssUnit] = None,
padding: Optional[CssUnit | list[CssUnit]] = None,
fillable: bool = False,
**kwargs: TagAttrValue,
) -> Sidebar:
# See [this article](https://rstudio.github.io/bslib/articles/sidebars.html)
Expand Down Expand Up @@ -521,6 +543,10 @@ def sidebar(
and right, and the third will be bottom.
* If four, then the values will be interpreted as top, right, bottom, and left
respectively.
fillable
Whether or not the sidebar should be considered a fillable container.
When `True`, the sidebar and its content can use `fill` to consume
available vertical space.
**kwargs
Named attributes are supplied to the sidebar content container.

Expand Down Expand Up @@ -567,6 +593,7 @@ def sidebar(
max_height_mobile=max_height_mobile,
gap=gap,
padding=padding,
fillable=fillable,
)


Expand Down
Loading