Blocks
Declare a block with defineBlock, type its inputs, and say where it may be placed.
A block is a typed building unit the workspace can place on a page, on a record
or inside a display. You declare it in code with defineBlock: a name, how it
looks in the picker, where it may go, and the inputs a person fills in. The
inputs are validated for you, so a block never receives parameters it did not
declare.
import { defineBlock, input } from "@standards/schema";
export const OPEN_DEALS = defineBlock({
name: "open_deals",
label: "Open deals",
icon: "chart-bar",
family: "data",
contexts: ["page", "record"],
inputs: {
deals: input.query("deals"),
measure: input.attribute("deals", { types: ["number", "currency"] }),
period: input.period().optional(),
},
});| Field | Notes |
|---|---|
name | Unique API name of the block. |
label, icon | What the picker shows. |
family | data, context, people or layout: the group the picker files it under. |
contexts | Where the block may be placed: page, record, display. |
inputs | A record of named inputs, built with input.*. |
layout | Optional: how the block sits on a page grid (see Layout). |
contexts replaces the former requiresRecord flag. A block that needs the
record it sits on is contexts: ["record"]; one that works anywhere lists all
three.
Layout
layout tells a page how to size the block. Every key is optional.
export const OPEN_DEALS_BOARD = defineBlock({
name: "open_deals_board",
label: "Open deals board",
icon: "chart-bar",
family: "data",
contexts: ["page"],
layout: { size: { w: 8, h: 6 }, minWidth: 6, height: "flexible", minHeight: 4, maxHeight: 12 },
inputs: { deals: input.query("deals") },
});| Key | Default | Notes |
|---|---|---|
size | { w: 4, h: 4 } | The size when the block is added: w in columns (3, 4, 6, 8, 9 or 12), h in rows. |
minWidth | 3 | The narrowest width the block accepts, one of the same steps. |
height | "flexible" | fit (its content's height, capped by maxHeight, then scrolls), flexible (stretches between its bounds) or fill (the whole page). |
minHeight | 2 | The lowest height of a flexible block, in rows. |
maxHeight | none | The highest height of a flexible block, or the cap of a fit one. |
figure | false | A KPI figure: a group of figures renders as a band. |
A page's .row() gives a block without .span(n) at least its minWidth, and
a content's .height(mode) overrides height. See
Pages.
Input kinds
| Input | Holds |
|---|---|
input.object() | The API name of an object |
input.attribute(object, { types? }) | An attribute of that object, optionally limited to some attribute types |
input.query(object?) | A typed query(...) over an object |
input.record(object?) | The id of a record |
input.agent(), input.routine(), input.view() | The name of an agent, a routine or a view |
input.url() | An http or https link |
input.enum(values) | One of a closed list of strings |
input.text(), input.number(), input.boolean() | A scalar |
input.period() | day, week, month, quarter or year |
Every input is immutable and required by default. .optional() makes it
optional. .many() makes an attribute or enum input hold a non-empty list.
An object-scoped input takes an object builder, which keeps attribute names
checked, or an object name.
Two modifiers shape the block's settings panel without changing what it
stores. .inSection("data" | "display") places the row with what the block
reads or with how it shows it, when its kind would place it elsewhere (a
filter switch reads, a trend attribute shows). .showWhen(key, { not }) shows
the row beside the input it depends on, only once that input is set to none of
the not values. A period that depends on an attribute shows only when the
attribute is a date.
inputs: {
measure: input.enum(["count", "sum"]).inSection("data"),
attribute: input.attribute(QUERY_OBJECT).optional().showWhen("measure", { not: ["count"] }),
by: input.attribute(QUERY_OBJECT).optional(),
byBucket: input.period().optional().inSection("data").showWhen("by"),
},A block that reads records measures them with the same query a collection uses, so its numbers follow the reader's permissions and visibility. See Aggregate.
Native block: Bubble
record_links (label Bubble, icon share, family context) draws the links around a record as a map of connected records and members. It may be placed on a record or on a page, and the default record view of an object created from now on includes it as a Bubble tab. On an existing view, add it from the "+" tab menu. It reads the record graph, so a reader only sees what they may see. On the map only the centre is named; another node shows its name while it is hovered, focused or selected.
| Input | Holds |
|---|---|
center | record (default) or me: centre on a record, or on the viewer. |
object, record | The chosen record's object and id. On a record the block centres on the host record, so both stay empty. record requires object. |
depth | "1" or "2" (default): how many rings are expanded on open. |
On a page there is no host record: set { object, record } or { center: "me" }, otherwise the block is refused.
Next steps
- Views: the screens a block is placed in
- Records:
aggregate()and the other terminals - Book a demo