Skip to content

Commit 23a66cb

Browse files
authored
feat(sidebar): Add fillable parameter (#2077)
1 parent 83d3952 commit 23a66cb

File tree

3 files changed

+48
-13
lines changed

3 files changed

+48
-13
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2929

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

32+
* `ui.sidebar()` gains a `fillable` argument to support vertical fill behavior in sidebars. (#2077)
33+
3234
### Changes
3335

3436
* `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)

shiny/express/ui/_cm_components.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def sidebar(
6464
max_height_mobile: Optional[str | float] = None,
6565
gap: Optional[CssUnit] = None,
6666
padding: Optional[CssUnit | list[CssUnit]] = None,
67+
fillable: bool = False,
6768
**kwargs: TagAttrValue,
6869
) -> RecallContextManager[ui.Sidebar]:
6970
"""
@@ -122,6 +123,10 @@ def sidebar(
122123
and right, and the third will be bottom.
123124
* If four, then the values will be interpreted as top, right, bottom, and left
124125
respectively.
126+
fillable
127+
Whether or not the sidebar should be considered a fillable container.
128+
When `True`, the sidebar and its content can use `fill` to consume
129+
available vertical space.
125130
**kwargs
126131
Named attributes are supplied to the sidebar content container.
127132
"""
@@ -139,6 +144,7 @@ def sidebar(
139144
max_height_mobile=max_height_mobile,
140145
gap=gap,
141146
padding=padding,
147+
fillable=fillable,
142148
**kwargs,
143149
),
144150
)

shiny/ui/_sidebar.py

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ class directly. Instead, supply the :func:`~shiny.ui.sidebar` object to
206206
and right, and the third will be bottom.
207207
* If four, then the values will be interpreted as top, right, bottom, and left
208208
respectively.
209+
fillable
210+
Whether or not the sidebar should be considered a fillable container.
211+
When `True`, the sidebar and its content can use `fill` to consume
212+
available vertical space.
209213
210214
Parameters
211215
----------
@@ -283,6 +287,7 @@ def __init__(
283287
max_height_mobile: Optional[str | float] = None,
284288
gap: Optional[CssUnit] = None,
285289
padding: Optional[CssUnit | list[CssUnit]] = None,
290+
fillable: bool = False,
286291
):
287292
if isinstance(title, (str, int, float)):
288293
title = tags.header(str(title), class_="sidebar-title")
@@ -292,6 +297,7 @@ def __init__(
292297
self.class_ = class_
293298
self.gap = as_css_unit(gap)
294299
self.padding = as_css_padding(padding)
300+
self.fillable = fillable
295301
# User-provided initial open state
296302
self._open: SidebarOpen | None = self._as_open(open)
297303
# Shiny or consumer-provided default open state, change with `_set_default_open()`
@@ -403,29 +409,44 @@ def _sidebar_tag(self, id: str | None) -> Tag:
403409
self.open().desktop == "closed" or self.open().mobile == "closed"
404410
)
405411

406-
return tags.aside(
412+
# Create the sidebar content div
413+
sidebar_content = div(
414+
{
415+
"class": "sidebar-content bslib-gap-spacing",
416+
"style": css(
417+
gap=self.gap,
418+
padding=self.padding,
419+
),
420+
},
421+
self.title,
422+
*self.children,
423+
self.attrs,
424+
)
425+
426+
# Apply fill_item to the content div if fillable
427+
if self.fillable:
428+
sidebar_content = as_fill_item(sidebar_content)
429+
sidebar_content = as_fillable_container(sidebar_content)
430+
431+
# Create the sidebar tag
432+
sidebar_tag = tags.aside(
407433
{
408434
"id": id,
409435
"class": "sidebar",
410436
"hidden": "true" if is_hidden_initially else None,
411437
},
412438
# If the user provided an id, we make the sidebar an input to report state
413439
{"class": "bslib-sidebar-input"} if self.id is not None else None,
414-
div(
415-
{
416-
"class": "sidebar-content bslib-gap-spacing",
417-
"style": css(
418-
gap=self.gap,
419-
padding=self.padding,
420-
),
421-
},
422-
self.title,
423-
*self.children,
424-
self.attrs,
425-
),
440+
sidebar_content,
426441
class_=self.class_,
427442
)
428443

444+
# Apply fillable container to the sidebar if needed
445+
if self.fillable:
446+
sidebar_tag = as_fillable_container(sidebar_tag)
447+
448+
return sidebar_tag
449+
429450
def tagify(self) -> TagList:
430451
id = self._get_sidebar_id()
431452
taglist = TagList(self._sidebar_tag(id), self._collapse_tag(id))
@@ -446,6 +467,7 @@ def sidebar(
446467
max_height_mobile: Optional[str | float] = None,
447468
gap: Optional[CssUnit] = None,
448469
padding: Optional[CssUnit | list[CssUnit]] = None,
470+
fillable: bool = False,
449471
**kwargs: TagAttrValue,
450472
) -> Sidebar:
451473
# See [this article](https://rstudio.github.io/bslib/articles/sidebars.html)
@@ -521,6 +543,10 @@ def sidebar(
521543
and right, and the third will be bottom.
522544
* If four, then the values will be interpreted as top, right, bottom, and left
523545
respectively.
546+
fillable
547+
Whether or not the sidebar should be considered a fillable container.
548+
When `True`, the sidebar and its content can use `fill` to consume
549+
available vertical space.
524550
**kwargs
525551
Named attributes are supplied to the sidebar content container.
526552
@@ -567,6 +593,7 @@ def sidebar(
567593
max_height_mobile=max_height_mobile,
568594
gap=gap,
569595
padding=padding,
596+
fillable=fillable,
570597
)
571598

572599

0 commit comments

Comments
 (0)