@object-ui/plugin-form 6.2.3 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,347 @@
1
1
  # @object-ui/plugin-form
2
2
 
3
+ ## 7.1.0
4
+
5
+ ### Patch Changes
6
+
7
+ - aae8791: Flow Screen preview: render inline master-detail subforms (follow-up to #1944)
8
+
9
+ The object-form mode of the Screen-node preview now renders inline master-detail
10
+ child grids, matching runtime. `ScreenPreview` feeds the SAME enriched object
11
+ list the runtime `FlowRunner` uses (`useMetadata().objects`, which derives
12
+ `form.subforms` from `inlineEdit` relationships via `attachInlineSubforms`), so
13
+ e.g. a `showcase_invoice` object-form step previews its **Line Items** grid
14
+ (with live Subtotal/Tax/Total) — only fetched in object-form mode.
15
+
16
+ To keep the preview non-persisting — consistent with the flat-field preview
17
+ (disabled Submit) and the simple object-form preview (no Save) — `MasterDetailForm`
18
+ now honours a `showSubmit` flag (default shown; backward-compatible) that
19
+ `ObjectForm` forwards, so the preview hides the master-detail Save bar. Also drops
20
+ a dead `e = formData` assignment in `ObjectForm` (lint `no-useless-assignment`).
21
+
22
+ - Updated dependencies [677f7ed]
23
+ - Updated dependencies [08c47da]
24
+ - Updated dependencies [a71be60]
25
+ - Updated dependencies [cb03bc3]
26
+ - @object-ui/types@7.1.0
27
+ - @object-ui/core@7.1.0
28
+ - @object-ui/react@7.1.0
29
+ - @object-ui/components@7.1.0
30
+ - @object-ui/fields@7.1.0
31
+ - @object-ui/permissions@7.1.0
32
+
33
+ ## 7.0.0
34
+
35
+ ### Minor Changes
36
+
37
+ - 5332639: feat(app-shell): render full object forms (incl. master-detail) in screen-flow wizard steps
38
+
39
+ `FlowRunner` now renders an `object-form` screen step: when the paused screen
40
+ carries `kind: 'object-form'`, it mounts the real `<ObjectForm>` for the named
41
+ object (auto-routing to `MasterDetailForm` for inline child collections),
42
+ prefilled from the step's `defaults`. The form persists itself (atomic
43
+ master-detail batch), then resumes the run with the saved record id bound to the
44
+ step's `idVariable`. `dataSource`/`objects` are threaded through all three
45
+ `FlowRunner` mount points.
46
+
47
+ Also fixes three pre-existing bugs this surfaced (each affects normal forms too):
48
+
49
+ - **plugin-form**: `ObjectForm` now forwards `initialValues`/`initialData` when
50
+ routing to `MasterDetailForm`, so prefilled header values are no longer
51
+ dropped on master-detail create forms.
52
+ - **fields**: `PercentField` treated values as `0–1` fractions (`value × 100`),
53
+ so a `0–100` field (e.g. `probability` default `50`) rendered as `5000%` —
54
+ exceeding `max=100`, which makes HTML5 constraint validation mark the field
55
+ `:invalid` and silently block the whole form's submit. It now treats a field
56
+ declaring `max > 1` as the `0–100` whole-number convention, matching the
57
+ read-side formatter.
58
+ - **data-objectstack**: `ObjectStackAdapter.batchTransaction` now sends
59
+ `credentials: 'include'`, so master-detail batch saves authenticate under the
60
+ console's cookie session (previously every batch save 401'd).
61
+
62
+ - 80c133c: Spreadsheet-style line-item grid editor.
63
+
64
+ `GridField`'s editable grid mode is reworked into an enterprise line-item editor (the QuickBooks / Stripe / NetSuite pattern), generalised across every inline grid:
65
+
66
+ - **Computed read-only columns** — a child field with an arithmetic `expression` (e.g. `amount = quantity * unit_price`) renders read-only, recomputes live as its inputs change, and writes the result back into the row so it persists and the running total reflects it. A small safe arithmetic evaluator (`+ - * / %`, parens, `record.<field>` refs; no `eval`) powers it.
67
+ - **Trailing "ghost" row** — start-with-one + auto-append: typing in the ghost materialises a real row (index-stable, so focus/caret survive), so you keep entering lines without clicking "Add".
68
+ - **Borderless click-to-focus cells** + role-based column widths (description flexes; qty/price/amount stay narrow).
69
+ - **Keyboard navigation** — Enter / ArrowUp / ArrowDown move between rows in the same column.
70
+ - Per-row "expand to full form" is gated to grids that omit fields (no redundant expand on thin lines).
71
+ - `deriveColumns` surfaces a field `expression` as a computed column; the running-total column prefers the computed/last-currency column. Blank/ghost rows are filtered from the persisted batch (`isBlankRow`).
72
+
73
+ - d16566f: Atomic master-detail create via the cross-object transactional batch endpoint (ObjectStack #1604).
74
+
75
+ When the server exposes the transactional batch endpoint, a NEW parent record and its child line items are now persisted in ONE server transaction — commit all or roll back all — instead of the previous client-orchestrated "create parent → create children → best-effort cleanup on failure" sequence.
76
+
77
+ **`@object-ui/data-objectstack` — `ObjectStackAdapter.batchTransaction(operations)`**
78
+
79
+ - New method posting `{ operations }` to `POST /api/v1/batch`. Operations run in one server transaction. A field value of `{ $ref: <earlier op index> }` resolves to that op's generated id, so a child can reference its parent created earlier in the same batch (master-detail FK). Throws `ObjectStackError('BATCH_ERROR')` on a non-2xx response.
80
+
81
+ **`@object-ui/plugin-form`**
82
+
83
+ - `MasterDetailForm` now detects `dataSource.batchTransaction` and, on a NEW parent, builds one atomic batch (parent at index 0, each child FK set to `{ $ref: 0 }`) via the new pure helper `buildMasterDetailBatch`. Client-side total rollups are merged into the parent payload before the batch. Edit mode and adapters without `batchTransaction` keep the existing client-orchestrated path.
84
+ - `ObjectForm` gained a `submitHandler` hook: when supplied, the form validates and hands the collected values to the host instead of calling `dataSource.create` / `dataSource.update`. `MasterDetailForm` uses it to own the atomic parent+children write while the parent fields are still rendered by `ObjectForm`.
85
+
86
+ **`@object-ui/types`**
87
+
88
+ - `ObjectFormSchema.submitHandler?: (values) => any | Promise<any>` — typed override for host-owned persistence.
89
+
90
+ Pairs with the framework-side ambient-transaction fix (ObjectQL `AsyncLocalStorage` transaction propagation) and the `/api/v1/batch` endpoint added in `@objectstack/rest`.
91
+
92
+ - 69510df: feat(master-detail): derive child columns + relationship FK from metadata
93
+
94
+ A master-detail child collection can now be configured with **just the child
95
+ object name** — the relationship FK and the editable grid columns are derived
96
+ from the child object's schema (via `DataSource.getObjectSchema`), instead of a
97
+ hand-authored columns block.
98
+
99
+ ```ts
100
+ // before: ~40 lines of columns + relationshipField
101
+ details: [{ childObject: 'task', relationshipField: 'project', columns: [ ...12 lines... ] }]
102
+ // after:
103
+ details: [{ childObject: 'task' }]
104
+ ```
105
+
106
+ - `relationshipField` is auto-detected from the child's `master_detail`/`lookup`
107
+ field that references the parent (master_detail preferred).
108
+ - `columns` are derived from the child's fields, skipping system/audit fields,
109
+ the back-reference FK, and non-editable types (formula/summary/autonumber/
110
+ file/json/…); select options and lookup references carry through.
111
+ - `amountField` (running-total source) defaults to the first numeric/currency
112
+ column.
113
+ - Any of these can still be set explicitly to override the derived defaults.
114
+ - Save is gated until derivation resolves; new pure helpers
115
+ (`deriveDetail`/`deriveColumns`/`findRelationshipField`) are unit-tested.
116
+
117
+ - b148daf: feat(master-detail): atomic EDIT via the cross-object batch endpoint
118
+
119
+ Edit mode now persists the parent update together with its child line-item
120
+ create/update/delete diffs in ONE server transaction (commit all or roll back
121
+ all), matching what create already did. Previously only create used the atomic
122
+ `/api/v1/batch` path; edit fell back to client-orchestrated writes with
123
+ best-effort cleanup.
124
+
125
+ - New pure helper `buildMasterDetailEditBatch(parentObject, parentId,
126
+ parentData, details)` — emits a parent `update` op (index 0) then diffs each
127
+ child collection against its loaded snapshot into `create` / `update` /
128
+ `delete` ops (children reference the known parent id directly, no `$ref`).
129
+ - `MasterDetailForm` now treats `canBatch` as available whenever the data
130
+ source exposes `batchTransaction` (create AND edit). `submitViaBatch` builds
131
+ create-ops or edit-ops by mode; `onSuccess` → `handleSaved` ("saved" toast,
132
+ no form reset in edit).
133
+
134
+ The server `/api/v1/batch` handler already supports `update`/`delete` actions,
135
+ and the adapter already forwards `action`/`id`, so this is a front-end change.
136
+ Unit-tested (parent update + child create/update/delete diff); the create path
137
+ remains verified by the live e2e.
138
+
139
+ - 90acb7f: Master-detail subform + lightweight list primitives (SDUI).
140
+
141
+ - `MasterDetailForm` (`object-master-detail-form`): enter a parent record and its child line items together; client-orchestrated transactional create (parent → FK → bulk children → rollup → cleanup). Enterprise-convention layout (header on top, line grid, single Save bar at the bottom).
142
+ - `LineItemsField` editable child grid (line numbers, right-aligned numerics, running total) and `LineItemsPanel` (`record:line_items`) for detail-page inline edit.
143
+ - `element:definition-list` and `element:repeater` — lightweight, low-chrome list primitives for simple data.
144
+
145
+ - 00f8d2d: Master-detail form: live Subtotal / Tax / Total stack.
146
+
147
+ `MasterDetailForm` now renders a right-aligned document totals stack under the line items when the parent form has a tax-rate field (`taxRateField`, default `tax_rate`): **Subtotal** (Σ line amounts) → **Tax** (header rate %) → **Total**, recomputed live as lines and the rate change. The header rate is read via scoped event delegation on the form host (no coupling into `ObjectForm` internals). When the stack is shown, the per-grid footer total is subsumed.
148
+
149
+ - 300d755: feat(form): inline master-detail in a plain ObjectForm via `subforms`
150
+
151
+ `ObjectFormSchema` gains a `subforms` array. When set, a regular `object-form`
152
+ renders as a master-detail form — the object's own fields on top, an editable
153
+ grid per child collection below, persisted together in one atomic transaction —
154
+ without a bespoke `object-master-detail-form` page.
155
+
156
+ ```ts
157
+ { type: 'object-form', objectName: 'expense_claim',
158
+ subforms: [{ childObject: 'expense_line' }] } // FK + columns auto-derived
159
+ ```
160
+
161
+ Each subform needs only `childObject` (relationship FK and columns are derived
162
+ from the child object's metadata; override with `relationshipField`/`columns`).
163
+ This is the config-driven, page-less way to express master-detail entry — a form
164
+ view can declare its child collections directly.
165
+
166
+ - 18728c1: Master-detail entry: lighter layout, compact lookup cells, persisted line order.
167
+
168
+ - **De-framed line-item section** — the subform no longer double-frames the grid in a `Card` (border + `p-6`); it renders as a light label + the grid's own bordered table, reclaiming the width the line table needs.
169
+ - **Compact lookup cells** — `LookupField` gains a `compact` mode (used by grid cells): the selected value shows inline in a borderless single-line trigger instead of a chip stacked above a separate "Select…" button.
170
+ - **Persisted drag-reorder** — `deriveMasterDetail` detects a sort field (`position`/`sort_order`/…), excludes it from the editable columns/row-form, and threads it as the grid's `sort_field` so reordering stamps `row[position] = index` and survives a reload.
171
+
172
+ - 8426db7: feat(form): standard New/Edit modal renders form-view subforms (Tier 0)
173
+
174
+ The console's standard create/edit record modal now renders inline child
175
+ collections when the object's form view declares `subforms` — master-detail
176
+ entry with **no bespoke page**, persisted as one atomic transaction.
177
+
178
+ - `ModalForm` (and the create/edit modal in app-shell `AppContent`) detects
179
+ `subforms` and renders `MasterDetailForm` inside the dialog (it owns its Save
180
+ bar; the modal footer is suppressed); on success the modal closes + refreshes.
181
+ - `AppContent` sources `subforms` from the object's default form view
182
+ (`form.subforms` / `formViews.default.subforms`).
183
+ - `ModalFormSchema` gains `subforms`.
184
+
185
+ With this, declaring `formViews.default.subforms: [{ childObject }]` is enough
186
+ to make an object's standard New/Edit screen a master-detail form — completing
187
+ the config-driven master-detail story (Tier 0 → derive everything from the
188
+ relationship + child metadata).
189
+
190
+ ### Patch Changes
191
+
192
+ - ddbe4a2: B2 step 3: client-side field-level conditional rules (`visibleWhen` / `readonlyWhen` / `requiredWhen`). The form renderer now evaluates these CEL predicates reactively against the live record and gates each field's visibility, read-only state, and required-ness accordingly. Evaluation delegates to the canonical `@objectstack/formula` `ExpressionEngine` — the _same_ dialect the server enforces (`requiredWhen` in the rule-validator, `readonlyWhen` in `stripReadonlyWhenFields`) — so the UX and the persisted verdict always agree. New core helpers `evalFieldPredicate` / `resolveFieldRuleState` (zero-React, fail-open). `FormField` gains `visibleWhen` / `readonlyWhen` / `requiredWhen` (+ deprecated `conditionalRequired` alias), and `ObjectForm` carries them through from object metadata.
193
+ - 2d47e94: B2 follow-ups (A): field conditional rules in inline grids + submit-time enforcement.
194
+
195
+ - **Grids**: a line-item column's `readonlyWhen` / `requiredWhen` CEL rule is now honored per row — `deriveMasterDetail` carries the props onto the `GridColumn` and `GridField` evaluates them against each row via `resolveFieldRuleState` (a `readonlyWhen`-TRUE cell locks; a `requiredWhen`-TRUE empty cell flags inline-invalid). Rules are row-scoped (`record.*`); the core helpers gained an optional `scope` (and `GridField` a `contextRecord` prop) so a future header-driven lock can bind `parent.*` — that wiring is deferred (it needs the master-detail header's re-renders isolated).
196
+ - **Submit enforcement**: `requiredWhen` already drove react-hook-form's `required` rule, so submit is blocked with a field error when the predicate is TRUE and the value is empty. Added a reactive cleanup so a stale _required_ error clears when the predicate flips FALSE (and all errors clear when a field is hidden by `visibleWhen`).
197
+
198
+ - f6044fa: feat(form): subforms in DrawerForm + full-page record form (Tier 0 everywhere)
199
+
200
+ Completes config-driven master-detail across all standard create/edit entry
201
+ points (after the modal in the previous change):
202
+
203
+ - `DrawerForm` now hosts `MasterDetailForm` inside the drawer when the schema
204
+ declares `subforms` (its own Save bar; closes + refreshes on success).
205
+ - `RecordFormPage` (full-page New/Edit) sources `subforms` from the object's
206
+ form view, so the full-page form renders inline child collections too.
207
+ - `ObjectForm`'s subforms shortcut now defers to the drawer/modal variants for
208
+ those formTypes (so they keep their envelope), and only renders the
209
+ master-detail form directly for inline/simple forms.
210
+
211
+ Declaring `formViews.default.subforms: [{ childObject }]` now yields a
212
+ master-detail experience in the modal, drawer, AND full-page form — no bespoke
213
+ page anywhere.
214
+
215
+ - ad8ade6: feat(components): metadata-derived field locators on generated forms (ADR-0054 Phase 4)
216
+
217
+ The form renderer now emits a stable `data-testid="field:{objectName}.{field}"`
218
+ (plus `data-field`) on every field wrapper, derived from the form's `objectName`
219
+ and each field's name — closing the locator gap at the source so every generated
220
+ form (`ObjectForm`/`ModalForm`/`DrawerForm`/`SplitForm`/`WizardForm`) inherits
221
+ testable fields with zero per-app work (ADR-0054 C4). `FormSchema` gains an
222
+ optional `objectName`; the object prefix is omitted (`field:{field}`) when a form
223
+ has none. `FormItem` now accepts `data-*` attributes.
224
+
225
+ - 3870c20: feat(forms): declarative `navigateOnSuccess` + `resetOnSuccess` on object-form
226
+
227
+ Rounds out declarative success behavior for metadata-only forms (which can't
228
+ pass an `onSuccess` function), complementing `successMessage`:
229
+
230
+ - **`navigateOnSuccess`** — after a successful create/update, navigate here.
231
+ Supports `{id}`/`{recordId}` interpolation from the saved record and is
232
+ same-origin-guarded; takes precedence over the toast (landing on the record
233
+ is the confirmation).
234
+ - **`resetOnSuccess`** — after a successful create, reset the form for another
235
+ entry (the wizard returns to a cleared step 1). Ignored when navigating.
236
+
237
+ Wired in both ObjectForm and WizardForm via a small shared `successBehavior`
238
+ helper (kept dependency-free to avoid an EmbeddableForm import cycle).
239
+
240
+ - b88c560: feat(forms): declarative `successMessage` on object-form
241
+
242
+ Metadata-only forms (a wizard/object-form authored as JSON) cannot pass an
243
+ `onSuccess` function, so the post-create/update feedback was a fixed
244
+ "Created"/"Saved" toast. `ObjectFormSchema` now accepts `successMessage`, which
245
+ ObjectForm and WizardForm use for the default success toast when no `onSuccess`
246
+ handler is supplied. Falls back to "Created"/"Saved".
247
+
248
+ - 7913390: fix(master-detail): never silent on save — feedback, reset, and a duplicate-submit guard
249
+
250
+ `MasterDetailForm`'s "Create" submitted successfully but gave **no feedback**: no toast, no form reset, no navigation. A successful create looked broken, and re-clicking created duplicate records.
251
+
252
+ - On success: a `toast.success`, and on create the form clears (line items reset + parent `<ObjectForm>` remounts) ready for the next entry. A page-supplied `onSuccess` still runs afterwards (e.g. to navigate).
253
+ - On failure (validation / network / atomic rollback): a `toast.error` surfaces the message instead of failing silently.
254
+ - In-flight guard: the Create button shows "Saving…" and is disabled while a submit is running, preventing duplicate submissions, with a safety release if client-side validation blocks the submit.
255
+ - `@object-ui/components` now re-exports `toast` (alongside `Toaster`) from its sonner wrapper.
256
+
257
+ Tests: two new `MasterDetailForm` tests assert success → toast + form clear, and failure → error toast.
258
+
259
+ - 514f426: fix(master-detail): reliable submit + stable e2e hooks
260
+
261
+ Fixes the "click Create, nothing happens" report, surfaced by a new live browser
262
+ e2e harness that drives the form with real input.
263
+
264
+ - **MasterDetailForm `handleSave`** now triggers the button-less parent form's
265
+ submit from a deferred macrotask and re-queries the live `<form>` inside it.
266
+ Calling `requestSubmit()` synchronously inside the click handler (right after
267
+ the `setSaving` state update) intermittently dropped the nested submit event,
268
+ so react-hook-form's `onSubmit` never ran and the click appeared to do nothing
269
+ — only the occasional click got through. Deferring makes it fire every time.
270
+ - **Stable `data-testid`s** so automation/e2e can drive the widgets
271
+ deterministically (Radix Select + react-hook-form cannot be driven by
272
+ synthetic DOM events): `select-trigger-{field}` / `select-option-{value}`
273
+ (SelectField), `lookup-trigger-{field}` (LookupField), `line-items-add`
274
+ (GridField), `md-form-submit` / `md-form-cancel` (MasterDetailForm).
275
+
276
+ - 586a027: B2 follow-up (#1581): parent-scoped conditional rules in inline grids — "paid invoice → lock lines". `MasterDetailForm` now binds the live header record to every line-item grid as `parent`, so a column's `readonlyWhen` / `requiredWhen` CEL rule can react to the header (e.g. `parent.status == 'paid'` locks quantity / unit price / product when the invoice is paid). The line grids + document totals moved into a dedicated `<MasterDetailLines>` child that owns the scraped header record, so a header edit re-renders only the lines and never resets the header `ObjectForm`'s react-hook-form state mid-edit; the scrape is deduped by value to avoid needless churn. (`@object-ui/fields`' `GridField.contextRecord` and column-rule derivation already existed — this wires the last link.)
277
+ - 9aac2b8: feat(form): modal forms can host a tabbed layout (modal + tabbed composes)
278
+
279
+ `ModalForm` rendered sections as a flat vertical stack — a modal create/edit
280
+ form could never be tabbed, because `formType` (one field) couldn't be both
281
+ `modal` (container) and `tabbed` (layout). Per ADR-0050 (additive first), the
282
+ modal container now accepts a `contentLayout` ('simple' | 'tabbed'): when
283
+ `tabbed`, sections render as tabs inside the dialog. The console record
284
+ New/Edit modal (`AppContent`) forwards the default form view's layout, so a
285
+ `type:'tabbed'` form view now renders tabbed in the modal too — not just on the
286
+ full-page route (#1762). Non-breaking; `FormView.type` enum unchanged.
287
+
288
+ Refs objectstack-ai/framework#1890, ADR-0050
289
+
290
+ - 650bd1f: fix(forms/dashboard/related-list): four business-facing rendering fixes found while QA-ing a showcase workspace
291
+
292
+ - **plugin-form / WizardForm**: a multi-step `object-form` with `formType: 'wizard'` posted an empty/partial body on submit, so the server rejected every required field. Two causes: (1) the footer Next/Create buttons bypassed the inner form and submitted the wizard's own (never-collected) `formData`; (2) the create-mode data-seeding effect re-ran on `dataSource`/`objectSchema` identity churn and reset `formData` to `{}` mid-wizard. Now the buttons submit the inner form natively (`<form id>` + `type="submit"`, which validates each step and collects values via `getValues()`), and the create seed is made idempotent.
293
+ - **plugin-dashboard / DashboardRenderer**: chart widgets rendered as empty cards (recharts logged `width(-1) height(-1)`) because the positioned grid used `auto-rows-min`, collapsing any widget with no intrinsic height. The explicit-columns grid now uses `gridAutoRows: minmax(5rem, auto)` so spanned chart rows get a real height while tables can still grow.
294
+ - **plugin-detail / RelatedList**: auto-derived related-list columns led with system audit fields (`created_at`, `updated_at`, …) for child objects without a name/title field, pushing business columns past the column cap. System audit fields are now sorted last.
295
+ - **plugin-form / ObjectForm + WizardForm**: a successful create/update gave no feedback for metadata-only pages (which can't pass an `onSuccess` function). They now show a default `toast.success('Created'/'Saved')` when no `onSuccess` handler is supplied (guarded so a `submitHandler` host like MasterDetailForm never double-toasts).
296
+
297
+ - Updated dependencies [5976ba3]
298
+ - Updated dependencies [a00e16d]
299
+ - Updated dependencies [eaccefd]
300
+ - Updated dependencies [f7f325d]
301
+ - Updated dependencies [c12986e]
302
+ - Updated dependencies [71d7ce0]
303
+ - Updated dependencies [053c948]
304
+ - Updated dependencies [ddbe4a2]
305
+ - Updated dependencies [2d47e94]
306
+ - Updated dependencies [9049bbe]
307
+ - Updated dependencies [6c0c92c]
308
+ - Updated dependencies [cb2fdb1]
309
+ - Updated dependencies [c3749eb]
310
+ - Updated dependencies [6cfa330]
311
+ - Updated dependencies [ad8ade6]
312
+ - Updated dependencies [d54346c]
313
+ - Updated dependencies [5332639]
314
+ - Updated dependencies [3870c20]
315
+ - Updated dependencies [2eb3096]
316
+ - Updated dependencies [b88c560]
317
+ - Updated dependencies [bd398df]
318
+ - Updated dependencies [66ed3ad]
319
+ - Updated dependencies [c6445b6]
320
+ - Updated dependencies [80c133c]
321
+ - Updated dependencies [5e1b838]
322
+ - Updated dependencies [d16566f]
323
+ - Updated dependencies [90acb7f]
324
+ - Updated dependencies [7913390]
325
+ - Updated dependencies [514f426]
326
+ - Updated dependencies [1394e34]
327
+ - Updated dependencies [e95cc25]
328
+ - Updated dependencies [abe8ebc]
329
+ - Updated dependencies [300d755]
330
+ - Updated dependencies [bd8b054]
331
+ - Updated dependencies [4eb9cb6]
332
+ - Updated dependencies [7c239fd]
333
+ - Updated dependencies [858ad94]
334
+ - Updated dependencies [2270239]
335
+ - Updated dependencies [db8cd00]
336
+ - Updated dependencies [18728c1]
337
+ - Updated dependencies [8d1195d]
338
+ - @object-ui/core@7.0.0
339
+ - @object-ui/components@7.0.0
340
+ - @object-ui/react@7.0.0
341
+ - @object-ui/types@7.0.0
342
+ - @object-ui/fields@7.0.0
343
+ - @object-ui/permissions@7.0.0
344
+
3
345
  ## 6.2.3
4
346
 
5
347
  ### Patch Changes
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FormField, DataSource } from '../../types/src';
2
+ import { FormField, DataSource } from '@object-ui/types';
3
3
  export interface DrawerFormSectionConfig {
4
4
  name?: string;
5
5
  label?: string;
@@ -53,6 +53,19 @@ export interface DrawerFormSchema {
53
53
  onError?: (error: Error) => void;
54
54
  onCancel?: () => void;
55
55
  className?: string;
56
+ /** Inline child collections — renders the drawer as an atomic master-detail
57
+ * form. Each entry needs only `childObject` (FK + columns derived). */
58
+ subforms?: Array<{
59
+ childObject: string;
60
+ relationshipField?: string;
61
+ columns?: any[];
62
+ amountField?: string;
63
+ totalField?: string;
64
+ title?: string;
65
+ addLabel?: string;
66
+ minRows?: number;
67
+ maxRows?: number;
68
+ }>;
56
69
  }
57
70
  export interface DrawerFormProps {
58
71
  schema: DrawerFormSchema;
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { DataSource, FormField } from '../../types/src';
2
+ import { DataSource, FormField } from '@object-ui/types';
3
3
  export interface EmbeddableFormTexts {
4
4
  submit?: string;
5
5
  submitting?: string;
@@ -0,0 +1,20 @@
1
+ import { default as React } from 'react';
2
+ import { GridColumn } from '@object-ui/fields';
3
+ export interface LineItemsPanelSchema {
4
+ type?: 'record:line_items';
5
+ childObject: string;
6
+ relationshipField: string;
7
+ columns: GridColumn[];
8
+ parentObject?: string;
9
+ parentId?: string;
10
+ recordId?: string;
11
+ amountField?: string;
12
+ totalField?: string;
13
+ title?: string;
14
+ readonly?: boolean;
15
+ minRows?: number;
16
+ maxRows?: number;
17
+ }
18
+ export declare const LineItemsPanel: React.FC<{
19
+ schema: LineItemsPanelSchema;
20
+ }>;
@@ -0,0 +1,70 @@
1
+ import { default as React } from 'react';
2
+ import { DataSource } from '@object-ui/types';
3
+ import { GridColumn } from '@object-ui/fields';
4
+ import { InlineMode } from './deriveMasterDetail';
5
+ export interface MasterDetailDetailConfig {
6
+ /** Child object name, e.g. 'expense_line'. */
7
+ childObject: string;
8
+ /** FK field on the child pointing back to the parent, e.g. 'expense_claim'.
9
+ * Optional — auto-detected from the child's master_detail/lookup field that
10
+ * references the parent object when omitted. */
11
+ relationshipField?: string;
12
+ /** Editable columns for the child grid. Optional — derived from the child
13
+ * object's fields (via DataSource.getObjectSchema) when omitted. */
14
+ columns?: GridColumn[];
15
+ /** Field names for the per-row expand form. Optional — derived from the child
16
+ * object's fields (broader than `columns`: includes rich types) when omitted. */
17
+ formFields?: string[];
18
+ /** Inline-edit form factor: 'grid' = editable cells; 'form' = read-only list +
19
+ * per-row full form. Optional — resolved from the relationship's `inlineEdit`
20
+ * (incl. the smart default) when omitted. */
21
+ inlineMode?: InlineMode;
22
+ /** Numeric child column to sum, e.g. 'amount'. */
23
+ amountField?: string;
24
+ /** Child field holding the line sort position — stamped on drag-reorder so
25
+ * order persists. Auto-derived from a `position`/`sort_order`/… field. */
26
+ sortField?: string;
27
+ /** Parent field to receive the rolled-up sum, e.g. 'total_amount'. */
28
+ totalField?: string;
29
+ /** Section title. */
30
+ title?: string;
31
+ minRows?: number;
32
+ maxRows?: number;
33
+ addLabel?: string;
34
+ }
35
+ export interface MasterDetailFormSchema {
36
+ type?: 'object-master-detail-form';
37
+ /** Parent object name, e.g. 'expense_claim'. */
38
+ objectName: string;
39
+ mode?: 'create' | 'edit';
40
+ recordId?: string;
41
+ /** Prefilled parent header values (create mode) — seeds the parent form's
42
+ * initial values, e.g. a conversion wizard carrying the lead/account over. */
43
+ initialValues?: Record<string, any>;
44
+ initialData?: Record<string, any>;
45
+ /** Parent form sections/fields — passed straight through to ObjectForm. */
46
+ sections?: any[];
47
+ fields?: any[];
48
+ formType?: 'simple' | 'tabbed';
49
+ title?: string;
50
+ submitText?: string;
51
+ /** Hide the bottom Save/Cancel action bar — e.g. a non-persisting design
52
+ * preview. Defaults to shown (the form owns the only Save in this layout). */
53
+ showSubmit?: boolean;
54
+ /** One or more child collections. */
55
+ details: MasterDetailDetailConfig[];
56
+ /** Parent header field holding a tax rate (percent). When the parent form has
57
+ * this field, a live Subtotal / Tax / Total stack renders under the lines.
58
+ * Defaults to `tax_rate`; the stack only appears if the field is present. */
59
+ taxRateField?: string;
60
+ onSuccess?: (parent: any) => void | Promise<void>;
61
+ onError?: (err: Error) => void;
62
+ onCancel?: () => void;
63
+ className?: string;
64
+ }
65
+ export interface MasterDetailFormProps {
66
+ schema: MasterDetailFormSchema;
67
+ dataSource?: DataSource;
68
+ className?: string;
69
+ }
70
+ export declare const MasterDetailForm: React.FC<MasterDetailFormProps>;
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FormField, DataSource } from '../../types/src';
2
+ import { FormField, DataSource } from '@object-ui/types';
3
3
  export interface ModalFormSectionConfig {
4
4
  name?: string;
5
5
  label?: string;
@@ -16,6 +16,9 @@ export interface ModalFormSchema {
16
16
  title?: string;
17
17
  description?: string;
18
18
  sections?: ModalFormSectionConfig[];
19
+ /** Internal content layout (ADR-0050, #1890): 'tabbed' renders sections as
20
+ * tabs inside the modal, so "modal + tabbed" composes. Default stacks them. */
21
+ contentLayout?: 'simple' | 'tabbed';
19
22
  fields?: string[];
20
23
  customFields?: FormField[];
21
24
  /**
@@ -50,6 +53,19 @@ export interface ModalFormSchema {
50
53
  onError?: (error: Error) => void;
51
54
  onCancel?: () => void;
52
55
  className?: string;
56
+ /** Inline child collections — renders the modal as an atomic master-detail
57
+ * form. Each entry needs only `childObject` (FK + columns derived). */
58
+ subforms?: Array<{
59
+ childObject: string;
60
+ relationshipField?: string;
61
+ columns?: any[];
62
+ amountField?: string;
63
+ totalField?: string;
64
+ title?: string;
65
+ addLabel?: string;
66
+ minRows?: number;
67
+ maxRows?: number;
68
+ }>;
53
69
  }
54
70
  export interface ModalFormProps {
55
71
  schema: ModalFormSchema;
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { ObjectFormSchema, DataSource } from '../../types/src';
2
+ import { ObjectFormSchema, DataSource } from '@object-ui/types';
3
3
  export interface ObjectFormProps {
4
4
  /**
5
5
  * The schema configuration for the form
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FormField, DataSource } from '../../types/src';
2
+ import { FormField, DataSource } from '@object-ui/types';
3
3
  export interface SplitFormSectionConfig {
4
4
  name?: string;
5
5
  label?: string;
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FormField, DataSource } from '../../types/src';
2
+ import { FormField, DataSource } from '@object-ui/types';
3
3
  export interface FormSectionConfig {
4
4
  /**
5
5
  * Section identifier (used as tab value)
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { DataSource } from '../../types/src';
2
+ import { DataSource } from '@object-ui/types';
3
3
  import { FormSectionConfig } from './TabbedForm';
4
4
  export interface WizardFormSchema {
5
5
  type: 'object-form';
@@ -44,6 +44,18 @@ export interface WizardFormSchema {
44
44
  * Submit button text (shown on last step)
45
45
  */
46
46
  submitText?: string;
47
+ /**
48
+ * Declarative success toast text shown when no `onSuccess` handler is given
49
+ * (metadata pages cannot pass a function). Falls back to 'Created'/'Saved'.
50
+ */
51
+ successMessage?: string;
52
+ /** Navigate here after a successful create/update (declarative; metadata
53
+ * pages can't pass onSuccess). Supports `{id}`/`{recordId}` interpolation
54
+ * from the saved record. Same-origin-guarded. Takes precedence over the toast. */
55
+ navigateOnSuccess?: string;
56
+ /** Reset the wizard (back to step 1, cleared) after a successful create so the
57
+ * user can enter another. Ignored when `navigateOnSuccess` is set. */
58
+ resetOnSuccess?: boolean;
47
59
  /**
48
60
  * Show cancel button
49
61
  * @default true
@@ -1,4 +1,4 @@
1
- import { FormField } from '../../types/src';
1
+ import { FormField } from '@object-ui/types';
2
2
  /**
3
3
  * Check if a field type is "wide" (should span full row in multi-column layout).
4
4
  */