The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

Views and grids

library(blockr.core)
library(blockr.dock)

A dock_board’s arrangement is two independent things, and it takes two constructor arguments to match:

This vignette covers the grid syntax you use to describe geometry, how the two slots fit together at construction, and the patterns you’ll reach for most.

The big picture

A board stores structure and geometry as two aligned slots, keyed by view id — read them with board_views() and board_grids(). They are kept apart on purpose: membership is owned by the update lifecycle (add / remove a block), geometry by the client (you drag a sash, the board records it). A panel is placed in a view when it is in both slots; the two are reconciled only where the placement is read.

There is a third representation you should know the name of but will rarely touch: dock_layout. It is dockView’s own form — the {grid, panels, activeGroup} payload the front end renders and echoes back. It is the wire type at the client boundary, not something you author with. Our model is dock_view + dock_grid; dock_layout is what crosses to dockView (?dock_layout).

The constructor is forgiving about which slot you fill:

You pass Result
grids only one view per grid (id = the list name), members taken from the grid’s panels
views only structure with no explicit geometry; each view renders a flat default over its members
both aligned by id: views sets membership + names, grids sets geometry
neither a single default view (sidebar + main when there are extensions)

So for a layout with real geometry you usually just pass grids; the views come along for free. Reach for views when you want a display name that differs from the id, or a view without an explicit arrangement.

Grid syntax

You describe geometry with dock_grid() (and its helpers panels() and group()). Two rules cover everything:

  1. List nesting alternates orientation. dock_grid()’s top level lays its children out horizontally; one level of list() nesting flips to vertical; another flips back, and so on.
  2. Character vectors create tabs. A vector of IDs shares one DockView panel as tabs; a list() of IDs is split into separate panels.

One panel

A single ID fills the whole view:

new_dock_board(
  blocks = c(a = new_dataset_block()),
  grids = list(Page = dock_grid("a"))
)
┌────────────────────────────┐
│  Page  [+]                 │
├────────────────────────────┤
│                            │
│            a               │
│                            │
└────────────────────────────┘

The view is named after the grid’s list name (Page), and its members are that grid’s panels (a).

Two panels side by side

Two top-level children → two columns split horizontally:

new_dock_board(
  blocks = c(a = new_dataset_block(), b = new_head_block()),
  grids = list(Page = dock_grid("a", "b"))
)
┌────────────────────────────┐
│  Page  [+]                 │
├─────────────┬──────────────┤
│             │              │
│      a      │      b       │
│             │              │
└─────────────┴──────────────┘

Two panels stacked vertically

Wrap the children in one extra layer of list() to introduce a vertical split:

new_dock_board(
  blocks = c(a = new_dataset_block(), b = new_head_block()),
  grids = list(Page = dock_grid(list("a", "b")))
)
┌────────────────────────────┐
│  Page  [+]                 │
├────────────────────────────┤
│            a               │
├────────────────────────────┤
│            b               │
└────────────────────────────┘

The outer level still describes a horizontal split, but with a single child that “split” is one full-width column. The inner list("a", "b") is at depth 1, so it splits vertically: a stacks on top of b.

Tabs (multiple panels in one)

Use a character vector (not a list) to put several panels in one DockView panel as tabs:

new_dock_board(
  blocks = c(a = new_dataset_block(), b = new_head_block()),
  grids = list(Page = dock_grid(c("a", "b")))
)
┌────────────────────────────┐
│  Page  [+]                 │
├────────────────────────────┤
│ ┌──────┐┌──────┐           │
│ │  a   ││  b   │           │
│ └──────┘└──────┘           │
│                            │
│  (a is shown, b is a tab)  │
│                            │
└────────────────────────────┘

dock_grid(list("a", "b")) (panels split) and dock_grid(c("a", "b")) (panels tabbed) read almost the same but produce very different UIs — the list/vector distinction flips between split a panel and tabify a panel.

Nested grids

Combine the two rules to build any arrangement.

Two columns, the right one stacked

new_dock_board(
  blocks = c(
    a = new_dataset_block(),
    b = new_head_block(),
    c = new_head_block()
  ),
  grids = list(Page = dock_grid("a", list("b", "c")))
)
┌────────────────────────────┐
│  Page  [+]                 │
├─────────────┬──────────────┤
│             │      b       │
│      a      ├──────────────┤
│             │      c       │
└─────────────┴──────────────┘

Two columns, both stacked

new_dock_board(
  blocks = c(
    a = new_dataset_block(),
    b = new_head_block(),
    c = new_head_block(),
    d = new_head_block()
  ),
  grids = list(Page = dock_grid(list("a", "b"), list("c", "d")))
)
┌────────────────────────────┐
│  Page  [+]                 │
├─────────────┬──────────────┤
│      a      │      c       │
├─────────────┼──────────────┤
│      b      │      d       │
└─────────────┴──────────────┘

Three rows in one column

A third level of nesting flips back to horizontal inside the vertical stack — a row holding two panels side by side:

new_dock_board(
  blocks = c(
    a = new_dataset_block(),
    b = new_head_block(),
    c = new_head_block()
  ),
  grids = list(Page = dock_grid(list("a", list("b", "c"))))
)
┌────────────────────────────┐
│  Page  [+]                 │
├────────────────────────────┤
│            a               │
├─────────────┬──────────────┤
│      b      │      c       │
└─────────────┴──────────────┘

Multiple views (pages)

Give grids more than one entry; each list name becomes a separate page in the view-nav dropdown, and each becomes its own view:

new_dock_board(
  blocks = c(a = new_dataset_block(), b = new_head_block()),
  extensions = new_edit_board_extension(),
  grids = list(
    Analysis = dock_grid("a", "b"),
    Editor = dock_grid("edit_board")
  )
)

Analysis view (active by default — the first view wins):

┌────────────────────────────┐
│ Analysis  [+]   ← view nav │
├────────────────────────────┤
│      a       │      b      │
└────────────────────────────┘

Switching to Editor via the dropdown:

┌────────────────────────────┐
│ Editor  [+]                │
├────────────────────────────┤
│                            │
│           edit             │
│                            │
└────────────────────────────┘

Each grid follows exactly the same syntax as the single-view form: character vectors for tabs, nested lists for splits.

Display names distinct from the id

A grid’s list name is the view id, and it doubles as the display label. When you want a label that differs from the id — or a view with no geometry at all — fill the views slot too. A dock_view() carries the membership and the display name; align it to the grid by id:

new_dock_board(
  blocks = c(a = new_dataset_block(), b = new_head_block()),
  views = list(
    main = dock_view(c("a", "b"), name = "Analysis")
  ),
  grids = list(
    main = dock_grid("a", "b")
  )
)

The nav shows Analysis; the stable id main is what active, renames and updates address.

Choosing the initially active view

The first view is active by default. To start elsewhere, name it with new_dock_board(active = ), using its view id:

new_dock_board(
  blocks = c(a = new_dataset_block(), b = new_head_block()),
  extensions = new_edit_board_extension(),
  grids = list(
    Analysis = dock_grid("a", "b"),
    Editor = dock_grid("edit_board")
  ),
  active = "Editor"
)
┌────────────────────────────┐
│ Editor  [+]   ← starts here│
├────────────────────────────┤
│           edit             │
└────────────────────────────┘

Which view is active is a single field on the views collection, keyed by id — it belongs to the collection, not to any one view, so it is always exactly one. Change it at runtime with active_view(board) <- id, and read it back with active_view(board); index board_grids(board)[[id]] if you then need that view’s geometry.

Views without geometry

Pass views alone (bare member vectors coerce to dock_views) for structure with no explicit arrangement. Each such view renders a flat default over its members until the client arranges it (which the board then records):

new_dock_board(
  blocks = c(a = new_dataset_block(), b = new_head_block()),
  views = list(
    Analysis = c("a", "b"),
    Empty = character()
  )
)

An empty view shows the same watermark prompt as an empty board, scoped to that tab.

Putting it all together

A pot-pourri: multiple views, nested grids, tabbed panels, an extension sidebar, and an explicit active view.

new_dock_board(
  blocks = c(
    raw = new_dataset_block(),
    cleaned = new_head_block(),
    summary = new_head_block(),
    plot1 = new_scatter_block(),
    plot2 = new_scatter_block()
  ),
  extensions = new_edit_board_extension(),
  links = list(
    new_link("raw", "cleaned", "data"),
    new_link("cleaned", "summary", "data"),
    new_link("cleaned", "plot1", "data"),
    new_link("cleaned", "plot2", "data")
  ),
  grids = list(
    Data = dock_grid(
      "edit_board",
      panels("raw", "cleaned", active = "cleaned"),
      sizes = c(0.25, 0.75)
    ),
    Analysis = dock_grid(
      group("summary", "plot1", sizes = c(0.4, 0.6)),
      "plot2",
      sizes = c(0.55, 0.45)
    ),
    Charts = dock_grid(panels("plot1", "plot2"))
  ),
  active = "Charts"
)

Three views; Charts is active, so the user lands there first.

Charts (active on load): one tabbed panel, plot1 shown, plot2 a tab.

┌────────────────────────────┐
│  Charts  [+]               │
├────────────────────────────┤
│ ┌───────┐┌───────┐         │
│ │ plot1 ││ plot2 │         │
│ └───────┘└───────┘         │
│  (plot1 shown, plot2 tab)  │
└────────────────────────────┘

Data: a slim extension sidebar on the left, a wide right column with raw / cleaned tabbed. active = "cleaned" opens the cleaned tab; sizes = c(0.25, 0.75) carves out the narrow sidebar.

┌────────────────────────────┐
│  Data  [+]                 │
├──────┬─────────────────────┤
│      │┌─────┐┌─────────┐   │
│ edit ││ raw ││ cleaned │   │
│      │└─────┘└─────────┘   │
│      │  (cleaned shown)    │
└──────┴─────────────────────┘
  25%          75%

Analysis: two columns at 55/45. The left is a nested group() with a 40/60 vertical split (summary over plot1); the right is plot2.

┌────────────────────────────┐
│  Analysis  [+]             │
├─────────────┬──────────────┤
│   summary   │              │   40% / 55%
├─────────────┤    plot2     │
│    plot1    │              │   60% / 45%
└─────────────┴──────────────┘
     55%             45%

Custom split ratios and active tabs

The bare list-of-IDs form splits space evenly and opens the first tab. For non-default ratios or a non-default open tab, use the typed helpers:

A 30/70 split

sizes runs parallel to the children in .... Two children split horizontally; two sizes make the split uneven:

new_dock_board(
  blocks = c(a = new_dataset_block(), b = new_head_block()),
  grids = list(
    Main = dock_grid("a", "b", sizes = c(0.3, 0.7))
  )
)
┌────────────────────────────┐
│  Main  [+]                 │
├──────────┬─────────────────┤
│          │                 │
│    a     │        b        │
│          │                 │
└──────────┴─────────────────┘
   30%             70%

Stacking with sizes

Same idea, vertical: orientation = "vertical" makes the root split run top-to-bottom.

new_dock_board(
  blocks = c(a = new_dataset_block(), b = new_head_block()),
  grids = list(
    Main = dock_grid("a", "b",
                     orientation = "vertical",
                     sizes = c(0.25, 0.75))
  )
)
┌────────────────────────────┐
│  Main  [+]                 │
├────────────────────────────┤
│            a               │   25%
├────────────────────────────┤
│                            │
│            b               │   75%
│                            │
└────────────────────────────┘

Choosing the open tab

panels() builds a tabbed leaf. Without active, the first ID opens — same as a bare character vector. Pass active to open a different tab:

new_dock_board(
  blocks = c(
    a = new_dataset_block(),
    b = new_head_block(),
    c = new_head_block()
  ),
  grids = list(
    Main = dock_grid(panels("a", "b", "c", active = "b"))
  )
)
┌────────────────────────────┐
│  Main  [+]                 │
├────────────────────────────┤
│ ┌────┐┌──────┐┌────┐       │
│ │ a  ││  b   ││ c  │       │
│ └────┘└──────┘└────┘       │
│         ↑                  │
│   b is open by default     │
└────────────────────────────┘

Sizes inside a nested branch

group() is an inner list(...) with its own sizes — use it for ratios on any non-root branch:

new_dock_board(
  blocks = c(
    a = new_dataset_block(),
    b = new_head_block(),
    c = new_head_block()
  ),
  grids = list(
    Main = dock_grid(
      "a",
      group("b", "c", sizes = c(0.6, 0.4)),
      sizes = c(0.3, 0.7)
    )
  )
)
┌────────────────────────────┐
│  Main  [+]                 │
├────────┬───────────────────┤
│        │      b            │   inner: 60% top
│        ├───────────────────┤
│   a    │      c            │   inner: 40% bottom
│        │                   │
└────────┴───────────────────┘
   30%          70%

Outer sizes = c(0.3, 0.7) controls the root split; inner group(..., sizes = c(0.6, 0.4)) controls the right column’s stack.

Cheat-sheet

Geometry (dock_grid(...)):

Goal Syntax
One panel dock_grid("a")
Two side-by-side dock_grid("a", "b")
Two stacked dock_grid(list("a", "b"))
Tabbed panel dock_grid(c("a", "b"))
Sidebar + main dock_grid("ext", "main")
Two columns, both stacked dock_grid(list("a", "b"), list("c", "d"))
Custom split ratio dock_grid("a", "b", sizes = c(0.3, 0.7))
Custom open tab dock_grid(panels("a", "b", active = "b"))
Vertical top-level split dock_grid("a", "b", orientation = "vertical")

Board (new_dock_board(...)):

Goal Syntax
One view with geometry grids = list(Page = dock_grid(...))
Several views grids = list(A = dock_grid(...), B = dock_grid(...))
A display name != id views = list(id = dock_view(members, name = "..."))
Structure, no geometry views = list(Page = c("a", "b"))
Start on view B new_dock_board(grids = list(A = ..., B = ...), active = "B")
Empty starter view views = list(Page = character())

Where to go from here

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.