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
34 changes: 34 additions & 0 deletions .changeset/page-element-blocks-inputs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
"@object-ui/components": patch
---

feat(page,element): declare inputs for the eight configurable page:\* / element:\* blocks

Same gap objectui#3027 closed for `record:*`, in the two namespaces next door:
renderers that read real props while every authoring surface reported "takes no
configuration". Declarations mirror what each renderer reads —

| block | inputs |
|---|---|
| `page:tabs` | items, tabStyle (line/card/pill), position (top/left) |
| `page:card` | title, bordered, body (slot), footer (slot) |
| `page:accordion` | items, allowMultiple, variant (flush/card) |
| `element:text` | content, variant (heading/subheading/body/caption), align |
| `element:number` | object, aggregate (count/sum/avg/min/max), field, filter, format, prefix, suffix |
| `element:button` | label, action (inline ActionDef), variant, size, icon, iconPosition, disabled |
| `element:definition-list` | items, columns, inline |
| `element:repeater` | object, titleField, fields, filter, sort, limit, emptyText, divided |

`page:section`, `page:footer` and `page:sidebar` are left at zero inputs on
purpose: their renderers render `children`/`body` and nothing else — like
`element:divider`, they are genuinely prop-less containers, and inventing
inputs for them would be the opposite falsehood.

`aria` and `className` stay undeclared throughout, per the convention on
`record:details`: escape hatches and styling pass-throughs, not authoring
choices. `element:button`'s registration also documents the split against
`action:button` — inline ActionDef vs a declared action referenced by name —
so the two stop reading as duplicates.

Declaration only; no renderer behavior changes. Curation (which of these join
`PUBLIC_BLOCKS`) is a separate change.
15 changes: 15 additions & 0 deletions packages/components/src/renderers/basic/data-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ ComponentRegistry.register('definition-list', DefinitionListRenderer, {
skipFallback: true,
label: 'Definition List',
category: 'content',
inputs: [
{ name: 'items', type: 'array', label: 'Items', required: true, description: 'Term/description pairs [{ term, description }]' },
{ name: 'columns', type: 'enum', label: 'Columns', enum: ['1', '2'], defaultValue: '1' },
{ name: 'inline', type: 'boolean', label: 'Inline', description: 'Term and description on one baseline-aligned row' },
],
});

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -178,4 +183,14 @@ ComponentRegistry.register('repeater', RepeaterRenderer, {
skipFallback: true,
label: 'Repeater',
category: 'content',
inputs: [
{ name: 'object', type: 'string', label: 'Object', required: true, description: 'Object whose records the list repeats over' },
{ name: 'titleField', type: 'string', label: 'Title Field' },
{ name: 'fields', type: 'array', label: 'Fields', description: 'Columns per row — bare names or { field, label? }' },
{ name: 'filter', type: 'array', label: 'Filter' },
{ name: 'sort', type: 'array', label: 'Sort' },
{ name: 'limit', type: 'number', label: 'Limit' },
{ name: 'emptyText', type: 'string', label: 'Empty Text' },
{ name: 'divided', type: 'boolean', label: 'Divided', description: 'Separator between rows' },
],
});
26 changes: 26 additions & 0 deletions packages/components/src/renderers/basic/elements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ ComponentRegistry.register('text', ElementTextRenderer, {
skipFallback: true,
label: 'Text',
category: 'content',
inputs: [
{ name: 'content', type: 'string', label: 'Content', required: true, description: 'Accepts an inline translation map ({ en, "zh-CN", … })' },
{ name: 'variant', type: 'enum', label: 'Variant', enum: ['heading', 'subheading', 'body', 'caption'], defaultValue: 'body' },
{ name: 'align', type: 'enum', label: 'Align', enum: ['left', 'center', 'right'], defaultValue: 'left' },
],
});

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -268,6 +273,18 @@ ComponentRegistry.register('button', ElementButtonRenderer, {
skipFallback: true,
label: 'Button',
category: 'action',
// Distinct from `action:button`, which references a DECLARED action by name
// and gates on visible/enabled predicates. This one carries an inline
// ActionDef — the standalone-page button.
inputs: [
{ name: 'label', type: 'string', label: 'Label', required: true, description: 'Accepts an inline translation map ({ en, "zh-CN", … })' },
{ name: 'action', type: 'object', label: 'Action', description: 'Inline ActionDef executed on click (url / navigation / api / script / modal / flow); omitted → renders inert' },
{ name: 'variant', type: 'enum', label: 'Variant', enum: ['primary', 'secondary', 'danger', 'ghost', 'link'], defaultValue: 'primary' },
{ name: 'size', type: 'enum', label: 'Size', enum: ['small', 'medium', 'large'], defaultValue: 'medium' },
{ name: 'icon', type: 'string', label: 'Icon', description: 'Lucide icon name' },
{ name: 'iconPosition', type: 'enum', label: 'Icon Position', enum: ['left', 'right'], defaultValue: 'left' },
{ name: 'disabled', type: 'boolean', label: 'Disabled' },
],
});

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -390,4 +407,13 @@ ComponentRegistry.register('number', ElementNumberRenderer, {
skipFallback: true,
label: 'Number',
category: 'content',
inputs: [
{ name: 'object', type: 'string', label: 'Object', required: true, description: 'Object the aggregate runs over' },
{ name: 'aggregate', type: 'enum', label: 'Aggregate', enum: ['count', 'sum', 'avg', 'min', 'max'], required: true },
{ name: 'field', type: 'string', label: 'Field', description: 'Measure field (required for every aggregate except count)' },
{ name: 'filter', type: 'array', label: 'Filter' },
{ name: 'format', type: 'enum', label: 'Format', enum: ['number', 'currency', 'percent'], defaultValue: 'number' },
{ name: 'prefix', type: 'string', label: 'Prefix' },
{ name: 'suffix', type: 'string', label: 'Suffix' },
],
});
16 changes: 16 additions & 0 deletions packages/components/src/renderers/layout/containers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,11 @@ ComponentRegistry.register('tabs', PageTabsRenderer, {
label: 'Page Tabs',
category: 'layout',
isContainer: true,
inputs: [
{ name: 'items', type: 'array', label: 'Tabs', required: true, description: 'Tab definitions [{ label, value?, icon?, count?, visibleWhen?, children }] — value is the stable ?tab= URL token, count auto-derives from record:related_list descendants when omitted' },
{ name: 'tabStyle', type: 'enum', label: 'Style', enum: ['line', 'card', 'pill'], defaultValue: 'line' },
{ name: 'position', type: 'enum', label: 'Position', enum: ['top', 'left'], defaultValue: 'top' },
],
});

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -602,6 +607,12 @@ ComponentRegistry.register('card', PageCardRenderer, {
label: 'Page Card',
category: 'layout',
isContainer: true,
inputs: [
{ name: 'title', type: 'string', label: 'Title', description: 'Accepts an inline translation map ({ en, "zh-CN", … })' },
{ name: 'bordered', type: 'boolean', label: 'Bordered', defaultValue: true },
{ name: 'body', type: 'slot', label: 'Body', description: 'Card content; plain `children` also render here' },
{ name: 'footer', type: 'slot', label: 'Footer' },
],
});

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -683,6 +694,11 @@ ComponentRegistry.register('accordion', PageAccordionRenderer, {
label: 'Page Accordion',
category: 'layout',
isContainer: true,
inputs: [
{ name: 'items', type: 'array', label: 'Panels', required: true, description: 'Panel definitions [{ label, collapsed?, children }] — collapsed: false opens a panel by default' },
{ name: 'allowMultiple', type: 'boolean', label: 'Allow Multiple Open', defaultValue: false },
{ name: 'variant', type: 'enum', label: 'Variant', enum: ['flush', 'card'], defaultValue: 'flush' },
],
});

// ---------------------------------------------------------------------------
Expand Down
Loading