From de5d2802ff951c15e295f1c966b491743d108961 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 6 Dec 2023 10:40:59 -0600 Subject: [PATCH] Add layout_sidebar() to express to make inline sidebar layouts possible --- shiny/express/layout.py | 75 +++++++++++++++++++++++++++++++++++++++++ shiny/ui/_sidebar.py | 14 ++++---- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/shiny/express/layout.py b/shiny/express/layout.py index f3fb7ade7..4d63a2391 100644 --- a/shiny/express/layout.py +++ b/shiny/express/layout.py @@ -18,6 +18,7 @@ "span", "pre", "sidebar", + "layout_sidebar", "layout_column_wrap", "column", "row", @@ -143,6 +144,80 @@ def sidebar( ) +def layout_sidebar( + *, + fillable: bool = True, + fill: bool = True, + bg: Optional[str] = None, + fg: Optional[str] = None, + border: Optional[bool] = None, + border_radius: Optional[bool] = None, + border_color: Optional[str] = None, + gap: Optional[CssUnit] = None, + padding: Optional[CssUnit | list[CssUnit]] = None, + height: Optional[CssUnit] = None, + **kwargs: TagAttrValue, +) -> RecallContextManager[ui.CardItem]: + """ + A sidebar layout. + + Create a collapsing sidebar layout. This function wraps :func:`~shiny.ui.layout_sidebar`. + + Parameters + ---------- + fillable + Whether or not the main content area should be wrapped in a fillable container. + See :func:`~shiny.ui.as_fillable_container` for details. + fill + Whether or not the sidebar layout should be wrapped in a fillable container. See + :func:`~shiny.ui.as_fill_item` for details. + bg,fg + A background or foreground color. + border + Whether or not to show a border around the sidebar layout. + border_radius + Whether or not to round the corners of the sidebar layout. + border_color + A border color. + gap + A CSS length unit defining the vertical `gap` (i.e., spacing) between elements + provided to `*args`. This value will only be used if `fillable` is `True`. + padding + Padding within the sidebar itself. This can be a numeric vector (which will be + interpreted as pixels) or a character vector with valid CSS lengths. `padding` + may be one to four values. If one, then that value will be used for all four + sides. If two, then the first value will be used for the top and bottom, while + the second value will be used for left and right. If three, then the first will + be used for top, the second will be left and right, and the third will be + bottom. If four, then the values will be interpreted as top, right, bottom, and + left respectively. + height + Any valid CSS unit to use for the height. + + Returns + ------- + : + A :class:`~shiny.ui.CardItem` object. + """ + return RecallContextManager( + ui.layout_sidebar, + default_page=page_fillable(padding=0, gap=0), + kwargs=dict( + fillable=fillable, + fill=fill, + bg=bg, + fg=fg, + border=border, + border_radius=border_radius, + border_color=border_color, + gap=gap, + padding=padding, + height=height, + **kwargs, + ), + ) + + def layout_column_wrap( *, width: CssUnit | None | MISSING_TYPE = MISSING, diff --git a/shiny/ui/_sidebar.py b/shiny/ui/_sidebar.py index f5a7bd1b2..bf87da51c 100644 --- a/shiny/ui/_sidebar.py +++ b/shiny/ui/_sidebar.py @@ -289,7 +289,7 @@ def sidebar( @add_example() def layout_sidebar( - sidebar: Sidebar, + sidebar: Sidebar | TagChild, *args: TagChild | TagAttrs, fillable: bool = True, fill: bool = True, @@ -423,22 +423,20 @@ def layout_sidebar( def _get_layout_sidebar_sidebar( - sidebar: Sidebar, + sidebar: Sidebar | DeprecatedPanelSidebar | TagChild, args: tuple[TagChild | TagAttrs, ...], ) -> tuple[Sidebar, tuple[TagChild | TagAttrs, ...]]: updated_args: list[TagChild | TagAttrs] = [] original_args = tuple(args) # sidebar: Sidebar | None = None - sidebar_orig_arg: Sidebar | DeprecatedPanelSidebar = sidebar + sidebar_orig_arg = sidebar if isinstance(sidebar, DeprecatedPanelSidebar): sidebar = sidebar.sidebar if not isinstance(sidebar, Sidebar): - raise ValueError( - "`layout_sidebar()` is not being supplied with a `sidebar()` object. Please supply a `sidebar()` object to `layout_sidebar(sidebar)`." - ) + sidebar = _sidebar_func(sidebar) # Use `original_args` here so `updated_args` can be safely altered in place for i, arg in zip(range(len(original_args)), original_args): @@ -496,6 +494,10 @@ def _get_layout_sidebar_sidebar( return (sidebar, tuple(updated_args)) +# Save an internal copy of sidebar func so we can use `sidebar` as an arg name +_sidebar_func = sidebar + + @add_example() def update_sidebar( id: str,