Skip to content

Commit 75f54d1

Browse files
authored
feat: Add ui.layout_columns() (#856)
1 parent 8f14c92 commit 75f54d1

File tree

15 files changed

+448
-25
lines changed

15 files changed

+448
-25
lines changed

CHANGELOG.md

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

1515
* Closed #814: The functions `reactive.Calc` and `reactive.Effect` have been changed to have lowercase names: `reactive.calc`, and `reactive.effect`. The old capitalized names are now aliases to the new lowercase names, so existing code will continue to work. Similarly, the class `reactive.Value` has a new alias, `reactive.value`, but in this case, since the original was a class, it keeps the original capitalized name as the primary name. The examples have not been changed yet, but will be changed in a future release. (#822)
1616

17+
* Added `ui.layout_columns()` for creating responsive column-forward layouts based on Bootstrap's 12-column CSS Grid. (#856)
18+
1719
### Bug fixes
1820

1921
* Fix support for `shiny.ui.accordion(multiple=)` (#799).

scripts/htmlDependencies.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ versions <- list()
1111
message("Installing GitHub packages: bslib, shiny, htmltools")
1212
withr::local_temp_libpaths()
1313
ignore <- capture.output({
14-
pak::pkg_install(c("cran::bslib", "cran::shiny", "cran::htmltools"))
14+
pak::pkg_install(c("rstudio/bslib", "cran::shiny", "cran::htmltools"))
1515
#pak::pkg_install(c("rstudio/bslib@main", "rstudio/shiny@main", "rstudio/htmltools@main"))
1616
})
1717

shiny/_versions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
shiny_html_deps = "1.8.0"
2-
bslib = "0.6.1"
2+
bslib = "0.6.1.9000"
33
htmltools = "0.5.7"
44
bootstrap = "5.3.1"
55
requirejs = "2.3.6"

shiny/express/layout.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .. import ui
99
from ..types import MISSING, MISSING_TYPE
10+
from ..ui._layout_columns import BreakpointsUser
1011
from ..ui.css import CssUnit
1112
from . import _run
1213
from ._recall_context import RecallContextManager, wrap_recall_context_manager
@@ -19,6 +20,7 @@
1920
"pre",
2021
"sidebar",
2122
"layout_column_wrap",
23+
"layout_columns",
2224
"column",
2325
"row",
2426
"card",
@@ -206,6 +208,11 @@ def layout_column_wrap(
206208
-------
207209
:
208210
A :class:`~htmltools.Tag` element.
211+
212+
See Also
213+
--------
214+
* :func:`~shiny.express.layout.layout_columns` for laying out elements into a
215+
responsive 12-column grid.
209216
"""
210217
return RecallContextManager(
211218
ui.layout_column_wrap,
@@ -225,6 +232,110 @@ def layout_column_wrap(
225232
)
226233

227234

235+
def layout_columns(
236+
*,
237+
col_widths: BreakpointsUser[int] = None,
238+
row_heights: BreakpointsUser[CssUnit] = None,
239+
fill: bool = True,
240+
fillable: bool = True,
241+
gap: Optional[CssUnit] = None,
242+
class_: Optional[str] = None,
243+
height: Optional[CssUnit] = None,
244+
**kwargs: TagAttrValue,
245+
):
246+
"""
247+
Create responsive, column-based grid layouts, based on a 12-column grid.
248+
249+
Parameters
250+
----------
251+
col_widths
252+
The widths of the columns, possibly at different breakpoints. Can be one of the
253+
following:
254+
255+
* `None` (the default): Automatically determines a sensible number of columns
256+
based on the number of children given to the layout.
257+
* A list or tuple of integers between 1 and 12, where each element represents
258+
the number of columns for the relevant UI element. Column widths are recycled
259+
to extend the values in `col_widths` to match the actual number of items in
260+
the layout, and children are wrapped onto the next row when a row exceeds 12
261+
column units. For example, `col_widths=(4, 8, 12)` allocates 4 columns to the
262+
first element, 8 columns to the second element, and 12 columns to the third
263+
element (which wraps to the next row). Negative values are also allowed, and
264+
are treated as empty columns. For example, `col_widths=(-2, 8, -2)` would
265+
allocate 8 columns to an element (with 2 empty columns on either side).
266+
* A dictionary of column widths at different breakpoints. The keys should be
267+
one of `"xs"`, `"sm"`, `"md"`, `"lg"`, `"xl"`, or `"xxl"`, and the values are
268+
either of the above. For example, `col_widths={"sm": (3, 3, 6), "lg": (4)}`.
269+
270+
row_heights
271+
The heights of the rows, possibly at different breakpoints. Can be one of the
272+
following:
273+
274+
* A numeric vector, where each value represents the
275+
[fractional unit](https://css-tricks.com/introduction-fr-css-unit/)
276+
(`fr`) height of the relevant row. If there are more rows than values
277+
provided, the pattern will be repeated. For example, `row_heights=(1, 2)`
278+
allows even rows to take up twice as much space as odd rows.
279+
* A list of numeric or CSS length units, where each value represents the height
280+
of the relevant row. If more rows are needed than values provided, the pattern
281+
will repeat. For example, `row_heights=["auto", 1]` allows the height of odd
282+
rows to be driven my it's contents and even rows to be
283+
[`1fr`](https://css-tricks.com/introduction-fr-css-unit/).
284+
* A single string containing CSS length units. In this case, the value is
285+
supplied directly to `grid-auto-rows`.
286+
* A dictionary of row heights at different breakpoints, where each key is a
287+
breakpoint name (one of `"xs"`, `"sm"`, `"md"`, `"lg"`, `"xl"`, or `"xxl"`)
288+
and where the values may be any of the above options.
289+
290+
fill
291+
Whether or not to allow the layout to grow/shrink to fit a fillable container
292+
with an opinionated height (e.g., :func:`~shiny.ui.page_fillable`).
293+
294+
fillable
295+
Whether or not each element is wrapped in a fillable container.
296+
297+
gap
298+
Any valid CSS unit to use for the gap between columns.
299+
300+
class_
301+
CSS class(es) to apply to the containing element.
302+
303+
height
304+
Any valid CSS unit to use for the height.
305+
306+
**kwargs
307+
Additional attributes to apply to the containing element.
308+
309+
Returns
310+
-------
311+
:
312+
An :class:`~htmltools.Tag` element.
313+
314+
See Also
315+
--------
316+
* :func:`~shiny.express.layout.layout_column_wrap` for laying out elements into a
317+
uniform grid.
318+
319+
Reference
320+
--------
321+
* [Bootstrap CSS Grid](https://getbootstrap.com/docs/5.3/layout/grid/)
322+
* [Bootstrap Breakpoints](https://getbootstrap.com/docs/5.3/layout/breakpoints/)
323+
"""
324+
return RecallContextManager(
325+
ui.layout_columns,
326+
kwargs=dict(
327+
col_widths=col_widths,
328+
row_heights=row_heights,
329+
fill=fill,
330+
fillable=fillable,
331+
gap=gap,
332+
class_=class_,
333+
height=height,
334+
**kwargs,
335+
),
336+
)
337+
338+
228339
def column(width: int, *, offset: int = 0, **kwargs: TagAttrValue):
229340
"""
230341
Responsive row-column based layout

shiny/ui/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
)
2424

2525
from ._layout import layout_column_wrap
26+
from ._layout_columns import layout_columns
2627

2728

2829
# Expose the following modules for extended usage: ex: ui.fill.as_fill_item(x)
@@ -185,6 +186,7 @@
185186
"panel_sidebar",
186187
"panel_main",
187188
# _layout
189+
"layout_columns",
188190
"layout_column_wrap",
189191
# _card
190192
"CardItem",

shiny/ui/_layout.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ def layout_column_wrap(
8383
-------
8484
:
8585
A :class:`~htmltools.Tag` element.
86+
87+
See Also
88+
--------
89+
* :func:`~shiny.ui.layout_columns` for laying out elements into a responsive
90+
12-column grid.
8691
"""
8792
attrs, children = consolidate_attrs(*args, class_=class_, **kwargs)
8893

0 commit comments

Comments
 (0)