Skip to content

Commit

Permalink
Clean up docs a bit more. .. image assets aren't working though
Browse files Browse the repository at this point in the history
- And I don't know why not
  • Loading branch information
mofojed committed Mar 28, 2024
1 parent 022d173 commit 18c42fc
Show file tree
Hide file tree
Showing 83 changed files with 803 additions and 39 deletions.
26 changes: 25 additions & 1 deletion plugins/ui/sphinx-docs/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
# deephaven.ui documentation

deephaven.ui is a plugin for Deephaven that allows for programmatic layouts and callbacks. It uses a React-like approach to building components and rendering them in the UI, allowing for creating reactive components that can be re-used and composed together, as well as reacting to user input from the UI.

## Getting Started

First, follow the guide to [Start a Deephaven Server](https://deephaven.io/core/docs/tutorials/pip-install/). Then, install the deephaven.ui plugin using `pip install`:

```sh
pip install deephaven-ui
```

You'll then need to restart your Deephaven server to load the plugin.

Once you have the Deephaven up, you can assign deephaven.ui components to variables and display them in the UI. For example, to display a simple button:

```python
from deephaven import ui

my_button = ui.button("Click Me!", on_press=lambda e: print(f"Button was clicked! {e}"))
```

## Contents

```{toctree}
:maxdepth: 3
:caption: Contents
:glob:
tutorials/*
components/README
hooks/README
architecture
```
Binary file modified plugins/ui/sphinx-docs/_build/doctrees/README.doctree
Binary file not shown.
Binary file not shown.
Binary file modified plugins/ui/sphinx-docs/_build/doctrees/components/README.doctree
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified plugins/ui/sphinx-docs/_build/doctrees/environment.pickle
Binary file not shown.
Binary file not shown.
32 changes: 31 additions & 1 deletion plugins/ui/sphinx-docs/_build/markdown/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
# deephaven.ui documentation

# Contents
deephaven.ui is a plugin for Deephaven that allows for programmatic layouts and callbacks. It uses a React-like approach to building components and rendering them in the UI, allowing for creating reactive components that can be re-used and composed together, as well as reacting to user input from the UI.

## Getting Started

First, follow the guide to [Start a Deephaven Server](https://deephaven.io/core/docs/tutorials/pip-install/). Then, install the deephaven.ui plugin using `pip install`:

```sh
pip install deephaven-ui
```

You’ll then need to restart your Deephaven server to load the plugin.

Once you have the Deephaven up, you can assign deephaven.ui components to variables and display them in the UI. For example, to display a simple button:

```python
from deephaven import ui

my_button = ui.button("Click Me!", on_press=lambda e: print(f"Button was clicked! {e}"))
```

## Contents

* [Quick start](tutorials/quick-start.md)
* [Handling events](tutorials/quick-start.md#handling-events)
* [Creating components](tutorials/quick-start.md#creating-components)
* [Using state](tutorials/quick-start.md#using-state)
* [Sharing state](tutorials/quick-start.md#sharing-state)
* [Components](components/README.md)
* [Buttons](components/README.md#buttons)
* [`action_button()`](components/buttons/action_button.md)
Expand All @@ -21,6 +46,7 @@
* [`content()`](components/content/content.md)
* [`contextual_help()`](components/content/contextual_help.md)
* [`flex()`](components/content/flex.md)
* [`flex()`](components/content/fragment.md)
* [`grid()`](components/content/grid.md)
* [`heading()`](components/content/heading.md)
* [`icon()`](components/content/icon.md)
Expand Down Expand Up @@ -55,3 +81,7 @@
* [`use_execution_context()`](hooks/advanced/use_execution_context.md)
* [`use_liveness_scope()`](hooks/advanced/use_liveness_scope.md)
* [`use_render_queue()`](hooks/advanced/use_render_queue.md)
* [Architecture](architecture.md)
* [Rendering](architecture.md#rendering)
* [Communication/Callbacks](architecture.md#communication-callbacks)
* [Communication Layers](architecture.md#communication-layers)
107 changes: 107 additions & 0 deletions plugins/ui/sphinx-docs/_build/markdown/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Architecture

Detailed architecture of the deephaven.ui plugin.

## Rendering

When you call a function decorated by `@ui.component`, it will return an `Element` object that has a reference to the function it is decorated; that is to say, the function does *not* get run immediately. The function is only run when the `Element` is rendered by the client, and the result is sent back to the client. This allows the `@ui.component` decorator to execute the function with the appropriate rendering context. The client must also set the initial state before rendering, allowing the client to persist the state and re-render in the future.

Let’s say we execute the following, where a table is filtered based on the value of a text input:

```python
from deephaven import ui


@ui.component
def text_filter_table(source, column, initial_value=""):
value, set_value = ui.use_state(initial_value)
ti = ui.text_field(value=value, on_change=set_value)
tt = source.where(f"{column}=`{value}`")
return [ti, tt]


# This will render two panels, one filtering the table by Sym, and the other by Exchange
@ui.component
def double_text_filter_table(source):
tft1 = text_filter_table(source, "sym")
tft2 = text_filter_table(source, "exchange")
return ui.panel(tft1, title="Sym"), ui.panel(tft2, title="Exchange")


import deephaven.plot.express as dx

_stocks = dx.data.stocks()

tft = double_text_filter_table(_stocks)
```

Which should result in a UI like this:

![Double Text Filter Tables](assets/double-tft.png)

How does that look when the notebook is executed? When does each code block execute?

```mermaid
sequenceDiagram
participant U as User
participant W as Web UI
participant UIP as UI Plugin
participant C as Core
participant SP as Server Plugin
U->>W: Run notebook
W->>C: Execute code
C->>SP: is_type(object)
SP-->>C: Matching plugin
C-->>W: VariableChanges(added=[t, tft])
W->>UIP: Open tft
UIP->>C: Export tft
C-->>UIP: tft (Element)
Note over UIP: UI knows about object tft<br/>double_text_filter_table not executed yet
UIP->>SP: Render tft (initialState)
SP->>SP: Run double_text_filter_table
Note over SP: double_text_filter_table executes, running text_filter_table twice
SP-->>UIP: Result (document=[panel(tft1), pane(tft2)], exported_objects=[tft1, tft2])
UIP-->>W: Display Result
U->>UIP: Change text input 1
UIP->>SP: Change state
SP->>SP: Run double_text_filter_table
Note over SP: double_text_filter_table executes, text_filter_table only <br/>runs once for the one changed input<br/>only exports the new table, as client already has previous tables
SP-->>UIP: Result (document=[panel(tft1'), panel(tft2)], state={}, exported_objects=[tft1'])
UIP-->>W: Display Result
```

## Communication/Callbacks

When the document is first rendered, it will pass the entire document to the client. When the client makes a callback, it needs to send a message to the server indicating which callback it wants to trigger, and with which parameters. For this, we use [JSON-RPC](https://www.jsonrpc.org/specification). When the client opens the message stream to the server, the communication looks like:

```mermaid
sequenceDiagram
participant UIP as UI Plugin
participant SP as Server Plugin
Note over UIP, SP: Uses JSON-RPC
UIP->>SP: setState(initialState)
SP-->>UIP: documentUpdated(Document, State)
loop Callback
UIP->>SP: foo(params)
SP-->>UIP: foo result
SP->>UIP: documentUpdated(Document, State)
Note over UIP: Client can store State to restore the same state later
end
```

## Communication Layers

A component that is created on the server side runs through a few steps before it is rendered on the client side:

1. [Element](../src/deephaven/ui/elements/Element.py) - The basis for all UI components. Generally a [FunctionElement](../src/deephaven/ui/elements/FunctionElement.py) created by a script using the [@ui.component](../src/deephaven/ui/components/make_component.py) decorator, and does not run the function until it is rendered. The result can change depending on the context that it is rendered in (e.g. what “state” is set).
2. [ElementMessageStream](../src/deephaven/ui/object_types/ElementMessageStream.py) - The `ElementMessageStream` is responsible for rendering one instance of an element in a specific rendering context and handling the server-client communication. The element is rendered to create a [RenderedNode](../src/deephaven/ui/renderer/RenderedNode.py), which is an immutable representation of a rendered document. The `RenderedNode` is then encoded into JSON using [NodeEncoder](../src/deephaven/ui/renderer/NodeEncoder.py), which pulls out all the non-serializable objects (such as Tables) and maps them to exported objects, and all the callables to be mapped to commands that can be accepted by JSON-RPC. This is the final representation of the document that is sent to the client, and ultimately handled by the `WidgetHandler`.
3. [DashboardPlugin](../src/js/src/DashboardPlugin.tsx) - Client side `DashboardPlugin` that listens for when a widget of type `Element` is opened, and manage the `WidgetHandler` instances that are created for each widget.
4. [WidgetHandler]() - Uses JSON-RPC communication with an `ElementMessageStream` instance to load the initial rendered document and associated exported objects. Listens for any changes and updates the document accordingly.
5. [DocumentHandler]() - Handles the root of a rendered document, laying out the appropriate panels or dashboard specified.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Display content in the UI with content components:
* [`content()`](content/content.md)
* [`contextual_help()`](content/contextual_help.md)
* [`flex()`](content/flex.md)
* [`flex()`](content/fragment.md)
* [`grid()`](content/grid.md)
* [`heading()`](content/heading.md)
* [`icon()`](content/icon.md)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
### deephaven.ui.action_button(\*children: Any, type: Literal['button', 'submit', 'reset'] = 'button', on_press: Callable[[PressEvent], None] | None = None, on_press_start: Callable[[PressEvent], None] | None = None, on_press_end: Callable[[PressEvent], None] | None = None, on_press_up: Callable[[PressEvent], None] | None = None, on_press_change: Callable[[bool], None] | None = None, on_focus: Callable[[FocusEvent], None] | None = None, on_blur: Callable[[FocusEvent], None] | None = None, on_focus_change: Callable[[bool], None] | None = None, on_key_down: Callable[[KeyboardEvent], None] | None = None, on_key_up: Callable[[KeyboardEvent], None] | None = None, auto_focus: bool | None = None, is_disabled: bool | None = None, is_quiet: bool | None = None, static_color: Literal['white', 'black'] | None = None, flex: str | int | float | bool | None = None, flex_grow: int | float | None = None, flex_shrink: int | float | None = None, flex_basis: str | int | float | None = None, align_self: Literal['auto', 'normal', 'start', 'end', 'center', 'flex-start', 'flex-end', 'self-start', 'self-end', 'stretch'] | None = None, justify_self: Literal['auto', 'normal', 'start', 'end', 'flex-start', 'flex-end', 'self-start', 'self-end', 'center', 'left', 'right', 'stretch'] | None = None, order: int | float | None = None, grid_area: str | None = None, grid_row: str | None = None, grid_row_start: str | None = None, grid_row_end: str | None = None, grid_column: str | None = None, grid_column_start: str | None = None, grid_column_end: str | None = None, margin: str | int | float | None = None, margin_top: str | int | float | None = None, margin_bottom: str | int | float | None = None, margin_start: str | int | float | None = None, margin_end: str | int | float | None = None, margin_x: str | int | float | None = None, margin_y: str | int | float | None = None, width: str | int | float | None = None, height: str | int | float | None = None, min_width: str | int | float | None = None, min_height: str | int | float | None = None, max_width: str | int | float | None = None, max_height: str | int | float | None = None, position: Literal['static', 'relative', 'absolute', 'fixed', 'sticky'] | None = None, top: str | int | float | None = None, bottom: str | int | float | None = None, start: str | int | float | None = None, end: str | int | float | None = None, left: str | int | float | None = None, right: str | int | float | None = None, z_index: int | float | None = None, is_hidden: bool | None = None, id: str | None = None, exclude_from_tab_order: bool | None = None, aria_expanded: Literal['true', 'false'] | bool | None = None, aria_haspopup: Literal['true', 'false'] | bool | Literal['menu', 'listbox', 'tree', 'grid', 'dialog'] | None = None, aria_controls: str | None = None, aria_label: str | None = None, aria_labelledby: str | None = None, aria_describedby: str | None = None, aria_pressed: Literal['true', 'false'] | bool | Literal['mixed'] | None = None, aria_details: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: Dict[str, Any] | None = None)

ActionButtons allow users to perform an action. They’re used for similar, task-based options within a workflow, and are ideal for interfaces where buttons aren’t meant to draw a lot of attention.
Python implementation for the Adobe React Spectrum ActionButton component: [https://react-spectrum.adobe.com/react-spectrum/ActionButton.html](https://react-spectrum.adobe.com/react-spectrum/ActionButton.html)
Based on Spectrum’s [ActionButton](https://react-spectrum.adobe.com/react-spectrum/ActionButton.html) component.
An action_button allow users to perform an action. They’re used for similar, task-based options within a workflow, and are ideal for interfaces where buttons aren’t meant to draw a lot of attention.

Usage:

```python
my_button = ui.action_button("Click me!", on_press=lambda: print("Button clicked!"))
```

* **Parameters:**
* **\*children** – The content to display inside the button.
Expand Down Expand Up @@ -68,3 +74,5 @@ Python implementation for the Adobe React Spectrum ActionButton component: [http
* **aria_details** – The details for the element.
* **UNSAFE_class_name** – A CSS class to apply to the element.
* **UNSAFE_style** – A CSS style to apply to the element.
* **Returns:**
The action button element.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Buttons allow users to perform an action or to navigate to another page. They ha
Usage:

```python
my_button = ui.button("Click me", on_press=lambda: print("Button clicked"))
my_button = ui.button("Click me!", on_press=lambda: print("Button clicked!"))
```

* **Parameters:**
Expand Down Expand Up @@ -79,3 +79,5 @@ my_button = ui.button("Click me", on_press=lambda: print("Button clicked"))
* **aria_details** – The details of the current element.
* **UNSAFE_class_name** – A CSS class to apply to the element.
* **UNSAFE_style** – A CSS style to apply to the element.
* **Returns:**
The button element.
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
### deephaven.ui.button_group(\*children: Any, is_disabled: bool | None = None, orientation: Literal['horizontal', 'vertical'] = 'horizontal', alignment: Literal['auto', 'normal', 'start', 'end', 'center', 'flex-start', 'flex-end', 'self-start', 'self-end', 'stretch'] = 'start', flex: str | int | float | bool | None = None, flex_grow: int | float | None = None, flex_shrink: int | float | None = None, flex_basis: str | int | float | None = None, align_self: Literal['auto', 'normal', 'start', 'end', 'center', 'flex-start', 'flex-end', 'self-start', 'self-end', 'stretch'] | None = None, justify_self: Literal['auto', 'normal', 'start', 'end', 'flex-start', 'flex-end', 'self-start', 'self-end', 'center', 'left', 'right', 'stretch'] | None = None, order: int | float | None = None, grid_area: str | None = None, grid_row: str | None = None, grid_column: str | None = None, grid_row_start: str | None = None, grid_row_end: str | None = None, grid_column_start: str | None = None, grid_column_end: str | None = None, margin: str | int | float | None = None, margin_top: str | int | float | None = None, margin_bottom: str | int | float | None = None, margin_start: str | int | float | None = None, margin_end: str | int | float | None = None, margin_x: str | int | float | None = None, margin_y: str | int | float | None = None, width: str | int | float | None = None, height: str | int | float | None = None, min_width: str | int | float | None = None, min_height: str | int | float | None = None, max_width: str | int | float | None = None, max_height: str | int | float | None = None, position: Literal['static', 'relative', 'absolute', 'fixed', 'sticky'] | None = None, top: str | int | float | None = None, bottom: str | int | float | None = None, left: str | int | float | None = None, right: str | int | float | None = None, start: str | int | float | None = None, end: str | int | float | None = None, z_index: int | float | None = None, is_hidden: bool | None = None, id: str | None = None, UNSAFE_class_name: str | None = None, UNSAFE_style: Dict[str, Any] | None = None)

Based on Spectrum’s [ButtonGroup](https://react-spectrum.adobe.com/react-spectrum/ButtonGroup.html) component.
A button group is a grouping of button whose actions are related to each other.

Usage:

```python
my_button_group = ui.button_group(
ui.button("Button 1"),
ui.button("Button 2"),
ui.button("Button 3"),
orientation="vertical",
alignment="center",
)
```

* **Parameters:**
* **\*children** – The children of the button group.
* **is_disabled** – Whether the button group is disabled.
Expand Down Expand Up @@ -40,3 +53,5 @@ A button group is a grouping of button whose actions are related to each other.
* **id** – The unique identifier of the element.
* **UNSAFE_class_name** – Set the CSS className for the element. Only use as a last resort. Use style props instead.
* **UNSAFE_style** – Set the inline style for the element. Only use as a last resort. Use style props instead.
* **Returns:**
The button group element.
15 changes: 13 additions & 2 deletions plugins/ui/sphinx-docs/_build/markdown/components/content/flex.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
### deephaven.ui.flex(\*children: Any, direction: Literal['row', 'column', 'row-reverse', 'column-reverse'] | None = None, wrap: Literal['wrap', 'nowrap', 'wrap-reverse'] | None = None, justify_content: Literal['start', 'end', 'center', 'left', 'right', 'space-between', 'space-around', 'space-evenly', 'stretch', 'baseline', 'first baseline', 'last baseline', 'safe center', 'unsafe center'] | None = None, align_content: Literal['start', 'end', 'center', 'space-between', 'space-around', 'space-evenly', 'stretch', 'baseline', 'first baseline', 'last baseline', 'safe center', 'unsafe center'] | None = None, align_items: Literal['start', 'end', 'center', 'stretch', 'self-start', 'self-end', 'baseline', 'first baseline', 'last baseline', 'safe center', 'unsafe center'] | None = None, gap: str | int | float | None = 'size-100', column_gap: str | int | float | None = None, row_gap: str | int | float | None = None, \*\*props: Any)

Python implementation for the Adobe React Spectrum Flex component.
[https://react-spectrum.adobe.com/react-spectrum/Flex.html](https://react-spectrum.adobe.com/react-spectrum/Flex.html)
Based on Spectrum’s [Flex](https://react-spectrum.adobe.com/react-spectrum/Flex.html) component.
Displays children in a flex layout.

Usage:

```python
my_flex = ui.flex(
ui.text("Hello"),
ui.text("World"),
direction="column",
gap="size-200",
)
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
### deephaven.ui.flex(\*children: Any, direction: Literal['row', 'column', 'row-reverse', 'column-reverse'] | None = None, wrap: Literal['wrap', 'nowrap', 'wrap-reverse'] | None = None, justify_content: Literal['start', 'end', 'center', 'left', 'right', 'space-between', 'space-around', 'space-evenly', 'stretch', 'baseline', 'first baseline', 'last baseline', 'safe center', 'unsafe center'] | None = None, align_content: Literal['start', 'end', 'center', 'space-between', 'space-around', 'space-evenly', 'stretch', 'baseline', 'first baseline', 'last baseline', 'safe center', 'unsafe center'] | None = None, align_items: Literal['start', 'end', 'center', 'stretch', 'self-start', 'self-end', 'baseline', 'first baseline', 'last baseline', 'safe center', 'unsafe center'] | None = None, gap: str | int | float | None = 'size-100', column_gap: str | int | float | None = None, row_gap: str | int | float | None = None, \*\*props: Any)

Based on Spectrum’s [Flex](https://react-spectrum.adobe.com/react-spectrum/Flex.html) component.
Displays children in a flex layout.

Usage:

```python
my_flex = ui.flex(
ui.text("Hello"),
ui.text("World"),
direction="column",
gap="size-200",
)
```
Loading

0 comments on commit 18c42fc

Please sign in to comment.