Skip to content

Commit 7d1e556

Browse files
fix: button state reset
1 parent 1fe29ac commit 7d1e556

File tree

11 files changed

+113
-44
lines changed

11 files changed

+113
-44
lines changed

Home.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@
1010

1111
ui_result = ui.button("Button", key="btn")
1212
st.write("UI Button Clicked:", ui_result)
13+
st.write(st.session_state.get("btn"))
1314

14-
st.write("before", st.session_state)
15-
16-
st_result = st.button("Button", key="btn2")
15+
st_result = ui.button("Button", key="btn2")
1716
st.write("ST Button Clicked:", st_result)
17+
st.write(st.session_state.get("btn2"))
1818

1919

20-
st.write("after", st.session_state)
21-
2220

2321
# Slider Component
2422
slider_value = slider(default_value=[20], min_value=0, max_value=100, step=2, label="Select a Range", key="slider1")
@@ -49,9 +47,21 @@
4947
trigger_btn = ui.button(text="Trigger Button", key="trigger_btn")
5048
ui.alert_dialog(show=trigger_btn, title="Alert Dialog", description="This is an alert dialog", confirm_label="OK", cancel_label="Cancel", key="alert_dialog1")
5149

52-
with ui.element("card", key="base_ele") as card:
53-
with ui.element("card", key="base_ele2") as card2:
54-
card2.add_child(ui.element("input", key="nst2_input", label="Value"))
55-
card2.add_child(ui.element("button", key="nst2_btn", text="Nest Submmit", variant="outline"))
56-
card.add_child(card2)
57-
card.add_child(ui.element("button", key="nst_btn", text="Hello World"))
50+
st.header("Nest Element 1")
51+
52+
with ui.card(key="base_ele_card_l1"):
53+
with ui.card(key="base_ele_card_l2"):
54+
# with ui.element("card", key="base_ele1") as card2:
55+
ui.element("input", key="nst2_input", label="Value")
56+
ui.element("button", key="nst2_btn", text="Nest Submmit", variant="outline")
57+
58+
ui.element("button", key="nst_btn", text="Hello World")
59+
60+
st.header("Nest Element 2")
61+
card = ui.element("card", key="base_ele_2")
62+
card2 = ui.element("card", key="base_ele2_2")
63+
card2.add_child(ui.element("input", key="nst2_input_2", label="Value"))
64+
card2.add_child(ui.element("button", key="nst2_btn_2", text="Nest Submmit", variant="outline"))
65+
card.add_child(card2)
66+
card.add_child(ui.element("button", key="nst_btn_2", text="Hello World"))
67+
card.render()

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "streamlit-shadcn-ui"
7-
version = "0.1.11"
7+
version = "0.1.12"
88
readme = "README.md"
99
keywords = ["streamlit", "shadcn", "ui", "components"]
1010
description = "Using shadcn components in Streamlit"

streamlit_shadcn_ui/components/packages/frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"clsx": "^2.0.0",
2828
"date-fns": "^2.30.0",
2929
"lucide-react": "^0.292.0",
30+
"nanoid": "^5.0.3",
3031
"react": "^18.2.0",
3132
"react-day-picker": "^8.9.1",
3233
"react-dom": "^18.2.0",

streamlit_shadcn_ui/components/packages/frontend/src/components/streamlit/button.tsx

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,30 @@
11
import { Button, ButtonProps } from "@/components/ui/button";
2+
import { StComponentValue } from "@/interfaces";
23
import { forwardRef, useCallback, useEffect, useRef } from "react";
34
import { Streamlit } from "streamlit-component-lib";
5+
import { nanoid } from 'nanoid'
6+
47

58
interface StButtonProps {
69
text?: string;
710
variant?: ButtonProps['variant'];
811
disabled?: boolean;
912
className?: string;
1013
}
14+
15+
type StButtonValue = StComponentValue<boolean, undefined>;
16+
17+
1118
export const StButton = forwardRef<HTMLButtonElement, StButtonProps>((props: StButtonProps, ref) => {
1219
const { text, disabled, variant, className } = props;
1320

1421
const queue = useRef<(() => void)[]>([]);
1522

1623
const clickHandler = useCallback(() => {
1724
queue.current = [];
18-
Streamlit.setComponentValue(true);
19-
queue.current.push(() => {})
20-
queue.current.push(() => {
21-
Streamlit.setComponentValue(false);
22-
})
23-
}, [])
25+
Streamlit.setComponentValue({ value: true, event_id: nanoid() } as StButtonValue);
2426

25-
useEffect(() => {
26-
if (queue.current.length > 0) {
27-
const fn = queue.current.shift();
28-
fn?.();
29-
}
30-
})
27+
}, [])
3128

3229
return (
3330
<Button className={className} variant={variant} ref={ref} disabled={disabled} onClick={clickHandler}>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export type StComponentValue<T = any, S = undefined> = {
2+
st_key: string;
3+
event_id: string;
4+
value: T;
5+
} & (S extends undefined ? Record<string, never> : S);
6+
7+
export interface StComponentProps {
8+
st_key: string;
9+
}

streamlit_shadcn_ui/components/yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4668,6 +4668,11 @@ nanoid@^3.3.6:
46684668
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
46694669
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
46704670

4671+
nanoid@^5.0.3:
4672+
version "5.0.3"
4673+
resolved "https://registry.npmjs.org/nanoid/-/nanoid-5.0.3.tgz#6c97f53d793a7a1de6a38ebb46f50f95bf9793c7"
4674+
integrity sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA==
4675+
46714676
natural-compare@^1.4.0:
46724677
version "1.4.0"
46734678
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .element import element, init_default_state

streamlit_shadcn_ui/py_components/base/context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
def get_context():
44
if (st.session_state.get("_shadcn_ui_context", None) is None):
55
st.session_state["_shadcn_ui_context"] = {
6-
"in_render": False,
6+
"current_element": None,
77
}
88
ctx = st.session_state.get("_shadcn_ui_context")
99
return ctx

streamlit_shadcn_ui/py_components/base/element.py

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,69 @@
55
__RELEASE = True
66
component_func = declare_component("element_renderer", release=__RELEASE)
77

8+
def init_default_state(key: str = None, default_value: Any = None, **component_state):
9+
return {
10+
"st_key": key,
11+
"value": default_value,
12+
"event_id": f"{key}_init",
13+
**component_state,
14+
}
15+
816
class UIElement:
9-
def __init__(self, name: str, props: Optional[Dict[str, Any]] = None, key: Optional[str] = None):
17+
def __init__(self, name: str, props: Optional[Dict[str, Any]] = None, key: Optional[str] = None, default_value: Any = None, default_component_state: Any = None):
1018
self.key = key
1119
self.props = props if props is not None else {}
1220
self.name = name
1321
self.children: List['UIElement'] = []
14-
self.is_root = False
22+
self.parent = None
23+
default_state = init_default_state(key, default_value, default_component_state)
24+
self.state = default_state
25+
self.default_state = default_state
26+
ctx = get_context()
27+
current_element = ctx.get("current_element")
28+
if current_element:
29+
current_element.add_child(self)
30+
self.parent = current_element
1531

1632
def render_tree(self, tree: Dict[str, Any]) -> Any:
17-
return component_func(comp="element_renderer", props={"tree": tree}, key=self.key, default=None)
33+
self.state = component_func(comp="element_renderer", props={"tree": tree}, key=self.key, default=self.default_state)
34+
return self.state
1835

19-
def render(self) -> Dict[str, Any]:
36+
def render_props(self) -> Dict[str, Any]:
2037
return {
2138
"name": self.name,
2239
"props": self.props,
23-
"children": [child.render() for child in self.children]
40+
"children": [child.render_props() for child in self.children]
2441
}
42+
43+
def render(self) -> Any:
44+
return self.render_tree(self.render_props())
2545

2646
def __enter__(self) -> 'UIElement':
2747
ctx = get_context()
28-
if not ctx["in_render"]:
29-
ctx["in_render"] = True
30-
self.is_root = True
48+
ctx["current_element"] = self
3149
return self
3250

3351
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
34-
if self.is_root:
35-
get_context()["in_render"] = False
36-
self.render_tree(self.render())
52+
ctx = get_context()
53+
if self.parent is None:
54+
self.render_tree(self.render_props())
55+
56+
# Reset the current element to this element's parent
57+
ctx["current_element"] = self.parent
3758

3859
def add_child(self, child: 'UIElement') -> None:
60+
child.parent = self
3961
self.children.append(child)
62+
63+
def __getattr__(self, item: str) -> Any:
64+
if item in self.props:
65+
return self.props[item]
66+
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'")
67+
68+
@property
69+
def value(self) -> Any:
70+
return self.state["value"]
4071

41-
def element(name: str, key: Optional[str] = None, **props) -> UIElement:
42-
return UIElement(name=name, props=props, key=key)
72+
def element(name: str, key: Optional[str] = None , default_value: Any = None, default_component_state: Any = None, **props) -> UIElement:
73+
return UIElement(name=name, props=props, key=key, default_value=default_value, default_component_state=default_component_state)
Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
1+
from streamlit_shadcn_ui.py_components.base.element import element, init_default_state
2+
from streamlit_shadcn_ui.py_components.utils.callback import register_callback
3+
from streamlit_shadcn_ui.py_components.utils.session import init_session
14
from .utils import declare_component
5+
import streamlit as st
6+
27
_RELEASE = True
38

49
_component_func = declare_component("button", release=_RELEASE)
510

611
# variant "default" | "destructive" | "outline" | "secondary" | "ghost" | "link"
712
def button(text: str, variant: str = "default", class_name: str = None, key = None):
813
props = {"text": text, "variant": variant, "className": class_name}
9-
clicked = _component_func(comp="button", props=props, key=key, default=False)
14+
default_state = init_default_state(key, default_value=False, default_component_state={})
15+
non_resettable_state_key = f"{key}__non_resettable_state"
16+
init_session(key, default_state)
17+
init_session(non_resettable_state_key, default_value=default_state)
18+
19+
if (st.session_state[non_resettable_state_key]['event_id'] != st.session_state[key]['event_id']):
20+
st.session_state[non_resettable_state_key]['value'] = st.session_state[key]['value']
21+
st.session_state[non_resettable_state_key]['event_id'] = st.session_state[key]['event_id']
22+
23+
else:
24+
st.session_state[non_resettable_state_key]['value'] = False
25+
26+
_component_func(comp="button", props=props, key=key, default=default_state)
27+
clicked = st.session_state[non_resettable_state_key]['value']
1028
return clicked

0 commit comments

Comments
 (0)