Views
Declare the list and detail views of your objects next to their schema, and ship them to the workspace in the same sync.
Views are the screens your team sees in the Standards app. listView is the
table of records; detailView is the page one record opens into. The ones you
declare here are a starting point: on app.standards.new your users change
them, add tabs and columns, and create views of their own, and sync keeps
theirs while owning yours. Declare them
next to your objects and they ship in the same schema.sync.
import { content, detailView, group, listView } from "@stndrds/client";
const IDENTITY = content.form(
group("identity", "Identity").fields("firstName", "lastName", "email")
);
export const CONTACT_DETAIL = detailView("contact_detail", "Contact")
.for("contacts")
.default()
.tab("general", "Info", IDENTITY)
.tab("activity", "Activity", content.activity())
.aside("details", "Details", content.form(group("details", "Details").field("status")))
.build();
export const CONTACTS = listView("contacts_list", "Contacts")
.for("contacts")
.default()
.tab("all", "All", content.collection().object("contacts").table().columns("firstName", "email"))
.build();The view chain
| Method | Effect |
|---|---|
detailView(name, label) / listView(name, label) | Starts a view in record or collection context |
.for(object) | The object the view belongs to; required |
.default() | Opens by default for that object and context; one per pair |
.tab(name, label, content) | Places a content in the main area |
.aside(name, label, content) | Places a content in the side panel |
.description(text), .icon(name) | View metadata |
.build() | Validates and returns { kind: "view", definition } |
Every call returns a new builder. A content has no name, label or placement of
its own; the view supplies them. Content names are unique within a view, and at
least one main content is required. Contents that need a record (everything
except a collection on an object) are refused on a listView at build time.
The nine content types
content.collection() // records of an object or of a relation
content.form(...groups) // fields of the current record, laid out in groups
content.activity() // the record's activity feed
content.richtext("body") // one richtext attribute, full width
content.documents() // the record's document packs
content.forms().object("x") // submissions of the form system for an object
content.emails("email") // threads matched on email attributes
content.meetings("email") // meetings matched on email attributes
content.custom(extension, o) // a registered content extensionAll of them accept .icon(name). content.custom names an extension already
registered on the platform; the extension registry itself is not part of the
client surface.
Collections
A collection reads one source: .object(name), .related(attribute) for a
relation the current record declares, or .relatedFrom(object, attribute) for
the records whose own relation points back at the current record. .related
needs one concrete target object; polymorphic and universal relations are not a
collection source.
import { content, detailView } from "@stndrds/client";
export const COMPANY_DETAIL = detailView("company_detail", "Company")
.for("companies")
.tab("contacts", "Contacts", content.collection()
.relatedFrom("contacts", "company")
.table()
.columns("firstName", "lastName", "email")
.sort("lastName")
.openMode("peek")
.createMode("inline")
.actions("create", "update", "delete"))
.build();| Option | Notes |
|---|---|
.table(), .list(), .kanban(groupByAttribute) | One layout; switching clears layout-specific settings |
.columns(...attributes) | Columns of a table, fields of a card |
.filter(filterState) | A FilterState (combinator + rules) |
.sort(attribute, direction?) | Defaults to asc; repeated sorts append |
.actions(...) | create, update, delete, attach, detach; .actions() hides every mutation |
.openMode("page" | "peek") | Omitted means page, and no peek history |
.createMode("page" | "peek" | "inline") | Omitted follows the resolved open mode |
.visibility("workspace" | "private" | "all") | Who sees the tab |
.displayAttributes(...), .descriptionAttribute(name) | Card rendering |
.columnSizing(widths) | Table and list only |
.kanbanColumnOrder(...), .kanbanColumnVisibility(map), .kanbanPinnedColumns(...) | Kanban only |
Actions never grant permissions: a user without update on the object does
not gain it from a collection.
Forms inside a view
group(name, label) holds fields: .field(attribute, { label?, span?, readOnly? })
or .fields(...attributes), plus .description(text) and .collapsible(collapsed?).
content.form(...groups) accepts .formColumns(1 | 2 | 3) and
.density("comfortable" | "compact" | "spacious").
content.form( is a layout inside a view: it arranges the record's own
attributes. The form system (form(), slots, steps, prefill) and workflows
are not pushable through a schema source, so they are not part of this SDK.
Views in a source
Pass views next to the objects. Each one is built and validated locally, then
sent with the objects on the next schema.sync.
import { defineSchemaSource } from "@stndrds/client";
export const crmSource = defineSchemaSource("crm", {
objects: [CONTACT, COMPANY],
views: [CONTACT_DETAIL, CONTACTS, COMPANY_DETAIL],
});On the workspace a source-owned view is adopted the first time it is pushed,
updated on every later change, and released when it leaves the source: the
row becomes a custom view and workspace overlays survive. Declaring it again
adopts the row back. The Sync page shows these counters in
the SyncResult.
@stndrds/schema exports relationProperty(name) to reference a qualified
property in a column list; @stndrds/client does not re-export it. Column
references from a client-only project stay on plain attributes.
Next steps
- Relations and documents: the sources a collection can read
- Sync: push objects and views in one call
- Objects: the objects your views point at
- Schema sources: what a workspace keeps when a view is released
Attributes
Declare the sixteen attribute types with their options, know what the API validates on write, and read back values with the types the builder promised.
Relations and documents
Link records to each other with typed relations, keep both sides in step, and attach packs of files with rules on type and size.