Metadata-Version: 2.4
Name: streamlit-react-components
Version: 1.8.2.2
Summary: Reusable React-based Streamlit components with Tailwind CSS styling
License: MIT
Project-URL: Homepage, https://github.com/your-org/streamlit-react-components
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: streamlit>=1.24.0

# Streamlit React Components

A collection of reusable React-based Streamlit components with Tailwind CSS styling.

## Installation

```bash
pip install streamlit-react-components
```

## Components

- [form_select](#form_select) - Styled dropdown select
- [form_slider](#form_slider) - Range slider
- [form_date_slider](#form_date_slider) - Date range slider
- [checkbox_group](#checkbox_group) - Multi-select checkboxes
- [radio_group](#radio_group) - Single-select radio buttons
- [button_group](#button_group) - Grouped action buttons (with `on_click`)
- [step_indicator](#step_indicator) - Wizard progress indicator (with `on_change`)
- [smart_chart](#smart_chart) - Simplified chart creation
- [Deferred Updates](#deferred-updates) - Batch changes with `defer_update`

---

## form_select

Styled dropdown select input.

```python
from streamlit_react_components import form_select

flavor = form_select(
    label="Ice Cream Flavor",
    options=["Vanilla", "Chocolate", "Strawberry", "Mint", "Cookies & Cream"],
    value="Chocolate",
    key="flavor_select",
)
st.write(f"Selected flavor: **{flavor}**")

# With value/label pairs
vehicle = form_select(
    label="Vehicle Type",
    options=[
        {"value": "sedan", "label": "Sedan"},
        {"value": "suv", "label": "SUV"},
        {"value": "truck", "label": "Pickup Truck"},
    ],
    value="suv",
    key="vehicle_select",
)
```

---

## form_slider

Range slider with customizable styling.

```python
from streamlit_react_components import form_slider

# Single value
temperature = form_slider(
    label="Room Temperature",
    min_val=60,
    max_val=90,
    value=72,
    unit="°F",
    key="temperature_slider",
)

# Range (tuple)
volume = form_slider(
    label="Speaker Volume",
    min_val=0,
    max_val=100,
    value=(25, 75),
    color="purple",
    key="volume_slider",
)
```

---

## form_date_slider

Date range slider for selecting date ranges.

```python
from datetime import date
from streamlit_react_components import form_date_slider

vacation_start, vacation_end = form_date_slider(
    label="Vacation Dates",
    min_val=date(2025, 1, 1),
    max_val=date(2025, 12, 31),
    value=(date(2025, 6, 1), date(2025, 6, 15)),
    step_type="day",
    format="MMM DD, YYYY",
    color="emerald",
    key="vacation_slider",
)
st.write(f"Vacation: **{vacation_start}** to **{vacation_end}**")
```

---

## checkbox_group

Multi-select checkbox group.

```python
from streamlit_react_components import checkbox_group

toppings = checkbox_group(
    label="Pizza Toppings",
    items=[
        {"id": "pepperoni", "label": "Pepperoni", "checked": True},
        {"id": "mushrooms", "label": "Mushrooms"},
        {"id": "olives", "label": "Olives"},
        {"id": "peppers", "label": "Bell Peppers", "checked": True},
    ],
    layout="vertical",
    key="toppings_checkbox",
)
st.write(f"Selected toppings: **{toppings}**")
# Returns: ["pepperoni", "peppers"]
```

---

## radio_group

Single-select radio button group.

```python
from streamlit_react_components import radio_group

size = radio_group(
    label="T-Shirt Size",
    items=[
        {"id": "sm", "label": "Small"},
        {"id": "md", "label": "Medium", "checked": True},
        {"id": "lg", "label": "Large"},
        {"id": "xl", "label": "Extra Large"},
    ],
    layout="vertical",
    key="size_radio",
)
st.write(f"Selected size: **{size}**")
# Returns: "md"
```

---

## button_group

Grouped action buttons with icons and colors. Supports `on_click` callback.

```python
from streamlit_react_components import button_group

# Basic usage
action = button_group(
    buttons=[
        {"id": "save", "label": "Save", "color": "blue"},
        {"id": "cancel", "label": "Cancel", "color": "slate"},
    ],
    key="basic_buttons",
)

if action:
    st.info(f"Clicked: **{action}**")
```

### With on_click callback

```python
def handle_transport(button_id):
    st.session_state.action_log.append(f"Transport: {button_id}")

transport = button_group(
    buttons=[
        {"id": "car", "label": "Car", "icon": "🚗", "color": "blue"},
        {"id": "bike", "label": "Bike", "icon": "🚲", "color": "emerald"},
        {"id": "bus", "label": "Bus", "icon": "🚌", "color": "amber"},
        {"id": "plane", "label": "Plane", "icon": "✈️", "color": "cyan"},
    ],
    on_click=handle_transport,
    key="transport_buttons",
)
```

---

## step_indicator

Multi-step wizard progress indicator. Supports `on_change` callback.

```python
from streamlit_react_components import step_indicator

# Basic usage
clicked_step = step_indicator(
    steps=["Cart", "Shipping", "Payment", "Confirm"],
    current_step=st.session_state.wizard_step,
    key="checkout_steps",
)

if clicked_step:
    st.session_state.wizard_step = clicked_step
    st.rerun()
```

### With on_change callback

```python
def handle_recipe_step(step_number):
    st.session_state.recipe_step = step_number

step_indicator(
    steps=["Ingredients", "Prep", "Cook", "Serve"],
    current_step=st.session_state.recipe_step,
    on_change=handle_recipe_step,
    key="recipe_steps",
)
```

---

## smart_chart

Simplified chart creation from DataFrames. Supports line, bar, scatter, pie, gauge, histogram, and waterfall charts.

### Line Chart

```python
import pandas as pd
from streamlit_react_components import smart_chart

bakery_data = pd.DataFrame({
    "month": ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
    "croissants": [120, 150, 180, 220, 200, 250],
    "muffins": [80, 95, 110, 130, 145, 160],
})

smart_chart(
    data=bakery_data,
    chart_type="line",
    x="month",
    y=["croissants", "muffins"],
    title="Monthly Bakery Sales",
    key="bakery_line",
)
```

### Bar Chart

```python
pet_data = pd.DataFrame({
    "animal": ["Dogs", "Cats", "Rabbits", "Birds", "Fish"],
    "adoptions": [45, 38, 12, 8, 22],
})

smart_chart(
    data=pet_data,
    chart_type="bar",
    x="animal",
    y="adoptions",
    title="Pet Adoptions This Month",
    color="emerald",
    key="pet_bar",
)
```

### Pie / Donut Chart

```python
fruit_data = pd.DataFrame({
    "fruit": ["Apples", "Oranges", "Bananas", "Grapes"],
    "quantity": [25, 18, 30, 15],
})

smart_chart(
    data=fruit_data,
    chart_type="pie",
    labels="fruit",
    values="quantity",
    hole=0.4,  # Makes it a donut chart
    title="Fruit Distribution",
    key="fruit_pie",
)
```

### Gauge Chart

```python
tank_data = pd.DataFrame({"water_level": [73]})

smart_chart(
    data=tank_data,
    chart_type="gauge",
    value_column="water_level",
    min_value=0,
    max_value=100,
    threshold_low=20,
    threshold_medium=50,
    threshold_high=80,
    title="Water Tank Level (%)",
    key="tank_gauge",
)
```

### Waterfall Chart

```python
budget_data = pd.DataFrame({
    "category": ["Starting", "Groceries", "Rent", "Salary", "Ending"],
    "amount": [0, -350, -1200, 3500, 0],
    "measure": ["total", "relative", "relative", "relative", "total"],
})

smart_chart(
    data=budget_data,
    chart_type="waterfall",
    category_column="category",
    value_column_waterfall="amount",
    measure_column="measure",
    title="Monthly Budget Flow",
    key="budget_waterfall",
)
```

---

## Deferred Updates

Components support `defer_update=True` to batch changes. Values are stored locally and only sent when a button is clicked - preventing unnecessary reruns.

```python
import streamlit as st
from datetime import date
from streamlit_react_components import (
    form_select,
    form_slider,
    form_date_slider,
    checkbox_group,
    button_group,
)

# Track reruns to demonstrate efficiency
if "rerun_count" not in st.session_state:
    st.session_state.rerun_count = 0
st.session_state.rerun_count += 1

st.info(f"🔄 Rerun count: {st.session_state.rerun_count}")

# Deferred form_select - NO rerun when changed
flavor = form_select(
    label="Smoothie Flavor",
    options=["Mango Tango", "Berry Blast", "Tropical Sunrise", "Green Machine"],
    value="Mango Tango",
    defer_update=True,
    key="flavor_select",
)

# Deferred form_slider - NO rerun when changed
spice_level = form_slider(
    label="Spice Level",
    min_val=1,
    max_val=10,
    value=5,
    defer_update=True,
    key="spice_slider",
)

# Deferred form_date_slider - NO rerun when changed
start_date, end_date = form_date_slider(
    label="Delivery Window",
    min_val=date(2025, 1, 1),
    max_val=date(2025, 12, 31),
    value=(date(2025, 3, 1), date(2025, 3, 15)),
    step_type="day",
    format="MMM DD",
    defer_update=True,
    key="delivery_dates",
)

# Deferred checkbox_group - NO rerun when changed
toppings = checkbox_group(
    label="Extra Toppings",
    items=[
        {"id": "whip", "label": "Whipped Cream"},
        {"id": "nuts", "label": "Crushed Nuts"},
        {"id": "choco", "label": "Chocolate Chips"},
    ],
    defer_update=True,
    key="toppings_check",
)

# Callback for button actions
def handle_button(button_id):
    if button_id == "apply":
        st.session_state.applied_flavor = flavor
        st.session_state.applied_spice = spice_level

# Button group triggers all deferred components to send their values
clicked = button_group(
    buttons=[
        {"id": "apply", "label": "Apply Order", "icon": "✅", "color": "emerald"},
        {"id": "reset", "label": "Reset", "icon": "🔄", "color": "slate"},
    ],
    on_click=handle_button,
    key="order_buttons",
)
```

### How defer_update Works

1. **Change any input** - Value is stored in browser sessionStorage, Python is NOT notified
2. **Click Apply** - button_group broadcasts a `SEND_DEFERRED` message
3. **Components respond** - Each deferred component sends its stored value to Python
4. **Single rerun** - Python receives all values in one rerun, not multiple

---

## License

MIT
