@object-ui/react 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 +4263 -0
- package/README.md +383 -42
- package/dist/SchemaRenderer.d.ts +64 -7
- package/dist/SchemaRenderer.js +1326 -106
- package/dist/context/ActionContext.js +40 -1
- package/dist/context/AppShellContext.d.ts +27 -0
- package/dist/context/DiscussionContext.d.ts +27 -0
- package/dist/context/DiscussionContext.js +4 -0
- package/dist/context/DrillNavigationContext.d.ts +13 -0
- package/dist/context/InlineEditContext.d.ts +151 -1
- package/dist/context/InlineEditContext.js +36 -2
- package/dist/context/PrintPageContext.d.ts +60 -0
- package/dist/context/PrintPageContext.js +32 -0
- package/dist/context/RecordContext.d.ts +48 -5
- package/dist/context/RecordContext.js +10 -1
- package/dist/context/RelatedRecordActionsContext.d.ts +72 -2
- package/dist/context/RelatedRecordActionsContext.js +4 -0
- package/dist/context/SchemaRendererContext.d.ts +61 -6
- package/dist/context/SchemaRendererContext.js +39 -6
- package/dist/context/ThemeContext.js +1 -1
- package/dist/context/actionRunnerTranslator.d.ts +27 -0
- package/dist/context/actionRunnerTranslator.js +37 -0
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.js +1 -0
- package/dist/element-data-source/ElementApiDataSourcePanel.d.ts +28 -0
- package/dist/element-data-source/ElementApiDataSourcePanel.js +10 -0
- package/dist/element-data-source/ElementDataSourceGate.d.ts +118 -3
- package/dist/element-data-source/ElementDataSourceGate.js +401 -19
- package/dist/element-data-source/useElementApiDataSource.d.ts +119 -0
- package/dist/element-data-source/useElementApiDataSource.js +198 -0
- package/dist/hooks/index.d.ts +3 -1
- package/dist/hooks/index.js +10 -1
- package/dist/hooks/useActionEngine.js +46 -3
- package/dist/hooks/useActionRunner.js +4 -0
- package/dist/hooks/useActionTextLocalizer.d.ts +35 -9
- package/dist/hooks/useActionTextLocalizer.js +70 -14
- package/dist/hooks/useConfigBagEvaluator.d.ts +31 -0
- package/dist/hooks/useConfigBagEvaluator.js +40 -0
- package/dist/hooks/useDatasetDimensionLabels.js +25 -10
- package/dist/hooks/useDensityMode.d.ts +8 -2
- package/dist/hooks/useDensityMode.js +8 -2
- package/dist/hooks/useDiscovery.d.ts +1 -10
- package/dist/hooks/useETagCache.js +25 -4
- package/dist/hooks/useElementDataSource.d.ts +15 -2
- package/dist/hooks/useElementDataSource.js +24 -7
- package/dist/hooks/useExpression.d.ts +52 -59
- package/dist/hooks/useExpression.js +58 -90
- package/dist/hooks/useFilterScope.d.ts +22 -2
- package/dist/hooks/useFilterScope.js +29 -4
- package/dist/hooks/useGlobalUndo.d.ts +10 -0
- package/dist/hooks/useGlobalUndo.js +112 -46
- package/dist/hooks/useNavigationOverlay.d.ts +76 -11
- package/dist/hooks/useNavigationOverlay.js +119 -9
- package/dist/hooks/useOffline.js +65 -11
- package/dist/hooks/usePageAssignment.d.ts +33 -7
- package/dist/hooks/usePageAssignment.js +85 -21
- package/dist/hooks/useRecordSearch.d.ts +34 -1
- package/dist/hooks/useRecordSearch.js +42 -7
- package/dist/hooks/useResolvedFilter.d.ts +46 -0
- package/dist/hooks/useResolvedFilter.js +130 -0
- package/dist/hooks/useSchemaPersistence.d.ts +7 -0
- package/dist/hooks/useSchemaPersistence.js +144 -5
- package/dist/hooks/useSettledSchema.d.ts +123 -0
- package/dist/hooks/useSettledSchema.js +186 -0
- package/dist/hooks/useViewData.d.ts +10 -3
- package/dist/hooks/useViewData.js +5 -14
- package/dist/index.d.ts +9 -1
- package/dist/index.js +48 -1
- package/dist/schema-input.d.ts +38 -8
- package/dist/schema-input.js +39 -7
- package/dist/utils/configBag.d.ts +68 -0
- package/dist/utils/configBag.js +70 -0
- package/dist/utils/error-message.d.ts +1 -1
- package/dist/utils/error-message.js +1 -1
- package/dist/utils/inlineAria.d.ts +61 -0
- package/dist/utils/inlineAria.js +70 -0
- package/dist/utils/localeMap.d.ts +27 -0
- package/dist/utils/localeMap.js +106 -0
- package/dist/utils/nonGridRowCeiling.d.ts +44 -0
- package/dist/utils/nonGridRowCeiling.js +78 -0
- package/dist/utils/paramsBag.d.ts +28 -0
- package/dist/utils/paramsBag.js +146 -0
- package/dist/utils/propsBagDiagnostic.d.ts +196 -0
- package/dist/utils/propsBagDiagnostic.js +422 -0
- package/dist/utils/refusedDataPropDiagnostic.d.ts +44 -0
- package/dist/utils/refusedDataPropDiagnostic.js +94 -0
- package/dist/utils/unevaluatedExpression.d.ts +44 -1
- package/dist/utils/unevaluatedExpression.js +92 -11
- package/dist/utils/visibilityDiagnostic.d.ts +282 -21
- package/dist/utils/visibilityDiagnostic.js +321 -46
- package/package.json +10 -10
- package/dist/hooks/useClientNotifications.d.ts +0 -47
- package/dist/hooks/useClientNotifications.js +0 -162
- package/dist/spec-bridge/SpecBridge.d.ts +0 -28
- package/dist/spec-bridge/SpecBridge.js +0 -56
- package/dist/spec-bridge/bridges/form-view.d.ts +0 -158
- package/dist/spec-bridge/bridges/form-view.js +0 -170
- package/dist/spec-bridge/bridges/list-view.d.ts +0 -20
- package/dist/spec-bridge/bridges/list-view.js +0 -268
- package/dist/spec-bridge/index.d.ts +0 -11
- package/dist/spec-bridge/index.js +0 -10
- package/dist/spec-bridge/types.d.ts +0 -55
- package/dist/spec-bridge/types.js +0 -8
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
* it can route to, so the low-level renderers on a record page can turn a
|
|
31
31
|
* lookup VALUE into a link to the referenced record without assembling a URL
|
|
32
32
|
* of their own. Same provider, same builder, one route shape.
|
|
33
|
+
*
|
|
34
|
+
* Since objectui#12703 it also publishes the object-list URL builder
|
|
35
|
+
* ({@link RelatedRecordActionsValue.listHref} / `openList`), for a renderer
|
|
36
|
+
* that shows a few of an object's records and links to the rest.
|
|
33
37
|
*/
|
|
34
38
|
import React from 'react';
|
|
35
39
|
/** A child-object action surfaced in a related list's row menu. */
|
|
@@ -83,7 +87,11 @@ export interface RelatedRecordHandlers {
|
|
|
83
87
|
* the deletion resolves.
|
|
84
88
|
*/
|
|
85
89
|
onDelete?: (recordId: string | number, record?: unknown) => void | Promise<void>;
|
|
86
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Child object row-level actions, localized: its `list_item` actions, and
|
|
92
|
+
* its `record_related` ones when the list sits inside a parent record
|
|
93
|
+
* (objectui#11270).
|
|
94
|
+
*/
|
|
87
95
|
rowActions?: RelatedRowActionDef[];
|
|
88
96
|
/** Execute one of {@link rowActions} against a specific child row. */
|
|
89
97
|
onRowAction?: (action: RelatedRowActionDef, record: unknown) => void | Promise<void>;
|
|
@@ -96,8 +104,40 @@ export interface RelatedRecordHandlers {
|
|
|
96
104
|
toolbarActions?: RelatedRowActionDef[];
|
|
97
105
|
/** Execute one of {@link toolbarActions} (no row context). */
|
|
98
106
|
onToolbarAction?: (action: RelatedRowActionDef) => void | Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Open the child object's full list page, scoped to THIS parent on the
|
|
109
|
+
* relationship `resolve` was handed (objectui#12741). The related list's
|
|
110
|
+
* "View all" link calls it, unless the block declares `showViewAll: false`.
|
|
111
|
+
*
|
|
112
|
+
* The host builds the destination because only the host knows the console's
|
|
113
|
+
* routes, as it does for {@link onView}. It omits the handler when no route
|
|
114
|
+
* it has can show that scoped list, and the link is then not drawn. One case
|
|
115
|
+
* is a multi-value relationship: the list route's URL filter has no
|
|
116
|
+
* membership operator. Another is a relationship field the viewer cannot
|
|
117
|
+
* read: the list route drops that condition and would show every row.
|
|
118
|
+
*/
|
|
119
|
+
onViewAll?: () => void;
|
|
99
120
|
}
|
|
100
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* Input to {@link RelatedRecordActionsValue.resolve}.
|
|
123
|
+
*
|
|
124
|
+
* Its SHAPE says which kind of list asks (objectui#13089):
|
|
125
|
+
*
|
|
126
|
+
* - **A related list always names its `relationshipField`.** The spec's
|
|
127
|
+
* `record:related_list` declares it required, so a spec-valid related list
|
|
128
|
+
* resolves with it, with or without a resolved `parentId`.
|
|
129
|
+
* - **An input with no `relationshipField` (absent or empty) asks for an
|
|
130
|
+
* OBJECT-SCOPED list's handlers**, a list of the object's own records with
|
|
131
|
+
* no parent: a page `list-view` node resolves `{ objectName }` for its
|
|
132
|
+
* `addRecord` Add button. A host may serve such an input a create that
|
|
133
|
+
* links no parent, which it must not serve a related list whose parent did
|
|
134
|
+
* not resolve.
|
|
135
|
+
*
|
|
136
|
+
* ⚠️ The limit of reading the shape: a future consumer that resolves a list
|
|
137
|
+
* with no parent and no relationship, and must NOT be offered a create, cannot
|
|
138
|
+
* say so here. It needs an explicit member on this input, not a second reading
|
|
139
|
+
* of the same shape.
|
|
140
|
+
*/
|
|
101
141
|
export interface ResolveRelatedRecordActionsInput {
|
|
102
142
|
/** Child object machine name (the related list's `api`). */
|
|
103
143
|
objectName: string;
|
|
@@ -128,6 +168,11 @@ export interface RelatedRecordActionsValue {
|
|
|
128
168
|
*
|
|
129
169
|
* Returns `null` when the host cannot route to that object (unknown object,
|
|
130
170
|
* no app context), which consumers must render as the plain value.
|
|
171
|
+
*
|
|
172
|
+
* The value is a document href, read by the browser on "open in new tab" and
|
|
173
|
+
* "copy link address", so it carries the host router's basename (for
|
|
174
|
+
* example `/_console/apps/…`), while {@link openRecord} navigates by the
|
|
175
|
+
* router path (objectui#12233).
|
|
131
176
|
*/
|
|
132
177
|
recordHref?: (objectName: string, recordId: string | number) => string | null;
|
|
133
178
|
/**
|
|
@@ -136,6 +181,31 @@ export interface RelatedRecordActionsValue {
|
|
|
136
181
|
* in-app; a modifier click is left to the browser and the href.
|
|
137
182
|
*/
|
|
138
183
|
openRecord?: (objectName: string, recordId: string | number) => void;
|
|
184
|
+
/**
|
|
185
|
+
* The host's list-page URL for an object it can route to, optionally opened
|
|
186
|
+
* on one of the object's saved views (objectui#12703). The list-side twin of
|
|
187
|
+
* {@link recordHref}: a renderer that shows a few of an object's records (the
|
|
188
|
+
* compact list a page draws in a narrow region) presents "View all" as a real
|
|
189
|
+
* anchor to this destination, without assembling a URL of its own.
|
|
190
|
+
*
|
|
191
|
+
* `viewName` names a saved view of that object, as the block binding states
|
|
192
|
+
* it (`dataSource.view`); absent, the destination is the object's own list.
|
|
193
|
+
* The destination is a view or the object, never a query: a caller does not
|
|
194
|
+
* encode its own filter, columns or sort into it, and the host has no
|
|
195
|
+
* argument to receive them.
|
|
196
|
+
*
|
|
197
|
+
* Returns `null` when the host cannot route to that object (unknown object,
|
|
198
|
+
* no app context); consumers then draw no link. Like {@link recordHref} it is
|
|
199
|
+
* a document href that carries the router's basename, while {@link openList}
|
|
200
|
+
* navigates by the router path.
|
|
201
|
+
*/
|
|
202
|
+
listHref?: (objectName: string, viewName?: string) => string | null;
|
|
203
|
+
/**
|
|
204
|
+
* SPA-navigate to the destination {@link listHref} addresses. Consumers call
|
|
205
|
+
* this from a plain left click on the anchor; a modifier click is left to the
|
|
206
|
+
* browser and the href.
|
|
207
|
+
*/
|
|
208
|
+
openList?: (objectName: string, viewName?: string) => void;
|
|
139
209
|
}
|
|
140
210
|
export declare const RelatedRecordActionsProvider: React.FC<{
|
|
141
211
|
value: RelatedRecordActionsValue;
|
|
@@ -31,6 +31,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
31
31
|
* it can route to, so the low-level renderers on a record page can turn a
|
|
32
32
|
* lookup VALUE into a link to the referenced record without assembling a URL
|
|
33
33
|
* of their own. Same provider, same builder, one route shape.
|
|
34
|
+
*
|
|
35
|
+
* Since objectui#12703 it also publishes the object-list URL builder
|
|
36
|
+
* ({@link RelatedRecordActionsValue.listHref} / `openList`), for a renderer
|
|
37
|
+
* that shows a few of an object's records and links to the rest.
|
|
34
38
|
*/
|
|
35
39
|
import { createContext, useContext } from 'react';
|
|
36
40
|
const RelatedRecordActionsContext = createContext(null);
|
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { DebugFlags } from '@object-ui/core';
|
|
3
|
+
import type { DataSource } from '@object-ui/types';
|
|
3
4
|
/**
|
|
4
|
-
* Host-provided fetch
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Host-provided fetch the renderers use for their own reads of the host's API
|
|
6
|
+
* (a chart's metadata reads, dataset dimension labels, record-permission
|
|
7
|
+
* probes), so those reads carry the same credentials (Authorization, tenant,
|
|
8
|
+
* locale headers) as the native data channel. Optional — when absent, those
|
|
9
|
+
* reads fall back to the bare global fetch (cookies only). A view does not
|
|
10
|
+
* reach it: a view binds `provider: 'object'` or `provider: 'value'`, and a
|
|
11
|
+
* code-built `ApiDataSource` takes its own `fetch` in its config.
|
|
8
12
|
*/
|
|
9
13
|
export type ApiFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
10
14
|
interface SchemaRendererContextType {
|
|
11
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The adapter the host injected, as the published `DataSource` contract
|
|
17
|
+
* declares it (objectui#7912).
|
|
18
|
+
*
|
|
19
|
+
* Typed, not `any`: this is the injection point for the ENTIRE renderer tree,
|
|
20
|
+
* and `useSchemaContext()` hands whatever is declared here to every consumer
|
|
21
|
+
* that reads it back. While it was `any`, a bare string passed where the
|
|
22
|
+
* host's adapter belongs raised nothing at compile time and failed at runtime
|
|
23
|
+
* on the first `find()` — the shape of a config value read from the wrong
|
|
24
|
+
* place. The type was never unknown: `@object-ui/types` exports `DataSource`,
|
|
25
|
+
* `useSettledSchema` in this same package already declares its parameter as
|
|
26
|
+
* `DataSource<any> | null | undefined`, and `app-shell`'s README writes
|
|
27
|
+
* `DataSource` against this very seam.
|
|
28
|
+
*
|
|
29
|
+
* `null | undefined` are part of the contract, not a weakening of it: a host
|
|
30
|
+
* renders with no adapter bound (a Studio preview, a react page before the
|
|
31
|
+
* AdapterProvider connects, a widget test that drives `apiFetch` alone), and
|
|
32
|
+
* every reader in the tree already guards for it. The union states the three
|
|
33
|
+
* states that actually occur and refuses everything else — a string, `{}`, a
|
|
34
|
+
* plain data bag, a partial adapter missing a required member. It is spelled
|
|
35
|
+
* exactly as `useSettledSchema` spells it, one hop away.
|
|
36
|
+
*/
|
|
37
|
+
dataSource: DataSource | null | undefined;
|
|
12
38
|
debug?: boolean;
|
|
13
39
|
debugFlags?: DebugFlags;
|
|
14
40
|
apiFetch?: ApiFetch;
|
|
@@ -17,10 +43,39 @@ declare const SchemaRendererContext: React.Context<SchemaRendererContextType | n
|
|
|
17
43
|
export { SchemaRendererContext };
|
|
18
44
|
export declare const SchemaRendererProvider: ({ children, dataSource, debug, debugFlags, apiFetch, }: {
|
|
19
45
|
children: React.ReactNode;
|
|
20
|
-
dataSource
|
|
46
|
+
/** The host's adapter — see {@link SchemaRendererContextType.dataSource}. */
|
|
47
|
+
dataSource: DataSource | null | undefined;
|
|
21
48
|
debug?: boolean;
|
|
22
49
|
debugFlags?: DebugFlags;
|
|
23
50
|
apiFetch?: ApiFetch;
|
|
24
51
|
}) => React.JSX.Element;
|
|
25
52
|
export declare const useSchemaContext: () => SchemaRendererContextType;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve a `bind` path against the ambient predicate scope.
|
|
55
|
+
*
|
|
56
|
+
* ## What it reads, and what it deliberately does not (objectui#9308)
|
|
57
|
+
*
|
|
58
|
+
* The scope a host publishes through `PredicateScopeProvider` — the same
|
|
59
|
+
* channel `useCondition` / `useExpression` merge under a locally-passed
|
|
60
|
+
* context, and the one app-shell's `ExpressionProvider` already feeds. It is
|
|
61
|
+
* NOT `SchemaRendererContext.dataSource`.
|
|
62
|
+
*
|
|
63
|
+
* `dataSource` is the host's injected ADAPTER, declared as the published
|
|
64
|
+
* `DataSource` contract (objectui#7912). An adapter has no `users` member, no
|
|
65
|
+
* `value` member, no member a `bind` path names — so walking it resolved
|
|
66
|
+
* `undefined` for every conformant host, and the nine production readers of
|
|
67
|
+
* this hook have all been running their fallback (`boundData || schema.items`,
|
|
68
|
+
* `|| schema.nodes`, or a fall-through to their own fetch chain) ever since.
|
|
69
|
+
* The only hosts it answered were ones injecting a data bag through a key the
|
|
70
|
+
* contract says is an adapter, which has been a compile error since
|
|
71
|
+
* objectui#7912.
|
|
72
|
+
*
|
|
73
|
+
* Maintainer ruling 2026-09-13 (option B) points it at the channel that can
|
|
74
|
+
* actually answer it. No published key is added: the provider, the hook and
|
|
75
|
+
* the host wiring all already exist.
|
|
76
|
+
*
|
|
77
|
+
* Returns `undefined` for an absent or empty path, and for any path the scope
|
|
78
|
+
* does not carry — the readers' fallbacks are what run then, exactly as
|
|
79
|
+
* before.
|
|
80
|
+
*/
|
|
26
81
|
export declare const useDataScope: (path?: string) => any;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useContext, useMemo } from 'react';
|
|
3
|
+
import { usePredicateScope } from '../hooks/useExpression.js';
|
|
3
4
|
const SchemaRendererContext = createContext(null);
|
|
4
5
|
export { SchemaRendererContext };
|
|
5
6
|
export const SchemaRendererProvider = ({ children, dataSource, debug, debugFlags, apiFetch, }) => {
|
|
6
7
|
// Nested providers (react-page, studio preview surfaces) re-wrap with their
|
|
7
8
|
// own dataSource but rarely know about the host's authenticated fetch —
|
|
8
|
-
// inherit it from the parent context so
|
|
9
|
+
// inherit it from the parent context so the host's auth survives nesting.
|
|
9
10
|
const parent = useContext(SchemaRendererContext);
|
|
10
11
|
const effectiveApiFetch = apiFetch ?? parent?.apiFetch;
|
|
11
12
|
const value = useMemo(() => ({ dataSource, debug, debugFlags, apiFetch: effectiveApiFetch }), [dataSource, debug, debugFlags, effectiveApiFetch]);
|
|
@@ -18,13 +19,45 @@ export const useSchemaContext = () => {
|
|
|
18
19
|
}
|
|
19
20
|
return context;
|
|
20
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* Resolve a `bind` path against the ambient predicate scope.
|
|
24
|
+
*
|
|
25
|
+
* ## What it reads, and what it deliberately does not (objectui#9308)
|
|
26
|
+
*
|
|
27
|
+
* The scope a host publishes through `PredicateScopeProvider` — the same
|
|
28
|
+
* channel `useCondition` / `useExpression` merge under a locally-passed
|
|
29
|
+
* context, and the one app-shell's `ExpressionProvider` already feeds. It is
|
|
30
|
+
* NOT `SchemaRendererContext.dataSource`.
|
|
31
|
+
*
|
|
32
|
+
* `dataSource` is the host's injected ADAPTER, declared as the published
|
|
33
|
+
* `DataSource` contract (objectui#7912). An adapter has no `users` member, no
|
|
34
|
+
* `value` member, no member a `bind` path names — so walking it resolved
|
|
35
|
+
* `undefined` for every conformant host, and the nine production readers of
|
|
36
|
+
* this hook have all been running their fallback (`boundData || schema.items`,
|
|
37
|
+
* `|| schema.nodes`, or a fall-through to their own fetch chain) ever since.
|
|
38
|
+
* The only hosts it answered were ones injecting a data bag through a key the
|
|
39
|
+
* contract says is an adapter, which has been a compile error since
|
|
40
|
+
* objectui#7912.
|
|
41
|
+
*
|
|
42
|
+
* Maintainer ruling 2026-09-13 (option B) points it at the channel that can
|
|
43
|
+
* actually answer it. No published key is added: the provider, the hook and
|
|
44
|
+
* the host wiring all already exist.
|
|
45
|
+
*
|
|
46
|
+
* Returns `undefined` for an absent or empty path, and for any path the scope
|
|
47
|
+
* does not carry — the readers' fallbacks are what run then, exactly as
|
|
48
|
+
* before.
|
|
49
|
+
*/
|
|
21
50
|
export const useDataScope = (path) => {
|
|
22
|
-
const
|
|
23
|
-
const dataSource = context?.dataSource;
|
|
51
|
+
const scope = usePredicateScope();
|
|
24
52
|
if (!path)
|
|
25
53
|
return undefined;
|
|
26
|
-
if (!
|
|
54
|
+
if (!scope)
|
|
27
55
|
return undefined;
|
|
28
|
-
// Simple path resolution for now. In real app might be more complex
|
|
29
|
-
|
|
56
|
+
// Simple path resolution for now. In real app might be more complex.
|
|
57
|
+
//
|
|
58
|
+
// The accumulator is `any` BY DECLARATION: this walk addresses arbitrary
|
|
59
|
+
// member names on a host-published bag, and `Record<string, any>` declares
|
|
60
|
+
// none of them beyond the first segment. The hook's published return type is
|
|
61
|
+
// unchanged — `any` before and after — so no reader of `useDataScope` moves.
|
|
62
|
+
return path.split('.').reduce((acc, part) => acc && acc[part], scope);
|
|
30
63
|
};
|
|
@@ -131,7 +131,7 @@ export function ThemeProvider({ themes: themesInput = [], defaultTheme, defaultM
|
|
|
131
131
|
// Apply mode class
|
|
132
132
|
el.classList.remove('light', 'dark');
|
|
133
133
|
el.classList.add(resolvedMode);
|
|
134
|
-
// No favicon here: spec v17 (#3494) pruned `Theme.logo`, so the key is
|
|
134
|
+
// No favicon here: spec v17 (objectstack-ai/objectstack#3494) pruned `Theme.logo`, so the key is
|
|
135
135
|
// stripped at parse and could never arrive. The live favicon comes from
|
|
136
136
|
// operator branding (`getFaviconUrl()`), applied in the console's
|
|
137
137
|
// index.html, main.tsx, and on route change.
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
/** What `ActionRunner.setTranslator` takes. */
|
|
9
|
+
type ActionRunnerTranslate = (key: string, options: {
|
|
10
|
+
defaultValue: string;
|
|
11
|
+
}) => string;
|
|
12
|
+
/**
|
|
13
|
+
* The translator both React owners of an `ActionRunner` — `<ActionProvider>`
|
|
14
|
+
* and `useActionRunner` — install on the runner they build, so the text the
|
|
15
|
+
* runner writes itself (the generic success toast an action with no
|
|
16
|
+
* `successMessage` ends in) renders in the session's language instead of
|
|
17
|
+
* English. Internal to this package; not re-exported from its entry.
|
|
18
|
+
*
|
|
19
|
+
* The function is created once per mount and reads the latest `t` when it is
|
|
20
|
+
* CALLED, so a language switch reaches a runner that was built before it. Both
|
|
21
|
+
* owners rebuild their runner only when their `context` changes, and a
|
|
22
|
+
* translator captured at build time would keep answering in the language that
|
|
23
|
+
* was active then. The identity comes from `useState`'s initializer, not from a
|
|
24
|
+
* memo, so nothing depends on a `useMemo` result staying put (AGENTS.md #10).
|
|
25
|
+
*/
|
|
26
|
+
export declare function useActionRunnerTranslator(): ActionRunnerTranslate;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
import { useEffect, useRef, useState } from 'react';
|
|
9
|
+
import { useObjectTranslation } from '@object-ui/i18n';
|
|
10
|
+
/**
|
|
11
|
+
* The translator both React owners of an `ActionRunner` — `<ActionProvider>`
|
|
12
|
+
* and `useActionRunner` — install on the runner they build, so the text the
|
|
13
|
+
* runner writes itself (the generic success toast an action with no
|
|
14
|
+
* `successMessage` ends in) renders in the session's language instead of
|
|
15
|
+
* English. Internal to this package; not re-exported from its entry.
|
|
16
|
+
*
|
|
17
|
+
* The function is created once per mount and reads the latest `t` when it is
|
|
18
|
+
* CALLED, so a language switch reaches a runner that was built before it. Both
|
|
19
|
+
* owners rebuild their runner only when their `context` changes, and a
|
|
20
|
+
* translator captured at build time would keep answering in the language that
|
|
21
|
+
* was active then. The identity comes from `useState`'s initializer, not from a
|
|
22
|
+
* memo, so nothing depends on a `useMemo` result staying put (AGENTS.md #10).
|
|
23
|
+
*/
|
|
24
|
+
export function useActionRunnerTranslator() {
|
|
25
|
+
const { t } = useObjectTranslation();
|
|
26
|
+
const latest = useRef(t);
|
|
27
|
+
// No dependency list: after every render, the ref holds that render's `t`.
|
|
28
|
+
// Actions run from user events, which arrive after the commit.
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
latest.current = t;
|
|
31
|
+
});
|
|
32
|
+
const [translate] = useState(() => {
|
|
33
|
+
const read = (key, options) => String(latest.current(key, options));
|
|
34
|
+
return read;
|
|
35
|
+
});
|
|
36
|
+
return translate;
|
|
37
|
+
}
|
package/dist/context/index.d.ts
CHANGED
package/dist/context/index.js
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
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 error panel of a page element's API binding (objectui#12197), titled in
|
|
9
|
+
* the page's language.
|
|
10
|
+
*
|
|
11
|
+
* `SchemaRenderer` draws it in place of a node whose binding the spec refused
|
|
12
|
+
* or whose read failed. The translation hook lives HERE, not in
|
|
13
|
+
* `SchemaRenderer`'s body: that body runs for every node on every page, and a
|
|
14
|
+
* language subscription there would re-render the whole tree on a language
|
|
15
|
+
* change. This component mounts only on the error path, so only it subscribes.
|
|
16
|
+
* The panel itself is {@link ElementDataSourceErrorPanel}; the inline
|
|
17
|
+
* `defaultValue`s are the `en` pack's values (`pnpm check:i18n-keys`).
|
|
18
|
+
*/
|
|
19
|
+
import * as React from 'react';
|
|
20
|
+
export interface ElementApiDataSourcePanelProps {
|
|
21
|
+
/** `data-testid` stem — the node's registry key. */
|
|
22
|
+
testId: string;
|
|
23
|
+
/** `refused`: the spec's schema refused the binding. `error`: the read failed. */
|
|
24
|
+
state: 'refused' | 'error';
|
|
25
|
+
/** Why — the spec's refusal, or the failed read's sentence. */
|
|
26
|
+
message?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function ElementApiDataSourcePanel({ testId, state, message, }: ElementApiDataSourcePanelProps): React.ReactElement;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useObjectTranslation } from '@object-ui/i18n';
|
|
3
|
+
import { ElementDataSourceErrorPanel } from './ElementDataSourceGate.js';
|
|
4
|
+
export function ElementApiDataSourcePanel({ testId, state, message, }) {
|
|
5
|
+
const { t } = useObjectTranslation();
|
|
6
|
+
const title = state === 'refused'
|
|
7
|
+
? t('element.component.apiRefused', { defaultValue: 'This component’s API data source is refused' })
|
|
8
|
+
: t('element.component.apiUnreadable', { defaultValue: 'This component’s API data source could not be read' });
|
|
9
|
+
return _jsx(ElementDataSourceErrorPanel, { testId: testId, title: title, message: message });
|
|
10
|
+
}
|
|
@@ -50,6 +50,27 @@
|
|
|
50
50
|
* designer emits for an unconfigured column list, and supplying the columns is
|
|
51
51
|
* exactly why a view was named.
|
|
52
52
|
*
|
|
53
|
+
* The row cap answers "did the component author one?" the same way, and for the
|
|
54
|
+
* same reason (objectui#9899). A cap the contract REFUSES — `pageSize: 0`,
|
|
55
|
+
* a negative, a fraction — is not a cap this relay may treat as the author's
|
|
56
|
+
* intent, so it does not suppress the one the bound view supplies. Deciding
|
|
57
|
+
* that question by PRESENCE is how a view's legitimate cap was dropped in
|
|
58
|
+
* favour of the consuming renderer's own default: measured end to end on
|
|
59
|
+
* `list-view` before the repair, a component carrying `pagination.pageSize: 0`
|
|
60
|
+
* under a view supplying `7` put `$top: 100` on the wire — the view's cap never
|
|
61
|
+
* reached it, so the read was WIDER than the view asked for.
|
|
62
|
+
*
|
|
63
|
+
* The VIEW's own cap can be refused too, and `@object-ui/core` drops it before
|
|
64
|
+
* it reaches this relay (objectui#9928). The drop is right, but it left the
|
|
65
|
+
* consuming renderer nothing to refuse, so it went silent — and this relay
|
|
66
|
+
* reports the view's refusal instead (objectui#10015). See
|
|
67
|
+
* {@link describeRefusedViewRowLimit} for when that message applies.
|
|
68
|
+
*
|
|
69
|
+
* The BINDING's own cap follows the same rule (objectui#10016): a `limit` the
|
|
70
|
+
* contract refuses is not authored, so `@object-ui/core` lets the view's cap
|
|
71
|
+
* through in its place, and that cap is a baseline here like any other
|
|
72
|
+
* view-sourced value. See {@link describeRefusedBindingRowLimit}.
|
|
73
|
+
*
|
|
53
74
|
* ## What a mapping may NOT do
|
|
54
75
|
*
|
|
55
76
|
* {@link ElementDataSourceMapping} names only keys the target block genuinely
|
|
@@ -79,7 +100,7 @@
|
|
|
79
100
|
* like it works.
|
|
80
101
|
*/
|
|
81
102
|
import * as React from 'react';
|
|
82
|
-
import { type ElementDataSourceConfig } from '@object-ui/core';
|
|
103
|
+
import { type ElementDataSourceConfig, type FilterOperatorError } from '@object-ui/core';
|
|
83
104
|
import { type ElementDataSourceStatus } from '../hooks/useElementDataSource.js';
|
|
84
105
|
/**
|
|
85
106
|
* Where a block's row cap lives. Three spellings are real in this repo and each
|
|
@@ -139,6 +160,15 @@ export interface UseElementDataSourceSchemaResult<S> {
|
|
|
139
160
|
config?: ElementDataSourceConfig;
|
|
140
161
|
/** Author-facing explanation, set only for `missing`. */
|
|
141
162
|
error?: string;
|
|
163
|
+
/**
|
|
164
|
+
* The refused filter that stopped the binding from applying — set only with
|
|
165
|
+
* `missing`, and `error` then carries its message (objectui#10789). Either
|
|
166
|
+
* merge can refuse: the view's filter with the binding's own
|
|
167
|
+
* ({@link useElementDataSource}), or the component's filter with that result
|
|
168
|
+
* (this hook). {@link ElementDataSourceGate} draws the malformed-filter
|
|
169
|
+
* notice from it.
|
|
170
|
+
*/
|
|
171
|
+
filterRefusal?: FilterOperatorError;
|
|
142
172
|
}
|
|
143
173
|
/**
|
|
144
174
|
* Resolve a block's `dataSource` binding and apply it to the block's own schema
|
|
@@ -163,7 +193,10 @@ export interface ElementDataSourceStatusPanelProps {
|
|
|
163
193
|
* asserting about rather than a shared anonymous id.
|
|
164
194
|
*/
|
|
165
195
|
testId: string;
|
|
166
|
-
/**
|
|
196
|
+
/**
|
|
197
|
+
* Panel heading; defaults to a block-neutral sentence read from the bundle
|
|
198
|
+
* (objectui#12920). A passed `title`, an empty string included, wins.
|
|
199
|
+
*/
|
|
167
200
|
title?: string;
|
|
168
201
|
/** The author-facing explanation from {@link useElementDataSourceSchema}. */
|
|
169
202
|
message?: string;
|
|
@@ -178,6 +211,13 @@ export declare function ElementDataSourceErrorPanel({ testId, title, message, }:
|
|
|
178
211
|
* The "saved views are still being fetched" placeholder. Distinct from the error
|
|
179
212
|
* panel because a component that treated "not resolved yet" as "does not exist"
|
|
180
213
|
* would flash a configuration error on every mount.
|
|
214
|
+
*
|
|
215
|
+
* Its copy reads `common.loading` (objectui#12889), the key the rest of the
|
|
216
|
+
* tree reads for a bare "Loading…": it printed an English "Loading view…" on
|
|
217
|
+
* every locale before. `useObjectTranslation` runs only while a view resolves,
|
|
218
|
+
* for the reason {@link ElementDataSourceMalformedFilterPanel} gives, and the
|
|
219
|
+
* inline `defaultValue` is the `en` pack's value, held byte-identical to it by
|
|
220
|
+
* `pnpm check:i18n-keys`.
|
|
181
221
|
*/
|
|
182
222
|
export declare function ElementDataSourceLoadingPanel({ testId, }: Pick<ElementDataSourceStatusPanelProps, 'testId'>): React.ReactElement;
|
|
183
223
|
/**
|
|
@@ -225,6 +265,63 @@ export declare function noDataSourceMessage(blockKey: string, objectName?: unkno
|
|
|
225
265
|
* metadata is answerable, so the answer is rendered where the author can see it.
|
|
226
266
|
*/
|
|
227
267
|
export declare function NoDataSourcePanel({ testId, title, message, }: ElementDataSourceStatusPanelProps): React.ReactElement;
|
|
268
|
+
/**
|
|
269
|
+
* The wrapping seam — mark `renderer` as one that wraps {@link
|
|
270
|
+
* ElementDataSourceGate}, so its registrations DECLARE the `dataSource` key this
|
|
271
|
+
* gate READS (objectui#6678).
|
|
272
|
+
*
|
|
273
|
+
* ```tsx
|
|
274
|
+
* export const ObjectMapRenderer = elementDataSourceBlock<React.FC<any>>(({ schema }) => (
|
|
275
|
+
* <ElementDataSourceGate schema={schema} testId="object-map" …>
|
|
276
|
+
* {(bound) => <ObjectMap schema={bound} />}
|
|
277
|
+
* </ElementDataSourceGate>
|
|
278
|
+
* ));
|
|
279
|
+
* ```
|
|
280
|
+
*
|
|
281
|
+
* ## What this closes
|
|
282
|
+
*
|
|
283
|
+
* `PageComponentSchema.dataSource` is the one spelling that resolves a saved
|
|
284
|
+
* view for an object-bound block. It works — and because no registration
|
|
285
|
+
* declared it, `sdui-parser`'s `validateTree` reported it with the SAME
|
|
286
|
+
* `unknown-prop` warning it gives the spellings that do nothing. On the tier
|
|
287
|
+
* built to accept AI-authored pages the diagnostic IS the contract, so the
|
|
288
|
+
* tier's only signal pointed away from the one key that works.
|
|
289
|
+
*
|
|
290
|
+
* ## Why a seam and not nine declarations
|
|
291
|
+
*
|
|
292
|
+
* Maintainer ruling, 2026-08-29: option B **in the injection form**. Nine
|
|
293
|
+
* hand-written copies across nine packages is the shape that ruling refused —
|
|
294
|
+
* they drift, and the tenth block forgets. Passing through here is the only
|
|
295
|
+
* thing a block does; `Registry.register` emits the declaration
|
|
296
|
+
* (`withElementDataSourceInput`), and `ELEMENT_DATA_SOURCE_INPUT` in
|
|
297
|
+
* `@object-ui/core` is its single copy, beside the binding's own semantics.
|
|
298
|
+
*
|
|
299
|
+
* ## And it cannot be forgotten
|
|
300
|
+
*
|
|
301
|
+
* `scripts/check-element-data-source-declaration.mjs` fails any source that
|
|
302
|
+
* renders `ElementDataSourceGate` for a registration without passing that
|
|
303
|
+
* registration's renderer through this function. That is the mechanical half of
|
|
304
|
+
* "a tenth block gets it automatically"; this function is the seam it enforces.
|
|
305
|
+
*
|
|
306
|
+
* Returns the renderer UNCHANGED (the mark is held in a `WeakSet`), so it is safe
|
|
307
|
+
* over a `React.forwardRef` object, over `React.memo`, and over a component that
|
|
308
|
+
* something else already re-exports by reference.
|
|
309
|
+
*
|
|
310
|
+
* ⚠️ Re-exported from `@object-ui/core` — where the marker and the declaration
|
|
311
|
+
* live — rather than wrapped here: ONE function under ONE name. This export
|
|
312
|
+
* exists for DISCOVERABILITY, so the seam is findable beside the gate it is
|
|
313
|
+
* about; **call sites must import it from `@object-ui/core`**, and
|
|
314
|
+
* `check:element-data-source-declaration` enforces that.
|
|
315
|
+
*
|
|
316
|
+
* The rule is measured, not stylistic. A registration runs at MODULE SCOPE, 101
|
|
317
|
+
* suites in this repo partially mock `@object-ui/react` by hand-listing the
|
|
318
|
+
* exports they return, and a module-scope read of a name absent from such a list
|
|
319
|
+
* throws at COLLECTION time — the importing test file dies before running one
|
|
320
|
+
* assertion. Taking the seam from here reddened 17 files across all four CI
|
|
321
|
+
* shards with zero failed assertions among them. Nothing mocks `@object-ui/core`,
|
|
322
|
+
* and every registration module already imports `ComponentRegistry` from it.
|
|
323
|
+
*/
|
|
324
|
+
export { elementDataSourceBlock } from '@object-ui/core';
|
|
228
325
|
export interface ElementDataSourceGateProps<S> {
|
|
229
326
|
/** The block's schema node. */
|
|
230
327
|
schema: S;
|
|
@@ -253,6 +350,24 @@ export interface ElementDataSourceGateProps<S> {
|
|
|
253
350
|
requiresDataSource?: boolean;
|
|
254
351
|
/** Explanation for the no-adapter panel; see {@link noDataSourceMessage}. */
|
|
255
352
|
noDataSourceMessage?: string;
|
|
353
|
+
/**
|
|
354
|
+
* Whether THIS placement has nothing to show unless it names an object — when
|
|
355
|
+
* true and the bound schema names none on the mapping's object key (neither
|
|
356
|
+
* the node's own key nor `dataSource.object` landing on it), the gate renders
|
|
357
|
+
* the "no object named" hint instead of the block (objectui#11605).
|
|
358
|
+
*
|
|
359
|
+
* Read AFTER the binding is applied, so a node bound by `dataSource.object`
|
|
360
|
+
* is never told it names no object; and AFTER the view states, so a binding
|
|
361
|
+
* that is still resolving or failed to resolve keeps its own panel.
|
|
362
|
+
*
|
|
363
|
+
* The call site states it, for the reason {@link requiresDataSource} gives:
|
|
364
|
+
* the other record sources a block can draw from (inline `data`, inline
|
|
365
|
+
* `customFields`, a `dataset`, a `bind` path) are each block's own, and a
|
|
366
|
+
* predicate guessed here would paint a configuration hint over a block that
|
|
367
|
+
* is working. A mapping whose `object` is `false` lands no object key, so
|
|
368
|
+
* this prop does nothing there.
|
|
369
|
+
*/
|
|
370
|
+
requiresObject?: boolean;
|
|
256
371
|
/**
|
|
257
372
|
* Renders the block with the bound schema. Called during the gate's own
|
|
258
373
|
* render, so it must RETURN AN ELEMENT and never call hooks itself — the
|
|
@@ -269,4 +384,4 @@ export interface ElementDataSourceGateProps<S> {
|
|
|
269
384
|
* against a half-resolved query is how a page shows a wider answer than the one
|
|
270
385
|
* that was authored.
|
|
271
386
|
*/
|
|
272
|
-
export declare function ElementDataSourceGate<S>({ schema, mapping, dataSource, testId, errorTitle, requiresDataSource, noDataSourceMessage: noDataSourceText, children, }: ElementDataSourceGateProps<S>): React.ReactElement | null;
|
|
387
|
+
export declare function ElementDataSourceGate<S>({ schema, mapping, dataSource, testId, errorTitle, requiresDataSource, noDataSourceMessage: noDataSourceText, requiresObject, children, }: ElementDataSourceGateProps<S>): React.ReactElement | null;
|