@object-ui/components 11.3.0 → 11.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,148 @@
1
1
  # @object-ui/components
2
2
 
3
+ ## 11.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6fffd3d: Client-side data-invalidation bus — refresh data, don't rebuild UI (objectui#2269 P1).
8
+
9
+ - `@object-ui/react` gains the bus: `notifyDataChanged({objectName, recordId?})`, `useDataInvalidation(objectName, recordId?)` (reader nonce), `subscribeDataChanges`, and `useMutationInvalidationBridge(dataSource)` which fans every dataSource write (`MutationEvent`) onto the bus. The bus also dispatches the legacy `objectui:related-changed` window event, so pre-bus listeners keep working.
10
+ - The `key={refreshKey}` remount of `RecordDetailView` (AppContent) and the `key={actionRefreshKey}` remount of `DetailView` (RecordDetailView) are GONE: record data now refetches in place via the bus — scroll, collapsed sections, tabs and in-progress inline edits survive every save/action/undo. All nine action-success bumps became precisely-scoped `notifyDataChanged` calls; undo/redo use the operation's own `objectName`/`recordId`.
11
+ - `RelatedCountStore` is wired to the bus (tab count badges refetch after any change to their object) and its `useSyncExternalStore` snapshot is now a monotonic version — previously it returned the same `Map` reference, so `emit()` never re-rendered subscribers and invalidations left badges stale; `useRelatedCountVersion()` is exported and drives the probe effect's re-fetch.
12
+ - app-shell also gains the reserved URL-param registry (`urlParams.ts` — `form`/`formObject`/`formLink`/`tab`/`recordId`/`palette`/`shortcuts` constants replace scattered string literals) and AGENTS.md Commandment #8 (UI-state classification: state that must survive a data refresh may never live only in an uncontrolled component).
13
+
14
+ - 9255686: Record detail tabs are URL-addressable (`?tab=`) and survive subtree remounts (objectui#2257, ADR-0054 C3).
15
+
16
+ - `buildDefaultTabs` emits STABLE semantic tab values (`details` / `related:<child>` / `related` / `activity` / `history`) instead of leaving the renderer to synthesize index-derived ones.
17
+ - `PageTabsRenderer` honors `item.value`, a host-provided `schema.defaultTab` (validated against actual tabs) and `schema.onTabChange`; index fallback kept for authored schemas without values.
18
+ - app-shell `RecordDetailView` restores the active tab from `?tab=` and writes it back with `replace` (tab switches never stack history), via the pure `withPageTabsUrlSync` page-tree injector (never mutates authored/memoized page schemas). Legacy `DetailView.autoTabs` wired to the same contract (`defaultTab`/`onTabChange`).
19
+ - Fixes the tab strip resetting to Details after save-refresh remounts (`refreshKey`-style) and dev-StrictMode URL churn; enables `?tab=` deep links; invalid values fall back to Details.
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [544d8eb]
24
+ - Updated dependencies [6fffd3d]
25
+ - Updated dependencies [9255686]
26
+ - Updated dependencies [fae75e2]
27
+ - Updated dependencies [1072701]
28
+ - @object-ui/i18n@11.5.0
29
+ - @object-ui/react@11.5.0
30
+ - @object-ui/types@11.5.0
31
+ - @object-ui/core@11.5.0
32
+ - @object-ui/react-runtime@11.5.0
33
+ - @object-ui/sdui-parser@11.5.0
34
+
35
+ ## 11.4.0
36
+
37
+ ### Patch Changes
38
+
39
+ - 1948c5b: fix(plugin-grid): keep the grid's row selection in sync when a bulk-action dialog closes
40
+
41
+ Closing a bulk-action result dialog (e.g. 派工 / 下推) on **Done** cleared
42
+ ObjectGrid's `selectedRows` — which drives the selection toolbar — but never
43
+ touched the DataTable's internal checkbox state. Two visible problems:
44
+
45
+ - **Desync on success.** The toolbar disappeared while every row stayed visibly
46
+ ticked, because the checkboxes are table-internal state the grid couldn't
47
+ reach.
48
+
49
+ - **Lost selection on total failure.** When the run failed for _every_ row
50
+ (0 succeeded — a precondition error, say), the toolbar still vanished,
51
+ stranding the user with no way to retry the exact rows they'd picked.
52
+
53
+ The dialog-close handler now gates the reset on `result.succeeded > 0`: a total
54
+ failure keeps both the selection _and_ the toolbar (and skips the phantom
55
+ refetch) so the user can fix the cause and retry. When it does reset, a new
56
+ `selectionResetKey` prop on DataTable clears the internal checkbox selection in
57
+ lockstep with the toolbar, so the two never drift apart.
58
+
59
+ - bce581a: Fix dependent (cascading) lookups: unlock on parent selection and enforce the
60
+ cascade filter on every candidate surface (#2215).
61
+
62
+ Two breaks made `depends_on` unusable end to end:
63
+
64
+ - **The gate never unlocked in create mode.** `LookupField` resolved dependent
65
+ values from `ctx.formValues` — a member `SchemaRendererContext` never had —
66
+ and nothing injected the `dependentValues` prop, so with a fresh record
67
+ (`ctx.data = {}`) the child lookup stayed disabled no matter what the user
68
+ picked in the parent field. The form renderer now injects its live form
69
+ values (the same reactive snapshot that drives field rules) as
70
+ `dependentValues` for data-source fields.
71
+ - **The Level-2 table picker bypassed the cascade.** The `depends_on` chain
72
+ only reached the quick-select popover filter; `RecordPickerDialog` (and the
73
+ search-first `PeoplePicker`) received just `lookup_filters`, listing the full
74
+ unfiltered record set. Both pickers now take a `baseFilter` — a hard
75
+ `$filter` constraint merged after `lookupFilters` and user filter-bar input,
76
+ so it can never be widened back out — and `LookupField` passes the dependent
77
+ chain there, shares the same filter with the popover query, and disables the
78
+ browse-all button while dependencies are missing.
79
+
80
+ - c38d107: Fix view-level `FormField.visibleOn` (CEL) never taking effect (#2212).
81
+
82
+ The spec ships `visibleOn` as an Expression object `{ dialect: 'cel', source }`
83
+ (what the `P` template emits) or a bare string, but the whole chain dropped it:
84
+
85
+ - `sectionFields.ts` / `ObjectForm.tsx` only accepted the bare-string shape and
86
+ attached a dead `visible()` closure no renderer ever called — the Expression
87
+ object shape was silently discarded.
88
+ - The form renderer destructured `visibleOn` out of the field config and never
89
+ evaluated it.
90
+ - `RecordFormPage` dropped a `simple` form view's `sections` entirely, so
91
+ page-mode create/edit fell back to the raw schema (every field, no authored
92
+ selection/grouping) while the modal path honored the same view.
93
+ - `ObjectForm`'s grouped-sections path matched section fields by name only,
94
+ dropping per-field `visibleOn` overrides.
95
+
96
+ `visibleOn` now flows through normalization verbatim (both wire shapes) and is
97
+ evaluated reactively by the form renderer with the canonical expression engine
98
+ (`evalFieldPredicate` — same engine, record scope, and fail-open semantics as
99
+ field-level `visibleWhen`; both predicates must allow a field for it to show).
100
+ Sectioned/flat normalization also copies field-level `visibleWhen` /
101
+ `readonlyWhen` / `requiredWhen` rules it previously lost.
102
+
103
+ - 7782698: fix(components): page:header record title honours `nameField` via the unified ADR-0079 resolver
104
+
105
+ The default console record detail page renders the synthesized `page:header`
106
+ (`buildDefaultPageSchema`, renderViaSchema default-on), whose record-chip title
107
+ chain probed `objSchema.primaryField` (not a spec property — always undefined),
108
+ `titleFormat`, then hardcoded `name`/`full_name`/`title`/`subject`/
109
+ `display_name`/`label` record keys. It never consulted the object's declared
110
+ `nameField`/`displayNameField`, so an object titled by e.g. `subject` rendered
111
+ `<ObjectLabel> <id-prefix>` as its H1 instead of the record's real name.
112
+
113
+ `PageHeaderRenderer` now resolves through `getRecordDisplayName(objSchema, data,
114
+ { deriveFromRecordKeys: false })` after the author overrides and before the
115
+ legacy probes — mirroring `DetailView.resolveDisplayTitle` so both headers
116
+ agree. `RecordDetailView`'s `primaryField` derivation and
117
+ `buildDefaultPageSchema`'s highlight-strip dedup also honour
118
+ `nameField`/`displayNameField`.
119
+
120
+ - e84d64d: Block record-scoped toolbar actions launched with zero rows selected (#2210).
121
+
122
+ A flow/script action that also mounts on list rows (`locations` includes
123
+ `list_item`) has no record to run on when triggered from the list toolbar with
124
+ nothing selected — pre-fix the wizard opened anyway, collected input, and died
125
+ at its first record-bound node ("Update requires an ID or options.multi=true").
126
+ The console runtime now blocks up front with "select a row first", mirroring
127
+ the existing multi-selection guard. Pure object-level toolbar actions
128
+ (`locations: ['list_toolbar']` only) keep triggering without a record.
129
+
130
+ The action renderers (button/icon/menu/group) now forward the `locations`
131
+ declaration to the ActionRunner — previously it was dropped by their
132
+ allow-list payloads, so the runtime could not tell the two shapes apart.
133
+
134
+ - Updated dependencies [8bf6295]
135
+ - Updated dependencies [1948c5b]
136
+ - Updated dependencies [9cd9be1]
137
+ - Updated dependencies [c38d107]
138
+ - Updated dependencies [790558b]
139
+ - @object-ui/types@11.4.0
140
+ - @object-ui/i18n@11.4.0
141
+ - @object-ui/core@11.4.0
142
+ - @object-ui/react@11.4.0
143
+ - @object-ui/react-runtime@11.4.0
144
+ - @object-ui/sdui-parser@11.4.0
145
+
3
146
  ## 11.3.0
4
147
 
5
148
  ### Minor Changes
@@ -24,4 +24,24 @@ import * as DialogPrimitive from "@radix-ui/react-dialog";
24
24
  * interaction with the dialog's own dropdown — it must not close the dialog.
25
25
  */
26
26
  export declare function isInsidePopperLayer(target: Element | null | undefined): boolean;
27
+ type InteractOutsideHandler = NonNullable<React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>['onInteractOutside']>;
28
+ /**
29
+ * Popper-aware interact-outside guard for Radix Dialog/Sheet content.
30
+ *
31
+ * Covers a second dismissal path `isInsidePopperLayer` alone cannot: with a
32
+ * dropdown OPEN, one outside click must only close the dropdown — but
33
+ * radix-dialog@1.1.17 defers its outside-pointerdown verdict to the `click`
34
+ * phase (`deferPointerDownOutside`), while radix-select@2.3.1 dismisses and
35
+ * unregisters on `pointerdown`. By the deferred verdict the dialog has become
36
+ * the top layer, so it treats the dropdown-closing click as its own outside
37
+ * click and dismisses too (#2156).
38
+ *
39
+ * The guard snapshots "was a popper flyout open?" on every `pointerdown`
40
+ * (document capture phase — runs before Radix's bubble-phase dismissal
41
+ * unmounts the flyout) and swallows the pointer-initiated interact-outside
42
+ * that follows. Focus-driven interact-outside (Tab out) is untouched, as is a
43
+ * plain backdrop click with no flyout open.
44
+ */
45
+ export declare function usePopperAwareInteractOutside(onInteractOutside?: InteractOutsideHandler): InteractOutsideHandler;
27
46
  export declare const MobileDialogContent: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
47
+ export {};
@@ -56,6 +56,14 @@ declare function invalidateAll(): void;
56
56
  * probe is in flight or before the first request.
57
57
  */
58
58
  export declare function useRelatedCount(objectName: string | undefined, relField: string | undefined, parentId: string | undefined): number | undefined;
59
+ /**
60
+ * Subscribe to the store's monotonic version. Put it in an effect dependency
61
+ * array to RE-RUN count probes after an invalidation (#2269) — invalidate
62
+ * deletes the cached numbers, and this version bump is what makes the probe
63
+ * effect fetch them again (fetchCount returns cached values without emitting,
64
+ * so the loop settles once every probed key is warm again).
65
+ */
66
+ export declare function useRelatedCountVersion(): number;
59
67
  /**
60
68
  * Imperative store API for non-React callers (mutation handlers, tests).
61
69
  * Prefer `useRelatedCount` in components.
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export { useResizeObserver } from './hooks/use-resize-observer';
13
13
  export type { ElementSize } from './hooks/use-resize-observer';
14
14
  export { useExportJob } from './hooks/use-export-job';
15
15
  export type { UseExportJobOptions, UseExportJobReturn } from './hooks/use-export-job';
16
- export { useRelatedCount, RelatedCountStore } from './hooks/related-count-store';
16
+ export { useRelatedCount, useRelatedCountVersion, RelatedCountStore } from './hooks/related-count-store';
17
17
  export type { ControlType, ConfigField, ConfigSection, ConfigPanelSchema, } from './types/config-panel';
18
18
  export declare function initializeComponents(): boolean;
19
19
  export * from './debug';