diff --git a/examples/app-showcase/src/objects/task.object.ts b/examples/app-showcase/src/objects/task.object.ts index 216ce539a..8bb437cea 100644 --- a/examples/app-showcase/src/objects/task.object.ts +++ b/examples/app-showcase/src/objects/task.object.ts @@ -33,7 +33,11 @@ export const Task = ObjectSchema.create({ project: Field.masterDetail('showcase_project', { label: 'Project', required: true, - inlineEdit: true, + // Pin the editable-grid form factor (fast bulk line-item entry, with the + // column chooser + per-row expand). Left at `true`, the smart default + // would pick `form` for this fat child — the right call for many apps; + // here we keep the grid demo. Use `'form'` to force the per-row form. + inlineEdit: 'grid', inlineTitle: 'Tasks', relatedListTitle: 'Tasks', relatedListColumns: ['title', 'status', 'priority', 'assignee', 'due_date'], diff --git a/packages/spec/src/data/field.zod.ts b/packages/spec/src/data/field.zod.ts index 67eab2ea9..0f17b7326 100644 --- a/packages/spec/src/data/field.zod.ts +++ b/packages/spec/src/data/field.zod.ts @@ -405,15 +405,24 @@ export const FieldSchema = lazySchema(() => z.object({ deleteBehavior: z.enum(['set_null', 'cascade', 'restrict']).optional().default('set_null').describe('What happens if referenced record is deleted'), /** * Master-detail INLINE EDITING. On a child's `master_detail`/`lookup` field - * (whose `reference` is the parent object), set `inlineEdit: true` to declare - * "this child is entered/edited inline within the parent's form". The - * parent's standard create/edit form then renders an editable grid for these - * children and saves parent + children in ONE atomic transaction — no form - * view config and no bespoke page. The intent lives here in the data model; - * forms derive the UI. Use for true line-item/composition children (invoice - * lines, order items); leave off for associations (comments, attachments). + * (whose `reference` is the parent object), declare that "this child is + * entered/edited inline within the parent's form". The parent's standard + * create/edit form then renders the children and saves parent + children in + * ONE atomic transaction — no form view config and no bespoke page. The intent + * lives here in the data model; forms derive the UI. + * + * The value also selects the EDITING FORM FACTOR: + * - `true` → auto: the UI picks `grid` or `form` from the child's shape + * (rich types / many fields → `form`, else `grid`). + * - `'grid'` → an editable line-item grid (fast bulk entry; thin children + * like invoice lines / order items). + * - `'form'` → a compact read-only list; "Add" / per-row edit opens the + * child's FULL form (fat children with rich types, e.g. long + * text, attachments, many fields). + * Use for true line-item/composition children; leave off for associations + * (comments, attachments) — surface those as detail-page related lists. */ - inlineEdit: z.boolean().optional().describe('Edit these child records inline within the parent object\'s form (atomic master-detail).'), + inlineEdit: z.union([z.boolean(), z.enum(['grid', 'form'])]).optional().describe('Edit these child records inline within the parent\'s form (atomic master-detail). true = auto-pick grid/form by child shape; \'grid\' = editable line-item grid; \'form\' = list + per-row full form.'), /** Optional section title for the inline grid (defaults to the child object label). */ inlineTitle: z.string().optional().describe('Title for the inline master-detail grid'), /** Optional explicit grid columns for the inline editor (derived from the child object when omitted). */ diff --git a/skills/objectstack-data/rules/relationships.md b/skills/objectstack-data/rules/relationships.md index ad8796229..f855fc899 100644 --- a/skills/objectstack-data/rules/relationships.md +++ b/skills/objectstack-data/rules/relationships.md @@ -230,6 +230,25 @@ A form view may still set `subforms` to override the derived columns/order, but the relationship `inlineEdit` is the primary, zero-config path. See the objectstack-ui skill (Master-Detail Forms) for the rendering side. +#### Inline-edit form factor (`grid` vs `form`) + +`inlineEdit` also picks how the children are entered: + +```typescript +inlineEdit: true // auto — pick grid/form from the child's shape (default) +inlineEdit: 'grid' // editable line-item grid (fast bulk entry; thin children) +inlineEdit: 'form' // read-only list; "Add" / per-row edit opens the FULL form +``` + +- **`'grid'`** — spreadsheet-like editable grid. Best for *thin* line items + (invoice/order lines): few columns, high volume, keyboard-fast. +- **`'form'`** — compact read-only list; Add / per-row edit opens the child's + complete form. Best for *fat* children (long text, attachments, many fields) + that don't fit a narrow grid cell. +- **`true`** / omitted — **smart default**: picks `form` when the child has + rich/form-only fields (textarea, file, image, json, location…) or more than ~8 + editable fields, else `grid`. Set the string to override. + ### Detail-page related lists (the read-side mirror) Where `inlineEdit` is the **write** side (child pulled into the parent's entry diff --git a/skills/objectstack-ui/SKILL.md b/skills/objectstack-ui/SKILL.md index b0d4646b4..aefa6a1ab 100644 --- a/skills/objectstack-ui/SKILL.md +++ b/skills/objectstack-ui/SKILL.md @@ -71,8 +71,12 @@ custom page or form config. Prefer, in order: DATA MODEL — set `inlineEdit: true` on the child's `master_detail` field that references the parent (see the objectstack-data skill → Relationships → Inline Editing). Every standard New/Edit form for the parent (modal, drawer, - full-page) then auto-renders an editable child grid and saves parent + - children in one atomic `/api/v1/batch`. **No view metadata needed.** + full-page) then auto-renders the children and saves parent + children in one + atomic `/api/v1/batch`. **No view metadata needed.** The value picks the + form factor: `'grid'` (editable line-item grid — thin children), `'form'` + (read-only list whose Add / per-row edit opens the child's FULL form — fat + children with rich types), or `true` (smart default: `form` when the child + has rich/form-only fields or >~8 fields, else `grid`). 2. **Form view `subforms` (override / tuning).** Add to a form view only when you need to override the derived columns/order, or expose a child the