@object-ui/plugin-detail 17.6.0 → 18.0.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 +7755 -0
- package/README.md +557 -71
- package/dist/ActivityTimeline.d.ts.map +1 -1
- package/dist/CommentAttachment.d.ts.map +1 -1
- package/dist/CommentAuthorControls.d.ts +40 -0
- package/dist/CommentAuthorControls.d.ts.map +1 -0
- package/dist/ConcurrentUpdateDialog.d.ts +16 -1
- package/dist/ConcurrentUpdateDialog.d.ts.map +1 -1
- package/dist/DetailSection.d.ts.map +1 -1
- package/dist/DetailSectionNode.d.ts +97 -0
- package/dist/DetailSectionNode.d.ts.map +1 -0
- package/dist/DetailView.d.ts +13 -0
- package/dist/DetailView.d.ts.map +1 -1
- package/dist/HeaderHighlight.d.ts.map +1 -1
- package/dist/HistoryTimeline.d.ts +21 -4
- package/dist/HistoryTimeline.d.ts.map +1 -1
- package/dist/InlineEditSaveBar.d.ts +68 -0
- package/dist/InlineEditSaveBar.d.ts.map +1 -1
- package/dist/InlineFieldInput.d.ts +80 -7
- package/dist/InlineFieldInput.d.ts.map +1 -1
- package/dist/ReactionPicker.d.ts.map +1 -1
- package/dist/RecordActivityTimeline.d.ts +32 -4
- package/dist/RecordActivityTimeline.d.ts.map +1 -1
- package/dist/RecordChatterPanel.d.ts +10 -1
- package/dist/RecordChatterPanel.d.ts.map +1 -1
- package/dist/RecordComments.d.ts +1 -0
- package/dist/RecordComments.d.ts.map +1 -1
- package/dist/RecordDetailDrawer.d.ts +20 -6
- package/dist/RecordDetailDrawer.d.ts.map +1 -1
- package/dist/RecordDetailPanel.d.ts +101 -0
- package/dist/RecordDetailPanel.d.ts.map +1 -0
- package/dist/RecordMetaFooter.d.ts +1 -1
- package/dist/RecordMetaFooter.d.ts.map +1 -1
- package/dist/RelatedList.d.ts +83 -10
- package/dist/RelatedList.d.ts.map +1 -1
- package/dist/ThreadedReplies.d.ts +9 -1
- package/dist/ThreadedReplies.d.ts.map +1 -1
- package/dist/emptiness.d.ts +142 -0
- package/dist/emptiness.d.ts.map +1 -0
- package/dist/fieldEnrichment.d.ts +46 -5
- package/dist/fieldEnrichment.d.ts.map +1 -1
- package/dist/headerColor.d.ts +20 -0
- package/dist/headerColor.d.ts.map +1 -0
- package/dist/index.d.ts +94 -77
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5693 -5487
- package/dist/index.umd.cjs +2 -5
- package/dist/recordPageProvenance.d.ts +39 -0
- package/dist/recordPageProvenance.d.ts.map +1 -0
- package/dist/renderers/record-activity.d.ts.map +1 -1
- package/dist/renderers/record-alert.d.ts +23 -3
- package/dist/renderers/record-alert.d.ts.map +1 -1
- package/dist/renderers/record-alert.readProps.d.ts +40 -0
- package/dist/renderers/record-alert.readProps.d.ts.map +1 -0
- package/dist/renderers/record-chatter.d.ts.map +1 -1
- package/dist/renderers/record-details.d.ts +12 -0
- package/dist/renderers/record-details.d.ts.map +1 -1
- package/dist/renderers/record-highlights.d.ts +10 -0
- package/dist/renderers/record-highlights.d.ts.map +1 -1
- package/dist/renderers/record-history.d.ts +1 -1
- package/dist/renderers/record-history.d.ts.map +1 -1
- package/dist/renderers/record-path.d.ts.map +1 -1
- package/dist/renderers/record-quick-actions.d.ts +26 -6
- package/dist/renderers/record-quick-actions.d.ts.map +1 -1
- package/dist/renderers/record-reference-rail.d.ts +40 -0
- package/dist/renderers/record-reference-rail.d.ts.map +1 -1
- package/dist/renderers/record-related-list.d.ts +51 -7
- package/dist/renderers/record-related-list.d.ts.map +1 -1
- package/dist/renderers/recordActivityFeed.d.ts +343 -33
- package/dist/renderers/recordActivityFeed.d.ts.map +1 -1
- package/dist/renderers/recordBinding.d.ts +180 -0
- package/dist/renderers/recordBinding.d.ts.map +1 -0
- package/dist/renderers/recordComponentAria.d.ts +44 -0
- package/dist/renderers/recordComponentAria.d.ts.map +1 -0
- package/dist/renderers/relatedListActions.d.ts +102 -0
- package/dist/renderers/relatedListActions.d.ts.map +1 -0
- package/dist/summaryChipPercent.d.ts +83 -0
- package/dist/summaryChipPercent.d.ts.map +1 -0
- package/dist/summaryChipRenderers.d.ts +83 -0
- package/dist/summaryChipRenderers.d.ts.map +1 -0
- package/dist/synth/buildDefaultPageSchema.d.ts +188 -21
- package/dist/synth/buildDefaultPageSchema.d.ts.map +1 -1
- package/dist/useDetailTranslation.d.ts.map +1 -1
- package/dist/useRecordEditable.d.ts +4 -1
- package/dist/useRecordEditable.d.ts.map +1 -1
- package/package.json +19 -18
- package/dist/CommentInput.d.ts +0 -24
- package/dist/CommentInput.d.ts.map +0 -1
- package/dist/DiffView.d.ts +0 -24
- package/dist/DiffView.d.ts.map +0 -1
- package/dist/InlineCreateRelated.d.ts +0 -32
- package/dist/InlineCreateRelated.d.ts.map +0 -1
- package/dist/MentionAutocomplete.d.ts +0 -43
- package/dist/MentionAutocomplete.d.ts.map +0 -1
- package/dist/PointInTimeRestore.d.ts +0 -28
- package/dist/PointInTimeRestore.d.ts.map +0 -1
- package/dist/RecordNavigationEnhanced.d.ts +0 -18
- package/dist/RecordNavigationEnhanced.d.ts.map +0 -1
- package/dist/RelationshipGraph.d.ts +0 -23
- package/dist/RelationshipGraph.d.ts.map +0 -1
- package/dist/renderers/PermissionFacetLink.d.ts +0 -38
- package/dist/renderers/PermissionFacetLink.d.ts.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FeedItem, FeedItemType } from '@object-ui/types';
|
|
2
|
-
import { FeedFilterMode } from '../RecordActivityTimeline';
|
|
2
|
+
import { FeedFilterMode } from '../RecordActivityTimeline.js';
|
|
3
3
|
/** Spec default for `RecordActivityProps.limit`. */
|
|
4
4
|
export declare const DEFAULT_ACTIVITY_LIMIT = 20;
|
|
5
5
|
/**
|
|
@@ -39,33 +39,182 @@ export declare const DEFAULT_ACTIVITY_LIMIT = 20;
|
|
|
39
39
|
* ADR-0052 §5b.2 `activityMilestones[].type` is applied verbatim by
|
|
40
40
|
* plugin-audit's `audit-writers.ts` (`if (milestone.type) activityType =
|
|
41
41
|
* milestone.type`). That is how `completed` is produced, and it is a general
|
|
42
|
-
* door, not a special case.
|
|
42
|
+
* door, not a special case. A row that door wrote also carries
|
|
43
|
+
* `metadata.kind: 'milestone'`, and the mapper overrides this table's
|
|
44
|
+
* `completed` entry for it: see {@link MILESTONE_ACTIVITY_FEED_TYPE}
|
|
45
|
+
* (objectui#12106).
|
|
43
46
|
*
|
|
44
47
|
* So `scheduled` is a value that is written, stored and queryable while being
|
|
45
48
|
* undeclared upstream. Dropping it was not a decision this map made; it was the
|
|
46
|
-
* absence of one. Its producer is HotCRM's
|
|
47
|
-
* (`src/actions/global.actions.ts`
|
|
48
|
-
* 'completed' : 'scheduled'`),
|
|
49
|
-
* `
|
|
50
|
-
*
|
|
49
|
+
* absence of one. Its producer is HotCRM's activity-action factory
|
|
50
|
+
* (`src/sales/actions/activity-actions.ts`, once `src/actions/global.actions.ts`
|
|
51
|
+
* — `type: EVENT_STATUS === 'held' ? 'completed' : 'scheduled'`), which builds
|
|
52
|
+
* `log_call`, `log_meeting` and `schedule_meeting` for `crm_lead`,
|
|
53
|
+
* `crm_contact`, `crm_account`, `crm_opportunity` and `crm_case`: the held
|
|
54
|
+
* branch reached the timeline, the scheduled branch never did.
|
|
51
55
|
*
|
|
52
56
|
* `scheduled` → `event` is the semantic pairing and it is what makes the
|
|
53
|
-
* declared `event` feed type reachable at all.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* `
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
57
|
+
* declared `event` feed type reachable at all. Both branches of that producer
|
|
58
|
+
* show by default: a not-yet-held meeting is `scheduled` → `event`, because an
|
|
59
|
+
* upcoming meeting is the part of a timeline you still act on; a held one is
|
|
60
|
+
* `completed` → `task`, and shows because it is a LOGGED INTERACTION, a `task`
|
|
61
|
+
* item that points at the record it was logged as (objectui#12228, see
|
|
62
|
+
* {@link isLoggedInteraction}). Until objectui#12228 the held branch was hidden
|
|
63
|
+
* unless `showCompleted`.
|
|
64
|
+
*
|
|
65
|
+
* ## The vocabulary is OPEN — ruled. Do NOT restore set-equality.
|
|
66
|
+
*
|
|
67
|
+
* Maintainer ruling of 2026-08-24 (objectstack `88b9d749a`), **direction 4**:
|
|
68
|
+
* `sys_activity.type` is AUTHOR-EXTENSIBLE. The declared select options are the
|
|
69
|
+
* platform's BUILT-IN set, not the column's domain, and the two facts above are
|
|
70
|
+
* why — readonly fields are never validated on write, and ADR-0052 §5b.2
|
|
71
|
+
* forwards an author's `activityMilestones[].type` into the column verbatim.
|
|
72
|
+
*
|
|
73
|
+
* So the relation this table stands in is a SUPERSET, in one direction only:
|
|
74
|
+
*
|
|
75
|
+
* - **map ⊇ built-ins** — every value the platform declares has an entry here,
|
|
76
|
+
* either a feed type or a deliberate `undefined` exclusion. A new built-in
|
|
77
|
+
* with no entry is a gap, and objectui#5969's pin turns red for it.
|
|
78
|
+
* - **NOT built-ins ⊇ map**, and NOT set-equality in either spelling. An
|
|
79
|
+
* author-extended value legitimately exists outside the declaration, so a
|
|
80
|
+
* pin that required the two sets to match would be false by construction.
|
|
81
|
+
*
|
|
82
|
+
* ⛔ A reader who finds no equality check here is looking at a DECISION, not an
|
|
83
|
+
* omission: objectui#5840 removed the old set-equality pin on purpose, because
|
|
84
|
+
* pinning to the closed declaration meant dropping stored rows, and
|
|
85
|
+
* objectui#5969 replaced it with the two-directional pin under this ruling.
|
|
86
|
+
*
|
|
87
|
+
* (`scheduled` — the value #5840 was about — has since been declared upstream
|
|
88
|
+
* by objectstack#11522, superseding objectstack#11424. That is precisely the
|
|
89
|
+
* drift a hand-maintained equality pin cannot survive, and the reason the pin
|
|
90
|
+
* is a superset assertion plus a fallback rather than a census of two groups.)
|
|
91
|
+
*
|
|
92
|
+
* A value outside this table is NO LONGER DROPPED: it renders through
|
|
93
|
+
* {@link UNMAPPED_ACTIVITY_FEED_TYPE}. See {@link activityRowToFeedItem}.
|
|
67
94
|
*/
|
|
68
95
|
export declare const ACTIVITY_TYPE_TO_FEED_TYPE: Readonly<Record<string, FeedItemType | undefined>>;
|
|
96
|
+
/**
|
|
97
|
+
* The presentation an `sys_activity.type` value outside
|
|
98
|
+
* {@link ACTIVITY_TYPE_TO_FEED_TYPE} renders through.
|
|
99
|
+
*
|
|
100
|
+
* The second half of the direction-4 ruling (2026-08-24, objectstack `88b9d749a`): if
|
|
101
|
+
* the column is author-extensible, then a value this map has never heard of is
|
|
102
|
+
* REAL RECORD ACTIVITY that an author extended the platform with — not a
|
|
103
|
+
* mistake — and dropping it is the objectui#5840 failure mode reappearing for
|
|
104
|
+
* every author who ever writes one. Stored, queryable, invisible.
|
|
105
|
+
*
|
|
106
|
+
* `system` is the generic bucket rather than a new feed type because
|
|
107
|
+
* `FeedItemType` is a CLOSED spec enum owned by `@objectstack/spec` — minting a
|
|
108
|
+
* kind for "we don't know" is a platform change, not this block's.
|
|
109
|
+
*
|
|
110
|
+
* ⚠️ This is a FLOOR under the map, never a substitute for it. objectui#5840
|
|
111
|
+
* rejected a catch-all *offered as the fix* — an unmeasured type rendering as
|
|
112
|
+
* `system` is not the same data as a type someone read and mapped. What makes
|
|
113
|
+
* the catch-all safe here is that it does not stand alone: the superset pin
|
|
114
|
+
* above forces every built-in to keep its OWN presentation, so the fallback can
|
|
115
|
+
* only ever receive values nobody has ruled on yet, and the diagnostic below
|
|
116
|
+
* names each one so somebody can.
|
|
117
|
+
*/
|
|
118
|
+
export declare const UNMAPPED_ACTIVITY_FEED_TYPE: FeedItemType;
|
|
119
|
+
/**
|
|
120
|
+
* The feed type a milestone row renders as when its `type` would otherwise land
|
|
121
|
+
* on a feed type `showCompleted` hides (objectui#12106).
|
|
122
|
+
*
|
|
123
|
+
* Today that is a `completed` row that a milestone fired, such as "Case
|
|
124
|
+
* resolved", or a task's own "Task completed" on that task's page. Triage
|
|
125
|
+
* ruled route B: a milestone is not a completed activity, so it shows whatever
|
|
126
|
+
* `showCompleted` says. `showCompleted` keeps its spec meaning ("Include
|
|
127
|
+
* completed activities") for every row without the marker.
|
|
128
|
+
*
|
|
129
|
+
* `system`, not `task` with an exemption, because every other reader of the
|
|
130
|
+
* feed type then stays right with no new member on `FeedItem`:
|
|
131
|
+
*
|
|
132
|
+
* - `filterMode: 'tasks_only'` and `types: ['task']` keep meaning tasks. A
|
|
133
|
+
* case milestone is not one.
|
|
134
|
+
* - {@link COMPLETED_FEED_TYPES} stays exact: `task` is still produced only by
|
|
135
|
+
* completed activities.
|
|
136
|
+
* - The icon and colour are the ones every other author-typed milestone
|
|
137
|
+
* already gets (`escalated` reaches `system` through
|
|
138
|
+
* {@link UNMAPPED_ACTIVITY_FEED_TYPE}), and the ones app-shell's
|
|
139
|
+
* `ActivityItem` surface gives `completed`.
|
|
140
|
+
* - A host feed arrives already mapped, so the rule needs nothing carried
|
|
141
|
+
* past this module.
|
|
142
|
+
*/
|
|
143
|
+
export declare const MILESTONE_ACTIVITY_FEED_TYPE: FeedItemType;
|
|
144
|
+
/**
|
|
145
|
+
* Feed kinds an ObjectUI surface produces WITHOUT going through
|
|
146
|
+
* {@link ACTIVITY_TYPE_TO_FEED_TYPE}.
|
|
147
|
+
*
|
|
148
|
+
* Exactly one today: `comment`, built from `sys_comment` rows by app-shell's
|
|
149
|
+
* `RecordDetailView` (the `sys_comment` read, and the two optimistic rows the
|
|
150
|
+
* composer writes) and handed to this block through `DiscussionContext`. It
|
|
151
|
+
* cannot be derived here — app-shell depends on this package, not the other way
|
|
152
|
+
* round — so it is DECLARED, and `feedTypeProducerCensus-5877.test.ts` re-runs
|
|
153
|
+
* the census over the repository so the declaration cannot go stale silently.
|
|
154
|
+
*
|
|
155
|
+
* ## Census, and the pathspec it was taken over (objectui#5877)
|
|
156
|
+
*
|
|
157
|
+
* Every `.ts` / `.tsx` file in the repository that is not a test, at every top
|
|
158
|
+
* level (`packages/`, `apps/`, `examples/`, `scripts/`, `e2e/`), narrowed to the
|
|
159
|
+
* files that mention `FeedItem` at all — you cannot construct one without
|
|
160
|
+
* naming the type — then read for `FeedItemType` literals. 1622 files scanned,
|
|
161
|
+
* 13 mention `FeedItem`, one produces a kind off this map. A census is only as
|
|
162
|
+
* wide as its pathspec, so the pathspec is stated rather than implied, and the
|
|
163
|
+
* test above holds it.
|
|
164
|
+
*
|
|
165
|
+
* ⛔ NOT counted, deliberately: app-shell's `sharedUserFeeds.ts` /
|
|
166
|
+
* `activityItemType.ts` read the same `sys_activity` rows but produce
|
|
167
|
+
* `ActivityItemType` (`create` / `update` / `delete` / `comment` / `system`), a
|
|
168
|
+
* DIFFERENT five-value vocabulary that is not a projection of this one in either
|
|
169
|
+
* direction (objectui#6730 records why converging them costs rows). A row it
|
|
170
|
+
* turns into an `ActivityItem` never becomes a `FeedItem`, so it produces
|
|
171
|
+
* nothing for this feed. `ActivityEntry` in `@object-ui/types` is a third
|
|
172
|
+
* vocabulary again, and has no producer at all.
|
|
173
|
+
*
|
|
174
|
+
* ⚠️ Also NOT counted, and the reason the diagnostic below names it: a HOST can
|
|
175
|
+
* hand this block a feed of its own (`items` on the node, or a
|
|
176
|
+
* `DiscussionContext` it mounts itself). Those items are produced outside this
|
|
177
|
+
* repository, so no census taken here can bound their kinds.
|
|
178
|
+
*/
|
|
179
|
+
export declare const FEED_TYPES_PRODUCED_OFF_MAP: readonly FeedItemType[];
|
|
180
|
+
/**
|
|
181
|
+
* Feed kinds this repository has decided NOT to adopt — as opposed to kinds
|
|
182
|
+
* nothing happens to produce.
|
|
183
|
+
*
|
|
184
|
+
* The distinction matters more than the diagnostic that uses it. Reporting a
|
|
185
|
+
* decision as a defect is how a warning channel gets trained out of an author's
|
|
186
|
+
* attention, and these three are decisions: `created` / `deleted` / `shared`
|
|
187
|
+
* rows are mapped to `field_change` ON PURPOSE, because `RecordDetailView` and
|
|
188
|
+
* this block must agree about what a `created` row IS before either can move to
|
|
189
|
+
* the richer kind. Adopting them is a change to that shared map, not to this
|
|
190
|
+
* block. The reasoning is written out at {@link ACTIVITY_TYPE_TO_FEED_TYPE}.
|
|
191
|
+
*
|
|
192
|
+
* ⚠️ This list is a TRANSCRIPTION of that prose, not a derivation, and there is
|
|
193
|
+
* no way to make it one: the decision exists as a comment, and a comment is not
|
|
194
|
+
* machine-readable. Two consequences, both load-bearing:
|
|
195
|
+
*
|
|
196
|
+
* 1. it is only as true as the prose it was copied from, so it is edited
|
|
197
|
+
* together with that prose and never on its own;
|
|
198
|
+
* 2. a kind that is NOT on this list is not thereby known to be an oversight.
|
|
199
|
+
* It is only known to have no producer. Whether somebody decided against it
|
|
200
|
+
* and did not write it down is not recorded anywhere this code can read, and
|
|
201
|
+
* the message below says so rather than guessing.
|
|
202
|
+
*
|
|
203
|
+
* ⛔ Do not extend it by inference. A kind belongs here when a decision about it
|
|
204
|
+
* is written down, not when its absence looks deliberate.
|
|
205
|
+
*/
|
|
206
|
+
export declare const DELIBERATELY_UNADOPTED_FEED_TYPES: readonly FeedItemType[];
|
|
207
|
+
/**
|
|
208
|
+
* Every feed kind some ObjectUI surface can put on a record feed.
|
|
209
|
+
*
|
|
210
|
+
* DERIVED from the producers themselves — the map's range, its fallback, and
|
|
211
|
+
* the declared off-map producer above — never a second hand-written list of
|
|
212
|
+
* kinds. Giving an unproduced kind a producer (one new entry in
|
|
213
|
+
* {@link ACTIVITY_TYPE_TO_FEED_TYPE}) therefore retires its diagnostic in the
|
|
214
|
+
* same edit, with nothing else to remember. A hand-kept copy would not, and
|
|
215
|
+
* that copy is exactly what this file's header records the cost of.
|
|
216
|
+
*/
|
|
217
|
+
export declare const PRODUCED_FEED_TYPES: ReadonlySet<FeedItemType>;
|
|
69
218
|
/** A row as `sys_activity` returns it. Loose on purpose: the block reads a
|
|
70
219
|
* system table it does not own, and an unknown column is not an error. */
|
|
71
220
|
export interface SysActivityRow {
|
|
@@ -80,6 +229,12 @@ export interface SysActivityRow {
|
|
|
80
229
|
source_id?: string | number | null;
|
|
81
230
|
[k: string]: unknown;
|
|
82
231
|
}
|
|
232
|
+
/** Test seam: forget which `types` entries have already been named. */
|
|
233
|
+
export declare function resetUnrecognisedFeedTypeWarnings(): void;
|
|
234
|
+
/** Test seam: forget which unproduced `types` entries have already been named. */
|
|
235
|
+
export declare function resetUnproducedFeedTypeWarnings(): void;
|
|
236
|
+
/** Test seam: forget which `filterMode` values have already been named. */
|
|
237
|
+
export declare function resetUnrecognisedFilterModeWarnings(): void;
|
|
83
238
|
/**
|
|
84
239
|
* Coerce an authored `filterMode` to a value the timeline understands.
|
|
85
240
|
*
|
|
@@ -87,10 +242,40 @@ export interface SysActivityRow {
|
|
|
87
242
|
* `<Select>` handed a value with no matching item renders blank and the
|
|
88
243
|
* dropdown reads as broken. Same posture as objectui#3151 — an unrecognised
|
|
89
244
|
* filter token is skipped, never turned into a filter that can't match.
|
|
245
|
+
*
|
|
246
|
+
* ## Why that fallback needs a diagnostic (objectui#5891)
|
|
247
|
+
*
|
|
248
|
+
* `'all'` is the WIDEST of the four declared modes — `filterItems` in
|
|
249
|
+
* `RecordActivityTimeline` narrows to ONE feed type for each of the other three
|
|
250
|
+
* and returns the feed untouched for `'all'` — so every unrecognised value
|
|
251
|
+
* lands on the one option that shows the author MORE than they asked for. A
|
|
252
|
+
* near-miss like `comments-only` or `commentsOnly` therefore opens the panel on
|
|
253
|
+
* the unfiltered stream, and the tell is a PLAUSIBLE result (a populated
|
|
254
|
+
* timeline) rather than an empty one that gets investigated. That is the same
|
|
255
|
+
* shape objectui#5841 removed from the sibling `types` sanitiser.
|
|
256
|
+
*
|
|
257
|
+
* The ruling on this card (triage, 2026-08-25) is that the defect is the
|
|
258
|
+
* INVISIBILITY, not the fallback: `filterMode` seeds a control the user can
|
|
259
|
+
* change, and there is no defensible narrower default to fall back to instead.
|
|
260
|
+
* So nothing that renders changes here — what changes is that the fold is now
|
|
261
|
+
* SAID OUT LOUD, once per distinct offending value on each block (see
|
|
262
|
+
* {@link warnOnce}).
|
|
263
|
+
*
|
|
264
|
+
* `block` is the registered name of the block the value was authored on
|
|
265
|
+
* (`record:activity`, `record:chatter`, `record:discussion`). It is REQUIRED,
|
|
266
|
+
* not defaulted: the message is addressed to that block's author, and a default
|
|
267
|
+
* would hand every caller that forgot it some other block's name — the defect
|
|
268
|
+
* objectui#9557 removed.
|
|
269
|
+
*
|
|
270
|
+
* Absent (`undefined` / `null`) is silent: no `filterMode` was authored, which
|
|
271
|
+
* is not a mistake, and a warning about a decision teaches authors to ignore
|
|
272
|
+
* the channel. Only a value the author actually wrote is reported.
|
|
273
|
+
*
|
|
274
|
+
* The declared modes named in the message are read from `@objectstack/spec`'s
|
|
275
|
+
* `FeedFilterMode` at runtime, never re-typed here — see the file header for
|
|
276
|
+
* what a hand copy of a spec enum cost.
|
|
90
277
|
*/
|
|
91
|
-
export declare function normalizeFilterMode(value: unknown): FeedFilterMode;
|
|
92
|
-
/** Test seam: forget which `types` entries have already been named. */
|
|
93
|
-
export declare function resetUnrecognisedFeedTypeWarnings(): void;
|
|
278
|
+
export declare function normalizeFilterMode(value: unknown, block: string): FeedFilterMode;
|
|
94
279
|
/**
|
|
95
280
|
* Sanitise an authored `types` allow-list. Narrowing or refusal, never widening.
|
|
96
281
|
*
|
|
@@ -103,6 +288,7 @@ export declare function resetUnrecognisedFeedTypeWarnings(): void;
|
|
|
103
288
|
* | `[]` | `[]` | nothing — the author said "no kinds" |
|
|
104
289
|
* | `['crm_task']` (none known) | `[]` | nothing, plus one diagnostic |
|
|
105
290
|
* | `['task', 'crm_task']` | `['task']` | the recognised members, plus one diagnostic |
|
|
291
|
+
* | `['approval']` (no producer) | `['approval']` | nothing, plus one diagnostic — the kind is declared and honoured, but nothing emits it (objectui#5877) |
|
|
106
292
|
*
|
|
107
293
|
* `undefined` now means "no `types` key was authored" and ONLY that. It used to
|
|
108
294
|
* mean that OR "everything you authored was dropped", and {@link applyFeedConfig}
|
|
@@ -121,13 +307,57 @@ export declare function resetUnrecognisedFeedTypeWarnings(): void;
|
|
|
121
307
|
* Membership is read from `@objectstack/spec`'s `FeedItemType` at runtime, never
|
|
122
308
|
* re-typed here — see the file header for what a hand copy of a spec enum cost.
|
|
123
309
|
*
|
|
124
|
-
* NOT pure
|
|
125
|
-
*
|
|
126
|
-
* the fix rather than a nicety
|
|
310
|
+
* NOT pure, on TWO independent channels (see {@link warnOnce}) — the failure
|
|
311
|
+
* being repaired is invisibility in both cases, so the diagnostics are the other
|
|
312
|
+
* half of the fix rather than a nicety:
|
|
313
|
+
*
|
|
314
|
+
* - an entry it cannot recognise logs once (no declared kind matches it);
|
|
315
|
+
* - a RECOGNISED entry that no producer emits logs once as well, on its own
|
|
316
|
+
* channel — see {@link warnUnproducedFeedTypes}. That one changes nothing
|
|
317
|
+
* about the return value: the kind is declared, so it is kept and honoured.
|
|
318
|
+
*
|
|
319
|
+
* Both channels name, and dedupe per, the REQUIRED `block` the list was
|
|
320
|
+
* authored on — see {@link normalizeFilterMode} for why it has no default.
|
|
321
|
+
*/
|
|
322
|
+
export declare function normalizeFeedTypes(value: unknown, block: string): FeedItemType[] | undefined;
|
|
323
|
+
/**
|
|
324
|
+
* The ONE resolver for this block's row cap (objectui#10096, objectui#10145).
|
|
325
|
+
*
|
|
326
|
+
* A refused value is dropped for this block's own default.
|
|
327
|
+
*
|
|
328
|
+
* ⛔ Not `Math.floor`, which objectui#10096 replaced: flooring REPAIRED an
|
|
329
|
+
* authored `2.5` into a two-row window and handed it back as a result, so the
|
|
330
|
+
* author got a silently different number than the one they wrote. Refusing and
|
|
331
|
+
* defaulting is the answer the sibling `record:history` gives through
|
|
332
|
+
* `normalizeHistoryLimit`, and the answer objectui#9925 landed at three further
|
|
333
|
+
* read points (all three test `typeof value === 'number'` first, as this does).
|
|
334
|
+
*
|
|
335
|
+
* `NaN` and both infinities are refused (not integers); `0` and negatives are
|
|
336
|
+
* refused (not positive); `undefined` is the unauthored case and resolves to
|
|
337
|
+
* the default without being a refusal.
|
|
338
|
+
*
|
|
339
|
+
* ⚠️ FAIL-SOFT on purpose: throwing would take out a record page over one
|
|
340
|
+
* declaration. It is NOT silent — the renderers that call this state every
|
|
341
|
+
* refusal through `describeRefusedFeedLimit` (objectui#10097 ruled "always
|
|
342
|
+
* warn" for the family; objectui#10145 extends the refused set to non-numbers).
|
|
343
|
+
* This function stays pure so it can run on every render.
|
|
344
|
+
*
|
|
345
|
+
* ⭐ Shared with `record:chatter` / `record:discussion`, which resolve
|
|
346
|
+
* `feed.limit` through this same function — `RecordChatterProps.feed` is
|
|
347
|
+
* `RecordActivityProps`, so it is the same declared member and the same
|
|
348
|
+
* refusal.
|
|
127
349
|
*/
|
|
128
|
-
export declare function normalizeFeedTypes(value: unknown): FeedItemType[] | undefined;
|
|
129
|
-
/** Coerce `limit` to a positive integer, falling back to the spec default. */
|
|
130
350
|
export declare function normalizeLimit(value: unknown): number;
|
|
351
|
+
/**
|
|
352
|
+
* The diagnostic half of the row-cap refusal for the blocks that resolve
|
|
353
|
+
* through `normalizeLimit` (`record:activity`, `record:chatter`,
|
|
354
|
+
* `record:discussion`). `null` means "nothing to say": an absent `limit`
|
|
355
|
+
* (`undefined` / `null`) is not a mistake, and a usable one is not either.
|
|
356
|
+
*
|
|
357
|
+
* Callers fire it from an effect keyed on the declaration — the channel
|
|
358
|
+
* objectui#9925 uses at its three read points — never from render.
|
|
359
|
+
*/
|
|
360
|
+
export declare function describeRefusedFeedLimit(block: string, authored: unknown): string | null;
|
|
131
361
|
/**
|
|
132
362
|
* Timestamp of a `sys_activity` row.
|
|
133
363
|
*
|
|
@@ -139,13 +369,49 @@ export declare function normalizeLimit(value: unknown): number;
|
|
|
139
369
|
export declare function activityTimestamp(row: SysActivityRow): string;
|
|
140
370
|
/** Test seam: forget which unknown types have already been warned about. */
|
|
141
371
|
export declare function resetUnknownActivityTypeWarnings(): void;
|
|
372
|
+
/**
|
|
373
|
+
* The name of whoever acted on a `sys_activity` row, or `null` when the row
|
|
374
|
+
* names nobody this viewer can name (objectui#12067). One reading for every
|
|
375
|
+
* surface that shows these rows: `record:history`, and through
|
|
376
|
+
* {@link activityRowToFeedItem} the `record:activity` block and the console
|
|
377
|
+
* record page's merged feed.
|
|
378
|
+
*
|
|
379
|
+
* 1. `actor_name`, when it is non-blank. It is the snapshot the writer took at
|
|
380
|
+
* the time of the action, so it wins even over the user's current name.
|
|
381
|
+
* 2. `actor_id`, a lookup to `sys_user`. A read that passes
|
|
382
|
+
* `$expand: ['actor_id']` gets the user's record in place of the id, and
|
|
383
|
+
* that record's `name` (`sys_user`'s declared name field) names the actor.
|
|
384
|
+
* The engine expands every row of a page in one batched read. This is the
|
|
385
|
+
* row the `@objectstack/*` 17.7.0 writer leaves: `actor_id` set and no
|
|
386
|
+
* `actor_name` (objectstack#22510 fills `actor_name` from then on).
|
|
387
|
+
* 3. `null`, so the caller shows its existing fallback. That covers a row
|
|
388
|
+
* with neither, and an `actor_id` the engine kept as the bare id because
|
|
389
|
+
* this viewer may not read that user or the user is gone. A raw id is
|
|
390
|
+
* never shown as a name.
|
|
391
|
+
*/
|
|
392
|
+
export declare function activityActorName(row: SysActivityRow): string | null;
|
|
142
393
|
/**
|
|
143
394
|
* One `sys_activity` row → one {@link FeedItem}, or `null` when the row is not
|
|
144
395
|
* record activity (see {@link ACTIVITY_TYPE_TO_FEED_TYPE}).
|
|
145
396
|
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
397
|
+
* Three outcomes, and the difference between the last two is the whole of the
|
|
398
|
+
* direction-4 ruling (2026-08-24, objectstack `88b9d749a`):
|
|
399
|
+
*
|
|
400
|
+
* - a type mapped to a feed type renders with THAT presentation;
|
|
401
|
+
* - a type the table maps to `undefined` is a DELIBERATE exclusion — `null`,
|
|
402
|
+
* quietly, because a warning about a decision teaches authors to ignore the
|
|
403
|
+
* channel;
|
|
404
|
+
* - a type the table does not contain at all is an AUTHOR-EXTENDED value under
|
|
405
|
+
* the ruled open vocabulary. It renders through
|
|
406
|
+
* {@link UNMAPPED_ACTIVITY_FEED_TYPE} and says so once. It used to return
|
|
407
|
+
* `null` here, which made every extended value invisible — the same outcome
|
|
408
|
+
* objectui#5840 was filed for, reached by a different route.
|
|
409
|
+
*
|
|
410
|
+
* One override sits on top of the first outcome (objectui#12106): a row whose
|
|
411
|
+
* type maps to a feed type `showCompleted` hides, but which carries the
|
|
412
|
+
* milestone marker, renders as {@link MILESTONE_ACTIVITY_FEED_TYPE}. A
|
|
413
|
+
* milestone is not a completed activity. A row without the marker is mapped
|
|
414
|
+
* exactly as before.
|
|
149
415
|
*/
|
|
150
416
|
export declare function activityRowToFeedItem(row: SysActivityRow, systemActorLabel: string): FeedItem | null;
|
|
151
417
|
/** The subset of `RecordActivityProps` that decides which items survive. */
|
|
@@ -173,8 +439,52 @@ export interface AppliedFeed {
|
|
|
173
439
|
* `pageSize` is the effective window (`limit` on first render, grown by
|
|
174
440
|
* "Load more"); it is separate from `config.limit` so paging does not have to
|
|
175
441
|
* rewrite the authored config.
|
|
442
|
+
*
|
|
443
|
+
* `block` is the registered name of the block whose config this is; the
|
|
444
|
+
* `types` diagnostics are addressed to it (objectui#9557, see
|
|
445
|
+
* {@link normalizeFilterMode}).
|
|
176
446
|
*/
|
|
177
|
-
export declare function applyFeedConfig(items: readonly FeedItem[], config: FeedConfigFilters, pageSize: number): AppliedFeed;
|
|
447
|
+
export declare function applyFeedConfig(items: readonly FeedItem[], config: FeedConfigFilters, pageSize: number, block: string): AppliedFeed;
|
|
178
448
|
/** Sort a feed chronologically and de-duplicate by id (feeds are append-only). */
|
|
179
449
|
export declare function mergeFeedItems(...groups: readonly FeedItem[][]): FeedItem[];
|
|
450
|
+
/**
|
|
451
|
+
* Whether a rejected feed read — `sys_activity` or `sys_comment` — was REFUSED
|
|
452
|
+
* (objectui#11195): the server answered, and the answer was "not for you".
|
|
453
|
+
*
|
|
454
|
+
* A refused read and an empty read are different answers about the record, and
|
|
455
|
+
* the panel used to give both the same one. Every failure on the feed landed on
|
|
456
|
+
* `No activity recorded` / `No comments yet`, so a member the server refused
|
|
457
|
+
* read a permission wall as a record where nothing had happened. The panel now
|
|
458
|
+
* shows a no-permission state for a refused read instead, and keeps the empty
|
|
459
|
+
* state for a read that answered with zero rows.
|
|
460
|
+
*
|
|
461
|
+
* The verdict is `classifyLoadError`'s (`@object-ui/react`), the read
|
|
462
|
+
* classifier `ListView`'s error panel and `RecordAttachmentsPanel` already
|
|
463
|
+
* share. Nothing here reads a status or a code of its own. Refused means the
|
|
464
|
+
* two kinds that are about the caller:
|
|
465
|
+
*
|
|
466
|
+
* - `forbidden`: 403, or a `PERMISSION_DENIED` / `FORBIDDEN` envelope;
|
|
467
|
+
* - `unauthorized`: 401, or `UNAUTHORIZED` / `UNAUTHENTICATED`.
|
|
468
|
+
*
|
|
469
|
+
* ⚠️ The 401 half is a deliberate difference from `RecordAttachmentsPanel`,
|
|
470
|
+
* whose `denied` state does not claim a 401. The triage on objectui#11195 ruled
|
|
471
|
+
* it for this panel: any refused read (401 / 403, or a permission envelope)
|
|
472
|
+
* shows the no-permission state.
|
|
473
|
+
*
|
|
474
|
+
* NOT refused, so these keep today's empty state:
|
|
475
|
+
*
|
|
476
|
+
* - `api-disabled` (`OBJECT_API_DISABLED` 404 / `OBJECT_API_METHOD_NOT_ALLOWED`
|
|
477
|
+
* 405): a property of the object's metadata, not of the caller, and no
|
|
478
|
+
* permission grant changes it;
|
|
479
|
+
* - a bare 404 (`sys_activity` on a deployment without the audit plugin).
|
|
480
|
+
* The ObjectStack adapter already answers that as zero rows; another
|
|
481
|
+
* adapter's rejection classifies as `network` and lands here;
|
|
482
|
+
* - `rejected` (400) and `network` (a 5xx, an unreachable server). Those are
|
|
483
|
+
* failures, not refusals. The panel has no error state of its own, so they
|
|
484
|
+
* still render the empty state.
|
|
485
|
+
*
|
|
486
|
+
* ⛔ A refusal is not retried. A refused read is the same answer on every
|
|
487
|
+
* retry, so the callers write it into state and do not re-issue the read.
|
|
488
|
+
*/
|
|
489
|
+
export declare function isRefusedFeedRead(err: unknown): boolean;
|
|
180
490
|
//# sourceMappingURL=recordActivityFeed.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordActivityFeed.d.ts","sourceRoot":"","sources":["../../src/renderers/recordActivityFeed.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"recordActivityFeed.d.ts","sourceRoot":"","sources":["../../src/renderers/recordActivityFeed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*
|
|
8
|
+
* The ONE-RECORD binding of the record blocks (objectui#12294).
|
|
9
|
+
*
|
|
10
|
+
* `PageComponentSchema.dataSource` is the spec's per-element data binding, and
|
|
11
|
+
* the spec declares it on every page component ("Overrides the page-level
|
|
12
|
+
* object context for this block"). `ElementDataSourceGate` (`@object-ui/react`)
|
|
13
|
+
* honours it for the LIST-shaped blocks by mapping the binding onto keys the
|
|
14
|
+
* block then queries with. The record blocks query nothing: they read the row
|
|
15
|
+
* the record page put in `RecordContextProvider`. So on an `app` page, with no
|
|
16
|
+
* record page around them, `record:details`, `record:highlights` and
|
|
17
|
+
* `record:quick_actions` drew an empty box or "no actions configured" even
|
|
18
|
+
* with a binding that returns one row.
|
|
19
|
+
*
|
|
20
|
+
* This module is the one-record consumer those three share, and
|
|
21
|
+
* {@link recordBindingBlock} is the one way a record block takes it. The gate
|
|
22
|
+
* still resolves the binding (the named view, the AND-combined filter, the
|
|
23
|
+
* sort, and every configuration panel the family draws), so a record block's
|
|
24
|
+
* binding means exactly what it means on a list block. What this adds is the
|
|
25
|
+
* last hop: read the bound set, take its FIRST row, and provide that row
|
|
26
|
+
* through the existing `RecordContextProvider` — so the block bodies are
|
|
27
|
+
* unchanged and there is no second rendering path inside a block.
|
|
28
|
+
*
|
|
29
|
+
* ## Semantics of the bound set (the card's, verbatim in intent)
|
|
30
|
+
*
|
|
31
|
+
* - 0 rows → the block's authored `emptyState` when it declares one (see
|
|
32
|
+
* "The authored empty state" below). Without one, the block is mounted under
|
|
33
|
+
* a record context that names the bound object and NO record, so each block
|
|
34
|
+
* draws what it draws for a record that is not there: `record:details` its
|
|
35
|
+
* "record not found" state, `record:highlights` nothing,
|
|
36
|
+
* `record:quick_actions` its empty bar. A row the caller may not read never
|
|
37
|
+
* comes back from the server, and a read the server refuses outright
|
|
38
|
+
* (`classifyLoadError` → `forbidden`) is treated the same, so "absent" and
|
|
39
|
+
* "not yours to read" are indistinguishable — and the authored empty state
|
|
40
|
+
* is drawn for both.
|
|
41
|
+
* - 1 row → that record.
|
|
42
|
+
* - more than one row → the FIRST by the binding's `sort`. Only one row is
|
|
43
|
+
* ever asked for (`$top: 1`): the first row of the authored `limit` and the
|
|
44
|
+
* first row of a one-row read are the same row under the same `sort`.
|
|
45
|
+
* `objectui validate` warns on a record block whose binding has no
|
|
46
|
+
* `limit: 1`, so an author says which one they mean.
|
|
47
|
+
*
|
|
48
|
+
* The row is read with the adapter the page already uses, so under the
|
|
49
|
+
* caller's identity: nothing here widens what the server serves.
|
|
50
|
+
*
|
|
51
|
+
* ## The authored empty state (objectui#12932)
|
|
52
|
+
*
|
|
53
|
+
* The spec declares `emptyState` on the three bound record blocks
|
|
54
|
+
* (`RecordDetailsProps`, `RecordHighlightsProps`, `RecordQuickActionsProps`;
|
|
55
|
+
* objectstack#24266), the list view's own `EmptyStateSchema` by reference:
|
|
56
|
+
* `{ title, message, icon, link }`. It is drawn on ONE condition, the spec's:
|
|
57
|
+
* the block carries a `dataSource` binding and the settled read answered no
|
|
58
|
+
* row. {@link BoundRecordEmptyState} draws it IN PLACE of the block body, with
|
|
59
|
+
* the pieces `ObjectGrid` draws its authored empty state with —
|
|
60
|
+
* `DataEmptyState`, `resolveIcon` for the `icon`, the spec's own
|
|
61
|
+
* `resolveI18nLabel` for `title` and `message` against the display locale,
|
|
62
|
+
* and the one shared `EmptyStateLink` for `link` — so one key draws one
|
|
63
|
+
* picture on every surface that takes it.
|
|
64
|
+
*
|
|
65
|
+
* It is NOT drawn while the read is in flight (the loading state), for a read
|
|
66
|
+
* that failed (the error panel stays the error panel), for a bound row that is
|
|
67
|
+
* there but leaves the block nothing to show, or on an unbound block: an
|
|
68
|
+
* unbound block reads the record page's record and never reaches this module's
|
|
69
|
+
* read. A block that declares none keeps its own no-record state above.
|
|
70
|
+
*
|
|
71
|
+
* ## The record page path is untouched
|
|
72
|
+
*
|
|
73
|
+
* {@link BoundRecordScope} renders its children AS THEY ARE when the block
|
|
74
|
+
* carries no binding: no provider, no element, no fetch. A record page's own
|
|
75
|
+
* `RecordContextProvider` therefore still reaches the block, byte-identically.
|
|
76
|
+
* With a binding, the binding wins, as the spec says it does: the bound record
|
|
77
|
+
* shadows the page record for this block only.
|
|
78
|
+
*
|
|
79
|
+
* ## Freshness: the data-invalidation bus (objectstack#4444's bar)
|
|
80
|
+
*
|
|
81
|
+
* A fetching record context that does not listen to the bus shows a STALE
|
|
82
|
+
* record that looks correct after a write elsewhere on the page — harder to
|
|
83
|
+
* notice than an empty one. So the read is keyed on `useDataInvalidation` for
|
|
84
|
+
* the bound OBJECT, the same bus `RecordDetailView` keys its record read on.
|
|
85
|
+
* Object-scoped, not row-scoped, because the binding is a query: a write to any
|
|
86
|
+
* row of the object can change which row is first. A refetch replaces the row
|
|
87
|
+
* in place (no remount), and an equal answer is not republished as a new object
|
|
88
|
+
* (AGENTS.md #10).
|
|
89
|
+
*
|
|
90
|
+
* ## What a bound block does NOT join
|
|
91
|
+
*
|
|
92
|
+
* A bound block sits inside the record page's providers when it is placed on a
|
|
93
|
+
* record page, and two of them are about THAT page's record:
|
|
94
|
+
*
|
|
95
|
+
* - the page's inline-edit session (`InlineEditProvider`): a bound
|
|
96
|
+
* `record:details` or `record:highlights` joining it would put the bound
|
|
97
|
+
* record's fields into the page record's draft, and the page's Save bar
|
|
98
|
+
* would write them to the page record. The bound scope mounts a read-only
|
|
99
|
+
* session instead (`canEdit: false`), which is also what an `app` page,
|
|
100
|
+
* with no session at all, already gives these blocks;
|
|
101
|
+
* - the page's highlight-field registry (`HighlightFieldsProvider`): a bound
|
|
102
|
+
* `record:highlights` would otherwise hide fields from the PAGE record's
|
|
103
|
+
* `record:details`. The bound scope gets its own registry.
|
|
104
|
+
*
|
|
105
|
+
* ## The PARENT binding of `record:related_list` ({@link ParentRecordBinding})
|
|
106
|
+
*
|
|
107
|
+
* `record:related_list` is the one record block with two bindings (ruling B on
|
|
108
|
+
* objectui#12296): its node-level `dataSource` is the CHILD rows the list
|
|
109
|
+
* draws (objectstack#6953), and the spec row's `parentDataSource` is the record
|
|
110
|
+
* the list hangs off. The parent binding is read here, by the same one-record
|
|
111
|
+
* read: the same gate, the same first row by `sort`, the same invalidation bus,
|
|
112
|
+
* provided through the same `RecordContextProvider` the related-list body
|
|
113
|
+
* already reads. It differs from the three blocks above in ONE place, and the
|
|
114
|
+
* spec's describe is why: no row is a LOUD ABSENCE drawn in the block's place,
|
|
115
|
+
* never an empty record context — under one the list would query nothing and
|
|
116
|
+
* draw an empty list, which reads as "this record has no related records", a
|
|
117
|
+
* wrong fact in the silent direction.
|
|
118
|
+
*/
|
|
119
|
+
import * as React from 'react';
|
|
120
|
+
/**
|
|
121
|
+
* Wrap a record block's body so its `dataSource` binding supplies its record,
|
|
122
|
+
* and declare the key through the seam.
|
|
123
|
+
*
|
|
124
|
+
* The one way a record block takes the binding, so a fourth one cannot take it
|
|
125
|
+
* differently: `ElementDataSourceGate` resolves the binding (and draws the
|
|
126
|
+
* family's panels for an unresolvable view, a refused filter, or no adapter),
|
|
127
|
+
* {@link BoundRecordScope} reads and provides the first row, and
|
|
128
|
+
* `elementDataSourceBlock` makes the registration publish the injected
|
|
129
|
+
* `dataSource` input — from `@object-ui/core`, the import source
|
|
130
|
+
* `check:element-data-source-declaration` holds every call site to.
|
|
131
|
+
*
|
|
132
|
+
* `requiresDataSource` is set for a BOUND block only: a bound block with no
|
|
133
|
+
* adapter has nothing to read its row with, and says so through the family's
|
|
134
|
+
* no-data-source panel. An unbound block is the record page path, left as it was.
|
|
135
|
+
*
|
|
136
|
+
* @param Body The block's renderer, unchanged: it reads `useRecordContext()`.
|
|
137
|
+
* @param blockKey The registry key (`record:details`, …); the panel test ids are
|
|
138
|
+
* derived from it the way `record-related-list` spells its own.
|
|
139
|
+
*/
|
|
140
|
+
export declare function recordBindingBlock<RendererProps extends {
|
|
141
|
+
schema?: unknown;
|
|
142
|
+
}>(Body: React.ComponentType<RendererProps>, blockKey: string): React.FC<RendererProps>;
|
|
143
|
+
export interface ParentRecordBindingProps {
|
|
144
|
+
/**
|
|
145
|
+
* The PARENT binding as authored — `record:related_list`'s
|
|
146
|
+
* `parentDataSource`, the spec's `ElementDataSourceSchema` by reference.
|
|
147
|
+
* Anything that is not a binding the gate would resolve (absent included)
|
|
148
|
+
* leaves the children exactly as they are.
|
|
149
|
+
*/
|
|
150
|
+
binding: unknown;
|
|
151
|
+
/** The block's registry key, named in the no-adapter panel. */
|
|
152
|
+
blockKey: string;
|
|
153
|
+
/** `data-testid` stem for this scope's states (`<stem>-loading-record`, …). */
|
|
154
|
+
testId: string;
|
|
155
|
+
/** Heading of the panels a binding that cannot be read draws. */
|
|
156
|
+
errorTitle?: string;
|
|
157
|
+
/** What the block draws in its own place when no row answers: the loud absence. */
|
|
158
|
+
absent: React.ReactNode;
|
|
159
|
+
children: React.ReactNode;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* The parent record of a block, from a binding of its own (ruling B on
|
|
163
|
+
* objectui#12296, the spec's `RecordRelatedListProps.parentDataSource`).
|
|
164
|
+
*
|
|
165
|
+
* Unbound → the children as they are, so the record page's own context still
|
|
166
|
+
* reaches the block, byte-identically. Bound → the binding is resolved by
|
|
167
|
+
* `ElementDataSourceGate` (a named view, the AND-combined filter, the sort, and
|
|
168
|
+
* the family's panels for a view that does not resolve, a refused filter, or no
|
|
169
|
+
* adapter), its FIRST row is read by {@link BoundRecordProvider} — the one
|
|
170
|
+
* read the three one-record blocks take, keyed on the same invalidation bus —
|
|
171
|
+
* and provided through `RecordContextProvider` as the children's record. No row
|
|
172
|
+
* draws `absent` instead of the children.
|
|
173
|
+
*
|
|
174
|
+
* The gate is handed the parent binding ALONE, as a node's `dataSource`, and
|
|
175
|
+
* never the block's own schema: with the schema, the gate's filter and sort
|
|
176
|
+
* mapping would compose the block's own `filter` and `sort` — the CHILD list's
|
|
177
|
+
* — into the parent's query.
|
|
178
|
+
*/
|
|
179
|
+
export declare function ParentRecordBinding({ binding, blockKey, testId, errorTitle, absent, children, }: ParentRecordBindingProps): React.ReactElement;
|
|
180
|
+
//# sourceMappingURL=recordBinding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordBinding.d.ts","sourceRoot":"","sources":["../../src/renderers/recordBinding.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqHG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA+C/B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,SAAS;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAC3E,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,EACxC,QAAQ,EAAE,MAAM,GACf,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,CA0BzB;AA6FD,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,QAAQ,EACR,MAAM,EACN,UAAU,EACV,MAAM,EACN,QAAQ,GACT,EAAE,wBAAwB,GAAG,KAAK,CAAC,YAAY,CAgC/C"}
|