# Dashboard

Dashboards allow you to create a page layout containing a collection of components. The user can move and resize panels within the dashboard.

## Example

```python
from deephaven import ui

my_dash = ui.dashboard(
    ui.row(
        ui.column(ui.panel("A"), ui.panel("C")),
        ui.column(ui.panel("B"), ui.panel("D")),
    )
)
```

![Dashboard Basic Example](../_assets/dashboard_basic.png)

## Rules

1. Dashboards must have one and only one child, typically a row or column.
2. Height and width of panels are summed to 100% within a row or column.

## Key Components

Four main children make up a dashboard: row, column, stack, and panels.

- **Row**: A container used to group elements horizontally. Each element is placed to the right of the previous one.
- **Column**: A container used to group elements vertically. Each element is placed below the previous one.
- **Stack**: A container used to group elements into a stack of tabs. Each element gets its own tab, with only one element visible at a time.
- **Panel**: A container used to group and label elements.

## Layout Hierarchy

### Top-Level

Your dashboard must start with a row or column, which is the “top” of the layout tree. Columns should go inside rows and rows should go inside columns

Note: Nesting rows within rows or columns within columns will sub-divide the row or column.

### Bottom-Level

Stacks and panels are considered the “bottom” of the layout tree. Once added, the layout in that section is considered complete. For layouts within a panel, see [`tabs`](./tabs.md), [`flex`](./flex.md), [`grid`](./grid.md), [`view`](./view.md) and [nested dashboards](#nested-dashboards).

## Automatic Wrapping

Children are implicitly wrapped when necessary, so the entire layout does not need to be explicitly defined.

End to end example: `dashboard([t1, t2])` would become `dashboard(column(stack(panel(t1)), stack(panel(t2))))`.

Automatic wrapping is applied by the following rules:

1. Dashboard: wrap in row/column if no single node is the default (e.g., `[t1, t2]` as the child to the dashboard would become `row(t1, t2)`).
2. Row/Column:
   - If there are children that are rows/columns, wrap the non-wrapped children with the same element (e.g., `row(col(t1), t2)` becomes `row(col(t1), col(t2))`).
   - If none of the children are wrapped by rows/columns, they are wrapped in stacks (e.g., `row(col(t1), col(t2))` from above becomes `row(col(stack(t1)), col(stack(t2)))`).
3. Stacks: wrap non-panel children in panels (e.g., `row(col(stack(t1)), col(stack(t2)))` becomes `row(col(stack(panel(t1))), col(stack(panel(t2))))`).

## Varying dimensions

Rows can have a specified height, while columns can have a specified width.

Stacks directly within a column can have a height or a width if they are within a row. Setting the other dimension will be ignored.

### Varying row heights

```python
from deephaven import ui

dash_row_heights = ui.dashboard(
    [
        ui.row(ui.stack(ui.panel("A", title="A")), ui.panel("B", title="B"), height=70),
        ui.row(ui.stack(ui.panel("C", title="C")), ui.panel("D", title="D")),
    ]
)
```

### Varying column widths

```python
from deephaven import ui

dash_column_widths = ui.dashboard(
    [
        ui.column(
            ui.stack(ui.panel("A", title="A")), ui.panel("C", title="C"), width=70
        ),
        ui.column(ui.stack(ui.panel("B", title="B")), ui.panel("D", title="D")),
    ]
)
```

### Varying stack widths

```python
from deephaven import ui

dash_stack_widths = ui.dashboard(
    ui.row(ui.stack(ui.panel("A", title="A"), width=70), ui.panel("B", title="B"))
)
```

### Varying stack heights

```python
from deephaven import ui

dash_stack_heights = ui.dashboard(
    ui.column(ui.stack(ui.panel("A", title="A"), height=70), ui.panel("B", title="B"))
)
```

## Layout Examples

### Row split (2x1)

```python
from deephaven import ui

dash_2x1 = ui.dashboard(ui.row(ui.panel("A", title="A"), ui.panel("B", title="B")))
```

### Column split (1x2)

```python
from deephaven import ui

dash_1x2 = ui.dashboard(ui.column(ui.panel("A", title="A"), ui.panel("B", title="B")))
```

### 2x2

```python
from deephaven import ui

dash_2x2 = ui.dashboard(
    ui.row(
        ui.column(ui.panel("A", title="A"), ui.panel("C", title="C")),
        ui.column(ui.panel("B", title="B"), ui.panel("D", title="D")),
    )
)
```

### 3x1

```python
from deephaven import ui

dash_3x1 = ui.dashboard(
    ui.row(ui.panel("A", title="A"), ui.panel("B", title="B"), ui.panel("C", title="C"))
)
```

### Basic stack

```python
from deephaven import ui

dash_stack = ui.dashboard(
    ui.stack(
        ui.panel("A", title="A"), ui.panel("B", title="B"), ui.panel("C", title="C")
    )
)
```

### Stack with an active item index

```python
from deephaven import ui

dash_stack_active = ui.dashboard(
    ui.stack(
        ui.panel("A", title="A"),
        ui.panel("B", title="B"),  # shown as active item
        ui.panel("C", title="C"),
        active_item_index=1,
    )
)
```

### Stack with nested tabs

```python
from deephaven import ui

dash_stack = ui.dashboard(
    ui.stack(
        ui.panel(
            ui.tabs(ui.tab("A1 content", title="A1"), ui.tab("A2 content", title="A2")),
            title="A",
        ),
        ui.panel(
            ui.tabs(ui.tab("B1 content", title="B1"), ui.tab("B2 content", title="B2")),
            title="B",
        ),
    )
)
```

### Stack in a layout

```python
from deephaven import ui

dash_layout_stack = ui.dashboard(
    ui.row(
        ui.stack(
            ui.panel("A", title="A"), ui.panel("B", title="B"), ui.panel("C", title="C")
        ),
        ui.panel("D", title="D"),
        ui.panel("E", title="E"),
    )
)
```

### Holy Grail

```python
from deephaven import ui

dash_holy_grail = ui.dashboard(
    ui.column(
        ui.panel("Header", title="Header"),
        ui.row(
            ui.panel("Left Sidebar", title="Left Sidebar"),
            ui.stack(ui.panel("Main Content", title="Main Content"), width=70),
            ui.panel("Right Sidebar", title="Right Sidebar"),
        ),
        ui.panel("Footer", title="Footer"),
    )
)
```

## Nested Dashboards

Dashboards can be nested inside panels to create complex layouts with isolated drag-and-drop regions. Each nested dashboard creates its own independent layout that users can rearrange without affecting the parent dashboard.

> [!NOTE]
> Nested dashboards will not appear in your Panels list, or in your Shared Dashboards list on Enterprise. They are only accessible through the parent dashboard that contains them.
> To have them appear in your Panels list or Shared Dashboards list, a dashboard must be assigned to a variable at the top level.

### Basic nested dashboard

```python
from deephaven import ui

dash_nested_basic = ui.dashboard(
    ui.row(
        ui.panel(
            ui.dashboard(
                ui.row(
                    ui.panel("Nested A", title="A"),
                    ui.panel("Nested B", title="B"),
                )
            ),
            title="Nested Dashboard",
        ),
        ui.panel("Main Content", title="Main"),
    )
)
```

### Nested dashboard inside a component

Unlike root-level dashboards, nested dashboards can be returned from `@ui.component` functions:

```python
from deephaven import ui


@ui.component
def nested_dashboard_component():
    return ui.panel(
        ui.dashboard(
            ui.row(
                ui.panel("A", title="A"),
                ui.panel("B", title="B"),
            )
        ),
        title="Nested Dashboard",
    )


dash_nested_component = ui.dashboard(
    ui.row(
        nested_dashboard_component(),
        ui.panel("Main Content", title="Main"),
    )
)
```

### Deeply nested dashboards

Dashboards can be nested arbitrarily deep, with each level creating its own isolated layout:

```python
from deephaven import ui

dash_deeply_nested = ui.dashboard(
    ui.row(
        ui.panel(
            ui.dashboard(
                ui.column(
                    ui.panel("Level 1 - A", title="L1-A"),
                    ui.panel(
                        ui.dashboard(
                            ui.row(
                                ui.panel("Level 2 - A", title="L2-A"),
                                ui.panel("Level 2 - B", title="L2-B"),
                            )
                        ),
                        title="Level 2 Dashboard",
                    ),
                )
            ),
            title="Level 1 Dashboard",
        ),
        ui.panel("Root Content", title="Root"),
    )
)
```

### Nested dashboard with state

Nested dashboards can manage their own state within a `@ui.component`:

```python
from deephaven import ui


@ui.component
def stateful_nested_dashboard():
    count, set_count = ui.use_state(0)

    return ui.panel(
        ui.dashboard(
            ui.row(
                ui.panel(
                    ui.button(f"Count: {count}", on_press=lambda: set_count(count + 1)),
                    title="Counter",
                ),
                ui.panel(f"Current count is {count}", title="Display"),
            )
        ),
        title="Stateful Dashboard",
    )


dash_nested_state = ui.dashboard(
    ui.row(
        stateful_nested_dashboard(),
        ui.panel("Main Content", title="Main"),
    )
)
```

> [!NOTE]
> Panels within a nested dashboard can only be dragged within that nested dashboard. Cross-dashboard drag-and-drop is not supported.

## Stateful Example

By hoisting state management to the dashboard component, interacting and sharing data between the child components is much easier to maintain and debug.

### Simple

```python
from deephaven import ui


@ui.component
def layout():
    message, set_message = ui.use_state("Hello world!")

    return ui.row(
        ui.panel(ui.text_field(value=message, on_change=set_message, width="100%")),
        ui.panel(message),
    )


dash_simple_state = ui.dashboard(layout())
```

### Complex

```python
from deephaven import ui, time_table
from deephaven.ui import use_memo, use_state
from deephaven.plot.figure import Figure


def use_wave_input():
    """
    Demonstrating a custom hook.
    Creates an input panel that controls the amplitude, frequency, and phase for a wave
    """
    amplitude, set_amplitude = use_state(1.0)
    frequency, set_frequency = use_state(1.0)
    phase, set_phase = use_state(1.0)

    input_panel = ui.flex(
        ui.slider(
            label="Amplitude",
            default_value=amplitude,
            min_value=-100.0,
            max_value=100.0,
            on_change=set_amplitude,
            step=0.1,
        ),
        ui.slider(
            label="Frequency",
            default_value=frequency,
            min_value=-100.0,
            max_value=100.0,
            on_change=set_frequency,
            step=0.1,
        ),
        ui.slider(
            label="Phase",
            default_value=phase,
            min_value=-100.0,
            max_value=100.0,
            on_change=set_phase,
            step=0.1,
        ),
        direction="column",
    )

    return amplitude, frequency, phase, input_panel


@ui.component
def multiwave():
    amplitude, frequency, phase, wave_input = use_wave_input()

    tt = use_memo(lambda: time_table("PT1s").update("x=i"), [])
    t = use_memo(
        lambda: tt.update(
            [
                f"y_sin={amplitude}*Math.sin({frequency}*x+{phase})",
                f"y_cos={amplitude}*Math.cos({frequency}*x+{phase})",
                f"y_tan={amplitude}*Math.tan({frequency}*x+{phase})",
            ]
        ),
        [amplitude, frequency, phase],
    )
    p_sin = use_memo(
        lambda: Figure().plot_xy(series_name="Sine", t=t, x="x", y="y_sin").show(), [t]
    )
    p_cos = use_memo(
        lambda: Figure().plot_xy(series_name="Cosine", t=t, x="x", y="y_cos").show(),
        [t],
    )
    p_tan = use_memo(
        lambda: Figure().plot_xy(series_name="Tangent", t=t, x="x", y="y_tan").show(),
        [t],
    )

    return ui.column(
        ui.row(
            ui.stack(
                ui.panel(wave_input, title="Wave Input"),
                ui.panel(t, title="Wave Table"),
                active_item_index=0,
            ),
            height=25,
        ),
        ui.row(
            ui.stack(ui.panel(p_sin, title="Sine"), width=50),
            ui.stack(ui.panel(p_cos, title="Cosine"), width=30),
            ui.stack(ui.panel(p_tan, title="Tangent")),
        ),
    )


dash_complex_state = ui.dashboard(multiwave())
```

## API Reference

A dashboard is the container for an entire layout.

**Returns:** `DashboardElement` The rendered dashboard.

<ParamTable param={{"module_name": "deephaven.ui.", "name": "dashboard", "parameters": [{"name": "element", "type": "FunctionElement", "description": "Element to render as the dashboard. The element should render a layout that contains 1 root column or row."}]}} />

### Row API Reference

A row is a container that can be used to group elements.
Each element will be placed to the right of its prior sibling.

**Returns:** `Element` The rendered row element.

<ParamTable param={{"module_name": "deephaven.ui.", "name": "row", "parameters": [{"name": "*children", "type": "Any", "description": "Elements to render in the row."}, {"name": "height", "type": "float | None", "description": "The percent height of the row relative to other children of its parent. If not provided, the row will be sized automatically.", "default": "None"}, {"name": "key", "type": "str | None", "description": "A unique identifier used by React to render elements in a list.", "default": "None"}]}} />

### Column API Reference

A column is a container that can be used to group elements.
Each element will be placed below its prior sibling.

**Returns:** `Element` The rendered column element.

<ParamTable param={{"module_name": "deephaven.ui.", "name": "column", "parameters": [{"name": "*children", "type": "Any", "description": "Elements to render in the column."}, {"name": "width", "type": "float | None", "description": "The percent width of the column relative to other children of its parent. If not provided, the column will be sized automatically.", "default": "None"}, {"name": "key", "type": "str | None", "description": "A unique identifier used by React to render elements in a list.", "default": "None"}]}} />

### Stack API Reference

A stack is a container that can be used to group elements which creates a set of tabs.
Each element will get a tab and only one element can be visible at a time.

**Returns:** `Element` The rendered stack element.

<ParamTable param={{"module_name": "deephaven.ui.", "name": "stack", "parameters": [{"name": "*children", "type": "Any", "description": "Elements to render in the row."}, {"name": "height", "type": "float | None", "description": "The percent height of the stack relative to other children of its parent. If not provided, the stack will be sized automatically.", "default": "None"}, {"name": "width", "type": "float | None", "description": "The percent width of the stack relative to other children of its parent. If not provided, the stack will be sized automatically.", "default": "None"}, {"name": "active_item_index", "type": "int | None", "description": "The index of the active item in the stack.", "default": "None"}, {"name": "key", "type": "str | None", "description": "A unique identifier used by React to render elements in a list.", "default": "None"}]}} />
