diff --git a/CHANGELOG.md b/CHANGELOG.md index 92fadfd52..7bb431847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added support for using `shiny.express` in Quarto Dashboards. (#1217) +* `ui.value_box()`, `ui.layout_columns()` and `ui.layout_column_wrap()` now all have `min_height` and `max_height` arguments. These are useful in filling layouts, like `ui.page_fillable()`, `ui.page_sidebar(fillable=True)` or `ui.page_navbar(fillable=True)`. For example, you can use `ui.layout_columns(min_height=300, max_height=500)` to ensure that a set of items (likely arranged in a row of columns) are always between 300 and 500 pixels tall. (#1223) + ### Bug fixes * On Windows, Shiny Express app files are now read in as UTF-8. (#1203) diff --git a/shiny/express/ui/_cm_components.py b/shiny/express/ui/_cm_components.py index f7ff8386c..9143180ad 100644 --- a/shiny/express/ui/_cm_components.py +++ b/shiny/express/ui/_cm_components.py @@ -222,6 +222,8 @@ def layout_column_wrap( fill: bool = True, fillable: bool = True, height: Optional[CssUnit] = None, + min_height: Optional[CssUnit] = None, + max_height: Optional[CssUnit] = None, height_mobile: Optional[CssUnit] = None, gap: Optional[CssUnit] = None, class_: Optional[str] = None, @@ -267,8 +269,10 @@ def layout_column_wrap( with an opinionated height (e.g., :func:`~shiny.ui.page_fillable`). fillable Whether or not each element is wrapped in a fillable container. - height - Any valid CSS unit to use for the height. + height,max_height,min_height + A valid CSS unit (e.g., `height="200px"`). Use `min_height` and `max_height` in + a filling layout to ensure that the layout container does not shrink below a + `min_height` or grow beyond a `max_height`. height_mobile Any valid CSS unit to use for the height when on mobile devices (or narrow windows). @@ -288,6 +292,8 @@ def layout_column_wrap( fill=fill, fillable=fillable, height=height, + min_height=min_height, + max_height=max_height, height_mobile=height_mobile, gap=gap, class_=class_, @@ -306,6 +312,8 @@ def layout_columns( gap: Optional[CssUnit] = None, class_: Optional[str] = None, height: Optional[CssUnit] = None, + min_height: Optional[CssUnit] = None, + max_height: Optional[CssUnit] = None, **kwargs: TagAttrValue, ) -> RecallContextManager[Tag]: """ @@ -368,8 +376,10 @@ def layout_columns( class_ CSS class(es) to apply to the containing element. - height - Any valid CSS unit to use for the height. + height,max_height,min_height + A valid CSS unit (e.g., `height="200px"`). Use `min_height` and `max_height` in + a filling layout to ensure that the layout container does not shrink below a + `min_height` or grow beyond a `max_height`. **kwargs Additional attributes to apply to the containing element. @@ -399,6 +409,8 @@ def layout_columns( gap=gap, class_=class_, height=height, + min_height=min_height, + max_height=max_height, **kwargs, ), ) @@ -1153,6 +1165,7 @@ def value_box( theme: Optional[str | ui.ValueBoxTheme] = None, height: Optional[CssUnit] = None, max_height: Optional[CssUnit] = None, + min_height: Optional[CssUnit] = None, fill: bool = True, class_: Optional[str] = None, **kwargs: TagAttrValue, @@ -1195,9 +1208,9 @@ def value_box( full_screen If `True`, an icon will appear when hovering over the card body. Clicking the icon expands the card to fit viewport size. - height,max_height - Any valid CSS unit (e.g., `height="200px"`). Doesn't apply when a card is made - `full_screen`. + height,max_height,min_height + Any valid CSS unit (e.g., `height="200px"`). Doesn't apply when a value box is + made `full_screen`. fill Whether to allow the value box to grow/shrink to fit a fillable container with an opinionated height (e.g., :func:`~shiny.ui.page_fillable`). @@ -1217,6 +1230,7 @@ def value_box( theme=theme, height=height, max_height=max_height, + min_height=min_height, fill=fill, class_=class_, **kwargs, diff --git a/shiny/ui/_layout.py b/shiny/ui/_layout.py index 4d44cca6f..c55385518 100644 --- a/shiny/ui/_layout.py +++ b/shiny/ui/_layout.py @@ -24,6 +24,8 @@ def layout_column_wrap( fill: bool = True, fillable: bool = True, height: Optional[CssUnit] = None, + min_height: Optional[CssUnit] = None, + max_height: Optional[CssUnit] = None, height_mobile: Optional[CssUnit] = None, gap: Optional[CssUnit] = None, class_: Optional[str] = None, @@ -74,8 +76,10 @@ def layout_column_wrap( with an opinionated height (e.g., :func:`~shiny.ui.page_fillable`). fillable Whether or not each element is wrapped in a fillable container. - height - Any valid CSS unit to use for the height. + height,max_height,min_height + A valid CSS unit (e.g., `height="200px"`). Use `min_height` and `max_height` in + a filling layout to ensure that the layout container does not shrink below a + `min_height` or grow beyond a `max_height`. height_mobile Any valid CSS unit to use for the height when on mobile devices (or narrow windows). @@ -146,6 +150,8 @@ def layout_column_wrap( "auto" if height_mobile is None else height_mobile ), "gap": as_css_unit(gap), + "min-height": as_css_unit(min_height), + "max-height": as_css_unit(max_height), } tag = div( diff --git a/shiny/ui/_layout_columns.py b/shiny/ui/_layout_columns.py index d2de2d38e..96b2db86c 100644 --- a/shiny/ui/_layout_columns.py +++ b/shiny/ui/_layout_columns.py @@ -43,6 +43,8 @@ def layout_columns( gap: Optional[CssUnit] = None, class_: Optional[str] = None, height: Optional[CssUnit] = None, + min_height: Optional[CssUnit] = None, + max_height: Optional[CssUnit] = None, **kwargs: TagAttrValue, ) -> Tag: """ @@ -98,8 +100,10 @@ def layout_columns( Any valid CSS unit to use for the gap between columns. class_ CSS class(es) to apply to the containing element. - height - Any valid CSS unit to use for the height. + height,max_height,min_height + A valid CSS unit (e.g., `height="200px"`). Use `min_height` and `max_height` in + a filling layout to ensure that the layout container does not shrink below a + `min_height` or grow beyond a `max_height`. **kwargs Additional attributes to apply to the containing element. @@ -129,6 +133,8 @@ def layout_columns( "style": css( gap=as_css_unit(gap), height=as_css_unit(height), + min_height=as_css_unit(min_height), + max_height=as_css_unit(max_height), ), }, col_widths_attrs(col_widths_spec), diff --git a/shiny/ui/_valuebox.py b/shiny/ui/_valuebox.py index 913d6b422..e886eed8e 100644 --- a/shiny/ui/_valuebox.py +++ b/shiny/ui/_valuebox.py @@ -307,6 +307,7 @@ def value_box( theme: Optional[str | ValueBoxTheme] = None, height: Optional[CssUnit] = None, max_height: Optional[CssUnit] = None, + min_height: Optional[CssUnit] = None, fill: bool = True, class_: Optional[str] = None, id: Optional[str] = None, @@ -358,9 +359,9 @@ def value_box( full_screen If `True`, an icon will appear when hovering over the card body. Clicking the icon expands the card to fit viewport size. - height,max_height - Any valid CSS unit (e.g., `height="200px"`). Doesn't apply when a card is made - `full_screen`. + height,max_height,min_height + Any valid CSS unit (e.g., `height="200px"`). Doesn't apply when a value box is + made `full_screen`. fill Whether to allow the value box to grow/shrink to fit a fillable container with an opinionated height (e.g., :func:`~shiny.ui.page_fillable`). @@ -463,6 +464,7 @@ def value_box( full_screen=full_screen, height=height, max_height=max_height, + min_height=min_height, fill=fill, id=id, )