Skip to content

Commit 0e41554

Browse files
authored
Merge branch 'main' into fix/selectize-escaping
2 parents 5571ba0 + 24b02cf commit 0e41554

File tree

29 files changed

+1225
-355
lines changed

29 files changed

+1225
-355
lines changed

CHANGELOG.md

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

1010
### New features
1111

12+
* Added `ui.insert_nav_panel()`, `ui.remove_nav_panel()`, and `ui.update_nav_panel()` to support dynamic navigation. (#90)
13+
1214
* Added support for python 3.13. (#1711)
1315

1416
* `ui.sidebar()` is now interactively resizable. (#2020)
@@ -19,6 +21,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1921

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

24+
### Changes
25+
26+
* `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)
27+
2228
### Improvements
2329

2430
* Improved the styling and readability of markdown tables rendered by `ui.Chat()` and `ui.MarkdownStream()`. (#1973)
@@ -29,6 +35,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2935

3036
* 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)
3137

38+
* Fixed false positive warning in `layout_columns()` about number of widths vs elements. (#1704)
39+
3240
### Bug fixes
3341

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

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

50+
### Deprecations
51+
52+
* `ui.panel_well()` is deprecated in favor of `ui.card()`. (#2038)
53+
4254

4355
## [1.4.0] - 2025-04-08
4456

docs/_quartodoc-core.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,16 @@ quartodoc:
8484
- ui.navset_pill_list
8585
- ui.navset_hidden
8686
- ui.navbar_options
87+
- ui.insert_nav_panel
88+
- ui.remove_nav_panel
89+
- ui.update_nav_panel
8790
- title: UI panels
8891
desc: Visually group together a section of UI components.
8992
contents:
9093
- ui.panel_absolute
9194
- ui.panel_fixed
9295
- ui.panel_conditional
9396
- ui.panel_title
94-
- ui.panel_well
9597
- title: Uploads & downloads
9698
desc: Allow users to upload and download files.
9799
contents:

docs/_quartodoc-express.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ quartodoc:
7878
- express.ui.navset_pill_list
7979
- express.ui.navset_hidden
8080
- express.ui.navbar_options
81+
- express.ui.insert_nav_panel
82+
- express.ui.remove_nav_panel
83+
- express.ui.update_nav_panel
8184
- title: Chat interface
8285
desc: Build a chatbot interface
8386
contents:
@@ -157,7 +160,6 @@ quartodoc:
157160
- express.ui.panel_absolute
158161
- express.ui.panel_fixed
159162
- express.ui.panel_title
160-
- express.ui.panel_well
161163
- title: Uploads & downloads
162164
desc: Allow users to upload and download files.
163165
contents:

examples/airmass/app.py

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,45 +14,37 @@
1414

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

17-
app_ui = ui.page_fixed(
18-
ui.tags.h3("Air mass calculator"),
19-
ui.div(
17+
app_ui = ui.page_fillable(
18+
ui.tags.h2("Air mass calculator", {"class": "lead display-6 text-center"}),
19+
ui.layout_columns(
2020
ui.markdown(
2121
"""This Shiny app uses [Astropy](https://www.astropy.org/) to calculate the
22-
altitude (degrees above the horizon) and airmass (the amount of atmospheric
23-
air along your line of sight to an object) of one or more astronomical
24-
objects, over a given evening, at a given geographic location.
25-
"""
22+
altitude (degrees above the horizon) and airmass (the amount of atmospheric
23+
air along your line of sight to an object) of one or more astronomical
24+
objects, over a given evening, at a given geographic location.
25+
"""
2626
),
27-
class_="mb-5",
28-
),
29-
ui.row(
30-
ui.column(
31-
8,
27+
ui.card(
3228
ui.output_ui("timeinfo"),
33-
ui.output_plot("plot", height="800px"),
29+
ui.output_plot("plot", height="600px"),
3430
# For debugging
3531
# ui.output_table("table"),
36-
class_="order-2 order-sm-1",
3732
),
38-
ui.column(
39-
4,
40-
ui.panel_well(
41-
ui.input_date("date", "Date"),
42-
class_="pb-1 mb-3",
43-
),
44-
ui.panel_well(
33+
ui.TagList(
34+
ui.card(ui.input_date("date", "Date"), fill=False),
35+
ui.card(
4536
ui.input_text_area(
46-
"objects", "Target object(s)", "M1, NGC35, PLX299", rows=3
37+
"objects",
38+
"Target object(s)",
39+
"M1, NGC35, PLX299",
40+
rows=2,
4741
),
48-
class_="pb-1 mb-3",
49-
),
50-
ui.panel_well(
51-
location_ui("location"),
52-
class_="mb-3",
42+
fill=False,
5343
),
54-
class_="order-1 order-sm-2",
44+
ui.card(location_ui("location")),
5545
),
46+
col_widths=[-2, 8, -2, -1, 7, 3, -1],
47+
min_height="600px",
5648
),
5749
)
5850

examples/inputs-update/app.py

Lines changed: 60 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4,78 +4,70 @@
44

55
app_ui = ui.page_fluid(
66
ui.panel_title("Changing the values of inputs from the server"),
7-
ui.row(
8-
ui.column(
9-
4,
10-
ui.panel_well(
11-
ui.tags.h4("These inputs control the other inputs on the page"),
12-
ui.input_text(
13-
"control_label", "This controls some of the labels:", "LABEL TEXT"
14-
),
15-
ui.input_slider(
16-
"control_num", "This controls values:", min=1, max=20, value=15
17-
),
7+
ui.layout_columns(
8+
ui.card(
9+
ui.card_header("These inputs control the other inputs on the page"),
10+
ui.input_text(
11+
"control_label", "This controls some of the labels:", "LABEL TEXT"
12+
),
13+
ui.input_slider(
14+
"control_num", "This controls values:", min=1, max=20, value=15
1815
),
1916
),
20-
ui.column(
21-
4,
22-
ui.panel_well(
23-
ui.tags.h4("These inputs are controlled by the other inputs"),
24-
ui.input_text("inText", "Text input:", value="start text"),
25-
ui.input_numeric(
26-
"inNumber", "Number input:", min=1, max=20, value=5, step=0.5
27-
),
28-
ui.input_numeric(
29-
"inNumber2", "Number input 2:", min=1, max=20, value=5, step=0.5
30-
),
31-
ui.input_slider("inSlider", "Slider input:", min=1, max=20, value=15),
32-
ui.input_slider(
33-
"inSlider2", "Slider input 2:", min=1, max=20, value=(5, 15)
34-
),
35-
ui.input_slider(
36-
"inSlider3", "Slider input 3:", min=1, max=20, value=(5, 15)
37-
),
38-
ui.input_date("inDate", "Date input:"),
39-
ui.input_date_range("inDateRange", "Date range input:"),
17+
ui.card(
18+
ui.card_header("These inputs are controlled by the other inputs"),
19+
ui.input_text("inText", "Text input:", value="start text"),
20+
ui.input_numeric(
21+
"inNumber", "Number input:", min=1, max=20, value=5, step=0.5
22+
),
23+
ui.input_numeric(
24+
"inNumber2", "Number input 2:", min=1, max=20, value=5, step=0.5
25+
),
26+
ui.input_slider("inSlider", "Slider input:", min=1, max=20, value=15),
27+
ui.input_slider(
28+
"inSlider2", "Slider input 2:", min=1, max=20, value=(5, 15)
29+
),
30+
ui.input_slider(
31+
"inSlider3", "Slider input 3:", min=1, max=20, value=(5, 15)
4032
),
33+
ui.input_date("inDate", "Date input:"),
34+
ui.input_date_range("inDateRange", "Date range input:"),
4135
),
42-
ui.column(
43-
4,
44-
ui.panel_well(
45-
ui.input_checkbox("inCheckbox", "Checkbox input", value=False),
46-
ui.input_checkbox_group(
47-
"inCheckboxGroup",
48-
"Checkbox group input:",
49-
{
50-
"option1": "label 1",
51-
"option2": "label 2",
52-
},
53-
),
54-
ui.input_radio_buttons(
55-
"inRadio",
56-
"Radio buttons:",
57-
{
58-
"option1": "label 1",
59-
"option2": "label 2",
60-
},
61-
),
62-
ui.input_select(
63-
"inSelect",
64-
"Select input:",
65-
{
66-
"option1": "label 1",
67-
"option2": "label 2",
68-
},
69-
),
70-
ui.input_select(
71-
"inSelect2",
72-
"Select input 2:",
73-
{
74-
"option1": "label 1",
75-
"option2": "label 2",
76-
},
77-
multiple=True,
78-
),
36+
ui.card(
37+
ui.card_header("These inputs are updated by the server"),
38+
ui.input_checkbox("inCheckbox", "Checkbox input", value=False),
39+
ui.input_checkbox_group(
40+
"inCheckboxGroup",
41+
"Checkbox group input:",
42+
{
43+
"option1": "label 1",
44+
"option2": "label 2",
45+
},
46+
),
47+
ui.input_radio_buttons(
48+
"inRadio",
49+
"Radio buttons:",
50+
{
51+
"option1": "label 1",
52+
"option2": "label 2",
53+
},
54+
),
55+
ui.input_select(
56+
"inSelect",
57+
"Select input:",
58+
{
59+
"option1": "label 1",
60+
"option2": "label 2",
61+
},
62+
),
63+
ui.input_select(
64+
"inSelect2",
65+
"Select input 2:",
66+
{
67+
"option1": "label 1",
68+
"option2": "label 2",
69+
},
70+
multiple=True,
7971
),
8072
ui.navset_tab(
8173
ui.nav_panel("panel1", ui.h2("This is the first panel.")),
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
import random
22

3-
from shiny import reactive, ui
4-
from shiny.express import input
5-
6-
7-
def make_panel(letter):
8-
return ui.accordion_panel(
9-
f"Section {letter}", f"Some narrative for section {letter}"
10-
)
11-
3+
from shiny import reactive
4+
from shiny.express import input, ui
125

136
ui.input_action_button("add_panel", "Add random panel", class_="mt-3 mb-3")
14-
ui.accordion(*[make_panel(letter) for letter in "ABCDE"], id="acc", multiple=True)
7+
8+
with ui.accordion(id="acc", multiple=True):
9+
for letter in "ABCDE":
10+
with ui.accordion_panel(f"Section {letter}"):
11+
f"Some narrative for section {letter}"
1512

1613

1714
@reactive.effect
1815
@reactive.event(input.add_panel)
1916
def _():
20-
ui.insert_accordion_panel("acc", make_panel(str(random.randint(0, 10000))))
17+
ui.insert_accordion_panel(
18+
"acc",
19+
f"Section {random.randint(0, 10000)}",
20+
f"Some narrative for section {random.randint(0, 10000)}",
21+
)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from shiny import App, Inputs, Outputs, Session, reactive, ui
2+
3+
app_ui = ui.page_sidebar(
4+
ui.sidebar(
5+
ui.input_action_button("add", "Add 'Dynamic' tab"),
6+
ui.input_action_button("update_foo", "Add/Remove 'Foo' tab"),
7+
),
8+
ui.navset_tab(
9+
ui.nav_panel("Hello", "This is the hello tab", value="Hello"),
10+
ui.nav_panel("Foo", "This is the Foo tab", value="Foo"),
11+
ui.nav_menu(
12+
"Static",
13+
ui.nav_panel("Static 1", "Static 1", value="s1"),
14+
ui.nav_panel("Static 2", "Static 2", value="s2"),
15+
value="Menu",
16+
),
17+
id="tabs",
18+
),
19+
)
20+
21+
22+
def server(input: Inputs, output: Outputs, session: Session):
23+
24+
@reactive.effect
25+
@reactive.event(input.update_foo)
26+
def _():
27+
if input.update_foo() % 2 == 0:
28+
ui.insert_nav_panel(
29+
"tabs",
30+
ui.nav_panel("Foo", "Foo is back now", value="Foo"),
31+
target="Menu",
32+
position="before",
33+
select=True,
34+
)
35+
else:
36+
ui.remove_nav_panel("tabs", target="Foo")
37+
38+
@reactive.effect
39+
@reactive.event(input.add)
40+
def _():
41+
id = "Dynamic-" + str(input.add())
42+
ui.insert_nav_panel(
43+
"tabs",
44+
ui.nav_panel(id, id, value=id),
45+
target="s2",
46+
position="before",
47+
)
48+
49+
ui.notification_show(f"Added tab to menu: {id}")
50+
51+
52+
app = App(app_ui, server)

0 commit comments

Comments
 (0)