streamlit-react-components 1.7.3__py3-none-any.whl → 1.8.0__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.
@@ -2,12 +2,14 @@
2
2
 
3
3
  from .form_select import form_select
4
4
  from .form_slider import form_slider
5
+ from .form_date_slider import form_date_slider
5
6
  from .checkbox_group import checkbox_group
6
7
  from .radio_group import radio_group
7
8
 
8
9
  __all__ = [
9
10
  "form_select",
10
11
  "form_slider",
12
+ "form_date_slider",
11
13
  "checkbox_group",
12
14
  "radio_group",
13
15
  ]
@@ -19,6 +19,7 @@ def checkbox_group(
19
19
  style: Optional[Dict[str, Any]] = None,
20
20
  class_name: str = "",
21
21
  theme: Optional[Dict[str, Any]] = None,
22
+ defer_update: bool = False,
22
23
  key: Optional[str] = None,
23
24
  ) -> List[str]:
24
25
  """
@@ -36,7 +37,10 @@ def checkbox_group(
36
37
  class_name: Tailwind CSS classes
37
38
  theme: Optional theme dictionary. If None, uses active global theme.
38
39
  Set to False to disable theming for this component.
39
- key: Unique key for the component
40
+ defer_update: If True, don't trigger Streamlit rerun on change.
41
+ Value is stored locally and sent on next rerun (e.g., Apply button).
42
+ Requires 'key' to be set.
43
+ key: Unique key for the component (required if defer_update=True)
40
44
 
41
45
  Returns:
42
46
  List of checked item IDs
@@ -60,6 +64,16 @@ def checkbox_group(
60
64
  items=[...],
61
65
  layout="horizontal"
62
66
  )
67
+
68
+ # Deferred update (no rerun until Apply button clicked)
69
+ selected = checkbox_group(
70
+ label="Parameters",
71
+ items=[...],
72
+ defer_update=True,
73
+ key="params_checkbox"
74
+ )
75
+ if st.button("Apply"):
76
+ st.rerun()
63
77
  """
64
78
  # Get default checked items
65
79
  default_checked = [item["id"] for item in items if item.get("checked", False)]
@@ -78,6 +92,8 @@ def checkbox_group(
78
92
  style=style,
79
93
  className=class_name,
80
94
  theme=resolved_theme,
95
+ deferUpdate=defer_update,
96
+ componentKey=key,
81
97
  key=key,
82
98
  default=default_checked,
83
99
  )
@@ -0,0 +1,147 @@
1
+ """FormDateSlider component - A date slider with optional range selection."""
2
+
3
+ import streamlit.components.v1 as components
4
+ from pathlib import Path
5
+ from datetime import date, timedelta
6
+ from typing import Dict, Any, Optional, Union, Tuple
7
+
8
+ _FRONTEND_DIR = Path(__file__).parent.parent / "_frontend"
9
+
10
+ _component = components.declare_component(
11
+ "streamlit_react_components",
12
+ path=str(_FRONTEND_DIR),
13
+ )
14
+
15
+
16
+ def form_date_slider(
17
+ label: str,
18
+ min_val: date,
19
+ max_val: date,
20
+ value: Union[date, Tuple[date, date]],
21
+ step_type: Optional[str] = "month",
22
+ step: Optional[timedelta] = None,
23
+ format: str = "YYYY-MM",
24
+ color: str = "blue",
25
+ style: Optional[Dict[str, Any]] = None,
26
+ class_name: str = "",
27
+ theme: Optional[Dict[str, Any]] = None,
28
+ defer_update: bool = False,
29
+ key: Optional[str] = None,
30
+ ) -> Union[date, Tuple[date, date]]:
31
+ """
32
+ Display a date slider with single or range selection.
33
+
34
+ Args:
35
+ label: Label text for the slider
36
+ min_val: Minimum selectable date
37
+ max_val: Maximum selectable date
38
+ value: Current value - single date or tuple of (start, end) for range mode
39
+ step_type: Step granularity - "month", "quarter", "year", "week", "day", or None
40
+ When set to a calendar unit, snaps to boundaries (e.g., 1st of month).
41
+ When "day" or None, uses the `step` timedelta parameter.
42
+ step: Step increment as timedelta (only used when step_type is "day" or None)
43
+ Defaults to timedelta(days=1)
44
+ format: Display format string - "YYYY-MM", "YYYY-MM-DD", "MMM YYYY", etc.
45
+ color: Accent color - preset name or hex value
46
+ style: Inline CSS styles as a dictionary
47
+ class_name: Tailwind CSS classes
48
+ theme: Optional theme dictionary. If None, uses active global theme.
49
+ defer_update: If True, don't trigger Streamlit rerun on change.
50
+ key: Unique key for the component
51
+
52
+ Returns:
53
+ Single date if value was a date, or tuple (start, end) if value was a tuple
54
+
55
+ Example:
56
+ # Monthly range selection
57
+ start, end = form_date_slider(
58
+ label="Date Range",
59
+ min_val=date(2024, 1, 1),
60
+ max_val=date(2025, 12, 31),
61
+ value=(date(2024, 3, 1), date(2024, 9, 1)),
62
+ step_type="month",
63
+ format="YYYY-MM",
64
+ key="date_range"
65
+ )
66
+
67
+ # Single month selection
68
+ selected = form_date_slider(
69
+ label="Select Month",
70
+ min_val=date(2024, 1, 1),
71
+ max_val=date(2025, 12, 31),
72
+ value=date(2024, 6, 1),
73
+ step_type="month",
74
+ format="YYYY-MM",
75
+ key="single_month"
76
+ )
77
+
78
+ # Daily stepping with custom interval
79
+ selected = form_date_slider(
80
+ label="Select Date",
81
+ min_val=date(2024, 1, 1),
82
+ max_val=date(2024, 12, 31),
83
+ value=date(2024, 6, 15),
84
+ step_type="day",
85
+ step=timedelta(days=7), # Weekly steps
86
+ format="YYYY-MM-DD",
87
+ key="weekly_date"
88
+ )
89
+ """
90
+ # Determine if range mode based on value type
91
+ is_range = isinstance(value, (tuple, list))
92
+
93
+ # Convert dates to ISO strings
94
+ min_val_str = min_val.isoformat()
95
+ max_val_str = max_val.isoformat()
96
+
97
+ if is_range:
98
+ value_data = [value[0].isoformat(), value[1].isoformat()]
99
+ default_data = value_data
100
+ else:
101
+ value_data = value.isoformat()
102
+ default_data = value_data
103
+
104
+ # Convert step timedelta to days if provided
105
+ step_days = None
106
+ if step is not None:
107
+ step_days = step.days
108
+ elif step_type in ("day", None):
109
+ step_days = 1 # Default to 1 day
110
+
111
+ # Resolve theme (None = use global, False = disable)
112
+ from ..themes import get_active_theme
113
+ resolved_theme = None
114
+ if theme is not False:
115
+ resolved_theme = theme if theme is not None else get_active_theme()
116
+
117
+ result = _component(
118
+ component="form_date_slider",
119
+ label=label,
120
+ minVal=min_val_str,
121
+ maxVal=max_val_str,
122
+ value=value_data,
123
+ stepType=step_type,
124
+ stepDays=step_days,
125
+ format=format,
126
+ color=color,
127
+ style=style,
128
+ className=class_name,
129
+ theme=resolved_theme,
130
+ deferUpdate=defer_update,
131
+ componentKey=key,
132
+ key=key,
133
+ default=default_data,
134
+ )
135
+
136
+ # Parse result back to date objects
137
+ if result is None:
138
+ return value
139
+
140
+ if is_range:
141
+ if isinstance(result, (list, tuple)) and len(result) == 2:
142
+ return (date.fromisoformat(result[0]), date.fromisoformat(result[1]))
143
+ return value
144
+ else:
145
+ if isinstance(result, str):
146
+ return date.fromisoformat(result)
147
+ return value
@@ -20,6 +20,7 @@ def form_select(
20
20
  style: Optional[Dict[str, Any]] = None,
21
21
  class_name: str = "",
22
22
  theme: Optional[Dict[str, Any]] = None,
23
+ defer_update: bool = False,
23
24
  key: Optional[str] = None,
24
25
  ) -> str:
25
26
  """
@@ -36,7 +37,10 @@ def form_select(
36
37
  class_name: Tailwind CSS classes
37
38
  theme: Optional theme dictionary. If None, uses active global theme.
38
39
  Set to False to disable theming for this component.
39
- key: Unique key for the component
40
+ defer_update: If True, don't trigger Streamlit rerun on change.
41
+ Value is stored locally and sent on next rerun (e.g., Apply button).
42
+ Requires 'key' to be set.
43
+ key: Unique key for the component (required if defer_update=True)
40
44
 
41
45
  Returns:
42
46
  The currently selected value
@@ -57,6 +61,16 @@ def form_select(
57
61
  {"label": "Scenarios", "options": ["Q2 Demand Surge"]}
58
62
  ]
59
63
  )
64
+
65
+ # Deferred update (no rerun until Apply button clicked)
66
+ site = form_select(
67
+ label="Site",
68
+ options=["AML_14", "ADL", "Devens"],
69
+ defer_update=True,
70
+ key="site_select"
71
+ )
72
+ if st.button("Apply"):
73
+ st.rerun()
60
74
  """
61
75
  # Resolve theme (None = use global, False = disable)
62
76
  from ..themes import get_active_theme
@@ -73,6 +87,8 @@ def form_select(
73
87
  style=style,
74
88
  className=class_name,
75
89
  theme=resolved_theme,
90
+ deferUpdate=defer_update,
91
+ componentKey=key,
76
92
  key=key,
77
93
  default=value,
78
94
  )
@@ -23,6 +23,7 @@ def form_slider(
23
23
  style: Optional[Dict[str, Any]] = None,
24
24
  class_name: str = "",
25
25
  theme: Optional[Dict[str, Any]] = None,
26
+ defer_update: bool = False,
26
27
  key: Optional[str] = None,
27
28
  ) -> float:
28
29
  """
@@ -41,7 +42,10 @@ def form_slider(
41
42
  class_name: Tailwind CSS classes
42
43
  theme: Optional theme dictionary. If None, uses active global theme.
43
44
  Set to False to disable theming for this component.
44
- key: Unique key for the component
45
+ defer_update: If True, don't trigger Streamlit rerun on change.
46
+ Value is stored locally and sent on next rerun (e.g., Apply button).
47
+ Requires 'key' to be set.
48
+ key: Unique key for the component (required if defer_update=True)
45
49
 
46
50
  Returns:
47
51
  The current slider value
@@ -65,6 +69,18 @@ def form_slider(
65
69
  max_val=100,
66
70
  color="#ff5733"
67
71
  )
72
+
73
+ # Deferred update (no rerun until Apply button clicked)
74
+ threshold = form_slider(
75
+ label="Threshold",
76
+ value=50,
77
+ min_val=0,
78
+ max_val=100,
79
+ defer_update=True,
80
+ key="threshold_slider"
81
+ )
82
+ if st.button("Apply"):
83
+ st.rerun()
68
84
  """
69
85
  # Resolve theme (None = use global, False = disable)
70
86
  from ..themes import get_active_theme
@@ -84,6 +100,8 @@ def form_slider(
84
100
  style=style,
85
101
  className=class_name,
86
102
  theme=resolved_theme,
103
+ deferUpdate=defer_update,
104
+ componentKey=key,
87
105
  key=key,
88
106
  default=value,
89
107
  )
@@ -19,6 +19,7 @@ def radio_group(
19
19
  style: Optional[Dict[str, Any]] = None,
20
20
  class_name: str = "",
21
21
  theme: Optional[Dict[str, Any]] = None,
22
+ defer_update: bool = False,
22
23
  key: Optional[str] = None,
23
24
  ) -> Optional[str]:
24
25
  """
@@ -36,7 +37,10 @@ def radio_group(
36
37
  class_name: Tailwind CSS classes
37
38
  theme: Optional theme dictionary. If None, uses active global theme.
38
39
  Set to False to disable theming for this component.
39
- key: Unique key for the component
40
+ defer_update: If True, don't trigger Streamlit rerun on change.
41
+ Value is stored locally and sent on next rerun (e.g., Apply button).
42
+ Requires 'key' to be set.
43
+ key: Unique key for the component (required if defer_update=True)
40
44
 
41
45
  Returns:
42
46
  ID of the selected item (string), or None if nothing selected
@@ -51,6 +55,16 @@ def radio_group(
51
55
  ]
52
56
  )
53
57
  # Returns: "credit" (only one can be selected)
58
+
59
+ # Deferred update (no rerun until Apply button clicked)
60
+ selected = radio_group(
61
+ label="Payment Method",
62
+ items=[...],
63
+ defer_update=True,
64
+ key="payment_radio"
65
+ )
66
+ if st.button("Apply"):
67
+ st.rerun()
54
68
  """
55
69
  # Get default selected item (first checked item)
56
70
  default_selected = None
@@ -73,6 +87,8 @@ def radio_group(
73
87
  style=style,
74
88
  className=class_name,
75
89
  theme=resolved_theme,
90
+ deferUpdate=defer_update,
91
+ componentKey=key,
76
92
  key=key,
77
93
  default=default_selected,
78
94
  )
@@ -0,0 +1,193 @@
1
+ """
2
+ Styled container component for wrapping native Streamlit components with Tailwind styling.
3
+
4
+ This module provides a context manager that enables Tailwind CSS-like styling
5
+ for native Streamlit components (st.slider, st.button, st.text_input, etc.).
6
+
7
+ Supports:
8
+ - All Tailwind utility classes
9
+ - Opacity modifiers (bg-blue-500/50)
10
+ - Hover, focus, and active variants
11
+ - Transitions and animations
12
+ """
13
+
14
+ from contextlib import contextmanager
15
+ from typing import Dict, Any, Optional, Generator
16
+ import uuid
17
+
18
+ import streamlit as st
19
+
20
+ from .tailwind import parse_tailwind_classes
21
+
22
+
23
+ @contextmanager
24
+ def styled_container(
25
+ *classes: str,
26
+ style: Optional[Dict[str, Any]] = None,
27
+ key: Optional[str] = None,
28
+ ) -> Generator[None, None, None]:
29
+ """
30
+ Context manager for styling native Streamlit components with Tailwind classes.
31
+
32
+ Wraps Streamlit components in a styled container with support for Tailwind CSS
33
+ utility classes, including hover:, focus:, and active: variants.
34
+
35
+ Args:
36
+ *classes: Tailwind CSS class names (e.g., "bg-slate-800", "hover:bg-slate-700")
37
+ style: Optional dict of additional inline CSS properties to merge
38
+ key: Optional unique key for the container (auto-generated if not provided)
39
+
40
+ Yields:
41
+ None - use as a context manager with 'with' statement
42
+
43
+ Example:
44
+ Basic usage with Tailwind classes::
45
+
46
+ with styled_container("bg-slate-800", "border", "border-blue-500", "rounded-xl", "p-4"):
47
+ st.slider("Risk Threshold", 0, 100, 75)
48
+ st.button("Apply")
49
+
50
+ With hover and focus states::
51
+
52
+ with styled_container(
53
+ "bg-slate-800",
54
+ "border-2",
55
+ "border-slate-600",
56
+ "rounded-xl",
57
+ "p-6",
58
+ "transition-all",
59
+ "duration-200",
60
+ "hover:bg-slate-700",
61
+ "hover:border-blue-500",
62
+ "focus:border-blue-400"
63
+ ):
64
+ st.text_input("Username")
65
+ st.text_input("Password", type="password")
66
+ st.button("Login")
67
+
68
+ With opacity modifiers::
69
+
70
+ with styled_container(
71
+ "bg-rose-500/20",
72
+ "border",
73
+ "border-rose-500/50",
74
+ "rounded-lg",
75
+ "p-4"
76
+ ):
77
+ st.write("Rose-tinted container")
78
+
79
+ With gradient backgrounds::
80
+
81
+ with styled_container(
82
+ "bg-gradient-to-br",
83
+ "from-slate-800",
84
+ "to-slate-900",
85
+ "rounded-2xl",
86
+ "p-6"
87
+ ):
88
+ st.metric("Revenue", "$12,450", "+8.2%")
89
+
90
+ With custom inline styles::
91
+
92
+ with styled_container(
93
+ "bg-slate-800",
94
+ "rounded-lg",
95
+ style={"min-height": "200px", "box-shadow": "0 0 20px rgba(59,130,246,0.3)"}
96
+ ):
97
+ st.write("Custom styled container")
98
+
99
+ Note:
100
+ - The focus: variant uses :focus-within, which triggers when any child
101
+ element (like an input) receives focus
102
+ - Transitions work with hover/focus states for smooth animations
103
+ - Gradient classes (from-, to-, via-) work with bg-gradient-to-* classes
104
+ """
105
+ # Generate unique container ID
106
+ container_id = key or f"stc-{uuid.uuid4().hex[:8]}"
107
+
108
+ # Parse classes into categorized CSS dicts
109
+ parsed = parse_tailwind_classes(classes)
110
+
111
+ # Merge with custom style dict
112
+ if style:
113
+ parsed["base"].update(style)
114
+
115
+ # Build CSS rules for each state
116
+ css_blocks = []
117
+
118
+ # Use a more specific selector that targets the container's parent block
119
+ selector = f'[data-testid="stVerticalBlock"]:has(> [data-stc-id="{container_id}"])'
120
+
121
+ # Base styles
122
+ if parsed["base"]:
123
+ base_css = "; ".join(f"{k}: {v}" for k, v in parsed["base"].items())
124
+ css_blocks.append(f"{selector} {{ {base_css} }}")
125
+
126
+ # Hover styles
127
+ if parsed["hover"]:
128
+ hover_css = "; ".join(f"{k}: {v}" for k, v in parsed["hover"].items())
129
+ css_blocks.append(f"{selector}:hover {{ {hover_css} }}")
130
+
131
+ # Focus styles (uses :focus-within for child focus)
132
+ if parsed["focus"]:
133
+ focus_css = "; ".join(f"{k}: {v}" for k, v in parsed["focus"].items())
134
+ css_blocks.append(f"{selector}:focus-within {{ {focus_css} }}")
135
+
136
+ # Active styles
137
+ if parsed["active"]:
138
+ active_css = "; ".join(f"{k}: {v}" for k, v in parsed["active"].items())
139
+ css_blocks.append(f"{selector}:active {{ {active_css} }}")
140
+
141
+ # Inject CSS and marker element
142
+ css_content = "\n ".join(css_blocks)
143
+ st.markdown(
144
+ f"""
145
+ <style>
146
+ {css_content}
147
+ </style>
148
+ <div data-stc-id="{container_id}" style="display:none;"></div>
149
+ """,
150
+ unsafe_allow_html=True,
151
+ )
152
+
153
+ yield
154
+
155
+
156
+ # Convenience function for inline style conversion
157
+ def css(*classes: str, **extra_styles: str) -> str:
158
+ """
159
+ Convert Tailwind classes to an inline CSS string.
160
+
161
+ Useful for applying Tailwind-like styles to st.markdown HTML content.
162
+
163
+ Args:
164
+ *classes: Tailwind CSS class names
165
+ **extra_styles: Additional CSS properties as keyword arguments
166
+
167
+ Returns:
168
+ CSS string suitable for inline style attribute
169
+
170
+ Example:
171
+ >>> css("bg-slate-800", "p-4", "rounded-lg")
172
+ 'background-color: #1e293b; padding: 1rem; border-radius: 0.5rem'
173
+
174
+ >>> css("text-blue-500", font_size="20px")
175
+ 'color: #3b82f6; font-size: 20px'
176
+
177
+ With st.markdown::
178
+
179
+ st.markdown(
180
+ f'<div style="{css("bg-slate-800", "p-4", "rounded-lg")}">Content</div>',
181
+ unsafe_allow_html=True
182
+ )
183
+ """
184
+ from .tailwind import tw
185
+
186
+ styles = tw(*classes)
187
+
188
+ # Add extra styles (convert underscores to hyphens)
189
+ for key, value in extra_styles.items():
190
+ css_key = key.replace("_", "-")
191
+ styles[css_key] = value
192
+
193
+ return "; ".join(f"{k}: {v}" for k, v in styles.items())