streamlit-react-components 1.0.5__py3-none-any.whl → 1.0.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- streamlit_react_components/__init__.py +5 -1
- streamlit_react_components/_frontend/index.css +1 -1
- streamlit_react_components/_frontend/index.js +142 -141
- streamlit_react_components/common/__init__.py +2 -0
- streamlit_react_components/common/button_group.py +10 -0
- streamlit_react_components/common/chart_legend.py +10 -0
- streamlit_react_components/common/data_table.py +10 -0
- streamlit_react_components/common/metric_row.py +10 -0
- streamlit_react_components/common/panel.py +10 -0
- streamlit_react_components/common/plotly_chart.py +10 -0
- streamlit_react_components/common/section_header.py +10 -0
- streamlit_react_components/common/smart_chart.py +327 -0
- streamlit_react_components/common/stat_card.py +10 -0
- streamlit_react_components/common/step_indicator.py +10 -0
- streamlit_react_components/form/__init__.py +2 -0
- streamlit_react_components/form/checkbox_group.py +10 -0
- streamlit_react_components/form/form_select.py +10 -0
- streamlit_react_components/form/form_slider.py +10 -0
- streamlit_react_components/form/radio_group.py +78 -0
- streamlit_react_components/themes.py +1203 -0
- {streamlit_react_components-1.0.5.dist-info → streamlit_react_components-1.0.6.dist-info}/METADATA +1 -1
- streamlit_react_components-1.0.6.dist-info/RECORD +25 -0
- {streamlit_react_components-1.0.5.dist-info → streamlit_react_components-1.0.6.dist-info}/WHEEL +1 -1
- streamlit_react_components-1.0.5.dist-info/RECORD +0 -22
- {streamlit_react_components-1.0.5.dist-info → streamlit_react_components-1.0.6.dist-info}/top_level.txt +0 -0
|
@@ -9,6 +9,7 @@ from .step_indicator import step_indicator
|
|
|
9
9
|
from .button_group import button_group
|
|
10
10
|
from .chart_legend import chart_legend
|
|
11
11
|
from .plotly_chart import plotly_chart
|
|
12
|
+
from .smart_chart import smart_chart
|
|
12
13
|
|
|
13
14
|
__all__ = [
|
|
14
15
|
"panel",
|
|
@@ -20,4 +21,5 @@ __all__ = [
|
|
|
20
21
|
"button_group",
|
|
21
22
|
"chart_legend",
|
|
22
23
|
"plotly_chart",
|
|
24
|
+
"smart_chart",
|
|
23
25
|
]
|
|
@@ -16,6 +16,7 @@ def button_group(
|
|
|
16
16
|
buttons: List[Dict[str, Any]],
|
|
17
17
|
style: Optional[Dict[str, Any]] = None,
|
|
18
18
|
class_name: str = "",
|
|
19
|
+
theme: Optional[Dict[str, Any]] = None,
|
|
19
20
|
key: Optional[str] = None,
|
|
20
21
|
) -> Optional[str]:
|
|
21
22
|
"""
|
|
@@ -33,6 +34,8 @@ def button_group(
|
|
|
33
34
|
- className: Tailwind CSS classes for this button (optional)
|
|
34
35
|
style: Inline CSS styles as a dictionary
|
|
35
36
|
class_name: Tailwind CSS classes
|
|
37
|
+
theme: Optional theme dictionary. If None, uses active global theme.
|
|
38
|
+
Set to False to disable theming for this component.
|
|
36
39
|
key: Unique key for the component
|
|
37
40
|
|
|
38
41
|
Returns:
|
|
@@ -59,11 +62,18 @@ def button_group(
|
|
|
59
62
|
if clicked == "approve":
|
|
60
63
|
approve_item()
|
|
61
64
|
"""
|
|
65
|
+
# Resolve theme (None = use global, False = disable)
|
|
66
|
+
from ..themes import get_active_theme
|
|
67
|
+
resolved_theme = None
|
|
68
|
+
if theme is not False:
|
|
69
|
+
resolved_theme = theme if theme is not None else get_active_theme()
|
|
70
|
+
|
|
62
71
|
return _component(
|
|
63
72
|
component="button_group",
|
|
64
73
|
buttons=buttons,
|
|
65
74
|
style=style,
|
|
66
75
|
className=class_name,
|
|
76
|
+
theme=resolved_theme,
|
|
67
77
|
key=key,
|
|
68
78
|
default=None,
|
|
69
79
|
)
|
|
@@ -16,6 +16,7 @@ def chart_legend(
|
|
|
16
16
|
items: List[Dict[str, str]],
|
|
17
17
|
style: Optional[Dict[str, Any]] = None,
|
|
18
18
|
class_name: str = "",
|
|
19
|
+
theme: Optional[Dict[str, Any]] = None,
|
|
19
20
|
key: Optional[str] = None,
|
|
20
21
|
) -> None:
|
|
21
22
|
"""
|
|
@@ -27,6 +28,8 @@ def chart_legend(
|
|
|
27
28
|
- label: Legend label text
|
|
28
29
|
style: Inline CSS styles as a dictionary
|
|
29
30
|
class_name: Tailwind CSS classes
|
|
31
|
+
theme: Optional theme dictionary. If None, uses active global theme.
|
|
32
|
+
Set to False to disable theming for this component.
|
|
30
33
|
key: Unique key for the component
|
|
31
34
|
|
|
32
35
|
Example:
|
|
@@ -39,11 +42,18 @@ def chart_legend(
|
|
|
39
42
|
]
|
|
40
43
|
)
|
|
41
44
|
"""
|
|
45
|
+
# Resolve theme (None = use global, False = disable)
|
|
46
|
+
from ..themes import get_active_theme
|
|
47
|
+
resolved_theme = None
|
|
48
|
+
if theme is not False:
|
|
49
|
+
resolved_theme = theme if theme is not None else get_active_theme()
|
|
50
|
+
|
|
42
51
|
_component(
|
|
43
52
|
component="chart_legend",
|
|
44
53
|
items=items,
|
|
45
54
|
style=style,
|
|
46
55
|
className=class_name,
|
|
56
|
+
theme=resolved_theme,
|
|
47
57
|
key=key,
|
|
48
58
|
default=None,
|
|
49
59
|
)
|
|
@@ -18,6 +18,7 @@ def data_table(
|
|
|
18
18
|
show_header: bool = True,
|
|
19
19
|
style: Optional[Dict[str, Any]] = None,
|
|
20
20
|
class_name: str = "",
|
|
21
|
+
theme: Optional[Dict[str, Any]] = None,
|
|
21
22
|
key: Optional[str] = None,
|
|
22
23
|
) -> Optional[Dict[str, Any]]:
|
|
23
24
|
"""
|
|
@@ -34,6 +35,8 @@ def data_table(
|
|
|
34
35
|
show_header: Whether to show the header row (default True)
|
|
35
36
|
style: Inline CSS styles as a dictionary
|
|
36
37
|
class_name: Tailwind CSS classes
|
|
38
|
+
theme: Optional theme dictionary. If None, uses active global theme.
|
|
39
|
+
Set to False to disable theming for this component.
|
|
37
40
|
key: Unique key for the component
|
|
38
41
|
|
|
39
42
|
Returns:
|
|
@@ -53,6 +56,12 @@ def data_table(
|
|
|
53
56
|
if clicked:
|
|
54
57
|
st.write(f"Clicked row: {clicked['rowData']}")
|
|
55
58
|
"""
|
|
59
|
+
# Resolve theme (None = use global, False = disable)
|
|
60
|
+
from ..themes import get_active_theme
|
|
61
|
+
resolved_theme = None
|
|
62
|
+
if theme is not False:
|
|
63
|
+
resolved_theme = theme if theme is not None else get_active_theme()
|
|
64
|
+
|
|
56
65
|
return _component(
|
|
57
66
|
component="data_table",
|
|
58
67
|
columns=columns,
|
|
@@ -60,6 +69,7 @@ def data_table(
|
|
|
60
69
|
showHeader=show_header,
|
|
61
70
|
style=style,
|
|
62
71
|
className=class_name,
|
|
72
|
+
theme=resolved_theme,
|
|
63
73
|
key=key,
|
|
64
74
|
default=None,
|
|
65
75
|
)
|
|
@@ -18,6 +18,7 @@ def metric_row(
|
|
|
18
18
|
value_color: str = "",
|
|
19
19
|
style: Optional[Dict[str, Any]] = None,
|
|
20
20
|
class_name: str = "",
|
|
21
|
+
theme: Optional[Dict[str, Any]] = None,
|
|
21
22
|
key: Optional[str] = None,
|
|
22
23
|
) -> None:
|
|
23
24
|
"""
|
|
@@ -29,12 +30,20 @@ def metric_row(
|
|
|
29
30
|
value_color: Tailwind text color class for the value (e.g., "text-green-400")
|
|
30
31
|
style: Inline CSS styles as a dictionary
|
|
31
32
|
class_name: Tailwind CSS classes
|
|
33
|
+
theme: Optional theme dictionary. If None, uses active global theme.
|
|
34
|
+
Set to False to disable theming for this component.
|
|
32
35
|
key: Unique key for the component
|
|
33
36
|
|
|
34
37
|
Example:
|
|
35
38
|
metric_row(label="Mean", value="78.4%")
|
|
36
39
|
metric_row(label="Trend", value="↑ +0.4%/mo", value_color="text-green-400")
|
|
37
40
|
"""
|
|
41
|
+
# Resolve theme (None = use global, False = disable)
|
|
42
|
+
from ..themes import get_active_theme
|
|
43
|
+
resolved_theme = None
|
|
44
|
+
if theme is not False:
|
|
45
|
+
resolved_theme = theme if theme is not None else get_active_theme()
|
|
46
|
+
|
|
38
47
|
_component(
|
|
39
48
|
component="metric_row",
|
|
40
49
|
label=label,
|
|
@@ -42,6 +51,7 @@ def metric_row(
|
|
|
42
51
|
valueColor=value_color,
|
|
43
52
|
style=style,
|
|
44
53
|
className=class_name,
|
|
54
|
+
theme=resolved_theme,
|
|
45
55
|
key=key,
|
|
46
56
|
default=None,
|
|
47
57
|
)
|
|
@@ -16,6 +16,7 @@ def panel(
|
|
|
16
16
|
children: str = "",
|
|
17
17
|
style: Optional[Dict[str, Any]] = None,
|
|
18
18
|
class_name: str = "",
|
|
19
|
+
theme: Optional[Dict[str, Any]] = None,
|
|
19
20
|
key: Optional[str] = None,
|
|
20
21
|
) -> None:
|
|
21
22
|
"""
|
|
@@ -25,6 +26,8 @@ def panel(
|
|
|
25
26
|
children: HTML content to render inside the panel
|
|
26
27
|
style: Inline CSS styles as a dictionary (e.g., {"background": "#1e293b"})
|
|
27
28
|
class_name: Tailwind CSS classes (e.g., "bg-slate-900 p-4")
|
|
29
|
+
theme: Optional theme dictionary. If None, uses active global theme.
|
|
30
|
+
Set to False to disable theming for this component.
|
|
28
31
|
key: Unique key for the component
|
|
29
32
|
|
|
30
33
|
Example:
|
|
@@ -33,11 +36,18 @@ def panel(
|
|
|
33
36
|
class_name="mt-4"
|
|
34
37
|
)
|
|
35
38
|
"""
|
|
39
|
+
# Resolve theme (None = use global, False = disable)
|
|
40
|
+
from ..themes import get_active_theme
|
|
41
|
+
resolved_theme = None
|
|
42
|
+
if theme is not False:
|
|
43
|
+
resolved_theme = theme if theme is not None else get_active_theme()
|
|
44
|
+
|
|
36
45
|
_component(
|
|
37
46
|
component="panel",
|
|
38
47
|
children=children,
|
|
39
48
|
style=style,
|
|
40
49
|
className=class_name,
|
|
50
|
+
theme=resolved_theme,
|
|
41
51
|
key=key,
|
|
42
52
|
default=None,
|
|
43
53
|
)
|
|
@@ -134,6 +134,7 @@ def plotly_chart(
|
|
|
134
134
|
modal_title: str = "",
|
|
135
135
|
style: Optional[Dict[str, Any]] = None,
|
|
136
136
|
class_name: str = "",
|
|
137
|
+
theme: Optional[Dict[str, Any]] = None,
|
|
137
138
|
key: Optional[str] = None,
|
|
138
139
|
) -> Optional[Dict[str, Any]]:
|
|
139
140
|
"""
|
|
@@ -162,6 +163,8 @@ def plotly_chart(
|
|
|
162
163
|
modal_title: Title displayed in dialog header when expanded
|
|
163
164
|
style: Inline CSS styles as a dictionary
|
|
164
165
|
class_name: Tailwind CSS classes
|
|
166
|
+
theme: Optional theme dictionary. If None, uses active global theme.
|
|
167
|
+
Set to False to disable theming for this component.
|
|
165
168
|
key: Unique key for the component
|
|
166
169
|
|
|
167
170
|
Returns:
|
|
@@ -249,6 +252,12 @@ def plotly_chart(
|
|
|
249
252
|
else:
|
|
250
253
|
raise ValueError("Either 'figure' or 'data' parameter is required")
|
|
251
254
|
|
|
255
|
+
# Resolve theme (None = use global, False = disable)
|
|
256
|
+
from ..themes import get_active_theme
|
|
257
|
+
resolved_theme = None
|
|
258
|
+
if theme is not False:
|
|
259
|
+
resolved_theme = theme if theme is not None else get_active_theme()
|
|
260
|
+
|
|
252
261
|
# Render the component
|
|
253
262
|
result = _component(
|
|
254
263
|
component="plotly_chart",
|
|
@@ -262,6 +271,7 @@ def plotly_chart(
|
|
|
262
271
|
modalTitle=modal_title,
|
|
263
272
|
style=style,
|
|
264
273
|
className=class_name,
|
|
274
|
+
theme=resolved_theme,
|
|
265
275
|
key=key,
|
|
266
276
|
default=None,
|
|
267
277
|
)
|
|
@@ -18,6 +18,7 @@ def section_header(
|
|
|
18
18
|
actions: Optional[List[Dict[str, Any]]] = None,
|
|
19
19
|
style: Optional[Dict[str, Any]] = None,
|
|
20
20
|
class_name: str = "",
|
|
21
|
+
theme: Optional[Dict[str, Any]] = None,
|
|
21
22
|
key: Optional[str] = None,
|
|
22
23
|
) -> Optional[str]:
|
|
23
24
|
"""
|
|
@@ -39,6 +40,8 @@ def section_header(
|
|
|
39
40
|
st.switch_page() (optional)
|
|
40
41
|
style: Inline CSS styles as a dictionary
|
|
41
42
|
class_name: Tailwind CSS classes
|
|
43
|
+
theme: Optional theme dictionary. If None, uses active global theme.
|
|
44
|
+
Set to False to disable theming for this component.
|
|
42
45
|
key: Unique key for the component
|
|
43
46
|
|
|
44
47
|
Returns:
|
|
@@ -77,6 +80,12 @@ def section_header(
|
|
|
77
80
|
if clicked == "home":
|
|
78
81
|
st.switch_page("pages/home.py")
|
|
79
82
|
"""
|
|
83
|
+
# Resolve theme (None = use global, False = disable)
|
|
84
|
+
from ..themes import get_active_theme
|
|
85
|
+
resolved_theme = None
|
|
86
|
+
if theme is not False:
|
|
87
|
+
resolved_theme = theme if theme is not None else get_active_theme()
|
|
88
|
+
|
|
80
89
|
return _component(
|
|
81
90
|
component="section_header",
|
|
82
91
|
title=title,
|
|
@@ -84,6 +93,7 @@ def section_header(
|
|
|
84
93
|
actions=actions or [],
|
|
85
94
|
style=style,
|
|
86
95
|
className=class_name,
|
|
96
|
+
theme=resolved_theme,
|
|
87
97
|
key=key,
|
|
88
98
|
default=None,
|
|
89
99
|
)
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""SmartChart component - Simplified chart creation for line, gauge, and waterfall charts."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Any, Optional, List, Union
|
|
4
|
+
from .plotly_chart import plotly_chart, _dataframe_to_figure
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _dataframe_to_gauge(
|
|
8
|
+
data: Any,
|
|
9
|
+
value_column: str,
|
|
10
|
+
min_value: Optional[float],
|
|
11
|
+
max_value: Optional[float],
|
|
12
|
+
threshold_low: Optional[float],
|
|
13
|
+
threshold_medium: Optional[float],
|
|
14
|
+
threshold_high: Optional[float],
|
|
15
|
+
title: Optional[str],
|
|
16
|
+
) -> Dict[str, Any]:
|
|
17
|
+
"""Convert a DataFrame to a Plotly gauge indicator figure."""
|
|
18
|
+
from ..themes import get_active_theme
|
|
19
|
+
|
|
20
|
+
# Validate required column exists
|
|
21
|
+
if value_column not in data.columns:
|
|
22
|
+
raise ValueError(f"Column '{value_column}' not found in DataFrame. Available columns: {list(data.columns)}")
|
|
23
|
+
|
|
24
|
+
# Extract value (first row if multiple)
|
|
25
|
+
value = float(data[value_column].iloc[0])
|
|
26
|
+
|
|
27
|
+
# Auto-calculate min/max if not provided
|
|
28
|
+
if min_value is None:
|
|
29
|
+
min_value = 0
|
|
30
|
+
if max_value is None:
|
|
31
|
+
max_value = float(data[value_column].max() * 1.2)
|
|
32
|
+
|
|
33
|
+
# Validate threshold ordering
|
|
34
|
+
if threshold_low is not None and threshold_medium is not None and threshold_low >= threshold_medium:
|
|
35
|
+
raise ValueError("threshold_low must be less than threshold_medium")
|
|
36
|
+
if threshold_medium is not None and threshold_high is not None and threshold_medium >= threshold_high:
|
|
37
|
+
raise ValueError("threshold_medium must be less than threshold_high")
|
|
38
|
+
|
|
39
|
+
# Get theme colors
|
|
40
|
+
theme = get_active_theme()
|
|
41
|
+
primary = theme['colors']['primary']
|
|
42
|
+
success = theme['colors']['success']
|
|
43
|
+
warning = theme['colors']['warning']
|
|
44
|
+
error = theme['colors']['error']
|
|
45
|
+
|
|
46
|
+
# Build gauge configuration
|
|
47
|
+
gauge_config = {
|
|
48
|
+
'axis': {'range': [min_value, max_value]},
|
|
49
|
+
'bar': {'color': primary}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Add colored threshold ranges
|
|
53
|
+
if threshold_low is not None or threshold_medium is not None or threshold_high is not None:
|
|
54
|
+
steps = []
|
|
55
|
+
if threshold_low is not None:
|
|
56
|
+
steps.append({'range': [min_value, threshold_low], 'color': error})
|
|
57
|
+
if threshold_medium is not None:
|
|
58
|
+
start = threshold_low if threshold_low is not None else min_value
|
|
59
|
+
steps.append({'range': [start, threshold_medium], 'color': warning})
|
|
60
|
+
if threshold_high is not None:
|
|
61
|
+
start = threshold_medium if threshold_medium is not None else min_value
|
|
62
|
+
steps.append({'range': [start, threshold_high], 'color': success})
|
|
63
|
+
# Add a final green band if there's space
|
|
64
|
+
if threshold_high < max_value:
|
|
65
|
+
steps.append({'range': [threshold_high, max_value], 'color': success})
|
|
66
|
+
|
|
67
|
+
gauge_config['steps'] = steps
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
'data': [{
|
|
71
|
+
'type': 'indicator',
|
|
72
|
+
'mode': 'gauge+number',
|
|
73
|
+
'value': value,
|
|
74
|
+
'title': {'text': title or ''},
|
|
75
|
+
'gauge': gauge_config,
|
|
76
|
+
'domain': {'x': [0, 1], 'y': [0, 1]}
|
|
77
|
+
}],
|
|
78
|
+
'layout': {}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _dataframe_to_waterfall(
|
|
83
|
+
data: Any,
|
|
84
|
+
category_column: str,
|
|
85
|
+
value_column: str,
|
|
86
|
+
measure_column: Optional[str],
|
|
87
|
+
title: Optional[str],
|
|
88
|
+
) -> Dict[str, Any]:
|
|
89
|
+
"""Convert a DataFrame to a Plotly waterfall figure."""
|
|
90
|
+
from ..themes import get_active_theme
|
|
91
|
+
|
|
92
|
+
# Validate required columns exist
|
|
93
|
+
if category_column not in data.columns:
|
|
94
|
+
raise ValueError(f"Column '{category_column}' not found in DataFrame. Available columns: {list(data.columns)}")
|
|
95
|
+
if value_column not in data.columns:
|
|
96
|
+
raise ValueError(f"Column '{value_column}' not found in DataFrame. Available columns: {list(data.columns)}")
|
|
97
|
+
|
|
98
|
+
categories = data[category_column].tolist()
|
|
99
|
+
values = data[value_column].tolist()
|
|
100
|
+
|
|
101
|
+
# Use measure_column if provided, otherwise auto-detect
|
|
102
|
+
if measure_column and measure_column in data.columns:
|
|
103
|
+
measures = data[measure_column].tolist()
|
|
104
|
+
else:
|
|
105
|
+
# Auto-detect: zero values are likely totals
|
|
106
|
+
measures = ['total' if v == 0 else 'relative' for v in values]
|
|
107
|
+
|
|
108
|
+
# Get theme colors
|
|
109
|
+
theme = get_active_theme()
|
|
110
|
+
success = theme['colors']['success']
|
|
111
|
+
error = theme['colors']['error']
|
|
112
|
+
info = theme['colors']['info']
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
'data': [{
|
|
116
|
+
'type': 'waterfall',
|
|
117
|
+
'x': categories,
|
|
118
|
+
'y': values,
|
|
119
|
+
'measure': measures,
|
|
120
|
+
'connector': {'line': {'color': '#475569'}}, # slate-600
|
|
121
|
+
'increasing': {'marker': {'color': success}},
|
|
122
|
+
'decreasing': {'marker': {'color': error}},
|
|
123
|
+
'totals': {'marker': {'color': info}},
|
|
124
|
+
}],
|
|
125
|
+
'layout': {
|
|
126
|
+
'title': title or '',
|
|
127
|
+
'showlegend': False,
|
|
128
|
+
'xaxis': {'title': category_column},
|
|
129
|
+
'yaxis': {'title': 'Value'}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def smart_chart(
|
|
135
|
+
data: Any,
|
|
136
|
+
chart_type: str,
|
|
137
|
+
# Line chart parameters
|
|
138
|
+
x: Optional[str] = None,
|
|
139
|
+
y: Optional[Union[str, List[str]]] = None,
|
|
140
|
+
# Gauge chart parameters
|
|
141
|
+
value_column: Optional[str] = None,
|
|
142
|
+
min_value: Optional[float] = None,
|
|
143
|
+
max_value: Optional[float] = None,
|
|
144
|
+
threshold_low: Optional[float] = None,
|
|
145
|
+
threshold_medium: Optional[float] = None,
|
|
146
|
+
threshold_high: Optional[float] = None,
|
|
147
|
+
# Waterfall chart parameters
|
|
148
|
+
category_column: Optional[str] = None,
|
|
149
|
+
value_column_waterfall: Optional[str] = None,
|
|
150
|
+
measure_column: Optional[str] = None,
|
|
151
|
+
# Common parameters
|
|
152
|
+
title: Optional[str] = None,
|
|
153
|
+
color: Optional[str] = None,
|
|
154
|
+
# Pass-through to plotly_chart
|
|
155
|
+
on_click: bool = False,
|
|
156
|
+
on_select: bool = False,
|
|
157
|
+
on_hover: bool = False,
|
|
158
|
+
on_relayout: bool = False,
|
|
159
|
+
expandable: bool = False,
|
|
160
|
+
modal_title: str = "",
|
|
161
|
+
# Styling
|
|
162
|
+
style: Optional[Dict[str, Any]] = None,
|
|
163
|
+
class_name: str = "",
|
|
164
|
+
theme: Optional[Dict[str, Any]] = None,
|
|
165
|
+
key: Optional[str] = None,
|
|
166
|
+
) -> Optional[Dict[str, Any]]:
|
|
167
|
+
"""
|
|
168
|
+
Create a smart chart that automatically switches between line, gauge, and waterfall charts.
|
|
169
|
+
|
|
170
|
+
This is a wrapper component that simplifies chart creation by transforming DataFrames
|
|
171
|
+
into appropriate Plotly figure configurations based on the specified chart_type.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
data: pandas DataFrame containing the data to plot
|
|
175
|
+
chart_type: Type of chart to create - 'line', 'gauge', or 'waterfall'
|
|
176
|
+
|
|
177
|
+
# Line Chart Parameters
|
|
178
|
+
x: Column name for x-axis (required for line charts)
|
|
179
|
+
y: Column name(s) for y-axis - string or list of strings (required for line charts)
|
|
180
|
+
|
|
181
|
+
# Gauge Chart Parameters
|
|
182
|
+
value_column: Column name containing the value to display (required for gauge charts)
|
|
183
|
+
min_value: Minimum value for gauge range (auto-calculated if not provided)
|
|
184
|
+
max_value: Maximum value for gauge range (auto-calculated if not provided)
|
|
185
|
+
threshold_low: Low threshold value (red zone below this)
|
|
186
|
+
threshold_medium: Medium threshold value (amber zone)
|
|
187
|
+
threshold_high: High threshold value (green zone above this)
|
|
188
|
+
|
|
189
|
+
# Waterfall Chart Parameters
|
|
190
|
+
category_column: Column name for categories (required for waterfall charts)
|
|
191
|
+
value_column_waterfall: Column name for values (required for waterfall charts)
|
|
192
|
+
measure_column: Optional column specifying 'relative' or 'total' for each row.
|
|
193
|
+
If not provided, auto-detects: zeros are 'total', non-zeros are 'relative'
|
|
194
|
+
|
|
195
|
+
# Common Parameters
|
|
196
|
+
title: Chart title
|
|
197
|
+
color: Column name for color grouping (line charts only)
|
|
198
|
+
on_click: Enable click events
|
|
199
|
+
on_select: Enable selection events (box/lasso)
|
|
200
|
+
on_hover: Enable hover events
|
|
201
|
+
on_relayout: Enable relayout events (zoom/pan)
|
|
202
|
+
expandable: Show expand button to open chart in full-page dialog
|
|
203
|
+
modal_title: Title displayed in dialog header when expanded
|
|
204
|
+
style: Inline CSS styles as a dictionary
|
|
205
|
+
class_name: Tailwind CSS classes
|
|
206
|
+
theme: Optional theme dictionary. If None, uses active global theme.
|
|
207
|
+
key: Unique key for the component
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Event dict with 'type' and event data, or None if no event.
|
|
211
|
+
|
|
212
|
+
Examples:
|
|
213
|
+
# Line Chart
|
|
214
|
+
import pandas as pd
|
|
215
|
+
|
|
216
|
+
df = pd.DataFrame({
|
|
217
|
+
'month': ['Jan', 'Feb', 'Mar', 'Apr'],
|
|
218
|
+
'revenue': [100, 150, 120, 180],
|
|
219
|
+
'costs': [60, 90, 70, 100]
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
smart_chart(
|
|
223
|
+
data=df,
|
|
224
|
+
chart_type='line',
|
|
225
|
+
x='month',
|
|
226
|
+
y=['revenue', 'costs'],
|
|
227
|
+
title='Monthly Performance'
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Gauge Chart
|
|
231
|
+
df_gauge = pd.DataFrame({'conversion_rate': [73.5]})
|
|
232
|
+
|
|
233
|
+
smart_chart(
|
|
234
|
+
data=df_gauge,
|
|
235
|
+
chart_type='gauge',
|
|
236
|
+
value_column='conversion_rate',
|
|
237
|
+
min_value=0,
|
|
238
|
+
max_value=100,
|
|
239
|
+
threshold_low=30,
|
|
240
|
+
threshold_medium=70,
|
|
241
|
+
threshold_high=90,
|
|
242
|
+
title='Conversion Rate'
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Waterfall Chart
|
|
246
|
+
df_waterfall = pd.DataFrame({
|
|
247
|
+
'category': ['Sales', 'Consulting', 'Net Revenue', 'Purchases', 'Other', 'Profit'],
|
|
248
|
+
'amount': [60, 80, 0, -40, -20, 0],
|
|
249
|
+
'measure': ['relative', 'relative', 'total', 'relative', 'relative', 'total']
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
smart_chart(
|
|
253
|
+
data=df_waterfall,
|
|
254
|
+
chart_type='waterfall',
|
|
255
|
+
category_column='category',
|
|
256
|
+
value_column_waterfall='amount',
|
|
257
|
+
measure_column='measure',
|
|
258
|
+
title='Revenue Breakdown'
|
|
259
|
+
)
|
|
260
|
+
"""
|
|
261
|
+
# Validate DataFrame
|
|
262
|
+
if data is None or (hasattr(data, 'empty') and data.empty):
|
|
263
|
+
raise ValueError("DataFrame cannot be None or empty")
|
|
264
|
+
|
|
265
|
+
# Validate chart_type
|
|
266
|
+
valid_types = ['line', 'gauge', 'waterfall']
|
|
267
|
+
if chart_type not in valid_types:
|
|
268
|
+
raise ValueError(f"Invalid chart_type: '{chart_type}'. Must be one of: {', '.join(valid_types)}")
|
|
269
|
+
|
|
270
|
+
# Route to appropriate transformer and validate required parameters
|
|
271
|
+
if chart_type == 'line':
|
|
272
|
+
if x is None or y is None:
|
|
273
|
+
raise ValueError("Line chart requires 'x' and 'y' parameters")
|
|
274
|
+
|
|
275
|
+
# Validate columns exist
|
|
276
|
+
if x not in data.columns:
|
|
277
|
+
raise ValueError(f"Column '{x}' not found in DataFrame. Available columns: {list(data.columns)}")
|
|
278
|
+
|
|
279
|
+
y_cols = [y] if isinstance(y, str) else y
|
|
280
|
+
for y_col in y_cols:
|
|
281
|
+
if y_col not in data.columns:
|
|
282
|
+
raise ValueError(f"Column '{y_col}' not found in DataFrame. Available columns: {list(data.columns)}")
|
|
283
|
+
|
|
284
|
+
# Use existing _dataframe_to_figure for line charts
|
|
285
|
+
figure = _dataframe_to_figure(data, x, y, color, 'line', title)
|
|
286
|
+
|
|
287
|
+
elif chart_type == 'gauge':
|
|
288
|
+
if value_column is None:
|
|
289
|
+
raise ValueError("Gauge chart requires 'value_column' parameter")
|
|
290
|
+
|
|
291
|
+
figure = _dataframe_to_gauge(
|
|
292
|
+
data,
|
|
293
|
+
value_column,
|
|
294
|
+
min_value,
|
|
295
|
+
max_value,
|
|
296
|
+
threshold_low,
|
|
297
|
+
threshold_medium,
|
|
298
|
+
threshold_high,
|
|
299
|
+
title
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
elif chart_type == 'waterfall':
|
|
303
|
+
if category_column is None or value_column_waterfall is None:
|
|
304
|
+
raise ValueError("Waterfall chart requires 'category_column' and 'value_column_waterfall' parameters")
|
|
305
|
+
|
|
306
|
+
figure = _dataframe_to_waterfall(
|
|
307
|
+
data,
|
|
308
|
+
category_column,
|
|
309
|
+
value_column_waterfall,
|
|
310
|
+
measure_column,
|
|
311
|
+
title
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Delegate to plotly_chart
|
|
315
|
+
return plotly_chart(
|
|
316
|
+
figure=figure,
|
|
317
|
+
on_click=on_click,
|
|
318
|
+
on_select=on_select,
|
|
319
|
+
on_hover=on_hover,
|
|
320
|
+
on_relayout=on_relayout,
|
|
321
|
+
expandable=expandable,
|
|
322
|
+
modal_title=modal_title or title or "",
|
|
323
|
+
style=style,
|
|
324
|
+
class_name=class_name,
|
|
325
|
+
theme=theme,
|
|
326
|
+
key=key,
|
|
327
|
+
)
|
|
@@ -25,6 +25,7 @@ def stat_card(
|
|
|
25
25
|
action: Optional[Dict[str, str]] = None,
|
|
26
26
|
style: Optional[Dict[str, Any]] = None,
|
|
27
27
|
class_name: str = "",
|
|
28
|
+
theme: Optional[Dict[str, Any]] = None,
|
|
28
29
|
key: Optional[str] = None,
|
|
29
30
|
) -> Optional[str]:
|
|
30
31
|
"""
|
|
@@ -52,6 +53,8 @@ def stat_card(
|
|
|
52
53
|
- className: Optional Tailwind classes for custom styling
|
|
53
54
|
style: Inline CSS styles as a dictionary
|
|
54
55
|
class_name: Tailwind CSS classes
|
|
56
|
+
theme: Optional theme dictionary. If None, uses active global theme.
|
|
57
|
+
Set to False to disable theming for this component.
|
|
55
58
|
key: Unique key for the component
|
|
56
59
|
|
|
57
60
|
Returns:
|
|
@@ -116,6 +119,12 @@ def stat_card(
|
|
|
116
119
|
}
|
|
117
120
|
)
|
|
118
121
|
"""
|
|
122
|
+
# Resolve theme (None = use global, False = disable)
|
|
123
|
+
from ..themes import get_active_theme
|
|
124
|
+
resolved_theme = None
|
|
125
|
+
if theme is not False:
|
|
126
|
+
resolved_theme = theme if theme is not None else get_active_theme()
|
|
127
|
+
|
|
119
128
|
return _component(
|
|
120
129
|
component="stat_card",
|
|
121
130
|
label=label,
|
|
@@ -130,6 +139,7 @@ def stat_card(
|
|
|
130
139
|
action=action,
|
|
131
140
|
style=style,
|
|
132
141
|
className=class_name,
|
|
142
|
+
theme=resolved_theme,
|
|
133
143
|
key=key,
|
|
134
144
|
default=None,
|
|
135
145
|
)
|
|
@@ -17,6 +17,7 @@ def step_indicator(
|
|
|
17
17
|
current_step: int,
|
|
18
18
|
style: Optional[Dict[str, Any]] = None,
|
|
19
19
|
class_name: str = "",
|
|
20
|
+
theme: Optional[Dict[str, Any]] = None,
|
|
20
21
|
key: Optional[str] = None,
|
|
21
22
|
) -> Optional[int]:
|
|
22
23
|
"""
|
|
@@ -27,6 +28,8 @@ def step_indicator(
|
|
|
27
28
|
current_step: Current active step (1-indexed)
|
|
28
29
|
style: Inline CSS styles as a dictionary
|
|
29
30
|
class_name: Tailwind CSS classes
|
|
31
|
+
theme: Optional theme dictionary. If None, uses active global theme.
|
|
32
|
+
Set to False to disable theming for this component.
|
|
30
33
|
key: Unique key for the component
|
|
31
34
|
|
|
32
35
|
Returns:
|
|
@@ -40,12 +43,19 @@ def step_indicator(
|
|
|
40
43
|
if step:
|
|
41
44
|
st.session_state.step = step
|
|
42
45
|
"""
|
|
46
|
+
# Resolve theme (None = use global, False = disable)
|
|
47
|
+
from ..themes import get_active_theme
|
|
48
|
+
resolved_theme = None
|
|
49
|
+
if theme is not False:
|
|
50
|
+
resolved_theme = theme if theme is not None else get_active_theme()
|
|
51
|
+
|
|
43
52
|
return _component(
|
|
44
53
|
component="step_indicator",
|
|
45
54
|
steps=steps,
|
|
46
55
|
currentStep=current_step,
|
|
47
56
|
style=style,
|
|
48
57
|
className=class_name,
|
|
58
|
+
theme=resolved_theme,
|
|
49
59
|
key=key,
|
|
50
60
|
default=None,
|
|
51
61
|
)
|