Skip to content

Commit b91abb4

Browse files
Example apps for custom components v2 (#1360)
* Example apps for custom components v2 * Add Tailwind example * Self-host Tailwind script
1 parent 8f239ef commit b91abb4

File tree

31 files changed

+12451
-25
lines changed

31 files changed

+12451
-25
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
streamlit>=1.50.0
1+
streamlit-nightly
22
webvtt-py
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import streamlit as st
2+
3+
JS = """
4+
export default function(component) {
5+
const { data, setTriggerValue, parentElement } = component;
6+
parentElement.innerHTML = data;
7+
const links = parentElement.querySelectorAll('a');
8+
9+
links.forEach((link) => {
10+
link.onclick = (e) => {
11+
setTriggerValue('clicked', link.getAttribute('data-link'));
12+
};
13+
});
14+
}
15+
"""
16+
17+
CSS = """
18+
a {
19+
color: var(--st-link-color);
20+
}
21+
"""
22+
23+
my_component = st.components.v2.component(
24+
"inline_links",
25+
css=CSS,
26+
js=JS,
27+
)
28+
29+
paragraph_html = """
30+
<p>This is an example paragraph with inline links. To see the response in
31+
Python, click on the <a href="#" data-link="link_1">first link</a> or
32+
<a href="#" data-link="link_2">second link</a>.</p>
33+
"""
34+
35+
result = my_component(data=paragraph_html, on_clicked_change=lambda: None)
36+
if result.clicked == "link_1":
37+
st.write("You clicked the first link!")
38+
elif result.clicked == "link_2":
39+
st.write("You clicked the second link!")
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import streamlit as st
2+
3+
HTML = """
4+
<p>Click on the triangle, square, or circle to interact with the shapes:</p>
5+
6+
<svg width="400" height="300">
7+
<polygon points="100,50 50,150 150,150" data-shape="triangle"></polygon>
8+
<rect x="200" y="75" width="100" height="100" data-shape="square"></rect>
9+
<circle cx="125" cy="225" r="40" data-shape="circle"></circle>
10+
</svg>
11+
"""
12+
13+
JS = """
14+
export default function(component) {
15+
const { setTriggerValue, parentElement } = component;
16+
const shapes = parentElement.querySelectorAll('[data-shape]');
17+
18+
shapes.forEach((shape) => {
19+
shape.onclick = (e) => {
20+
setTriggerValue('clicked', shape.getAttribute('data-shape'));
21+
};
22+
});
23+
}
24+
"""
25+
26+
CSS = """
27+
polygon, rect, circle {
28+
stroke: var(--st-primary-color);
29+
stroke-width: 2;
30+
fill: transparent;
31+
cursor: pointer;
32+
}
33+
polygon:hover, rect:hover, circle:hover {
34+
fill: var(--st-secondary-background-color);
35+
}
36+
"""
37+
38+
my_component = st.components.v2.component(
39+
"clickable_svg",
40+
html=HTML,
41+
css=CSS,
42+
js=JS,
43+
)
44+
45+
result = my_component(on_clicked_change=lambda: None)
46+
result
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import streamlit as st
2+
3+
JS = """
4+
export default function(component) {
5+
const { setTriggerValue } = component;
6+
const links = document.querySelectorAll('a[href="#"]');
7+
8+
links.forEach((link) => {
9+
link.onclick = (e) => {
10+
setTriggerValue('clicked', link.innerHTML);
11+
};
12+
});
13+
}
14+
"""
15+
16+
my_component = st.components.v2.component(
17+
"inline_links",
18+
js=JS,
19+
)
20+
21+
result = my_component(on_clicked_change=lambda: None)
22+
23+
st.markdown(
24+
"Components aren't [sandboxed](#), so you can write JS that [interacts](#) with the main [document](#)."
25+
)
26+
27+
if result.clicked:
28+
st.write(f"You clicked {result.clicked}!")
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import streamlit as st
2+
3+
with open("python/api-examples-source/tailwind_example.js", "r") as f:
4+
TAILWIND_SCRIPT = f.read()
5+
6+
HTML = """
7+
<button class="bg-blue-500 hover:bg-blue-700 text-white py-1 px-3 rounded">
8+
Click me!
9+
</button>
10+
"""
11+
JS = TAILWIND_SCRIPT + """
12+
export default function(component) {
13+
const { setTriggerValue, parentElement } = component;
14+
const button = parentElement.querySelector('button');
15+
button.onclick = () => {
16+
setTriggerValue('clicked', true);
17+
};
18+
}
19+
"""
20+
my_component = st.components.v2.component(
21+
"my_tailwind_button",
22+
html=HTML,
23+
js=JS,
24+
)
25+
result_1 = my_component(
26+
isolate_styles=False, on_clicked_change=lambda: None, key="one"
27+
)
28+
result_1
29+
30+
result_2 = my_component(
31+
isolate_styles=False, on_clicked_change=lambda: None, key="two"
32+
)
33+
result_2
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import streamlit as st
2+
3+
HTML = """
4+
<label style='padding-right: 1em;' for='txt'>Enter text</label>
5+
<input id='txt' type='text' />
6+
"""
7+
8+
JS = """
9+
export default function(component) {
10+
const { setStateValue, parentElement, data } = component;
11+
12+
const label = parentElement.querySelector('label');
13+
label.innerText = data.label;
14+
15+
const input = parentElement.querySelector('input');
16+
if (input.value !== data.value) {
17+
input.value = data.value ?? '';
18+
};
19+
20+
input.onkeydown = (e) => {
21+
if (e.key === 'Enter') {
22+
setStateValue('value', e.target.value);
23+
}
24+
};
25+
26+
input.onblur = (e) => {
27+
setStateValue('value', e.target.value);
28+
};
29+
}
30+
"""
31+
32+
my_component = st.components.v2.component(
33+
"my_text_input",
34+
html=HTML,
35+
js=JS,
36+
)
37+
38+
39+
def my_component_wrapper(
40+
label, *, default="", key=None, on_change=lambda: None
41+
):
42+
component_state = st.session_state.get(key, {})
43+
value = component_state.get("value", default)
44+
data = {"label": label, "value": value}
45+
result = my_component(
46+
data=data, default={"value": value}, key=key, on_value_change=on_change
47+
)
48+
return result
49+
50+
51+
st.title("My custom component")
52+
53+
if st.button("Hello World"):
54+
st.session_state["my_text_input_instance"]["value"] = "Hello World"
55+
if st.button("Clear text"):
56+
st.session_state["my_text_input_instance"]["value"] = ""
57+
result = my_component_wrapper(
58+
"Enter something",
59+
default="I love Streamlit!",
60+
key="my_text_input_instance",
61+
)
62+
63+
st.write("Result:", result)
64+
st.write("Session state:", st.session_state)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
streamlit>=1.50.0
1+
streamlit-nightly

python/api-examples-source/hello/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ pandas==1.5.3
22
numpy==1.23.5
33
altair==4.2.0
44
pydeck==0.8.0
5-
streamlit>=1.50.0
5+
streamlit-nightly

python/api-examples-source/mpa-hello/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ numpy==1.23.5
33
altair==4.2.0
44
pydeck==0.8.0
55
opencv-python-headless==4.8.1.78
6-
streamlit>=1.50.0
6+
streamlit-nightly
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
streamlit>=1.50.0
1+
streamlit-nightly

0 commit comments

Comments
 (0)