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.
A dock_board’s arrangement is two independent things,
and it takes two constructor arguments to match:
views — the structure: which
panels belong to each view (the global tabs across the top of the app)
and the view’s display name. One dock_view per view.grids — the geometry: how
each view’s panels are split, nested, tabbed and sized. One
dock_grid per view.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.
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.
You describe geometry with dock_grid() (and its helpers
panels() and group()). Two rules cover
everything:
dock_grid()’s top level lays its children out horizontally;
one level of list() nesting flips to vertical; another
flips back, and so on.list() of IDs is split
into separate panels.A single ID fills the whole view:
┌────────────────────────────┐
│ Page [+] │
├────────────────────────────┤
│ │
│ a │
│ │
└────────────────────────────┘
The view is named after the grid’s list name (Page), and
its members are that grid’s panels (a).
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 │
│ │ │
└─────────────┴──────────────┘
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.
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.
Combine the two rules to build any arrangement.
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 │
└─────────────┴──────────────┘
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 │
└─────────────┴──────────────┘
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 │
└─────────────┴──────────────┘
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.
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.
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.
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.
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%
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:
dock_grid(..., sizes = c(...)) — sizes parallel to the
root children. They are relative; they need not sum to 1
(they are normalised for you).group(..., sizes = c(...)) — the same, for a nested
branch below the root.panels(..., active = "...") — a tabbed leaf with an
explicit open tab.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%
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%
│ │
└────────────────────────────┘
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 │
└────────────────────────────┘
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.
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()) |
?dock_grid for the geometry syntax (with
panels() and group()).?dock_view for the structure type, and
?active_view for switching the active view at runtime.?dock_layout for the dockView boundary form, and
as_dock_grid() / as_dock_layout() for the
casts across it.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.