Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions content/docs/protocol/objectui/concept.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -601,7 +601,9 @@ actions:

### 3. Conditional Field Visibility

Fields appear based on other field values:
Fields appear based on other field values. The key is **`visibleWhen`**, and its value
is a **CEL predicate string** evaluated per record — the field is shown only when the
predicate is TRUE:

```yaml
fields:
Expand All@@ -611,9 +613,26 @@ fields:
- name: shipping_address
type: textarea
label: Shipping Address
visible: { shipping_required: true } # ← Show only if checkbox checked
# Shown only while the checkbox is checked. `has()` guards the unset case:
# an unbound reference faults, and visibility's fallback is "shown".
visibleWhen: "has(record.shipping_required) && record.shipping_required"
```

<Callout type="warn">
There is **no `visible` key on a field**. `FieldSchema` is a strict object, so
`visible` is refused *by name* — before any value shape is examined — and no
filter object, boolean map or breakpoint map makes it parse. Which key you want
depends on the form the answer takes, and the two have **opposite polarity**:

- a **static** answer is `hidden`, which is **inverted** — `visible: false`
becomes `hidden: true`;
- a **per-record** answer is `visibleWhen`, the CEL predicate above, shown when
the predicate is **true**.

Its siblings are `readonlyWhen` and `requiredWhen`. See the
[Field Reference](/docs/references/data/field).
</Callout>

### 4. Field Dependencies and Cascading

```yaml
Expand Down
59 changes: 36 additions & 23 deletions content/docs/protocol/objectui/layout-dsl.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -521,35 +521,48 @@ tab points at a named list view. See

## Responsive Layout Modifiers

### Device-Specific Visibility
**Responsive behaviour is not a form-field or form-section key.** A form field's
only conditional-visibility key is `visibleWhen` — a CEL predicate over the
record, so it varies per *record*, never per *viewport* — and a section's
`columns` is a single scalar (`1`–`4`). Both shapes are strict, so a breakpoint
or orientation map written on either is a **loud parse error** naming the key,
not a layout that quietly does nothing.

```yaml
fields:
- name: detailed_description
span: 12
visible:
desktop: true # Show on desktop
tablet: true # Show on tablet
mobile: false # Hide on mobile

- name: short_summary
span: 12
visible:
desktop: false # Hide on desktop
tablet: false # Hide on tablet
mobile: true # Show on mobile
```
### Per-Breakpoint Styling

### Orientation-Specific Layout
Breakpoint-driven show/hide and per-breakpoint layout live one tier up, on a
**page component**, and are expressed as scoped CSS through `responsiveStyles`
([ADR-0065](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0065-sdui-styling-model.md)) — desktop-first buckets compiled to id-scoped CSS at
render:

```yaml
section:
columns:
portrait: 1 # 1 column in portrait mode
landscape: 2 # 2 columns in landscape mode
fields: [field_a, field_b, field_c]
# On a page component (*.page.ts) — NOT on a form field or a form section
responsiveStyles:
large: { gridColumn: 'span 6' } # unconditional base (desktop-first)
medium: { gridColumn: 'span 12' } # applied at ≤ medium
xsmall: { display: 'none' } # hidden at the smallest breakpoint
```

The four buckets are `large` / `medium` / `small` / `xsmall`; values are a
CSS-property map with camelCase keys. Prefer design tokens
(`var(--space-6)`) over literals.

<Callout type="warn">
Earlier revisions of this page documented a field-level `visible:` breakpoint
map (`desktop` / `tablet` / `mobile`) and a section-level `columns:`
orientation map (`portrait` / `landscape`). **Neither existed on any schema.**
`visible` is not a form-field key at all — `FormFieldSchema` declares `hidden`
(static, and **inverted**: `visible: false` is `hidden: true`) and
`visibleWhen` (CEL predicate) — and `FormSection.columns` has always been a
scalar. Both are **removed rather than implemented**, because field-level
breakpoint visibility exists nowhere in the spec: there is no key to rename
them onto.

Nor is `responsive.hiddenOn` the answer. That layout block was retired in
v17.x under [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md) D2 precisely because no renderer ever applied it;
`responsiveStyles` above is the only per-breakpoint channel that is.
</Callout>

## Related Lists: Embedding Child Records

Display child records within a parent record's page.
Expand Down
Loading