Skip to content

Commit ea1751b

Browse files
feat(fields): a lookup pick fills the fields its lookupAutofill declares, in the record form and the line-item grid (#28322)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} --> _Requested by **Tesla** · [project thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoKfz877ZMAsxYB1FQDnMtZw)_ Fixes #28245 Clause-②: yes (widening: `@object-ui/fields` gains the widget prop `onAutofill`, the `GridField` prop `lookupAutofill`, an optional fourth argument on `lookupAutofillPatch`, and the exports `declaredLookupAutofillPatch`, `lookupAutofillByField`, `LookupAutofillPair`, `LookupAutofillByField`; `@object-ui/types` `LookupFieldMetadata` gains `lookupAutofill`; measured on the built declarations; no existing member, key or shape changes) Metadata-protocol: none ## 维护者速读 - 改了什么:查找字段(lookup)选中一条记录后,按字段上声明的 `lookupAutofill` 立刻把对应字段填进表单;明细行网格里选产品时同样生效(例如报价明细选产品,单价立即带出产品的标价),不再要等保存后服务端钩子才填。 - 为什么改:#28244 已在 spec 里声明了 `lookupAutofill`,但界面上没有任何读取方,作者写了也不生效;本 PR 是它的 ui 一半,并把 liveness 行从 planned 改为 live。 - 风险与回滚:默认不覆盖用户自己填的值,只填空字段或上一次选择填进去的值;只有声明了 `overwrite: true` 才覆盖。没有声明该键的查找字段行为不变。网格里从「浏览全部」弹窗选记录现在也会走同名字段复制(之前只写 id)。回滚即 revert 本 PR。 - 先看:`ui/packages/fields/src/widgets/lookupAutofill.ts`(填充规则,唯一一处),`ui/packages/fields/src/widgets/LookupField.tsx` 的 `fillDeclaredTargets` 与 `handleDialogSelect`,`ui/packages/fields/src/widgets/GridField.tsx` 的 `applyLookupSelection`。 - 两分钟验证:`pnpm --dir ui exec vitest run packages/plugin-form/src/ObjectForm.lookupAutofill-28245.test.tsx packages/fields/src/widgets/GridField.lookupAutofill-28245.test.tsx`,预期 2 个文件 17 passed。 - 需要你做的:无,席位落地。 ## What changes `FieldSchema.lookupAutofill` (`[{ target, source, overwrite? }]` on a single-value `lookup` / `master_detail`, declared by #28244) gets its reader in the record form and in the line-item grid. - **One fill rule** — `ui/packages/fields/src/widgets/lookupAutofill.ts#declaredLookupAutofillPatch`. Each `target` takes the picked record's `source`. Without `overwrite: true` a target is filled only while it is empty or still holds the value the earlier pick filled in (the record the lookup held from an earlier pick, read at the same `source`); a value the user typed, or one the record already stored, stays. `overwrite: true` replaces on every pick. A `source` the picked row does not carry (not served to this reader) writes nothing. `lookupAutofillByField` reads an object's pairs by lookup field name, single-value relationships only. - **Record form** — `LookupField#fillDeclaredTargets` runs on every single-value pick path the form owns: the dropdown, the keyboard, quick-create and the browse-all dialog (the dialog commits its id through `onChange`, then hands the row to `onSelectRecords`, where the fill runs). Current values are the host's live record (`dependentValues`); the earlier-pick memory is the widget's own `pickedRecords`, so it is local to the form instance. The writer is a new widget prop, `onAutofill(patch)`, declared on `FieldWidgetComponentProps` and added to `toHostProps`' host-plumbing list. - **Form renderer** — `ui/packages/components/src/renderers/form/form.tsx` hands `lookup` / `master_detail` widgets `onAutofill`, which writes each target with `form.setValue(..., { shouldDirty: true })` (re-validating a target already in error), and passes it through `stripRegisteredFieldProps` for those two types only. Nothing in the submit or save path changes. - **Line-item grid** — `GridField` takes the child object's pairs through a new host prop, `lookupAutofill` (an `InlineGridColumnSchema` column is spec-strict and carries no field definition). `MasterDetailForm` keeps them on each resolved entry beside `sortField`; `LineItemsPanel` derives them from the child schema it already loads. `lookupAutofillPatch` applies a declared pair whatever the column's `autofill` says and takes its target out of the same-named copy, so a target has one writer per pick. The earlier-pick memory is a per-grid map keyed by column and the row's own lookup value, so adding, removing or reordering rows cannot misattribute it. - **Browse-all dialog in the grid** — `LookupField#handleDialogSelect` holds back the dialog's bare-id `onSelect` when the host owns the pick (`onSelectRecord`), and `handleDialogSelectRecords` hands the host the record once, so the grid's autofill runs on that path too. Clearing and multi-value picks commit through `onChange` as before. The PeoplePicker path is unchanged. - **Types** — `LookupFieldMetadata.lookupAutofill` is typed `SpecField['lookupAutofill']`. - **Liveness** — `packages/spec/liveness/field.json` `lookupAutofill` (and its three children) moves `planned` → `live`, citing the read points above and the two test files. - **Changeset** — `.changeset/28245-lookup-autofill-fill.md`, `patch` on the four `@object-ui/*` packages and `@objectstack/spec` (the ledger ships in its `files[]`). ## Tests - `ui/packages/plugin-form/src/ObjectForm.lookupAutofill-28245.test.tsx` — end to end through `ObjectForm` (schema with the pairs → form renderer → `LookupField`): a pick fills an empty target and the filled value is saved; a typed value is kept while an `overwrite: true` pair replaces its own; a value an earlier pick filled is replaced by the next pick and kept once the user edits it; a browse-all dialog pick fills; control: a lookup without the key fills nothing, not even a same-named field. - `ui/packages/fields/src/widgets/GridField.lookupAutofill-28245.test.tsx` — the pure patch (empty target from the declared source rather than the same-named field; a written value kept because the declared target leaves the same-named copy; `overwrite: true`; earlier pick replaced; applies under `autofill: false`; control: no pair = the same-named copy, unchanged) and the grid itself (a cell pick fills the row and the next pick replaces it; a browse-all pick reaches the grid once and fills; a stored line's own price is kept). - **Ablation** (one-shot, at `0d887457`, `node scripts/ablation-replace.mjs`): the write in `declaredLookupAutofillPatch` (`patch[target] = picked[source]`) replaced by a no-op → **10 failed | 7 passed** of the 17 (every fill case red; the keep and control cases stay green, as they should); restored, blob equal to HEAD, `git diff HEAD` empty. - **Reverse type check**: a `lookupAutofill={{ product: [{ target: 'unit_price' }] }}` grid prop in `@object-ui/plugin-form`, compiled against the rebuilt `@object-ui/fields` declarations → `TS2741: Property 'source' is missing`; file removed. ## Acceptance notes - **Surface extension, agreed with the seat before editing.** The order's surface named `ui/packages/plugin-form/**` as the form renderer, but the record form's state lives in `ui/packages/components/src/renderers/form/form.tsx` (react-hook-form), and no widget could write a sibling field. That file carries only the writer prop and its `stripRegisteredFieldProps` pass-through. `ui/packages/types/src/field-types.ts` carries only the by-reference `lookupAutofill` declaration. - **Bound: a fully authored master-detail collection.** `MasterDetailForm` skips the child-schema fetch for an entry whose relationship and every column are authored, so such a grid sees no pairs and fills only the same-named copy. Loading the child schema for every such entry is a fetch-policy change outside this card. Derived, hydrated and `LineItemsPanel` grids are covered. - **Earlier-pick memory is per widget / grid instance.** A remount forgets it, which errs toward keeping the target's value, never toward overwriting it. - **A target the caller may not edit is not saved.** Read in `sanitizeFormData` (`stripUnwritable` with `canEdit`): the form's save drops a field the caller may not edit, so a fill into such a target never reaches the server. The fill does not check field-level security itself. - **Behaviour change on an existing path:** in a line-item grid, a pick from the browse-all dialog used to write only the id; it now runs the column's same-named copy as the dropdown always has. - **#28246 (skills/docs) must merge after this PR, not with it.** It edits `skills/**` (governed, Tier H), so riding along would make this PR Tier H; its card already carries `Blocked-by: #28245`. Its text should also state the fully authored master-detail bound above. ## Verification All readings at head `12601c27d` unless a step names another commit. - **New tests** — `pnpm --dir ui exec vitest run packages/fields/src/widgets/GridField.lookupAutofill-28245.test.tsx packages/plugin-form/src/ObjectForm.lookupAutofill-28245.test.tsx` → Test Files 2 passed (2), Tests 17 passed (17). - **Touched packages' suites** — `pnpm --dir ui exec vitest run packages/fields/ packages/plugin-form/ packages/components/src/renderers/form/ packages/types/` (under `os-verify-lock`, at `0d887457`; later commits touch only the liveness ledger and changeset) → Test Files 973 passed | 1 skipped (974), Tests 17791 passed | 25 skipped (17816), VERDICT command-exit 0. - **Type-check** — `pnpm --filter @object-ui/types --filter @object-ui/fields --filter @object-ui/components --filter @object-ui/plugin-form run type-check` against the rebuilt `@object-ui/plugin-form^...` closure (full declarations) → VERDICT command-exit 0. - **Clause-② measure** — on the built declarations: `ui/packages/fields/dist/widgets/types.d.ts` carries `onAutofill`, `dist/widgets/GridField.d.ts` the `lookupAutofill` prop and the fourth `lookupAutofillPatch` argument, `dist/widgets/lookupAutofill.d.ts` the new exports; `ui/packages/types/dist/field-types.d.ts` carries `lookupAutofill`. Additions only. - **Lint** — `eslint --no-inline-config` over the 12 changed `ui/` source and test files → 0 errors. - **Derived gates** — `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, every line run with stdin closed, exit codes landed on disk first, reconciled with `--ran`: `70 derived famil(ies) accounted for — 69 run, 1 NOT-MEASURED`, exit 0. Every run family exits 0, including `check:liveness`, `check:generated`, `check:sdui-manifest` (+ `--self-test`), `check:type-check-debt`, `check:test-source-alias`, `check:cross-package-test-inputs` and `pnpm --dir ui exec vitest run scripts/`. - `check:liveness` first went red on the generated `liveness/state-counts/field.md` shard (planned 9 → 6, live 100 → 103); regenerated with `gen:liveness-counts` in `12601c27d`, then green. - `check-plugin-teardown-shape --self-test` first answered exit 3 on the shallow clone; after fetching its pinned fixture commit, 48 cases pass, exit 0. - NOT MEASURED: `check:dual-build-cjs-loads`, exit 3 (PREREQUISITE NOT MET — it needs every package's `dist`, a full `pnpm build`); the diff touches no `@objectstack/*` package entry. CI runs it. - `pnpm --dir ui run check:new-line-citations` → 0 new citations, exit 0. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; `pnpm lint` passes. - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. - [x] 6. `packages/spec` touched: its generated artifacts are regenerated and committed. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. - [x] The *Before Submitting* list in CONTRIBUTING.md is done. --- _Generated by [Claude Code](https://claude.ai/code/session_01L9qibzEPqxBYQbxn2d72r5)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5da3a8c commit ea1751b

17 files changed

Lines changed: 685 additions & 26 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@object-ui/fields': patch
3+
'@object-ui/components': patch
4+
'@object-ui/types': patch
5+
'@object-ui/plugin-form': patch
6+
'@objectstack/spec': patch
7+
---
8+
9+
feat(fields): a lookup pick fills the fields its `lookupAutofill` declares, in the record form and in the line-item grid
10+
11+
Clause-②: yes (widening)
12+
Metadata-protocol: none
13+
14+
`FieldSchema.lookupAutofill` (`[{ target, source, overwrite? }]` on a single-value `lookup` / `master_detail`) now has its reader. Picking a record writes each declared `target` of the record being edited from the picked record's `source` — a quote line's `unit_price` from the picked product's `list_price` — before save, where a server hook used to be the only filler.
15+
16+
- **The overwrite rule is the spec's.** Without `overwrite: true` a pick fills a target only while it is empty or still holds the value an earlier pick in the same form (or grid row) filled in; a value the user typed, or one the record already stored, stays. A pair declaring `overwrite: true` replaces the target on every pick. A `source` the picked record does not carry (a field the reader may not read is not served) writes nothing.
17+
- **Record form.** `LookupField` applies the pairs on every single-value pick path: the dropdown, the keyboard, quick-create and the browse-all dialog. The form renderer hands it a new widget prop, `onAutofill(patch)`, which writes the targets as edits of the form (`setValue`, dirty), so the filled values are saved with the record.
18+
- **Line-item grid.** `GridField` takes the child object's pairs through a new host prop, `lookupAutofill` (lookup field name → pairs, built with the new `lookupAutofillByField`), which `MasterDetailForm` and `LineItemsPanel` pass from the child schema they load. The column key `autofill` is unchanged and still governs only the same-named copy; a declared pair applies whatever it says and takes its target out of that copy, so one target has one writer per pick. A pick from the grid's browse-all dialog now reaches the grid once, with its record, so both copies run on that path too.
19+
- **Types.** `LookupFieldMetadata.lookupAutofill` is declared by reference to the spec. New exports from `@object-ui/fields`: `declaredLookupAutofillPatch`, `lookupAutofillByField`, `LookupAutofillPair`, `LookupAutofillByField`; `lookupAutofillPatch` takes an optional fourth argument carrying the declared pairs.
20+
- **Liveness.** The `lookupAutofill` row of `@objectstack/spec`'s field ledger moves from `planned` to `live`, citing these readers.
21+
22+
A master-detail collection whose relationship and every column are authored loads no child schema, so its grid fills only the same-named copy.

‎packages/spec/liveness/field.json‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -382,26 +382,26 @@
382382
"note": "objectui LookupField — opt-in inline quick-create (dataSource.create from typed text). Read camelCase-only at objectui @daeb77fc84: packages/fields/src/widgets/LookupField.tsx#explicitAllowCreate (objectui#12157 retired the `allow_create` read)."
383383
},
384384
"lookupAutofill": {
385-
"status": "planned",
386-
"verifiedAt": "2026-10-30",
385+
"status": "live",
386+
"verifiedAt": "2026-10-31",
387387
"evidenceScope": "in-repo",
388-
"evidence": "packages/spec/src/data/field.zod.ts#lookupAutofill (the parse-time door, field half: the FieldSchema superRefine refuses the key off a single-value `lookup` / `master_detail`); packages/spec/src/data/object.zod.ts#refuseDishonestLookupAutofill (the parse-time door, target half: each target a declared, author-written field of the same object, not the lookup itself, named once); packages/spec/src/stack.zod.ts#collectLookupAutofillErrors (the `os validate` / `os build` door, source half: a declared, non-`internal` field of the referenced object whose value the target holds); packages/spec/src/data/field-lookup-autofill.test.ts (the accept and refuse pins)",
389-
"note": "PLANNED — declared ahead of its one reader by the card's own sequencing (#27945 is the spec half; the ui half, the record form's lookup widget and the line-item grid writing the declared targets on pick, is a separate domain:ui card). PRODUCER: HotCRM's quote line item (product lookup, `unit_price` from the product's `list_price`), today filled only after save by a server hook. CONSUMER: ui/packages/fields LookupField (the form) and ui/packages/fields/src/widgets/GridField.tsx#lookupAutofillPatch (the grid), which today copies only same-named fields under the column key `autofill`. Measured in this repo at 3a2f4c00: no reader of `lookupAutofill` anywhere outside packages/spec, ui/ included (the key is new). No `authorWarn`, on the `referenceDeletedWhen` precedent: the authoring half (parse, target and source checks) is enforced now, an author who sets it today re-authors nothing when the reader lands, and a warned field row would reopen the lint's field walk for one key. Flip to `live` when the ui card lands, citing both read points.",
388+
"evidence": "ui/packages/fields/src/widgets/lookupAutofill.ts#declaredLookupAutofillPatch (the one fill rule: each target from the picked record's source, filled only while empty or still holding the earlier pick's value unless `overwrite: true`); ui/packages/fields/src/widgets/LookupField.tsx#fillDeclaredTargets (the record form: every single-value pick path, popover, keyboard, quick-create and the browse-all dialog, reads the field's pairs and writes through the host's `onAutofill`); ui/packages/components/src/renderers/form/form.tsx#LOOKUP_AUTOFILL_WIDGET_TYPES (the form renderer hands a lookup its `onAutofill` writer, `form.setValue` as an edit); ui/packages/fields/src/widgets/GridField.tsx#lookupAutofillPatch (the line-item grid: the declared pair fills under the same rule and takes its target out of the same-named `autofill` copy), its pairs handed over by ui/packages/plugin-form/src/MasterDetailForm.tsx#lookupAutofill and ui/packages/plugin-form/src/LineItemsPanel.tsx#lineLookupAutofill through ui/packages/fields/src/widgets/lookupAutofill.ts#lookupAutofillByField; authoring doors unchanged: packages/spec/src/data/field.zod.ts#lookupAutofill, packages/spec/src/data/object.zod.ts#refuseDishonestLookupAutofill, packages/spec/src/stack.zod.ts#collectLookupAutofillErrors",
389+
"note": "LIVE — read by the record form and the line-item grid on a pick. Pinned end to end by ui/packages/plugin-form/src/ObjectForm.lookupAutofill-28245.test.tsx (an empty target fills and is saved, a typed value is kept, `overwrite: true` replaces, an earlier pick's value is replaced, the browse-all dialog fills too, a lookup without the key fills nothing) and ui/packages/fields/src/widgets/GridField.lookupAutofill-28245.test.tsx (the same rule in a grid row, the declared target out of the same-named copy, a stored line's own value kept). Bound: a master-detail collection whose relationship and every column are authored loads no child schema, so its grid fills only the same-named copy. PRODUCER: HotCRM's quote line item (product lookup, `unit_price` from the product's `list_price`).",
390390
"children": {
391391
"target": {
392-
"status": "planned",
393-
"verifiedAt": "2026-10-30",
394-
"note": "The field of the declaring object a pick fills. Judged at parse by the container's evidence (snake_case; declared on the same object, not the lookup itself, not readonly, not computed, named once); written by nothing until the ui card's form and grid read the pairs. Shares the container's verdict and its flip."
392+
"status": "live",
393+
"verifiedAt": "2026-10-31",
394+
"note": "The field of the declaring object a pick fills: the key `declaredLookupAutofillPatch` writes, in the form through `onAutofill` and in a grid row through the row patch. Shares the container's evidence."
395395
},
396396
"source": {
397-
"status": "planned",
398-
"verifiedAt": "2026-10-30",
399-
"note": "The field of the picked record whose value is copied. Judged at `os validate` / `os build` by the container's evidence (declared on the referenced object, not internal, a value the target holds); read by nothing until the ui card lands. Shares the container's verdict and its flip."
397+
"status": "live",
398+
"verifiedAt": "2026-10-31",
399+
"note": "The field of the picked record whose value is copied: read off the picked row by `declaredLookupAutofillPatch`; a source the row does not carry (not served to this reader) writes nothing. Shares the container's evidence."
400400
},
401401
"overwrite": {
402-
"status": "planned",
403-
"verifiedAt": "2026-10-30",
404-
"note": "Opt-in to replace a value the target already holds on every pick; absent, a pick fills only an empty target or one an earlier pick filled. Read by nothing until the ui card lands. Shares the container's verdict and its flip."
402+
"status": "live",
403+
"verifiedAt": "2026-10-31",
404+
"note": "TRUE makes the pick replace the target whatever it holds; absent, `declaredLookupAutofillPatch` fills only an empty target or one still holding the earlier pick's value. Shares the container's evidence."
405405
}
406406
}
407407
},

‎packages/spec/liveness/state-counts/field.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ committed anywhere: `check:liveness` sums the shards when it reads them.
1212

1313
| Type | live | exp | elsewhere | dead | planned | classified |
1414
|---|---|---|---|---|---|---|
15-
| `field` | 101 | 0 | 0 | 1 | 8 | 110 |
15+
| `field` | 104 | 0 | 0 | 1 | 5 | 110 |

‎ui/examples/schema-catalog/test/form-field-channel-census-12130.test.tsx‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,11 @@ const LEDGER: Readonly<Record<string, LedgerRow>> = {
326326
why: 'RUNTIME-ONLY (objectstack#27188) — object-field metadata (the spec\'s `FieldSchema.valueDomain`, ADR-0144 D2) read '
327327
+ 'off the resolved `field` stash: a record-currency code field takes its domain from the object, not from the form.',
328328
},
329+
lookupAutofill: {
330+
readers: [LOOKUP],
331+
why: 'RUNTIME-ONLY (objectstack#28245) — object-field metadata (the spec\'s `FieldSchema.lookupAutofill`) read off the '
332+
+ 'resolved `field` stash: which fields a pick fills is a fact about the object, not about one form.',
333+
},
329334
currency: {
330335
readers: [CURRENCY],
331336
why: 'RUNTIME-ONLY (objectui#11070 round 13) — the spec\'s `FieldSchema` refuses it; `currencyConfig` is the declared spelling.',

‎ui/packages/components/src/renderers/form/form.tsx‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,7 @@ function stripRegisteredFieldProps(type: string, props: RenderFieldProps): Rende
12461246
dependsOnLabels,
12471247
emptyHint,
12481248
onInvalidInputChange,
1249+
onAutofill,
12491250
// Renderer-only, and deliberately NOT a new key in the widget contract
12501251
// (objectui#3393). The call site started forwarding `label` for the
12511252
// built-in fullscreen dialog; registered widgets must keep receiving
@@ -1286,9 +1287,15 @@ function stripRegisteredFieldProps(type: string, props: RenderFieldProps): Rende
12861287
// Only the widgets that report unparseable text read it; every other one
12871288
// would spread an unknown handler onto its DOM (objectstack#27483).
12881289
...(INVALID_INPUT_REPORTING_WIDGET_TYPES.has(normalizedType) ? { onInvalidInputChange } : {}),
1290+
// Only a relationship pick fills sibling fields (`lookupAutofill`); every
1291+
// other widget would spread an unknown handler onto its DOM.
1292+
...(LOOKUP_AUTOFILL_WIDGET_TYPES.has(normalizedType) ? { onAutofill } : {}),
12891293
};
12901294
}
12911295

1296+
/** The registered widgets whose pick writes `lookupAutofill` targets through `onAutofill`. */
1297+
const LOOKUP_AUTOFILL_WIDGET_TYPES: ReadonlySet<string> = new Set(['lookup', 'master_detail']);
1298+
12921299
/** The registered widgets that fire `onInvalidInputChange`: the date picker's two faces. */
12931300
const INVALID_INPUT_REPORTING_WIDGET_TYPES: ReadonlySet<string> = new Set(['date', 'datetime']);
12941301

@@ -3904,6 +3911,18 @@ ComponentRegistry.register('form',
39043911
invalidInputMessages.set(name, message);
39053912
void form.trigger(name);
39063913
},
3914+
// A lookup pick's declared `lookupAutofill` targets, written
3915+
// as edits of this form's record; the widget decides what the
3916+
// overwrite rule lets it fill. A target already showing an
3917+
// error is re-validated, so a filled required field clears.
3918+
onAutofill: (patch: Record<string, unknown>) => {
3919+
for (const [target, filled] of Object.entries(patch)) {
3920+
form.setValue(target, filled, {
3921+
shouldDirty: true,
3922+
shouldValidate: !!form.getFieldState(target).error,
3923+
});
3924+
}
3925+
},
39073926
// Required is a STATE, so it travels the state channel
39083927
// (objectui#3290). Until now the resolved `required` (static
39093928
// `required` + `requiredWhen` CEL) drove exactly one thing —

‎ui/packages/fields/src/index.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5316,6 +5316,7 @@ export * from './widgets/UserField.js';
53165316
export * from './widgets/ObjectField.js';
53175317
export * from './widgets/VectorField.js';
53185318
export * from './widgets/GridField.js';
5319+
export * from './widgets/lookupAutofill.js';
53195320
// New widgets according to @objectstack/spec
53205321
export * from './widgets/ColorField.js';
53215322
export * from './widgets/SliderField.js';

0 commit comments

Comments
 (0)