@object-ui/core 17.6.0 → 17.7.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 +6610 -0
- package/README.md +101 -14
- package/dist/actions/ActionEngine.d.ts +4 -2
- package/dist/actions/ActionEngine.js +13 -8
- package/dist/actions/ActionRunner.d.ts +288 -27
- package/dist/actions/ActionRunner.js +443 -63
- package/dist/actions/TransactionManager.d.ts +7 -2
- package/dist/actions/actionKeys.d.ts +9 -7
- package/dist/actions/actionKeys.js +57 -14
- package/dist/actions/actionResponse.js +2 -2
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/actions/recordDelete.d.ts +118 -0
- package/dist/actions/recordDelete.js +112 -0
- package/dist/adapters/ValueDataSource.d.ts +6 -1
- package/dist/adapters/ValueDataSource.js +1090 -98
- package/dist/adapters/batchTransaction.d.ts +1 -1
- package/dist/adapters/batchTransaction.js +1 -1
- package/dist/builder/schema-builder.d.ts +38 -81
- package/dist/builder/schema-builder.js +33 -152
- package/dist/data-scope/ViewDataProvider.d.ts +1 -1
- package/dist/data-scope/ViewDataProvider.js +21 -1
- package/dist/data-scope/element-data-source.d.ts +261 -7
- package/dist/data-scope/element-data-source.js +342 -52
- package/dist/data-scope/index.d.ts +9 -4
- package/dist/data-scope/index.js +9 -4
- package/dist/evaluator/ExpressionEvaluator.d.ts +86 -0
- package/dist/evaluator/ExpressionEvaluator.js +184 -12
- package/dist/evaluator/FormulaFunctions.d.ts +0 -10
- package/dist/evaluator/FormulaFunctions.js +166 -27
- package/dist/evaluator/declaredPredicate.d.ts +93 -9
- package/dist/evaluator/declaredPredicate.js +117 -16
- package/dist/evaluator/fieldRules.d.ts +88 -4
- package/dist/evaluator/fieldRules.js +203 -31
- package/dist/evaluator/index.d.ts +1 -0
- package/dist/evaluator/index.js +1 -0
- package/dist/evaluator/listConditional.d.ts +103 -34
- package/dist/evaluator/listConditional.js +127 -50
- package/dist/evaluator/optionRules.d.ts +77 -6
- package/dist/evaluator/optionRules.js +87 -10
- package/dist/evaluator/predicateInput.d.ts +15 -4
- package/dist/evaluator/predicateInput.js +35 -2
- package/dist/evaluator/rowPredicateCanon.d.ts +7 -18
- package/dist/evaluator/rowPredicateCanon.js +65 -115
- package/dist/evaluator/subjectPermissions.d.ts +76 -0
- package/dist/evaluator/subjectPermissions.js +44 -0
- package/dist/evaluator/unevaluablePredicate.d.ts +57 -0
- package/dist/evaluator/unevaluablePredicate.js +70 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +80 -2
- package/dist/protocols/index.d.ts +0 -1
- package/dist/protocols/index.js +9 -1
- package/dist/registry/Registry.d.ts +358 -31
- package/dist/registry/Registry.js +263 -11
- package/dist/registry/WidgetRegistry.js +7 -3
- package/dist/registry/html-tier-intrinsics.d.ts +90 -0
- package/dist/registry/html-tier-intrinsics.js +110 -0
- package/dist/registry/public-blocks.d.ts +8 -0
- package/dist/registry/public-blocks.js +57 -0
- package/dist/theme/ThemeEngine.js +1 -1
- package/dist/utils/chart-category-key.d.ts +82 -0
- package/dist/utils/chart-category-key.js +84 -0
- package/dist/utils/chart-measure-key.d.ts +64 -0
- package/dist/utils/chart-measure-key.js +65 -0
- package/dist/utils/chart-presentation.d.ts +95 -112
- package/dist/utils/chart-presentation.js +169 -104
- package/dist/utils/chart-series.d.ts +131 -5
- package/dist/utils/chart-series.js +98 -16
- package/dist/utils/column-sortability.d.ts +157 -0
- package/dist/utils/column-sortability.js +217 -0
- package/dist/utils/dashboard-filters.d.ts +38 -6
- package/dist/utils/dashboard-filters.js +107 -61
- package/dist/utils/dataset-format.d.ts +211 -30
- package/dist/utils/dataset-format.js +389 -46
- package/dist/utils/date-display.d.ts +354 -0
- package/dist/utils/date-display.js +463 -0
- package/dist/utils/date-macros.js +7 -0
- package/dist/utils/drill-down.d.ts +78 -0
- package/dist/utils/drill-down.js +93 -0
- package/dist/utils/emptiness.d.ts +96 -0
- package/dist/utils/emptiness.js +101 -0
- package/dist/utils/expand-fields.d.ts +76 -6
- package/dist/utils/expand-fields.js +76 -6
- package/dist/utils/extract-records.d.ts +65 -5
- package/dist/utils/extract-records.js +65 -8
- package/dist/utils/filter-converter.d.ts +152 -4
- package/dist/utils/filter-converter.js +1092 -22
- package/dist/utils/filter-tokens.d.ts +49 -5
- package/dist/utils/filter-tokens.js +105 -33
- package/dist/utils/grouping-fields.d.ts +61 -0
- package/dist/utils/grouping-fields.js +84 -0
- package/dist/utils/managedBy.d.ts +3 -3
- package/dist/utils/managedBy.js +5 -5
- package/dist/utils/native-date-value.d.ts +48 -0
- package/dist/utils/native-date-value.js +167 -0
- package/dist/utils/non-grid-row-ceiling.d.ts +143 -0
- package/dist/utils/non-grid-row-ceiling.js +141 -0
- package/dist/utils/normalize-list-view.d.ts +121 -1
- package/dist/utils/normalize-list-view.js +186 -6
- package/dist/utils/number-display.d.ts +63 -14
- package/dist/utils/number-display.js +34 -12
- package/dist/utils/object-aggregate-query.d.ts +113 -0
- package/dist/utils/object-aggregate-query.js +50 -0
- package/dist/utils/parent-scope.d.ts +123 -0
- package/dist/utils/parent-scope.js +140 -0
- package/dist/utils/predicate-fields.d.ts +45 -12
- package/dist/utils/predicate-fields.js +137 -12
- package/dist/utils/record-color.d.ts +75 -0
- package/dist/utils/record-color.js +65 -0
- package/dist/utils/record-source.d.ts +298 -0
- package/dist/utils/record-source.js +331 -0
- package/dist/utils/record-title.d.ts +148 -8
- package/dist/utils/record-title.js +220 -26
- package/dist/utils/reference-keys.d.ts +21 -22
- package/dist/utils/reference-keys.js +532 -29
- package/dist/utils/sort-query.d.ts +115 -15
- package/dist/utils/sort-query.js +240 -13
- package/dist/utils/text-comparand.d.ts +92 -0
- package/dist/utils/text-comparand.js +99 -0
- package/dist/utils/without-denied-fields.d.ts +49 -0
- package/dist/utils/without-denied-fields.js +52 -0
- package/dist/validation/index.d.ts +8 -4
- package/dist/validation/index.js +8 -4
- package/dist/validation/schema-validator.d.ts +1 -1
- package/dist/validation/schema-validator.js +200 -82
- package/dist/validation/validators/object-validation-engine.d.ts +1 -1
- package/dist/validation/validators/object-validation-engine.js +14 -3
- package/package.json +8 -9
- package/dist/data-scope/DataScopeManager.d.ts +0 -127
- package/dist/data-scope/DataScopeManager.js +0 -229
- package/dist/protocols/ResponsiveProtocol.d.ts +0 -73
- package/dist/protocols/ResponsiveProtocol.js +0 -158
- package/dist/validation/validation-engine.d.ts +0 -88
- package/dist/validation/validation-engine.js +0 -434
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI — the shared record-source readers: the ruled three-rung ladder
|
|
3
|
+
* (`resolveRecordSourceConfig`) and the object-name it resolves to
|
|
4
|
+
* (`resolveRecordSourceObjectName`)
|
|
5
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The object a view block is bound to, resolved ONCE for the whole renderer
|
|
12
|
+
* (`b041b9c0c`).
|
|
13
|
+
*
|
|
14
|
+
* ## The question this answers, and the one it does not
|
|
15
|
+
*
|
|
16
|
+
* There are TWO separately-ruled precedence questions about `objectName`, and
|
|
17
|
+
* they only look like one question when a single reader is asked to answer
|
|
18
|
+
* both:
|
|
19
|
+
*
|
|
20
|
+
* 1. **Which object does this block RESOLVE, at render time, when it carries
|
|
21
|
+
* more than one binding?** — the published three-rung record-source ladder
|
|
22
|
+
* (`data`, then `staticData`, then `objectName`), declared on both faces of
|
|
23
|
+
* the contract (`ObjectMapSchema.objectName` / `ObjectGanttSchema.objectName`
|
|
24
|
+
* in `@object-ui/types`, and the `.describe` on their zod twins — the map
|
|
25
|
+
* twin's reads *"objectName — the THIRD record source `getDataConfig`
|
|
26
|
+
* resolves, after `data` and `staticData`"*, naming `ObjectMap.tsx`'s local
|
|
27
|
+
* wrapper; the gantt twin names `resolveRecordSourceConfig` itself), ruled
|
|
28
|
+
* 2026-09-02 (landed as `77cb489b4`) and pinned by
|
|
29
|
+
* `objectql-record-source-refinement-6939.test.ts`. **That is this
|
|
30
|
+
* function.**
|
|
31
|
+
* 2. **How does `objectName` get POPULATED when it is absent?** — the
|
|
32
|
+
* authoring-time gap-fill in `normalizeListViewSchema` (objectui#7477,
|
|
33
|
+
* ruling B, landed as `00d2fa682`), where an `objectName` already on the schema WINS
|
|
34
|
+
* and the `data` block only fills a gap: *"it can never re-point a binding
|
|
35
|
+
* that already resolves."*
|
|
36
|
+
*
|
|
37
|
+
* The two are NOT merged and neither is re-pointed at the other. Merging them
|
|
38
|
+
* would override a standing maintainer ruling in whichever direction the merged
|
|
39
|
+
* reader happened to pick: at the sites below the binding that already resolves
|
|
40
|
+
* IS `data.object`, so ruling B's own words argue for keeping rung 1 as it is.
|
|
41
|
+
*
|
|
42
|
+
* ## Why `staticData` does not appear here
|
|
43
|
+
*
|
|
44
|
+
* The ladder's second rung wraps inline rows as `{ provider: 'value', items }`,
|
|
45
|
+
* which names no object at all. So for the object-NAME question the three-rung
|
|
46
|
+
* ladder reduces to two rungs — the resolved config's object when it names one,
|
|
47
|
+
* else the schema's own `objectName`, which is what a `value`/`api`-backed block
|
|
48
|
+
* still needs for metadata reads, i18n field labels and permission verdicts.
|
|
49
|
+
* Callers pass the ALREADY-RESOLVED config (their
|
|
50
|
+
* {@link resolveRecordSourceConfig} output, direct or through a local wrapper),
|
|
51
|
+
* so rung ordering is settled before this function is reached.
|
|
52
|
+
*
|
|
53
|
+
* ## No lenient rung was added (AGENTS.md #0.1)
|
|
54
|
+
*
|
|
55
|
+
* `ViewDataSchema`'s `object` provider is a `strictObject` carrying exactly
|
|
56
|
+
* `{ provider, object }` with `object` REQUIRED, so `{ provider: 'object' }`
|
|
57
|
+
* without an `object` is off-contract. This reader does not coerce that shape
|
|
58
|
+
* back to `objectName`; the two call sites that used to (`ObjectGrid`'s
|
|
59
|
+
* `'object' in dataConfig` test and `ObjectTree`'s header `?? schema.objectName`
|
|
60
|
+
* tail) keep their own tail at the site, so the collapse changes nothing they
|
|
61
|
+
* resolve today while the shared rung stays contract-strict.
|
|
62
|
+
*
|
|
63
|
+
* @param schema - The block's schema; only `objectName` is read.
|
|
64
|
+
* @param dataConfig - The RESOLVED data config — the caller's own
|
|
65
|
+
* {@link resolveRecordSourceConfig} output, `null` when nothing is bound.
|
|
66
|
+
* @returns The bound object's name, or `undefined` when neither the resolved
|
|
67
|
+
* config nor the schema names one.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* const dataConfig = useMemo(() => resolveRecordSourceConfig(schema, 'view-data'), [schema]);
|
|
72
|
+
* const objectName = resolveRecordSourceObjectName(schema, dataConfig);
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export function resolveRecordSourceObjectName(schema, dataConfig) {
|
|
76
|
+
return dataConfig?.provider === 'object' ? dataConfig.object : schema?.objectName;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Does the authored `data` match the arm this block's published row declares?
|
|
80
|
+
*
|
|
81
|
+
* Falsy `data` is never a record source — the pre-8348 `if (schema.data)`
|
|
82
|
+
* truthiness test, kept, so `data: null` and `data: undefined` still fall
|
|
83
|
+
* through to `staticData`.
|
|
84
|
+
*/
|
|
85
|
+
function authoredDataIsOnTheDeclaredArm(authored, arm) {
|
|
86
|
+
if (!authored)
|
|
87
|
+
return false;
|
|
88
|
+
if (arm === 'array')
|
|
89
|
+
return Array.isArray(authored);
|
|
90
|
+
if (arm === 'view-data')
|
|
91
|
+
return !Array.isArray(authored);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The block's record source, resolved from the ruled three-rung ladder
|
|
96
|
+
* (`ce2aaefe1`), with rung 1 judged against the block's own published `data`
|
|
97
|
+
* row (objectui#8348).
|
|
98
|
+
*
|
|
99
|
+
* ## The ruled contract this is the ONE implementation of
|
|
100
|
+
*
|
|
101
|
+
* `data`, then `staticData`, then `objectName` — declared on both faces of the
|
|
102
|
+
* published contract and pinned by
|
|
103
|
+
* `objectql-record-source-refinement-6939.test.ts`. Quoted here from the
|
|
104
|
+
* `ObjectMapSchema` faces, whose `getDataConfig` is `ObjectMap.tsx`'s local
|
|
105
|
+
* wrapper around this function; the gantt and calendar faces name
|
|
106
|
+
* `resolveRecordSourceConfig` instead (objectui#9618):
|
|
107
|
+
*
|
|
108
|
+
* 1. **`data`** — *"Data source configuration. Read FIRST by `getDataConfig`"*,
|
|
109
|
+
* honoured ONLY on the arm `dataArm` names. Returned verbatim, so a config
|
|
110
|
+
* on the declared arm reaches the caller exactly as the author wrote it.
|
|
111
|
+
* 2. **`staticData`** — *"Inline records — read SECOND by `getDataConfig`,
|
|
112
|
+
* wrapped into a `{ provider: value }` config"*.
|
|
113
|
+
* 3. **`objectName`** — *"the THIRD record source `getDataConfig` resolves,
|
|
114
|
+
* after `data` and `staticData`"*, folded to `{ provider: 'object' }`.
|
|
115
|
+
*
|
|
116
|
+
* `null` when none of the three is present — the same "nothing is drawn" signal
|
|
117
|
+
* the zod `requireRecordSource` refinement is written against.
|
|
118
|
+
*
|
|
119
|
+
* This is the PRODUCER whose output {@link resolveRecordSourceObjectName} (the
|
|
120
|
+
* reader `b041b9c0c` published) consumes; that function's docblock describes the same
|
|
121
|
+
* ladder from the consuming end. Five plugins — calendar, gantt, grid, map and
|
|
122
|
+
* tree — each carried a hand-copy of this ladder with no gate holding them
|
|
123
|
+
* together, which is the AGENTS.md #0.1 drift class: a change to the ruled
|
|
124
|
+
* order had five edit sites and nothing noticed a missed one.
|
|
125
|
+
*
|
|
126
|
+
* ## Rung 1 is judged against the block's own row (objectui#8348)
|
|
127
|
+
*
|
|
128
|
+
* ⛔ This docblock used to state, as a fact about the whole ladder, that *"an
|
|
129
|
+
* array under `data` cannot be published"*. That is true of the blocks whose row
|
|
130
|
+
* is `ViewData` and FALSE of `object-calendar`, whose published row
|
|
131
|
+
* (`ComponentPropsMap['object-calendar'].data` on `@objectstack/spec` 17.4.0) is
|
|
132
|
+
* `z.array(z.unknown()).optional()` — *"Pre-fetched records — skips the internal
|
|
133
|
+
* fetch"*. Both directions of that disagreement were live at once: the renderers
|
|
134
|
+
* honoured the `{ provider, items }` object on a block whose row refuses it by
|
|
135
|
+
* kind, and `ObjectGrid` / `ObjectMap` lifted a bare array on blocks whose row
|
|
136
|
+
* refuses THAT by kind. Decision batch #83 settled it — the row decides — and
|
|
137
|
+
* `dataArm` is where each block says which row it has.
|
|
138
|
+
*
|
|
139
|
+
* JUDGED, per block, against that block's `ComponentPropsMap` row in the
|
|
140
|
+
* INSTALLED `@objectstack/spec` — and ⛔ the verdicts are not written down here
|
|
141
|
+
* as facts, because a spec release can move a row (AGENTS.md #9). Each pin
|
|
142
|
+
* below derives the arm from the row at test time and compares it with what the
|
|
143
|
+
* block passes, so a moved row turns it red:
|
|
144
|
+
*
|
|
145
|
+
* - `object-grid` — `'view-data'`; `gridBareArrayDataRefused-8348.test.tsx`,
|
|
146
|
+
* its "the spec row is what all of the above is judged against" test. The
|
|
147
|
+
* row's own description names the refusal: *"the bare-array shortcut is
|
|
148
|
+
* refused — see migration `object-grid-data-view-data-converged`"*.
|
|
149
|
+
* - `object-calendar` — `'array'`; the test of the same name in
|
|
150
|
+
* `ObjectCalendar.dataObjectArmRefused-8348.test.tsx`. The registration
|
|
151
|
+
* publishes the same arm (`{ name: 'data', type: 'array' }`).
|
|
152
|
+
* - `object-map`, `object-gantt`, `object-tree` — `'view-data'`; one
|
|
153
|
+
* `dataArmSpecRow-8348` pin per block, in each plugin's own tests. These
|
|
154
|
+
* three rows are what ruling batch #136 item 3 (Q1-C) had the protocol
|
|
155
|
+
* gain; until they were installable, map and gantt were judged through this
|
|
156
|
+
* repo's own `ObjectMapSchema.data` / `ObjectGanttSchema.data` and tree
|
|
157
|
+
* passed `'undeclared'`. The tree's row declares `data`, so the ruling's
|
|
158
|
+
* 「协议没有的能力删」 clause did not fire for it: it keeps the object arm and
|
|
159
|
+
* loses only the bare-array spelling. Its `staticData` rung is declared by
|
|
160
|
+
* the same row and is unchanged.
|
|
161
|
+
*
|
|
162
|
+
* ## The DECLARATION follows the arm too (`ab856ed30`)
|
|
163
|
+
*
|
|
164
|
+
* The `data` member below is {@link AuthoredRecordSourceData}`<Arm>`, not a flat
|
|
165
|
+
* `ViewData`: the arm decides what a site may DECLARE exactly as it decides
|
|
166
|
+
* what rung 1 HONOURS. Read that type's docblock for why it is arm-indexed
|
|
167
|
+
* rather than a union of the arms, and for the reading that says nothing at
|
|
168
|
+
* runtime moved.
|
|
169
|
+
*
|
|
170
|
+
* ## No lenient rung was added (AGENTS.md #0.1)
|
|
171
|
+
*
|
|
172
|
+
* **Null tolerance** stays out: all five copies dereference `schema` unguarded
|
|
173
|
+
* and would throw on `null`; no site passes one, so no `?.` was added.
|
|
174
|
+
*
|
|
175
|
+
* `ObjectCalendar`'s copy guarded with `'data' in schema && schema.data`. That
|
|
176
|
+
* `in` test was a TYPESCRIPT narrowing device, not a behavioural one: when the
|
|
177
|
+
* property is absent the read yields `undefined`, which is falsy either way, so
|
|
178
|
+
* the guard could never change which rung is taken. The optional-property
|
|
179
|
+
* parameter below admits a schema that declares neither key directly, which is
|
|
180
|
+
* why the guard is gone rather than flattened away. The equivalence itself is
|
|
181
|
+
* pinned, not argued, by `CALENDAR_IN_GUARD` in
|
|
182
|
+
* `record-source-config.behaviourNeutrality-7632.test.ts`.
|
|
183
|
+
*
|
|
184
|
+
* ⛔ This passage used to explain that guard by the PROPS UNION the calendar
|
|
185
|
+
* plugin once carried — `ObjectGridSchema | CalendarSchema`, whose second
|
|
186
|
+
* member declared neither `data` nor `staticData`. objectui#8651 RETIRED that
|
|
187
|
+
* union (`ObjectCalendarComponentProps.schema` is the published
|
|
188
|
+
* `ObjectCalendarSchema` today), so the sentence explained the guard by a type
|
|
189
|
+
* no reader could look up. Rewritten rather than deleted: the guard's history
|
|
190
|
+
* is still why this parameter's members are optional.
|
|
191
|
+
*
|
|
192
|
+
* @param schema - The block's schema; only `data`, `staticData` and
|
|
193
|
+
* `objectName` are read. Its `data` member is typed by the arm — see
|
|
194
|
+
* {@link AuthoredRecordSourceData}.
|
|
195
|
+
* @param dataArm - The arm the CALLING BLOCK's published `data` row declares.
|
|
196
|
+
* Required: there is no repo-wide default, because the answer differs per
|
|
197
|
+
* block and a default is how the second de-facto contract got in.
|
|
198
|
+
* @returns The resolved data config, or `null` when nothing is bound.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```ts
|
|
202
|
+
* const dataConfig = useMemo(() => resolveRecordSourceConfig(schema, 'view-data'), [schema]);
|
|
203
|
+
* const objectName = resolveRecordSourceObjectName(schema, dataConfig);
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
export function resolveRecordSourceConfig(schema, dataArm) {
|
|
207
|
+
if (authoredDataIsOnTheDeclaredArm(schema.data, dataArm)) {
|
|
208
|
+
return schema.data;
|
|
209
|
+
}
|
|
210
|
+
if (schema.staticData) {
|
|
211
|
+
return {
|
|
212
|
+
provider: 'value',
|
|
213
|
+
items: schema.staticData,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (schema.objectName) {
|
|
217
|
+
return {
|
|
218
|
+
provider: 'object',
|
|
219
|
+
object: schema.objectName,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* The arm each REGISTERED BLOCK TYPE declares — the one reading available to a
|
|
226
|
+
* caller that cannot be handed the arm as a parameter (objectui#9571).
|
|
227
|
+
*
|
|
228
|
+
* ## Why a type-keyed table exists beside the REQUIRED parameter
|
|
229
|
+
*
|
|
230
|
+
* {@link RecordSourceDataArm}'s docblock states, correctly, why
|
|
231
|
+
* {@link resolveRecordSourceConfig} takes the arm as a required PARAMETER: most
|
|
232
|
+
* of these renderers are registered under two spellings, a node reaches the same
|
|
233
|
+
* component under either, and a table keyed by `type` would answer for one tag
|
|
234
|
+
* and silently miss the other. That argument is about a call site that already
|
|
235
|
+
* has one block in front of it — there a parameter is strictly better, and it
|
|
236
|
+
* stays exactly as it is.
|
|
237
|
+
*
|
|
238
|
+
* `SchemaRenderer` is the one reader that has no such site. It is generic over
|
|
239
|
+
* every registered type and holds nothing but `schema.type`, and ruling
|
|
240
|
+
* objectui#8348 Q2-C (decision batch #136 item 3, maintainer 「同意」) makes it a
|
|
241
|
+
* reader anyway: it must stop spreading an authored `data` key as a React prop
|
|
242
|
+
* for blocks whose published row is the OBJECT arm, because that prop is the
|
|
243
|
+
* carrier the 「8348 以协议为准」 ruling did not otherwise reach. So "which arm
|
|
244
|
+
* does this TYPE declare" has to be askable, and this is the ONE place that
|
|
245
|
+
* answers it.
|
|
246
|
+
*
|
|
247
|
+
* ## The alias hazard is closed by ENUMERATION plus a registry-derived pin
|
|
248
|
+
*
|
|
249
|
+
* Every registered spelling is listed, and the listing is pinned per plugin
|
|
250
|
+
* AGAINST THE REGISTRY rather than against itself: each affected plugin's pin
|
|
251
|
+
* walks the keys its own `index.tsx` actually produced, groups them by the
|
|
252
|
+
* renderer they resolve to, and requires every member of a group to get the
|
|
253
|
+
* same answer here. An alias added without a row goes red there instead of
|
|
254
|
+
* quietly answering `'undeclared'` and keeping the prop seat.
|
|
255
|
+
*
|
|
256
|
+
* ⚠️ ONE `register()` CALL PRODUCES SEVERAL KEYS, and a row must be written for
|
|
257
|
+
* each of them — `SchemaRenderer` looks this table up with the raw
|
|
258
|
+
* `schema.type`, which is whatever spelling the author wrote. A registration
|
|
259
|
+
* with `namespace: 'view'` is reachable as `view:calendar` AND as bare `calendar`; one
|
|
260
|
+
* with `skipFallback` is reachable ONLY under its namespaced key. MEASURED via
|
|
261
|
+
* `ComponentRegistry.getAllTypes()` with the five plugins loaded, grouped by
|
|
262
|
+
* the renderer each key resolves to.
|
|
263
|
+
*
|
|
264
|
+
* ⛔ `grid` IS NOT A GRID KEY, and this is the trap the registry-derived pin
|
|
265
|
+
* caught on its first run. `plugin-grid` registers its `view` alias with
|
|
266
|
+
* `skipFallback: true` precisely so the bare key stays with
|
|
267
|
+
* `@object-ui/components`' LAYOUT grid container (`ui:grid`) — so `object-grid`
|
|
268
|
+
* resolves to `view:grid`, never to `grid`. A row for `grid` here would strip
|
|
269
|
+
* `data` from a layout container, which declares no `data` row at all.
|
|
270
|
+
*
|
|
271
|
+
* ## The rows are the call sites' literals — ⛔ not a second measurement
|
|
272
|
+
*
|
|
273
|
+
* Each value below is the literal its block already passes to
|
|
274
|
+
* {@link resolveRecordSourceConfig}, and each of those literals carries its own
|
|
275
|
+
* per-block measurement in a comment at that call site. ⛔ Do not re-derive the
|
|
276
|
+
* arm here and ⛔ do not let the two disagree: this table transports an answer,
|
|
277
|
+
* it does not compute one.
|
|
278
|
+
*
|
|
279
|
+
* - `ObjectGrid.tsx`'s `getDataConfig` — `'view-data'`.
|
|
280
|
+
* - `ObjectMap.tsx`'s `getDataConfig` — `'view-data'`. One block spelling
|
|
281
|
+
* only: the bare `map` key (and with it `view:map`) is retired
|
|
282
|
+
* (objectui#10393).
|
|
283
|
+
* - `ObjectGantt.tsx`'s `rawDataConfig` — `'view-data'`. One block spelling
|
|
284
|
+
* only: the bare `gantt` key is retired (objectui#8008).
|
|
285
|
+
* - `ObjectCalendar.tsx`'s `dataConfig` — `'array'`.
|
|
286
|
+
* - `ObjectTree.tsx`'s `dataConfig` — `'view-data'`. One block spelling only
|
|
287
|
+
* (`object-tree` under the `plugin-tree` namespace): the bare `tree` key
|
|
288
|
+
* (and with it `view:tree`) is retired (objectui#10859 batch 8). These rows
|
|
289
|
+
* used to read `'undeclared'` and were only
|
|
290
|
+
* documentary, because no published face declared a `data` row for the
|
|
291
|
+
* block; `ComponentPropsMap['object-tree']` now does (objectui#8348, ruling
|
|
292
|
+
* batch #136 item 3, Q1-C), so they DECIDE: an authored `data` on a tree
|
|
293
|
+
* node no longer takes a prop seat either. The registry-derived pin is
|
|
294
|
+
* `ObjectTree.dataArmSpecRow-8348.test.tsx`.
|
|
295
|
+
*/
|
|
296
|
+
const RECORD_SOURCE_DATA_ARM_BY_TYPE = Object.freeze({
|
|
297
|
+
// — object arm: `ViewData`, a bare array under `data` is off the row —
|
|
298
|
+
'object-grid': 'view-data',
|
|
299
|
+
'plugin-grid:object-grid': 'view-data',
|
|
300
|
+
'view:grid': 'view-data',
|
|
301
|
+
'object-map': 'view-data',
|
|
302
|
+
'plugin-map:object-map': 'view-data',
|
|
303
|
+
'object-gantt': 'view-data',
|
|
304
|
+
'plugin-gantt:object-gantt': 'view-data',
|
|
305
|
+
'object-tree': 'view-data',
|
|
306
|
+
'plugin-tree:object-tree': 'view-data',
|
|
307
|
+
// — array arm: `z.array(...)`, pre-fetched records —
|
|
308
|
+
'object-calendar': 'array',
|
|
309
|
+
'plugin-calendar:object-calendar': 'array',
|
|
310
|
+
'view:calendar': 'array',
|
|
311
|
+
calendar: 'array',
|
|
312
|
+
});
|
|
313
|
+
/**
|
|
314
|
+
* Which `data` arm the block registered under `type` declares.
|
|
315
|
+
*
|
|
316
|
+
* `'undeclared'` for every type not listed above — the same verdict the ladder
|
|
317
|
+
* gives a block no published face declares a `data` row for, and the same
|
|
318
|
+
* behaviour those types have today. That default is deliberately the
|
|
319
|
+
* NO-CHANGE direction: a caller acting on this reading (today:
|
|
320
|
+
* `SchemaRenderer`'s props spread) leaves an unlisted type exactly as it was,
|
|
321
|
+
* so a block that has never been measured is never silently re-decided by this
|
|
322
|
+
* table's mere existence.
|
|
323
|
+
*
|
|
324
|
+
* @param type - The node's registry key, either spelling.
|
|
325
|
+
* @returns The arm that type's published `data` row declares.
|
|
326
|
+
*/
|
|
327
|
+
export function recordSourceDataArmForType(type) {
|
|
328
|
+
if (typeof type !== 'string')
|
|
329
|
+
return 'undeclared';
|
|
330
|
+
return RECORD_SOURCE_DATA_ARM_BY_TYPE[type] ?? 'undeclared';
|
|
331
|
+
}
|
|
@@ -15,10 +15,20 @@
|
|
|
15
15
|
* supported. Dotted paths (`{account.name}`) walk `$expanded` lookup objects; a
|
|
16
16
|
* placeholder that resolves to a whole object is reduced to its display name.
|
|
17
17
|
*
|
|
18
|
-
* Empty placeholders (missing / null / empty fields) are
|
|
19
|
-
* any orphan separator they leave behind, so
|
|
20
|
-
* evaluated against `{ company: "Acme" }` yields
|
|
21
|
-
* Returns an empty string when no placeholder
|
|
18
|
+
* Empty placeholders (missing / null / empty / whitespace-only fields) are
|
|
19
|
+
* stripped along with any orphan separator they leave behind, so
|
|
20
|
+
* `"{full_name} - {company}"` evaluated against `{ company: "Acme" }` yields
|
|
21
|
+
* `"Acme"`, not `" - Acme"`. Returns an empty string when no placeholder
|
|
22
|
+
* resolved.
|
|
23
|
+
*
|
|
24
|
+
* What counts as "empty" is not decided here (objectui#10446). Each
|
|
25
|
+
* placeholder's value goes through {@link displayValueOf}, the body of
|
|
26
|
+
* {@link recordDisplayValueAt}: trim, then empty. This function used to test
|
|
27
|
+
* the raw value against `null` / `undefined` / `''` on its own, so a
|
|
28
|
+
* whitespace-only value counted as RESOLVED. The later whitespace collapse
|
|
29
|
+
* then left its separator dangling: `{contract_no} - {name}` with a blank
|
|
30
|
+
* `name` rendered `HT-2026-003 -`, while every value-keyed rung of
|
|
31
|
+
* {@link getRecordDisplayName} judged the same value empty.
|
|
22
32
|
*/
|
|
23
33
|
export declare function formatTitleTemplate(titleFormat: string | {
|
|
24
34
|
source?: string;
|
|
@@ -49,6 +59,135 @@ export declare function isTitleEligibleField(def: any): boolean;
|
|
|
49
59
|
* when `objectDef` is a non-null object (the WeakMap key requirement).
|
|
50
60
|
*/
|
|
51
61
|
export declare function deriveTitleField(objectDef: any): string | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* The declared name-field pointer, read exactly as {@link getRecordDisplayName}
|
|
64
|
+
* reads it: the canonical `nameField` (ADR-0079 Phase 2), then its deprecated
|
|
65
|
+
* `displayNameField` / `NAME_FIELD_KEY` aliases.
|
|
66
|
+
*
|
|
67
|
+
* One spelling, every reader. Inside this module that is the value-space
|
|
68
|
+
* resolver and the name-space {@link resolveNameField}. Re-typing the `??`
|
|
69
|
+
* chain at another reader is how two readers drift into disagreeing about
|
|
70
|
+
* which field titles an object. No new alias is read here; this is the
|
|
71
|
+
* existing ladder, extracted.
|
|
72
|
+
*
|
|
73
|
+
* Exported (objectui#9436) for callers that must rank the DECLARED pointer on
|
|
74
|
+
* its own, apart from the type-aware derivation that {@link resolveNameField}
|
|
75
|
+
* folds in after it. The record-page H1 needs exactly that: ADR-0079 puts the
|
|
76
|
+
* declared pointer (steps 1+2) above the legacy `titleFormat` (step 3), and the
|
|
77
|
+
* derivation (step 4) below it, so a caller that interleaves its own
|
|
78
|
+
* `titleFormat` rendering has to read the two halves separately. Read the value
|
|
79
|
+
* as `recordDisplayValueAt(record, declaredNameField(objectDef))`, which is how
|
|
80
|
+
* {@link getRecordDisplayName} reads steps 1+2.
|
|
81
|
+
*
|
|
82
|
+
* Returns the pointer exactly as declared, or `undefined` when none is. It
|
|
83
|
+
* never derives: an object that declares nothing answers `undefined` even when
|
|
84
|
+
* its `fields` would derive a title field.
|
|
85
|
+
*/
|
|
86
|
+
export declare function declaredNameField(objectDef: any): any;
|
|
87
|
+
/**
|
|
88
|
+
* WHICH FIELD titles this object — the name-space twin of
|
|
89
|
+
* {@link getRecordDisplayName}, which answers what that field SAYS on one
|
|
90
|
+
* record. Same ladder, minus the rungs that only exist per record:
|
|
91
|
+
*
|
|
92
|
+
* 1+2. the declared pointer ({@link declaredNameField});
|
|
93
|
+
* 4. else the type-aware derivation ({@link deriveTitleField}).
|
|
94
|
+
*
|
|
95
|
+
* The two omissions are deliberate, not oversights. `options.titleField`
|
|
96
|
+
* (step 0) belongs to one view, not to the object. `titleFormat` (step 3) is a
|
|
97
|
+
* render-only template, not a field name — there is no column, no sort key and
|
|
98
|
+
* no `$select` entry to be had from it, which is the same reason ADR-0079
|
|
99
|
+
* demoted it. A caller that wants a title for a `titleFormat`-only object wants
|
|
100
|
+
* {@link getRecordDisplayName}, per record.
|
|
101
|
+
*
|
|
102
|
+
* Deliberately does NOT require the answer to exist in `objectDef.fields`: in
|
|
103
|
+
* value-space the declared pointer is read off the RECORD, so demanding a field
|
|
104
|
+
* def here would make the two resolvers disagree. A caller that needs a
|
|
105
|
+
* RENDERABLE column checks presence itself — see {@link leadWithNameField}.
|
|
106
|
+
*
|
|
107
|
+
* Returns `undefined` when nothing declares or derives a name field.
|
|
108
|
+
*/
|
|
109
|
+
export declare function resolveNameField(objectDef: any): string | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* Put the object's name field at the FRONT of a **synthesized** default column
|
|
112
|
+
* list, so a list no author ever configured still lets you tell its rows apart.
|
|
113
|
+
*
|
|
114
|
+
* Why this exists (objectui#7245): the three faces that synthesize default list
|
|
115
|
+
* columns each took a declared `highlightFields` verbatim. `highlightFields` is
|
|
116
|
+
* the ADR-0085 "most important fields" role, and its canonical consumer — the
|
|
117
|
+
* detail-page highlight strip — deliberately *excludes* the title field,
|
|
118
|
+
* because the strip sits directly under the page H1 that already shows it. So
|
|
119
|
+
* well-authored metadata routinely omits the name from `highlightFields`: the
|
|
120
|
+
* showcase `showcase_account` declares `["status", "industry", "annual_revenue"]`
|
|
121
|
+
* against `nameField: "name"`, and its default grid rendered 14 rows with no
|
|
122
|
+
* name column and nothing to distinguish them.
|
|
123
|
+
*
|
|
124
|
+
* A list has no H1 to lean on, so the same role needs the opposite treatment
|
|
125
|
+
* here. This is not a new convention: `deriveLookupColumns` in `@object-ui/fields`
|
|
126
|
+
* already leads its picker columns with the display field and filters it out of
|
|
127
|
+
* the declared list — the record-picker equivalent of this problem, solved the
|
|
128
|
+
* same way. This makes the list faces agree with it.
|
|
129
|
+
*
|
|
130
|
+
* MOVES the name field rather than only prepending a missing one: an author who
|
|
131
|
+
* lists it third still gets it first, which is what "the column that identifies
|
|
132
|
+
* the row" means. Order among the remaining columns is untouched.
|
|
133
|
+
*
|
|
134
|
+
* NOT for author-declared column lists. A view that declares `columns` said what
|
|
135
|
+
* it wants; overriding that would be exactly the renderer-side second-guessing
|
|
136
|
+
* AGENTS.md Commandment #0.1 rules out. Every caller passes a list it
|
|
137
|
+
* synthesized itself.
|
|
138
|
+
*
|
|
139
|
+
* Three cases return `columns` untouched, each guarding a rule that already
|
|
140
|
+
* governs these column lists:
|
|
141
|
+
*
|
|
142
|
+
* - **No field def** (nothing resolved, or the pointer names a field the object
|
|
143
|
+
* does not carry) — a synthesized list must never fabricate a column the
|
|
144
|
+
* object cannot render.
|
|
145
|
+
* - **`hidden: true`** — the author said don't show this field. That outranks
|
|
146
|
+
* the fact that it also titles the record; every one of these faces already
|
|
147
|
+
* drops hidden fields from its own walk.
|
|
148
|
+
* - **DERIVED onto a system-managed field.** {@link deriveTitleField} filters
|
|
149
|
+
* by TYPE only, so on an object with no name-ish business field it can land
|
|
150
|
+
* on a framework-injected column — and leading a default list with a raw id
|
|
151
|
+
* is precisely the regression objectui#2702 / #2777 fixed. A *declared*
|
|
152
|
+
* `nameField` is exempt: `sys_migration` really does point at `id`, and an
|
|
153
|
+
* author's explicit designation is not a heuristic misfire.
|
|
154
|
+
*/
|
|
155
|
+
export declare function leadWithNameField(objectDef: any, columns: string[]): string[];
|
|
156
|
+
/**
|
|
157
|
+
* Read a non-empty string-ish display value off a record at `field`, else
|
|
158
|
+
* `undefined`.
|
|
159
|
+
*
|
|
160
|
+
* THE definition of "this record has a value here" — the question every
|
|
161
|
+
* value-keyed rung of {@link getRecordDisplayName} asks, and the reason a
|
|
162
|
+
* whitespace-only field is not a title.
|
|
163
|
+
*
|
|
164
|
+
* ## Why this is exported (objectui#8350)
|
|
165
|
+
*
|
|
166
|
+
* A second reader existed. `record:details`' dedupe ladder in
|
|
167
|
+
* `@object-ui/plugin-detail` asks the same question about the same record for
|
|
168
|
+
* the same field, to decide which body-grid row duplicates the page H1 — and it
|
|
169
|
+
* asked it with a hand-written raw `undefined` / `null` / `''` test, which a
|
|
170
|
+
* whitespace-only value passes and this one does not. So for a record whose
|
|
171
|
+
* title field held only spaces the two halves disagreed about whether that
|
|
172
|
+
* field had a value at all: the H1 walked on to the next rung, while the grid
|
|
173
|
+
* hid the row anyway — a field vanished to deduplicate against a heading that
|
|
174
|
+
* never showed it. The ladder now calls THIS. One authority, not two
|
|
175
|
+
* implementations that agree today.
|
|
176
|
+
*
|
|
177
|
+
* ⛔ Do not re-spell this test at a call site, and do not "just add a trim"
|
|
178
|
+
* there: three things beyond the trim are part of the definition, and a second
|
|
179
|
+
* implementation drifts on each of them independently —
|
|
180
|
+
* - an expanded/embedded reference object resolves through its Salesforce-
|
|
181
|
+
* style display chain and is EMPTY when that yields nothing (a bare
|
|
182
|
+
* `{ id }` lookup payload is not a title);
|
|
183
|
+
* - non-strings are stringified, so `0` and `false` ARE values, not blanks;
|
|
184
|
+
* - `null` and `undefined` are empty, and only those two.
|
|
185
|
+
*
|
|
186
|
+
* The rule itself is {@link displayValueOf}, which {@link formatTitleTemplate}
|
|
187
|
+
* applies to each placeholder value (objectui#10446). So a `titleFormat`
|
|
188
|
+
* placeholder is empty exactly when this function would call its field empty.
|
|
189
|
+
*/
|
|
190
|
+
export declare function recordDisplayValueAt(record: any, field: string | undefined): string | undefined;
|
|
52
191
|
export interface RecordDisplayNameOptions {
|
|
53
192
|
/**
|
|
54
193
|
* Optional explicit title field (e.g. a view's `titleField` / gallery
|
|
@@ -61,7 +200,7 @@ export interface RecordDisplayNameOptions {
|
|
|
61
200
|
fallback?: string;
|
|
62
201
|
/**
|
|
63
202
|
* Whether to probe standard name-ish keys (`name`/`title`/…) directly on the
|
|
64
|
-
* record when `objectDef.fields` is absent (precedence step
|
|
203
|
+
* record when `objectDef.fields` is absent (precedence step 4b). Defaults to
|
|
65
204
|
* `true`. Callers that have a better generic fallback to interleave (e.g. the
|
|
66
205
|
* detail header prefers `schema.title`, the object label, over a guessed
|
|
67
206
|
* record key) pass `false` so this step is skipped and the resolver returns
|
|
@@ -74,9 +213,10 @@ export interface RecordDisplayNameOptions {
|
|
|
74
213
|
*
|
|
75
214
|
* Precedence (ADR-0079 Phase 2 makes the object's declared field canonical and
|
|
76
215
|
* de-prioritizes the legacy `titleFormat` template):
|
|
77
|
-
* 0. `options.titleField
|
|
78
|
-
*
|
|
79
|
-
*
|
|
216
|
+
* 0. `options.titleField` (when provided & non-empty on the record) — lets
|
|
217
|
+
* a view keep its author-chosen title field. The object-level
|
|
218
|
+
* `objectDef.titleField` leg was removed in objectui#6531: the spec's
|
|
219
|
+
* object schema rejects that key, so nothing could produce it.
|
|
80
220
|
* 1. `objectDef.nameField` — the NEW canonical record-title pointer
|
|
81
221
|
* (ADR-0079). A field name the server can return and query.
|
|
82
222
|
* 2. `objectDef.displayNameField` / `objectDef.NAME_FIELD_KEY` — the
|