Skip to content

Commit

Permalink
fix state tracking;
Browse files Browse the repository at this point in the history
  • Loading branch information
m-wrzr committed Mar 17, 2024
1 parent f5ee91d commit b98ca54
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 146 deletions.
212 changes: 106 additions & 106 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,69 +79,69 @@ def search_empty_list(_: str):
# searchbox configurations, see __init__.py for details
# will pass all kwargs to the searchbox component
boxes = [
# dict(
# search_function=search_wikipedia_ids,
# placeholder="Search Wikipedia",
# label=search_wikipedia_ids.__name__,
# default="SOME DEFAULT",
# clear_on_submit=False,
# key=search_wikipedia_ids.__name__,
# ),
# dict(
# search_function=search,
# default=None,
# label=search.__name__,
# clear_on_submit=False,
# key=search.__name__,
# ),
# dict(
# search_function=search_rnd_delay,
# default=None,
# clear_on_submit=False,
# label=search_rnd_delay.__name__,
# key=search_rnd_delay.__name__,
# ),
# dict(
# search_function=search_enum_return,
# clear_on_submit=True,
# key=search_enum_return.__name__,
# label=search_enum_return.__name__,
# ),
# dict(
# search_function=search_empty_list,
# clear_on_submit=True,
# key=search_empty_list.__name__,
# label=search_empty_list.__name__,
# ),
# dict(
# search_function=search,
# default_options=["inital", "list", "of", "options"],
# key=f"{search.__name__}_default_options",
# label=f"{search.__name__}_default_options",
# ),
# dict(
# search_function=search,
# default="initial",
# default_options=["inital", "list", "of", "options"],
# key=f"{search.__name__}_default_options_all",
# label=f"{search.__name__}_default_options_all",
# ),
# dict(
# search_function=search,
# default_options=[("inital", "i"), ("list", "l")],
# key=f"{search.__name__}_default_options_tuple",
# label=f"{search.__name__}_default_options_tuple",
# ),
# dict(
# search_function=search,
# key=f"{search.__name__}_rerun_disabled",
# rerun_on_update=False,
# label=f"{search.__name__}_rerun_disabled",
# ),
dict(
search_function=search_wikipedia_ids,
placeholder="Search Wikipedia",
label=search_wikipedia_ids.__name__,
default="SOME DEFAULT",
clear_on_submit=False,
key=search_wikipedia_ids.__name__,
),
dict(
search_function=search,
default=None,
label=search.__name__,
clear_on_submit=False,
key=search.__name__,
),
dict(
search_function=search_rnd_delay,
default=None,
clear_on_submit=False,
label=search_rnd_delay.__name__,
key=search_rnd_delay.__name__,
),
dict(
search_function=search_enum_return,
clear_on_submit=True,
key=search_enum_return.__name__,
label=search_enum_return.__name__,
),
dict(
search_function=search_empty_list,
clear_on_submit=True,
key=search_empty_list.__name__,
label=search_empty_list.__name__,
),
dict(
search_function=search,
default_options=["inital", "list", "of", "options"],
key=f"{search.__name__}_default_options",
label=f"{search.__name__}_default_options",
),
dict(
search_function=search,
default="initial",
default_options=["inital", "list", "of", "options"],
key=f"{search.__name__}_default_options_all",
label=f"{search.__name__}_default_options_all",
),
dict(
search_function=search,
default_options=[("inital", "i"), ("list", "l")],
key=f"{search.__name__}_default_options_tuple",
label=f"{search.__name__}_default_options_tuple",
),
dict(
search_function=search,
key=f"{search.__name__}_rerun_disabled",
rerun_on_update=False,
label=f"{search.__name__}_rerun_disabled",
),
dict(
search_function=search,
key=f"{search.__name__}_keep_value_on_submit",
input_value="selection",
editable_after_submit=True,
label=f"{search.__name__}_keep_value_on_submit",
),
]
Expand All @@ -166,49 +166,49 @@ def search_empty_list(_: str):
st.markdown("---")


# with visual_ref:
# st.multiselect(
# "Multiselect",
# [1, 2, 3, 4, 5],
# default=[1, 2],
# key="multiselect",
# )
# st.selectbox(
# "Selectbox",
# [1, 2, 3],
# index=1,
# key="selectbox",
# )
#
# with form_example:
# with st.form("myform"):
# c1, c2 = st.columns(2)
# with c1:
# sr = st_searchbox(
# search_function=search,
# key=f"{search.__name__}_form",
# )
# with c2:
# st.form_submit_button("load suggestions")
#
# submit = st.form_submit_button("real submit")
# if submit:
# st.write("form submitted")
# st.write(sr)

# with manual_example:
# key = f"{search.__name__}_manual"
#
# if key in st.session_state:
# st.session_state[key]["options_js"] = [
# {"label": f"{st.session_state[key]['search']}_{i}", "value": i}
# for i in range(5)
# ]
# st.session_state[key]["options_py"] = [i for i in range(5)]
#
# manual = st_searchbox(
# search_function=lambda _: [],
# key=key,
# )
#
# st.write(manual)
with visual_ref:
st.multiselect(
"Multiselect",
[1, 2, 3, 4, 5],
default=[1, 2],
key="multiselect",
)
st.selectbox(
"Selectbox",
[1, 2, 3],
index=1,
key="selectbox",
)

with form_example:
with st.form("myform"):
c1, c2 = st.columns(2)
with c1:
sr = st_searchbox(
search_function=search,
key=f"{search.__name__}_form",
)
with c2:
st.form_submit_button("load suggestions")

submit = st.form_submit_button("real submit")
if submit:
st.write("form submitted")
st.write(sr)

with manual_example:
key = f"{search.__name__}_manual"

if key in st.session_state:
st.session_state[key]["options_js"] = [
{"label": f"{st.session_state[key]['search']}_{i}", "value": i}
for i in range(5)
]
st.session_state[key]["options_py"] = [i for i in range(5)]

manual = st_searchbox(
search_function=lambda _: [],
key=key,
)

st.write(manual)
6 changes: 3 additions & 3 deletions streamlit_searchbox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import logging
import os
import time
from typing import Any, Callable, List, Literal
from typing import Any, Callable, List

import streamlit as st
import streamlit.components.v1 as components
Expand Down Expand Up @@ -126,7 +126,7 @@ def st_searchbox(
default_options: List[Any] | None = None,
clear_on_submit: bool = False,
rerun_on_update: bool = True,
input_value: Literal["default", "selection", "append"] = "default",
editable_after_submit: bool = False,
key: str = "searchbox",
**kwargs,
) -> Any:
Expand Down Expand Up @@ -161,7 +161,7 @@ def st_searchbox(
clear_on_submit=clear_on_submit,
placeholder=placeholder,
label=label,
input_value=input_value,
editable_after_submit=editable_after_submit,
# react return state within streamlit session_state
key=st.session_state[key]["key_react"],
**kwargs,
Expand Down
58 changes: 21 additions & 37 deletions streamlit_searchbox/frontend/src/Searchbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface StreamlitReturn {
interaction: "submit" | "search" | "reset";
value: any;
}
const Input = (props: any) => <components.Input {...props} isHidden={false} />;

export function streamlitReturn(interaction: string, value: any): void {
Streamlit.setComponentValue({
Expand Down Expand Up @@ -50,6 +51,7 @@ class Searchbox extends StreamlitComponentBase<State> {
private callbackSearch = (input: string): void => {
this.setState({
inputValue: input,
option: null,
});

streamlitReturn("search", input);
Expand All @@ -73,27 +75,18 @@ class Searchbox extends StreamlitComponentBase<State> {
* @param option
*/
private callbackSubmit(option: Option) {
console.log("callbackSubmit");
console.log("option", this.state.option, option);

if (this.props.args.clear_on_submit) {
this.setState({
menu: false,
option: null,
inputValue: "",
option: null,
});
} else {
console.log("keep value on submit", this.props.args.input_value);
console.log("option", option);

this.setState({
menu: false,
// keep value on submit
option: {
value: option.value,
label: option.label,
},
inputValue: option.label,
option: option,
});
}

Expand All @@ -105,21 +98,12 @@ class Searchbox extends StreamlitComponentBase<State> {
* @returns
*/
public render = (): ReactNode => {
console.log(
"render",
this.state.option,
this.state.inputValue,
this.props.disabled
);
console.log(this.props);

// TODO: broken

// https://react-select.com/advanced
// "Below is an example of replicating the behaviour of the deprecated props from react-select v1, onSelectResetsInput and closeOnSelect"

// describes the issue with resetting input value
// https://github.com/JedWatson/react-select/discussions/4302
// always focus the input field to enable edits
const onFocus = () => {
if (this.props.args.editable_after_submit && this.state.inputValue) {
this.state.inputValue && this.ref.current.select.inputRef.select();
}
};

return (
<div>
Expand All @@ -128,15 +112,15 @@ class Searchbox extends StreamlitComponentBase<State> {
)}

<Select
{...{
onSelectResetsInput: false,
}}
name="stateful-select"
// showing the disabled react-select leads to the component
// not showing the inputValue but just an empty input field
// we therefore need to re-render the component if we want to keep the focus
value={this.state.option}
inputValue={this.state.inputValue}
inputValue={
this.props.args.editable_after_submit
? this.state.inputValue
: undefined
}
isClearable={true}
isSearchable={true}
styles={this.style.select}
Expand All @@ -146,10 +130,14 @@ class Searchbox extends StreamlitComponentBase<State> {
components={{
ClearIndicator: (props) => this.style.clearIndicator(props),
DropdownIndicator: () => this.style.iconDropdown(this.state.menu),
IndicatorSeparator: () => <div></div>,
IndicatorSeparator: () => null,
Input: this.props.args.editable_after_submit
? Input
: components.Input,
}}
// handlers
filterOption={(_, __) => true}
onFocus={() => onFocus()}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onChange={(option: any, a: any) => {
switch (a.action) {
Expand All @@ -161,7 +149,6 @@ class Searchbox extends StreamlitComponentBase<State> {
this.callbackReset();
return;
}
console.log("onChange", a);
}}
onInputChange={(
inputValue: string,
Expand All @@ -171,11 +158,8 @@ class Searchbox extends StreamlitComponentBase<State> {
// ignore menu close or blur/unfocus events
case "input-change":
this.callbackSearch(inputValue);
return inputValue;
return;
}

console.log("onInputChange", inputValue, action, prevInputValue);
return prevInputValue;
}}
onMenuOpen={() => this.setState({ menu: true })}
onMenuClose={() => this.setState({ menu: false })}
Expand Down

0 comments on commit b98ca54

Please sign in to comment.