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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* Added module support for `session.clientdata` methods. This allows you to access client data values in Shiny modules without needing to namespace the keys explicitly. (#1978)

* Fixed false positive warning in `layout_columns()` about number of widths vs elements. (#1704)

### Bug fixes

* Fixed an issue with `ui.Chat()` sometimes wanting to scroll a parent element. (#1996)
Expand All @@ -45,6 +47,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* Fixed `set()` method of `InputSelectize` controller so it clears existing selections before applying new values. (#2024)

### Deprecations

* `ui.panel_well()` is deprecated in favor of `ui.card()`. (#2038)


## [1.4.0] - 2025-04-08

Expand Down
1 change: 0 additions & 1 deletion docs/_quartodoc-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ quartodoc:
- ui.panel_fixed
- ui.panel_conditional
- ui.panel_title
- ui.panel_well
- title: Uploads & downloads
desc: Allow users to upload and download files.
contents:
Expand Down
1 change: 0 additions & 1 deletion docs/_quartodoc-express.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ quartodoc:
- express.ui.panel_absolute
- express.ui.panel_fixed
- express.ui.panel_title
- express.ui.panel_well
- title: Uploads & downloads
desc: Allow users to upload and download files.
contents:
Expand Down
48 changes: 20 additions & 28 deletions examples/airmass/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,37 @@

from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui

app_ui = ui.page_fixed(
ui.tags.h3("Air mass calculator"),
ui.div(
app_ui = ui.page_fillable(
ui.tags.h2("Air mass calculator", {"class": "lead display-6 text-center"}),
ui.layout_columns(
ui.markdown(
"""This Shiny app uses [Astropy](https://www.astropy.org/) to calculate the
altitude (degrees above the horizon) and airmass (the amount of atmospheric
air along your line of sight to an object) of one or more astronomical
objects, over a given evening, at a given geographic location.
"""
altitude (degrees above the horizon) and airmass (the amount of atmospheric
air along your line of sight to an object) of one or more astronomical
objects, over a given evening, at a given geographic location.
"""
),
class_="mb-5",
),
ui.row(
ui.column(
8,
ui.card(
ui.output_ui("timeinfo"),
ui.output_plot("plot", height="800px"),
ui.output_plot("plot", height="600px"),
# For debugging
# ui.output_table("table"),
class_="order-2 order-sm-1",
),
ui.column(
4,
ui.panel_well(
ui.input_date("date", "Date"),
class_="pb-1 mb-3",
),
ui.panel_well(
ui.TagList(
ui.card(ui.input_date("date", "Date"), fill=False),
ui.card(
ui.input_text_area(
"objects", "Target object(s)", "M1, NGC35, PLX299", rows=3
"objects",
"Target object(s)",
"M1, NGC35, PLX299",
rows=2,
),
class_="pb-1 mb-3",
),
ui.panel_well(
location_ui("location"),
class_="mb-3",
fill=False,
),
class_="order-1 order-sm-2",
ui.card(location_ui("location")),
),
col_widths=[-2, 8, -2, -1, 7, 3, -1],
min_height="600px",
),
)

Expand Down
128 changes: 60 additions & 68 deletions examples/inputs-update/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,70 @@

app_ui = ui.page_fluid(
ui.panel_title("Changing the values of inputs from the server"),
ui.row(
ui.column(
4,
ui.panel_well(
ui.tags.h4("These inputs control the other inputs on the page"),
ui.input_text(
"control_label", "This controls some of the labels:", "LABEL TEXT"
),
ui.input_slider(
"control_num", "This controls values:", min=1, max=20, value=15
),
ui.layout_columns(
ui.card(
ui.card_header("These inputs control the other inputs on the page"),
ui.input_text(
"control_label", "This controls some of the labels:", "LABEL TEXT"
),
ui.input_slider(
"control_num", "This controls values:", min=1, max=20, value=15
),
),
ui.column(
4,
ui.panel_well(
ui.tags.h4("These inputs are controlled by the other inputs"),
ui.input_text("inText", "Text input:", value="start text"),
ui.input_numeric(
"inNumber", "Number input:", min=1, max=20, value=5, step=0.5
),
ui.input_numeric(
"inNumber2", "Number input 2:", min=1, max=20, value=5, step=0.5
),
ui.input_slider("inSlider", "Slider input:", min=1, max=20, value=15),
ui.input_slider(
"inSlider2", "Slider input 2:", min=1, max=20, value=(5, 15)
),
ui.input_slider(
"inSlider3", "Slider input 3:", min=1, max=20, value=(5, 15)
),
ui.input_date("inDate", "Date input:"),
ui.input_date_range("inDateRange", "Date range input:"),
ui.card(
ui.card_header("These inputs are controlled by the other inputs"),
ui.input_text("inText", "Text input:", value="start text"),
ui.input_numeric(
"inNumber", "Number input:", min=1, max=20, value=5, step=0.5
),
ui.input_numeric(
"inNumber2", "Number input 2:", min=1, max=20, value=5, step=0.5
),
ui.input_slider("inSlider", "Slider input:", min=1, max=20, value=15),
ui.input_slider(
"inSlider2", "Slider input 2:", min=1, max=20, value=(5, 15)
),
ui.input_slider(
"inSlider3", "Slider input 3:", min=1, max=20, value=(5, 15)
),
ui.input_date("inDate", "Date input:"),
ui.input_date_range("inDateRange", "Date range input:"),
),
ui.column(
4,
ui.panel_well(
ui.input_checkbox("inCheckbox", "Checkbox input", value=False),
ui.input_checkbox_group(
"inCheckboxGroup",
"Checkbox group input:",
{
"option1": "label 1",
"option2": "label 2",
},
),
ui.input_radio_buttons(
"inRadio",
"Radio buttons:",
{
"option1": "label 1",
"option2": "label 2",
},
),
ui.input_select(
"inSelect",
"Select input:",
{
"option1": "label 1",
"option2": "label 2",
},
),
ui.input_select(
"inSelect2",
"Select input 2:",
{
"option1": "label 1",
"option2": "label 2",
},
multiple=True,
),
ui.card(
ui.card_header("These inputs are updated by the server"),
ui.input_checkbox("inCheckbox", "Checkbox input", value=False),
ui.input_checkbox_group(
"inCheckboxGroup",
"Checkbox group input:",
{
"option1": "label 1",
"option2": "label 2",
},
),
ui.input_radio_buttons(
"inRadio",
"Radio buttons:",
{
"option1": "label 1",
"option2": "label 2",
},
),
ui.input_select(
"inSelect",
"Select input:",
{
"option1": "label 1",
"option2": "label 2",
},
),
ui.input_select(
"inSelect2",
"Select input 2:",
{
"option1": "label 1",
"option2": "label 2",
},
multiple=True,
),
ui.navset_tab(
ui.nav_panel("panel1", ui.h2("This is the first panel.")),
Expand Down
5 changes: 3 additions & 2 deletions shiny/api-examples/panel_absolute/app-core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
app_ui = ui.page_fluid(
ui.panel_title("A basic absolute panel example", "Demo"),
ui.panel_absolute(
ui.panel_well(
"Drag me around!", ui.input_slider("n", "N", min=0, max=100, value=20)
ui.card(
ui.card_header("Drag me around!"),
ui.input_slider("n", "N", min=0, max=100, value=20),
),
draggable=True,
width="300px",
Expand Down
4 changes: 2 additions & 2 deletions shiny/api-examples/panel_absolute/app-express.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
ui.h2("A basic absolute panel example")

with ui.panel_absolute(draggable=True, width="300px", right="50px", top="25%"):
with ui.panel_well():
"Drag me around!"
with ui.card():
ui.card_header("Drag me around!")
ui.input_slider("n", "N", min=0, max=100, value=20)
9 changes: 3 additions & 6 deletions shiny/express/ui/_cm_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from htmltools import Tag, TagAttrs, TagAttrValue, TagChild, TagFunction, TagList

from ... import ui
from ..._deprecated import warn_deprecated
from ..._docstring import add_example, no_example
from ...types import DEPRECATED, MISSING, MISSING_TYPE
from ...ui._accordion import AccordionPanel
Expand Down Expand Up @@ -1363,13 +1364,9 @@ def value_box(
@no_example()
def panel_well(**kwargs: TagAttrValue) -> RecallContextManager[Tag]:
"""
Context manager for a well panel

This function wraps :func:`~shiny.ui.panel_well`.

A well panel is a simple container with a border and some padding. It's useful for
grouping related content together.
Deprecated. Use :func:`~shiny.express.ui.card` instead.
"""
warn_deprecated("panel_well() is deprecated. Use shiny.express.ui.card() instead.")
return RecallContextManager(
ui.panel_well,
kwargs=dict(
Expand Down
20 changes: 3 additions & 17 deletions shiny/ui/_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"help_text",
)


from typing import Literal, Optional

from htmltools import (
Expand All @@ -27,6 +26,7 @@
tags,
)

from .._deprecated import warn_deprecated
from .._docstring import add_example, no_example
from ..module import current_namespace
from ..types import MISSING, MISSING_TYPE
Expand Down Expand Up @@ -112,23 +112,9 @@ def column(
@no_example()
def panel_well(*args: TagChild | TagAttrs, **kwargs: TagAttrValue) -> Tag:
"""
Create a well panel.

Creates a panel with a slightly inset border and gray background. Equivalent to
Bootstrap's ``well`` CSS class.

Parameters
----------
*args
UI elements to include inside the panel.
**kwargs
Attributes to place on the panel tag.

Returns
-------
:
A UI element.
Deprecated. Use :func:`~shiny.ui.card` instead.
"""
warn_deprecated("panel_well() is deprecated. Use shiny.ui.card() instead.")
return div({"class": "well"}, *args, **kwargs)


Expand Down
36 changes: 20 additions & 16 deletions shiny/ui/_layout_columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,31 +186,35 @@ def validate_col_width(
x: Iterable[int] | int, n_kids: int, break_name: Breakpoint
) -> Iterable[int]:
if isinstance(x, int):
y = [x]
widths = [x]
else:
y = x
widths = list(x)

if not all(isinstance(i, int) for i in y):
raise ValueError(
f"Column values at breakpoint '{break_name}' must be integers. Values greater than 0 indicate width, and negative values indicate a column offset."
)

if any(i == 0 for i in y):
raise ValueError(
f"Column values at breakpoint '{break_name}' must be greater than 0 to indicate width, or negative to indicate a column offset."
)
positive_widths: list[int] = []
for w in widths:
if not isinstance(w, int):
raise TypeError(
f"Column widths at breakpoint '{break_name}' must be integers. Got {type(w).__name__}."
)
if w == 0:
raise ValueError(
f"Column widths at breakpoint '{break_name}' must be greater than 0 to indicate width, or negative to indicate a column offset."
)
if w > 0:
positive_widths.append(w)

if not any(b > 0 for b in y):
if not positive_widths:
raise ValueError(
f"Column values at breakpoint '{break_name}' must include at least one positive integer width."
f"Column widths at breakpoint '{break_name}' must include at least one positive integer width."
)

if len(list(y)) > n_kids:
if len(positive_widths) > n_kids:
warn(
f"More column widths than children at breakpoint '{break_name}', extra widths will be ignored."
f"More column widths than children at breakpoint '{break_name}', extra widths will be ignored.",
stacklevel=2,
)

return y
return widths


def col_widths_attrs(col_widths: BreakpointsOptional[int] | None) -> TagAttrs:
Expand Down
Loading