S
Solara•2w ago
Brian

InputDate with buttons

Hi there, I'm trying to replicate the "more advanced" example here: https://solara.dev/documentation/components/lab/input_date#a-more-advanced-example but with the InputDate component (so only one date). I want the menu to have a "Select" button (and maybe also a "Cancel" button) so that the value isn't automatically picked when the user clicks on a date. I'm having issues getting it to work though. Even though I am passing the open_value parameter (because I want to control when the menu closes), the menu closes whenever I click on a date. The example from the docs does work for me, though I don't understand how. (My guess is that it's because that example is using an InputDateRange, but it does not allow the user to ever pick the second date because it's set automatically based on the stay_length...?) Here's my simple example (which does not work):
import solara
import solara.lab
import datetime as dt


@solara.component
def Page():
date_picker_is_open = solara.use_reactive(False)

selected_date = solara.use_reactive(dt.date.today().replace(day=1))
prior_selected_date = solara.use_previous(selected_date.value)

def on_date_selected():
# Do some stuff here
date_picker_is_open.set(False)

def on_cancel_clicked():
selected_date.set(prior_selected_date)

with solara.Column(style="width: 400px;"):
with solara.lab.InputDate(
selected_date,
open_value=date_picker_is_open,
date_picker_type="month",
date_format="%b %Y",
):
with solara.Row(justify="end", style="width: 100%;"):
solara.Button(
label="Cancel",
on_click=on_cancel_clicked,
)
solara.Button(
label="Select",
color="primary",
on_click=on_date_selected,
)

Page()
import solara
import solara.lab
import datetime as dt


@solara.component
def Page():
date_picker_is_open = solara.use_reactive(False)

selected_date = solara.use_reactive(dt.date.today().replace(day=1))
prior_selected_date = solara.use_previous(selected_date.value)

def on_date_selected():
# Do some stuff here
date_picker_is_open.set(False)

def on_cancel_clicked():
selected_date.set(prior_selected_date)

with solara.Column(style="width: 400px;"):
with solara.lab.InputDate(
selected_date,
open_value=date_picker_is_open,
date_picker_type="month",
date_format="%b %Y",
):
with solara.Row(justify="end", style="width: 100%;"):
solara.Button(
label="Cancel",
on_click=on_cancel_clicked,
)
solara.Button(
label="Select",
color="primary",
on_click=on_date_selected,
)

Page()
7 Replies
Brian
BrianOP•2w ago
Looking at the source code, when you pick a date, on_v_model calls a helper function for casting the date value properly: https://github.com/widgetti/solara/blob/master/solara/lab/components/input_date.py#L157 And that function is closing the datepicker: https://github.com/widgetti/solara/blob/master/solara/lab/components/input_date.py#L120 Since those things are buried inside the implementation of InputDate, I can't control them. 😕
Monty Python
Monty Python•2w ago
solara/lab/components/input_date.py line 157
on_v_model=set_date_cast,
on_v_model=set_date_cast,
solara/lab/components/input_date.py line 120
datepicker_is_open.set(False)
datepicker_is_open.set(False)
Brian
BrianOP•2w ago
I'm able to achieve the desired behavior by doing the following -- although I do think this is a bit of a hack.
import solara
import solara.lab
import datetime as dt


@solara.component
def Page():
# reactive variables to capture the "true" state
selected_date = solara.use_reactive(dt.date.today().replace(day=1))
selection_in_progress = solara.use_reactive(False)

# reactive variables that will be used by the existing InputDate component
date_picker_is_open = solara.use_reactive(False)
clicked_date = solara.use_reactive(dt.date.today().replace(day=1))

solara.use_memo(
lambda: clicked_date.set(selected_date.value),
[selected_date.value],
)

def on_open_value(is_open: bool) -> None:

if is_open:
selection_in_progress.set(True)

elif selection_in_progress.value:
date_picker_is_open.set(True)

def on_date_selected():
selected_date.set(clicked_date.value)
selection_in_progress.set(False)
date_picker_is_open.set(False)

def on_cancel():
clicked_date.set(selected_date.value)
selection_in_progress.set(False)
date_picker_is_open.set(False)

with solara.Column(style="width: 400px;"):
with solara.lab.InputDate(
clicked_date,
open_value=date_picker_is_open,
on_open_value=on_open_value,
date_picker_type="month",
date_format="%b %Y",
):
with solara.Row(justify="end", style="width: 100%;"):
solara.Button(
label="Cancel",
on_click=on_cancel,
text=True,
)
solara.Button(
label="Select",
color="primary",
on_click=on_date_selected,
)

Page()
import solara
import solara.lab
import datetime as dt


@solara.component
def Page():
# reactive variables to capture the "true" state
selected_date = solara.use_reactive(dt.date.today().replace(day=1))
selection_in_progress = solara.use_reactive(False)

# reactive variables that will be used by the existing InputDate component
date_picker_is_open = solara.use_reactive(False)
clicked_date = solara.use_reactive(dt.date.today().replace(day=1))

solara.use_memo(
lambda: clicked_date.set(selected_date.value),
[selected_date.value],
)

def on_open_value(is_open: bool) -> None:

if is_open:
selection_in_progress.set(True)

elif selection_in_progress.value:
date_picker_is_open.set(True)

def on_date_selected():
selected_date.set(clicked_date.value)
selection_in_progress.set(False)
date_picker_is_open.set(False)

def on_cancel():
clicked_date.set(selected_date.value)
selection_in_progress.set(False)
date_picker_is_open.set(False)

with solara.Column(style="width: 400px;"):
with solara.lab.InputDate(
clicked_date,
open_value=date_picker_is_open,
on_open_value=on_open_value,
date_picker_type="month",
date_format="%b %Y",
):
with solara.Row(justify="end", style="width: 100%;"):
solara.Button(
label="Cancel",
on_click=on_cancel,
text=True,
)
solara.Button(
label="Select",
color="primary",
on_click=on_date_selected,
)

Page()
Essentially, I'm tracking two additional reactive variables for the "true" state, and re-opening the menu if it was not closed by the user clicking one of the buttons. If there's a cleaner way to do that, I'd be happy to hear it 😅.
iisakkirotko
iisakkirotko•2w ago
Hey @Brian! This looks to me to be the "correct" way to accomplish what you want The one thing that I think could be improved would be the implementation of keeping the date picker open, which you now use two reactive variables for. For the actual value of the picked date doing something like that is pretty much unavoidable, but that shouldn't be the case for the open state of the picker itself
Brian
BrianOP•2w ago
Hi @iisakkirotko, thanks for your reply! I don't see how to use only one reactive variable to keep the date picker open. It seems like the open_value parameter is supposed to let me control the state of the menu, but within the InputDate component, it is directly calling datepicker_is_open.set(False) during the set_date_cast() method, which is called whenever the input variable changes -- i.e., when a user clicks on a date. So what I was seeing is: even though I am trying to control the open/closed state of the menu, it is being closed whenever a date is clicked on. (It seems like that might be a bug? If the user supplies a value for open_value, then set_date_cast() should probably not close the menu when a date is clicked on.)
iisakkirotko
iisakkirotko•2w ago
I'm sorry I should have been clearer in my reply 😅 I don't think doing this with one reactive variable is possible now, because of how use_reactive works when given a callback function. We are looking into it, with a fix to be released in Solara 2.0 (hopefully the coming week)
Brian
BrianOP•2w ago
with a fix to be released in Solara 2.0 (hopefully the coming week)
Exciting! 😄

Did you find this page helpful?