-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5c4ed42
commit e53b322
Showing
16 changed files
with
560 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
# Toast | ||
|
||
Toasts display brief, temporary notifications of actions, errors, or other events in an application. | ||
|
||
## Example | ||
|
||
```python | ||
from deephaven import ui | ||
|
||
btn = ui.button( | ||
"Show toast", | ||
on_press=lambda: ui.toast("Toast is done!"), | ||
variant="primary", | ||
) | ||
``` | ||
|
||
## Content | ||
|
||
Toasts are triggered using the method `ui.toast`. Toasts use `variant` to specify the following styles: `neutral`, `positive`, `negative`, and `info`. Toast will default to `neutral` if `variant` is omitted. | ||
|
||
Toasts are shown according to the order they are added, with the most recent toast appearing at the bottom of the stack. Please use Toasts sparingly. | ||
|
||
```python | ||
from deephaven import ui | ||
|
||
toasts = ui.button_group( | ||
ui.button( | ||
"Show neutral toast", | ||
on_press=lambda: ui.toast("Toast available", variant="neutral"), | ||
variant="secondary", | ||
), | ||
ui.button( | ||
"Show positive toast", | ||
on_press=lambda: ui.toast("Toast is done!", variant="positive"), | ||
variant="primary", | ||
), | ||
ui.button( | ||
"Show negative toast", | ||
on_press=lambda: ui.toast("Toast is burned!", variant="negative"), | ||
variant="negative", | ||
), | ||
ui.button( | ||
"Show info toast", | ||
on_press=lambda: ui.toast("Toasting...", variant="info"), | ||
variant="accent", | ||
style="outline", | ||
), | ||
) | ||
``` | ||
|
||
## Events | ||
|
||
Toasts can include an optional action by specifying the `action_label` and `on_action` options when queueing a toast. In addition, the `on_close` event is triggered when the toast is dismissed. The `should_close_on_action` option automatically closes the toast when an action is performed. | ||
|
||
```python | ||
from deephaven import ui | ||
|
||
|
||
btn = ui.button( | ||
"Show toast", | ||
on_press=lambda: ui.toast( | ||
"An update is available", | ||
action_label="Update", | ||
on_action=lambda: print("Updating!"), | ||
should_close_on_action=True, | ||
on_close=lambda: print("Closed"), | ||
variant="positive", | ||
), | ||
variant="primary", | ||
) | ||
``` | ||
|
||
## Auto-dismiss | ||
|
||
Toasts support a `timeout` option to automatically hide them after a certain amount of time. For accessibility, toasts have a minimum `timeout` of 5 seconds, and actionable toasts will not auto dismiss. In addition, timers will pause when the user focuses or hovers over a toast. | ||
|
||
Be sure only to automatically dismiss toasts when the information is not important, or may be found elsewhere. Some users may require additional time to read a toast message, and screen zoom users may miss toasts entirely. | ||
|
||
```python | ||
from deephaven import ui | ||
|
||
|
||
btn = ui.button( | ||
"Show toast", | ||
on_press=lambda: ui.toast("Toast is done!", timeout=5000, variant="positive"), | ||
variant="primary", | ||
) | ||
``` | ||
|
||
## Show toast on mount | ||
|
||
This example shows how to display a toast when a component mounts. | ||
|
||
```python | ||
from deephaven import ui | ||
|
||
|
||
@ui.component | ||
def ui_toast_on_mount(): | ||
ui.toast("Mounted.", variant="info") | ||
return ui.heading("Toast was shown on mount.") | ||
|
||
|
||
my_mount_example = ui_toast_on_mount() | ||
``` | ||
|
||
## Toast from table example | ||
|
||
This example shows how to create a toast from the latest update of a ticking table. It is recommended to auto dismiss these toasts with a `timeout` and to avoid ticking faster than the value of the `timeout`. | ||
|
||
```python | ||
from deephaven import time_table | ||
from deephaven import ui | ||
|
||
_source = time_table("PT5S").update("X = i").tail(5) | ||
|
||
|
||
@ui.component | ||
def toast_table(t): | ||
render_queue = ui.use_render_queue() | ||
|
||
def listener_function(update, is_replay): | ||
data_added = update.added()["X"][0] | ||
render_queue(lambda: ui.toast(f"added {data_added}", timeout=5000)) | ||
|
||
ui.use_table_listener(t, listener_function, [t]) | ||
return t | ||
|
||
|
||
my_toast_table = toast_table(_source) | ||
``` | ||
|
||
# Multi threading example | ||
|
||
This example shows how to use toast with multi threading. | ||
|
||
```python | ||
import threading | ||
from deephaven import read_csv, ui | ||
|
||
|
||
@ui.component | ||
def csv_loader(): | ||
# The render_queue we fetch using the `use_render_queue` hook at the top of the component | ||
render_queue = ui.use_render_queue() | ||
table, set_table = ui.use_state() | ||
error, set_error = ui.use_state() | ||
|
||
def handle_submit(data): | ||
# We define a callable that we'll queue up on our own thread | ||
def load_table(): | ||
try: | ||
# Read the table from the URL | ||
t = read_csv(data["url"]) | ||
|
||
# Define our state updates in another callable. We'll need to call this on the render thread | ||
def update_state(): | ||
set_error(None) | ||
set_table(t) | ||
ui.toast("Table loaded", variant="positive", timeout=5000) | ||
|
||
# Queue up the state update on the render thread | ||
render_queue(update_state) | ||
except Exception as e: | ||
# In case we have any errors, we should show the error to the user. We still need to call this from the render thread, | ||
# so we must assign the exception to a variable and call the render_queue with a callable that will set the error | ||
error_message = e | ||
|
||
def update_state(): | ||
set_table(None) | ||
set_error(error_message) | ||
ui.toast( | ||
f"Unable to load table: {error_message}", | ||
variant="negative", | ||
timeout=5000, | ||
) | ||
|
||
# Queue up the state update on the render thread | ||
render_queue(update_state) | ||
|
||
# Start our own thread loading the table | ||
threading.Thread(target=load_table).start() | ||
|
||
return [ | ||
# Our form displaying input from the user | ||
ui.form( | ||
ui.flex( | ||
ui.text_field( | ||
default_value="https://media.githubusercontent.com/media/deephaven/examples/main/DeNiro/csv/deniro.csv", | ||
label="Enter URL", | ||
label_position="side", | ||
name="url", | ||
flex_grow=1, | ||
), | ||
ui.button(f"Load Table", type="submit"), | ||
gap=10, | ||
), | ||
on_submit=handle_submit, | ||
), | ||
( | ||
# Display a hint if the table is not loaded yet and we don't have an error | ||
ui.illustrated_message( | ||
ui.heading("Enter URL above"), | ||
ui.content("Enter a URL of a CSV above and click 'Load' to load it"), | ||
) | ||
if error is None and table is None | ||
else None | ||
), | ||
# The loaded table. Doesn't show anything if it is not loaded yet | ||
table, | ||
# An error message if there is an error | ||
( | ||
ui.illustrated_message( | ||
ui.icon("vsWarning"), | ||
ui.heading("Error loading table"), | ||
ui.content(f"{error}"), | ||
) | ||
if error != None | ||
else None | ||
), | ||
] | ||
|
||
|
||
my_loader = csv_loader() | ||
``` | ||
|
||
## API Reference | ||
|
||
```{eval-rst} | ||
.. dhautofunction:: deephaven.ui.toast | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
from __future__ import annotations | ||
|
||
import threading | ||
from typing import ( | ||
Any, | ||
Callable, | ||
Dict, | ||
Optional, | ||
Generator, | ||
) | ||
from contextlib import contextmanager | ||
from .NoContextException import NoContextException | ||
|
||
OnEventCallable = Callable[[str, Dict[str, Any]], None] | ||
""" | ||
Callable that is called when an event is queued up. | ||
""" | ||
|
||
_local_data = threading.local() | ||
|
||
|
||
def get_event_context() -> EventContext: | ||
""" | ||
Gets the currently active context, or throws NoContextException if none is set. | ||
Returns: | ||
The active EventContext, or throws if none is present. | ||
""" | ||
try: | ||
return _local_data.event_context | ||
except AttributeError: | ||
raise NoContextException("No context set") | ||
|
||
|
||
def _set_event_context(context: Optional[EventContext]): | ||
""" | ||
Set the current context for the thread. Can be set to None to unset the context for a thread. | ||
""" | ||
if context is None: | ||
del _local_data.event_context | ||
else: | ||
_local_data.event_context = context | ||
|
||
|
||
class EventContext: | ||
_on_send_event: OnEventCallable | ||
""" | ||
The callback to call when sending an event. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
on_send_event: OnEventCallable, | ||
): | ||
""" | ||
Create a new event context. | ||
Args: | ||
on_send_event: The callback to call when sending an event. | ||
""" | ||
|
||
self._on_send_event = on_send_event | ||
|
||
@contextmanager | ||
def open(self) -> Generator[EventContext, None, None]: | ||
""" | ||
Opens this context. | ||
Returns: | ||
A context manager to manage EventContext resources. | ||
""" | ||
old_context: Optional[EventContext] = None | ||
try: | ||
old_context = get_event_context() | ||
except NoContextException: | ||
pass | ||
_set_event_context(self) | ||
yield self | ||
_set_event_context(old_context) | ||
|
||
def send_event(self, name: str, params: Dict[str, Any]) -> None: | ||
""" | ||
Send an event to the client. | ||
Args: | ||
name: The name of the event. | ||
params: The params of the event. | ||
""" | ||
self._on_send_event(name, params) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
class NoContextException(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.