Skip to content

Commit bbdecaa

Browse files
os-zhuangclaude
andauthored
feat(spec): interface-editor polish — composite disclosure popover + buttons=object-actions (#2051)
* feat(spec): progressive-disclosure popover for composite form fields Adds `disclosure?: 'inline' | 'popover'` to FormFieldSchema. A composite field marked `disclosure: 'popover'` renders as a compact summary line + a gear that opens its sub-fields in a popover (Airtable-style progressive disclosure), instead of an always-expanded inline box — keeping the interface-page panel lean. Applied to the page form's `appearance`, `userActions`, and `addRecord` composites (the dense sub-toggle groups). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(spec): interface page `buttons` = object actions (action-multi picker) `InterfacePageConfig.buttons` is now a first-class field: an array of the source object's action names (ActionSchema) surfaced as page toolbar buttons. The page form renders it with the new `action-multi` widget (dependsOn: source), so the author picks from the object's real actions — buttons ARE object actions, correct-by-construction (no free-text IDs). - page.zod.ts — add `buttons: z.array(z.string())` to InterfacePageConfigSchema. - page.form.ts — add the Buttons field (widget: action-multi) under User actions. - showcase task-workbench demonstrates it: buttons: ['showcase_bulk_reassign', 'showcase_mark_done'] render as Reassign… / Mark Done in the page header. Pairs with the objectui action-multi widget + InterfaceListPage runtime render. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 04ed478 commit bbdecaa

4 files changed

Lines changed: 14 additions & 3 deletions

File tree

examples/app-showcase/src/pages/task-workbench.page.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ export const TaskWorkbenchPage: Page = {
6161
addRecordForm: false,
6262
},
6363

64+
// Toolbar buttons ARE object actions (ADR-0047) — referenced by name from
65+
// the showcase_task object's ActionSchema, not free text.
66+
buttons: ['showcase_bulk_reassign', 'showcase_mark_done'],
67+
6468
showRecordCount: true,
6569
},
6670
};

packages/spec/src/ui/page.form.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,18 @@ export const pageForm = defineForm({
9595
{ field: 'filterBy', type: 'repeater', helpText: 'Always-on base filter for the page' },
9696
{ field: 'levels', helpText: 'Hierarchy levels to display (tree-like sources)' },
9797
// ── Appearance ──
98-
{ field: 'appearance', type: 'composite', helpText: 'Allowed visualizations (Grid / Kanban / Calendar / …) and description visibility' },
98+
{ field: 'appearance', type: 'composite', disclosure: 'popover', helpText: 'Allowed visualizations (Grid / Kanban / Calendar / …) and description visibility' },
9999
// ── User filters ──
100100
{
101101
field: 'userFilters',
102102
widget: 'filter-mode',
103103
helpText: 'End-user filter bar: None (no bar) / Tabs (named presets) / Dropdown (per-field). None removes the config.',
104104
},
105105
// ── User actions ──
106-
{ field: 'userActions', type: 'composite', helpText: 'Toolbar toggles (search, sort, filter, row height)' },
107-
{ field: 'addRecord', type: 'composite', helpText: 'Add-record entry point' },
106+
{ field: 'userActions', type: 'composite', disclosure: 'popover', helpText: 'Toolbar toggles (search, sort, filter, row height)' },
107+
{ field: 'addRecord', type: 'composite', disclosure: 'popover', helpText: 'Add-record entry point' },
108+
// Buttons ARE object actions — pick from the source object's actions.
109+
{ field: 'buttons', widget: 'action-multi', dependsOn: 'source', helpText: "Toolbar buttons — pick from this object's actions" },
108110
{ field: 'showRecordCount', helpText: 'Show the record count bar' },
109111
{ field: 'allowPrinting', helpText: 'Allow users to print this page' },
110112
],

packages/spec/src/ui/page.zod.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ export const InterfacePageConfigSchema = lazySchema(() => z.object({
257257
/** Add record */
258258
addRecord: AddRecordConfigSchema.optional().describe('Add record entry point configuration'),
259259

260+
/** Toolbar buttons — references to the source object's actions (ActionSchema).
261+
* Buttons ARE object actions (not free text): correct-by-construction. */
262+
buttons: z.array(z.string()).optional().describe("Toolbar buttons — names of the source object's actions to surface in the page toolbar"),
263+
260264
/** Record count */
261265
showRecordCount: z.boolean().optional().describe('Show record count at page bottom'),
262266

packages/spec/src/ui/view.zod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ export const FormFieldSchema: z.ZodType<any> = lazySchema(() => z.object({
715715

716716
dependsOn: z.string().optional().describe('Parent field name for cascading'),
717717
visibleOn: ExpressionInputSchema.optional().describe('Visibility predicate (CEL).'),
718+
disclosure: z.enum(['inline', 'popover']).optional().describe('Composite rendering: inline bordered box (default) or a summary line + gear popover (progressive disclosure).'),
718719
}));
719720

720721
/**

0 commit comments

Comments
 (0)