-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: UI Dialog and DialogTrigger Components #953
Conversation
plugins/ui/docs/hooks/use_flag.md
Outdated
flag, set_true, set_false = ui.use_flag() | ||
return [ | ||
ui.text(f"{flag}"), | ||
ui.button("Set True", on_press=set_true), | ||
ui.button("Set False", on_press=set_false), | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main draw for this hook is so we don't need to have lambdas like the following, correct?
flag, set_flag = ui.use_flag(False)
return [
ui.text(f"{flag}"),
ui.button("Set True", on_press=lambda: set_flag(True)),
ui.button("Set False", on_press=lambda: set_flag(False)),
]
Seems convenient for these cases, particularly around something like opening/closing a dialog... My only concern is that for each public hook we publish, careful consideration is required about the API of said hook and it's future usability/maintainability.
With that in mind, I have a couple concerns about this hook as is:
use_flag
is a name that doesn't make it immediately apparent what's up - the nameflag
to me implies a setting of some sort, like reading a global flag that's set. I'd rather name this something likeuse_boolean
- It's returning two setters instead of one (as typical with
use_state
), and it would be easy to accidentally mix up the setters (e.g. I dovalue, set_false, set_true = ui.use_boolean()
, no warnings/errors in the code but my boolean will now be the opposite of what I want). - Another really common use case with a boolean would be to have a
toggle
function, which this does not address. You could have another setter function, but that's exacerbating the previous issue
Doing a search for existing React hooks for useBoolean
, seems there are a couple hooks that have done something similar:
- https://marcoghiani.com/blog/react-custom-hooks-usetoggle-useboolean
- https://dev.to/iamludal/react-custom-hooks-useboolean-3m6c
- https://usehooks-ts.com/react-hook/use-boolean
I don't want to check in this hook as is... let me check something out first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, take a look at this (@dgodinez-dh, @bmingles, @dsmmcken, and @jnumainville):
from deephaven import ui
from deephaven.ui._internal import InitializerFunction, UpdaterFunction
from typing import Callable, cast, overload, Protocol, Tuple
class BooleanCallable(Protocol):
on: Callable[[], None]
off: Callable[[], None]
toggle: Callable[[], None]
def __call__(self, new_value: UpdaterFunction[bool]) -> None: ...
def use_boolean(
initial_value: bool | InitializerFunction[bool] = False,
) -> Tuple[bool, BooleanCallable]:
"""
Hook to add a boolean flag variable to your component. The flag will persist across renders.
This is a convenience hook for when you only need functions to set a flag to True or False.
For more complex state management, use use_state.
Args:
initial_flag: The initial value for the flag.
It can be True or False, but passing a function will treat it as an initializer function.
An initializer function is called with no parameters once on the first render to get the initial value.
After the initial render the argument is ignored.
Default is False.
Returns:
A tuple containing the current value of the flag, a function to set the flag to True, and a function to set the flag to False.
"""
flag, set_flag = ui.use_state(initial_value)
on = ui.use_callback(lambda: set_flag(True), [set_flag])
off = ui.use_callback(lambda: set_flag(False), [set_flag])
toggle = ui.use_callback(lambda: set_flag(lambda old_value: not old_value), [set_flag])
def init_callable():
boolean_callable = cast(BooleanCallable, set_flag)
boolean_callable.on = on
boolean_callable.off = off
boolean_callable.toggle = toggle
return boolean_callable
boolean_callable = ui.use_memo(init_callable, [set_flag, on, off, toggle])
return flag, boolean_callable
# Example usage:
@ui.component
def ui_boolean_example():
value, set_value = use_boolean()
return [
ui.text(f"{value}"),
ui.checkbox("My value", is_selected=value, on_change=set_value),
ui.switch("My value", is_selected=value, on_change=set_value),
ui.button("Set True", on_press=set_value.on),
ui.button("Set False", variant="negative", on_press=set_value.off),
ui.button("Toggle", variant="secondary", on_press=set_value.toggle)
]
my_boolean_example = ui_boolean_example()
Some things I like about this:
- Still returns a value/set_value tuple like a regular use_state, so you can still use it with our controls like checkboxes/switches.
- Can pass convenient
.on
,.off
,.toggle
callables still, for the use case you're using it for
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems good. I really wanted an open / close, but decided to try to generalize it. I also wanted a toggle, but I didn't want to return a third setter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proposed api makes sense to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this. Keeps the api both flexible and clean.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed the new boolean hook. Changes look good to me.
Co-authored-by: Joe <josephnumainville@deephaven.io>
Co-authored-by: margaretkennedy <82049573+margaretkennedy@users.noreply.github.com>
d021d43
closes #933
closes #863
Note that there is a bug in the spectrum theme that causes the underlay to render as transparent. It is fixed here:
deephaven/web-client-ui#2267
The underlay may render incorrectly depending on your version of
web-client-ui
Because of this bug, we are waiting for a new version before adding a render test for dialog. See #952