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.
Files changed (25) hide show
  1. streamlit_react_components/__init__.py +5 -1
  2. streamlit_react_components/_frontend/index.css +1 -1
  3. streamlit_react_components/_frontend/index.js +142 -141
  4. streamlit_react_components/common/__init__.py +2 -0
  5. streamlit_react_components/common/button_group.py +10 -0
  6. streamlit_react_components/common/chart_legend.py +10 -0
  7. streamlit_react_components/common/data_table.py +10 -0
  8. streamlit_react_components/common/metric_row.py +10 -0
  9. streamlit_react_components/common/panel.py +10 -0
  10. streamlit_react_components/common/plotly_chart.py +10 -0
  11. streamlit_react_components/common/section_header.py +10 -0
  12. streamlit_react_components/common/smart_chart.py +327 -0
  13. streamlit_react_components/common/stat_card.py +10 -0
  14. streamlit_react_components/common/step_indicator.py +10 -0
  15. streamlit_react_components/form/__init__.py +2 -0
  16. streamlit_react_components/form/checkbox_group.py +10 -0
  17. streamlit_react_components/form/form_select.py +10 -0
  18. streamlit_react_components/form/form_slider.py +10 -0
  19. streamlit_react_components/form/radio_group.py +78 -0
  20. streamlit_react_components/themes.py +1203 -0
  21. {streamlit_react_components-1.0.5.dist-info → streamlit_react_components-1.0.6.dist-info}/METADATA +1 -1
  22. streamlit_react_components-1.0.6.dist-info/RECORD +25 -0
  23. {streamlit_react_components-1.0.5.dist-info → streamlit_react_components-1.0.6.dist-info}/WHEEL +1 -1
  24. streamlit_react_components-1.0.5.dist-info/RECORD +0 -22
  25. {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
  )
@@ -3,9 +3,11 @@
3
3
  from .form_select import form_select
4
4
  from .form_slider import form_slider
5
5
  from .checkbox_group import checkbox_group
6
+ from .radio_group import radio_group
6
7
 
7
8
  __all__ = [
8
9
  "form_select",
9
10
  "form_slider",
10
11
  "checkbox_group",
12
+ "radio_group",
11
13
  ]