@object-ui/react 17.5.0 → 17.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4100 -0
- package/README.md +303 -46
- package/dist/LazyPluginLoader.d.ts +14 -7
- package/dist/LazyPluginLoader.js +14 -7
- package/dist/SchemaRenderer.d.ts +38 -7
- package/dist/SchemaRenderer.js +1603 -60
- package/dist/context/ActionContext.js +40 -1
- package/dist/context/DiscussionContext.d.ts +12 -0
- package/dist/context/DiscussionContext.js +2 -0
- package/dist/context/DrillNavigationContext.d.ts +13 -0
- package/dist/context/HostNavigationContext.d.ts +82 -0
- package/dist/context/HostNavigationContext.js +65 -0
- package/dist/context/InlineEditContext.d.ts +81 -1
- package/dist/context/InlineEditContext.js +8 -2
- package/dist/context/RecordContext.d.ts +48 -5
- package/dist/context/RecordContext.js +10 -1
- package/dist/context/RelatedRecordActionsContext.d.ts +20 -1
- package/dist/context/SchemaRendererContext.d.ts +54 -2
- package/dist/context/SchemaRendererContext.js +38 -5
- 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 +13 -12
- package/dist/context/index.js +13 -12
- package/dist/element-data-source/ElementDataSourceGate.d.ts +163 -3
- package/dist/element-data-source/ElementDataSourceGate.js +407 -15
- package/dist/hooks/index.d.ts +37 -35
- package/dist/hooks/index.js +44 -35
- package/dist/hooks/useActionEngine.js +47 -4
- package/dist/hooks/useActionRunner.js +4 -0
- package/dist/hooks/useActionTextLocalizer.d.ts +21 -8
- package/dist/hooks/useActionTextLocalizer.js +33 -8
- package/dist/hooks/useCapabilityGate.js +6 -5
- package/dist/hooks/useConfigBagEvaluator.d.ts +30 -0
- package/dist/hooks/useConfigBagEvaluator.js +39 -0
- package/dist/hooks/useCrudShortcuts.js +1 -1
- package/dist/hooks/useDatasetDimensionLabels.js +1 -1
- package/dist/hooks/useDensityMode.d.ts +6 -2
- package/dist/hooks/useDensityMode.js +6 -2
- package/dist/hooks/useDiscovery.d.ts +1 -10
- package/dist/hooks/useDiscovery.js +1 -1
- package/dist/hooks/useETagCache.js +25 -4
- package/dist/hooks/useElementDataSource.d.ts +15 -2
- package/dist/hooks/useElementDataSource.js +29 -9
- package/dist/hooks/useExpression.d.ts +52 -38
- package/dist/hooks/useExpression.js +62 -42
- 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 +77 -12
- package/dist/hooks/useNavigationOverlay.js +119 -9
- package/dist/hooks/useOffline.js +65 -11
- package/dist/hooks/usePageAssignment.d.ts +8 -5
- package/dist/hooks/usePageAssignment.js +38 -17
- package/dist/hooks/usePageTransition.js +1 -1
- package/dist/hooks/usePageVariableActionBridge.js +2 -2
- 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 +7 -2
- package/dist/hooks/useViewData.js +1 -1
- package/dist/hooks/useViewTransition.js +1 -1
- package/dist/index.d.ts +18 -11
- package/dist/index.js +54 -10
- package/dist/schema-input.d.ts +55 -14
- package/dist/schema-input.js +55 -12
- package/dist/utils/configBag.d.ts +68 -0
- package/dist/utils/configBag.js +70 -0
- package/dist/utils/error-message.d.ts +59 -0
- package/dist/utils/error-message.js +116 -1
- package/dist/utils/inlineAria.d.ts +61 -0
- package/dist/utils/inlineAria.js +70 -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 +421 -0
- package/dist/utils/refusedDataPropDiagnostic.d.ts +44 -0
- package/dist/utils/refusedDataPropDiagnostic.js +94 -0
- package/dist/utils/unevaluatedExpression.d.ts +110 -0
- package/dist/utils/unevaluatedExpression.js +257 -0
- package/dist/utils/visibilityDiagnostic.d.ts +371 -0
- package/dist/utils/visibilityDiagnostic.js +551 -0
- 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 -82
- package/dist/spec-bridge/bridges/form-view.js +0 -126
- package/dist/spec-bridge/bridges/list-view.d.ts +0 -20
- package/dist/spec-bridge/bridges/list-view.js +0 -241
- 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
|
@@ -13,7 +13,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
13
13
|
* Components can consume actions without creating their own runner instances.
|
|
14
14
|
*/
|
|
15
15
|
import { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
|
16
|
-
import { ActionRunner, } from '@object-ui/core';
|
|
16
|
+
import { ActionRunner, subjectPermissionsOf, } from '@object-ui/core';
|
|
17
|
+
import { useActionRunnerTranslator } from './actionRunnerTranslator.js';
|
|
18
|
+
import { usePredicateScope } from '../hooks/useExpression.js';
|
|
17
19
|
export const ActionCtxReact = createContext(null);
|
|
18
20
|
/**
|
|
19
21
|
* ActionProvider — Provides a shared ActionRunner to the tree.
|
|
@@ -34,6 +36,8 @@ export const ActionProvider = ({ children, context = {}, onConfirm, onToast, onM
|
|
|
34
36
|
const [loading, setLoading] = useState(false);
|
|
35
37
|
const [error, setError] = useState(null);
|
|
36
38
|
const [result, setResult] = useState(null);
|
|
39
|
+
// The runner's own copy (the generic success toast) in the session's language.
|
|
40
|
+
const translate = useActionRunnerTranslator();
|
|
37
41
|
const runner = useMemo(() => {
|
|
38
42
|
// Normalize the evaluator scope so predicates can use *either* flat
|
|
39
43
|
// (`record`, `user`, `objectName`) *or* the canonical `ctx.*` namespace
|
|
@@ -49,6 +53,7 @@ export const ActionProvider = ({ children, context = {}, onConfirm, onToast, onM
|
|
|
49
53
|
: { ...context },
|
|
50
54
|
};
|
|
51
55
|
const r = new ActionRunner(normalizedContext);
|
|
56
|
+
r.setTranslator(translate);
|
|
52
57
|
if (onConfirm)
|
|
53
58
|
r.setConfirmHandler(onConfirm);
|
|
54
59
|
if (onToast)
|
|
@@ -69,6 +74,40 @@ export const ActionProvider = ({ children, context = {}, onConfirm, onToast, onM
|
|
|
69
74
|
return r;
|
|
70
75
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
71
76
|
}, [JSON.stringify(context)]);
|
|
77
|
+
// The acting subject the surrounding predicate scope binds as `current_user`
|
|
78
|
+
// (objectui#11242): the ONE object `ExpressionProvider` publishes under
|
|
79
|
+
// `current_user` / `user` / `ctx.user` / `os.user`, carrying the caller's
|
|
80
|
+
// effective object permissions once they have loaded. The runner evaluates
|
|
81
|
+
// its own gates — `execute`'s `disabled` re-check, `params[].visible`, the
|
|
82
|
+
// engine's `visible` filter — against ITS bag, which a host seeds with its
|
|
83
|
+
// own `user` and never with `current_user`. Bound here, every `execute` gate
|
|
84
|
+
// under this provider answers `current_user.can(…)` and every other
|
|
85
|
+
// `current_user.*` predicate, whatever else the page mounts. Before, only
|
|
86
|
+
// `useActionEngine` wrote it onto a shared runner, so the answer depended on
|
|
87
|
+
// whether the page happened to mount `record:quick_actions`, `record:alert`
|
|
88
|
+
// or a dashboard (the objectui#6493 "one bag" rule, broken per page).
|
|
89
|
+
//
|
|
90
|
+
// The same object, not a copy: the engine answers `can` only for a receiver
|
|
91
|
+
// IDENTICAL to the bound `current_user`. Only `current_user` is added — the
|
|
92
|
+
// runner's `user` / `ctx.user` / `os.user` stay the host's object, which
|
|
93
|
+
// carries the `systemPermissions` the capability gate reads — so on this bag
|
|
94
|
+
// `user.can(…)` still faults while `current_user.can(…)` answers. With no
|
|
95
|
+
// scope above this provider (`{}`) nothing is bound, and a host's own
|
|
96
|
+
// `context.current_user`, if it passed one, is left as it is.
|
|
97
|
+
//
|
|
98
|
+
// Bound during render, before any child renders or executes, and keyed on
|
|
99
|
+
// what the binding READS, never on the subject's identity (a memoised value
|
|
100
|
+
// upstream — AGENTS.md #10): its serialisable fields, and the permissions map
|
|
101
|
+
// it carries, which the upstream adapter caches per payload outside React. A
|
|
102
|
+
// new runner (the host `context` changed) is bound again.
|
|
103
|
+
const subject = usePredicateScope().current_user;
|
|
104
|
+
const subjectKey = subject === undefined ? '' : JSON.stringify(subject);
|
|
105
|
+
const subjectPermissions = subjectPermissionsOf(subject);
|
|
106
|
+
useMemo(() => {
|
|
107
|
+
if (subject !== undefined)
|
|
108
|
+
runner.updateContext({ current_user: subject });
|
|
109
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
110
|
+
}, [runner, subjectKey, subjectPermissions]);
|
|
72
111
|
const execute = useCallback(async (action) => {
|
|
73
112
|
setLoading(true);
|
|
74
113
|
setError(null);
|
|
@@ -50,6 +50,18 @@ export interface DiscussionContextValue {
|
|
|
50
50
|
onUploadAttachments?: (files: FileList) => Promise<DiscussionAttachment[]>;
|
|
51
51
|
loading?: boolean;
|
|
52
52
|
error?: Error | null;
|
|
53
|
+
/**
|
|
54
|
+
* The host's `sys_activity` read was REFUSED: 401 / 403, or a permission
|
|
55
|
+
* envelope (objectui#11195). The feed renderers then show a no-permission
|
|
56
|
+
* state rather than claiming the record has no activity. Produced by the
|
|
57
|
+
* host that owns the fetch (app-shell `RecordDetailView`), which judges the
|
|
58
|
+
* rejection with plugin-detail's `isRefusedFeedRead`. A read that answered
|
|
59
|
+
* with zero rows, a 404, or any other failure is not a refusal and leaves
|
|
60
|
+
* this unset.
|
|
61
|
+
*/
|
|
62
|
+
activityDenied?: boolean;
|
|
63
|
+
/** The same, for the host's `sys_comment` read. */
|
|
64
|
+
commentsDenied?: boolean;
|
|
53
65
|
}
|
|
54
66
|
export interface DiscussionContextProviderProps extends DiscussionContextValue {
|
|
55
67
|
children: React.ReactNode;
|
|
@@ -28,6 +28,8 @@ export const DiscussionContextProvider = ({ children, ...value }) => {
|
|
|
28
28
|
value.onUploadAttachments,
|
|
29
29
|
value.loading,
|
|
30
30
|
value.error,
|
|
31
|
+
value.activityDenied,
|
|
32
|
+
value.commentsDenied,
|
|
31
33
|
]);
|
|
32
34
|
return (_jsx(DiscussionContext.Provider, { value: memo, children: children }));
|
|
33
35
|
};
|
|
@@ -27,6 +27,19 @@ export interface DrillNavigationValue {
|
|
|
27
27
|
* Open the full list page for `objectName`, scoped by `filter` (an object
|
|
28
28
|
* FIELD name → raw stored value map). Implemented by the app shell as a
|
|
29
29
|
* SPA navigation to the object's list route. Absent when no host wired it.
|
|
30
|
+
*
|
|
31
|
+
* ⚠️ `filter` is the widget's AUTHORED filter composed with the click
|
|
32
|
+
* context, so it may still carry unresolved placeholders — `{current_user_id}`,
|
|
33
|
+
* `{current_org_id}`, and the relative-date macros. **Resolving them is the
|
|
34
|
+
* host implementation's job**: call `resolveFilterPlaceholders(filter, scope)`
|
|
35
|
+
* from `@object-ui/core` with the session scope the host holds
|
|
36
|
+
* (`useFilterScope`), exactly once, before the value reaches a query or a URL.
|
|
37
|
+
*
|
|
38
|
+
* A host that skips it gives ONE drill TWO scopes: the in-place drawer arm is
|
|
39
|
+
* already scoped by the resolved value, because the `object-data-table` it
|
|
40
|
+
* renders resolves `schema.filter` in its own fetch, so only the navigate arm
|
|
41
|
+
* would carry the literal — and a literal placeholder matches no record
|
|
42
|
+
* (objectui#9022).
|
|
30
43
|
*/
|
|
31
44
|
openRecordList?: (objectName: string, filter?: Record<string, unknown>) => void;
|
|
32
45
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* HostNavigationContext — the host's own in-app navigate, offered to renderers.
|
|
10
|
+
*
|
|
11
|
+
* A published renderer package cannot navigate correctly on its own. Its only
|
|
12
|
+
* dependency-free option is a browser-level navigation
|
|
13
|
+
* (`window.location.assign`), which resolves a rooted path such as `/thanks`
|
|
14
|
+
* against the ORIGIN. Under a host mounted at a sub-path — the framework CLI
|
|
15
|
+
* configures one for every embedded deployment — that leaves the application
|
|
16
|
+
* entirely. This is objectui#4989 defect 4, and the same class as
|
|
17
|
+
* objectui#4190 / #4181.
|
|
18
|
+
*
|
|
19
|
+
* The host is the only party that knows its mount, so the host supplies the
|
|
20
|
+
* navigate. This context is that seam: **optional injection**, ruled by the
|
|
21
|
+
* maintainer on 2026-08-17. Two mechanisms were rejected in that ruling and are
|
|
22
|
+
* named here so they are not re-proposed:
|
|
23
|
+
*
|
|
24
|
+
* - **router-presence sniffing** (a renderer reading React Router's context
|
|
25
|
+
* when it happens to be there) — implicit magic, and it forces every
|
|
26
|
+
* renderer that wants mount-awareness to import `react-router`;
|
|
27
|
+
* - **a `react-router` peer requirement** — it imposes module resolution on
|
|
28
|
+
* router-less consumers. Two real ones exist in this repo (`apps/site`,
|
|
29
|
+
* `packages/plugin-view`), and a published renderer's whole property is
|
|
30
|
+
* that it drops into ANY React application.
|
|
31
|
+
*
|
|
32
|
+
* Explicit injection keeps every renderer dependency-free and puts
|
|
33
|
+
* mount-awareness on the type face where a host can see it is missing.
|
|
34
|
+
*
|
|
35
|
+
* ## Absent is a supported state, not a degraded one
|
|
36
|
+
*
|
|
37
|
+
* The default value is an empty object, so `useHostNavigation()` outside a
|
|
38
|
+
* provider answers `{ navigate: undefined }` and never throws. A consumer reads
|
|
39
|
+
* it as "no host navigate — fall back to whatever this renderer did before",
|
|
40
|
+
* which for a router-less host is already the correct behaviour: with no
|
|
41
|
+
* router there is no basename, so origin-rooted resolution is right. The seam
|
|
42
|
+
* changes what a MOUNTED host gets, and changes nothing else.
|
|
43
|
+
*
|
|
44
|
+
* ## Relationship to the other navigation seams in this package
|
|
45
|
+
*
|
|
46
|
+
* `DrillNavigationContext.openRecordList` is a SEMANTIC navigation — "show me
|
|
47
|
+
* the records behind this aggregate" — that only the console can turn into a
|
|
48
|
+
* URL. `ActionProvider`'s `onNavigate` is the ActionRunner's handler for
|
|
49
|
+
* `type: 'navigate'` actions, reachable only through an action dispatch. This
|
|
50
|
+
* one is neither: it is the plain "go to this already-resolved in-app path"
|
|
51
|
+
* that a renderer holds a destination for and cannot perform correctly itself.
|
|
52
|
+
* All three are supplied by the same shell from the same router `navigate`.
|
|
53
|
+
*/
|
|
54
|
+
import React from 'react';
|
|
55
|
+
export interface HostNavigationValue {
|
|
56
|
+
/**
|
|
57
|
+
* Navigate to an in-app path, resolved by the HOST — so a mounted host's
|
|
58
|
+
* basename is applied and the transition stays inside the application.
|
|
59
|
+
*
|
|
60
|
+
* `to` is an already-resolved, application-relative path (e.g. `/thanks`,
|
|
61
|
+
* `/apps/crm/accounts/r1`), never an absolute URL: a renderer that holds an
|
|
62
|
+
* external destination must not launder it through the host's router. It is
|
|
63
|
+
* the CALLER's job to have judged the destination — this seam performs a
|
|
64
|
+
* navigation, it does not authorize one.
|
|
65
|
+
*
|
|
66
|
+
* Absent when no host wired it; consumers fall back to their own default.
|
|
67
|
+
*/
|
|
68
|
+
navigate?: (to: string, options?: {
|
|
69
|
+
replace?: boolean;
|
|
70
|
+
}) => void;
|
|
71
|
+
}
|
|
72
|
+
export declare const HostNavigationContext: React.Context<HostNavigationValue>;
|
|
73
|
+
export declare const HostNavigationProvider: React.FC<{
|
|
74
|
+
value: HostNavigationValue;
|
|
75
|
+
children: React.ReactNode;
|
|
76
|
+
}>;
|
|
77
|
+
/**
|
|
78
|
+
* Consume the host-provided in-app navigate. Answers `{}` (i.e. `navigate`
|
|
79
|
+
* undefined) when no host wired one — that is a supported state, see the module
|
|
80
|
+
* docblock.
|
|
81
|
+
*/
|
|
82
|
+
export declare function useHostNavigation(): HostNavigationValue;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ObjectUI
|
|
4
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* HostNavigationContext — the host's own in-app navigate, offered to renderers.
|
|
11
|
+
*
|
|
12
|
+
* A published renderer package cannot navigate correctly on its own. Its only
|
|
13
|
+
* dependency-free option is a browser-level navigation
|
|
14
|
+
* (`window.location.assign`), which resolves a rooted path such as `/thanks`
|
|
15
|
+
* against the ORIGIN. Under a host mounted at a sub-path — the framework CLI
|
|
16
|
+
* configures one for every embedded deployment — that leaves the application
|
|
17
|
+
* entirely. This is objectui#4989 defect 4, and the same class as
|
|
18
|
+
* objectui#4190 / #4181.
|
|
19
|
+
*
|
|
20
|
+
* The host is the only party that knows its mount, so the host supplies the
|
|
21
|
+
* navigate. This context is that seam: **optional injection**, ruled by the
|
|
22
|
+
* maintainer on 2026-08-17. Two mechanisms were rejected in that ruling and are
|
|
23
|
+
* named here so they are not re-proposed:
|
|
24
|
+
*
|
|
25
|
+
* - **router-presence sniffing** (a renderer reading React Router's context
|
|
26
|
+
* when it happens to be there) — implicit magic, and it forces every
|
|
27
|
+
* renderer that wants mount-awareness to import `react-router`;
|
|
28
|
+
* - **a `react-router` peer requirement** — it imposes module resolution on
|
|
29
|
+
* router-less consumers. Two real ones exist in this repo (`apps/site`,
|
|
30
|
+
* `packages/plugin-view`), and a published renderer's whole property is
|
|
31
|
+
* that it drops into ANY React application.
|
|
32
|
+
*
|
|
33
|
+
* Explicit injection keeps every renderer dependency-free and puts
|
|
34
|
+
* mount-awareness on the type face where a host can see it is missing.
|
|
35
|
+
*
|
|
36
|
+
* ## Absent is a supported state, not a degraded one
|
|
37
|
+
*
|
|
38
|
+
* The default value is an empty object, so `useHostNavigation()` outside a
|
|
39
|
+
* provider answers `{ navigate: undefined }` and never throws. A consumer reads
|
|
40
|
+
* it as "no host navigate — fall back to whatever this renderer did before",
|
|
41
|
+
* which for a router-less host is already the correct behaviour: with no
|
|
42
|
+
* router there is no basename, so origin-rooted resolution is right. The seam
|
|
43
|
+
* changes what a MOUNTED host gets, and changes nothing else.
|
|
44
|
+
*
|
|
45
|
+
* ## Relationship to the other navigation seams in this package
|
|
46
|
+
*
|
|
47
|
+
* `DrillNavigationContext.openRecordList` is a SEMANTIC navigation — "show me
|
|
48
|
+
* the records behind this aggregate" — that only the console can turn into a
|
|
49
|
+
* URL. `ActionProvider`'s `onNavigate` is the ActionRunner's handler for
|
|
50
|
+
* `type: 'navigate'` actions, reachable only through an action dispatch. This
|
|
51
|
+
* one is neither: it is the plain "go to this already-resolved in-app path"
|
|
52
|
+
* that a renderer holds a destination for and cannot perform correctly itself.
|
|
53
|
+
* All three are supplied by the same shell from the same router `navigate`.
|
|
54
|
+
*/
|
|
55
|
+
import { createContext, useContext } from 'react';
|
|
56
|
+
export const HostNavigationContext = createContext({});
|
|
57
|
+
export const HostNavigationProvider = ({ value, children }) => (_jsx(HostNavigationContext.Provider, { value: value, children: children }));
|
|
58
|
+
/**
|
|
59
|
+
* Consume the host-provided in-app navigate. Answers `{}` (i.e. `navigate`
|
|
60
|
+
* undefined) when no host wired one — that is a supported state, see the module
|
|
61
|
+
* docblock.
|
|
62
|
+
*/
|
|
63
|
+
export function useHostNavigation() {
|
|
64
|
+
return useContext(HostNavigationContext);
|
|
65
|
+
}
|
|
@@ -95,6 +95,37 @@ export interface InlineEditContextValue {
|
|
|
95
95
|
* doesn't resolve approvals at all.
|
|
96
96
|
*/
|
|
97
97
|
approvalProgress?: ApprovalProgress;
|
|
98
|
+
/**
|
|
99
|
+
* Whether the viewer is the SUBMITTER of the pending approval request
|
|
100
|
+
* (`830ed5803`). Recall is the submitter's lever and the server enforces
|
|
101
|
+
* exactly that — a non-submitter's recall is refused — so a recall button
|
|
102
|
+
* lit for everyone who can read the record is a button whose click must
|
|
103
|
+
* fail. The host resolves the identity (server-resolved `viewer.is_submitter`
|
|
104
|
+
* on the pending request, with an id comparison as the pre-framework#3310
|
|
105
|
+
* fallback) for the same reason it resolves `locked`: the renderer stays
|
|
106
|
+
* DataSource-agnostic and never re-derives who submitted what.
|
|
107
|
+
*
|
|
108
|
+
* TRI-STATE, and the third state is the load-bearing one:
|
|
109
|
+
* `true` — the viewer submitted this approval; offer recall.
|
|
110
|
+
* `false` — the host consulted the pending request and this viewer is
|
|
111
|
+
* not its submitter; withdraw the recall affordance.
|
|
112
|
+
* `undefined` — the host does not resolve submitter identity at all
|
|
113
|
+
* (no approvals API, band driven by the record's
|
|
114
|
+
* `approval_status` mirror alone). Behaviour is UNCHANGED
|
|
115
|
+
* from before this signal existed: recall stays offered.
|
|
116
|
+
*
|
|
117
|
+
* The `undefined` case defaults to *showing* on purpose, mirroring how
|
|
118
|
+
* `approvalPending` falls back to `locked`: omission preserves a host's
|
|
119
|
+
* previous behaviour. Defaulting it to "hide" would silently take the
|
|
120
|
+
* submitter's only unlock lever away from every host that never learns of
|
|
121
|
+
* this prop — trading a cosmetic defect for a functional loss.
|
|
122
|
+
*
|
|
123
|
+
* ⚠️ FEEDBACK ONLY. This decides who is SHOWN the entry, never who MAY
|
|
124
|
+
* recall: the server authorizes the recall itself and rejects a
|
|
125
|
+
* non-submitter regardless of what any client renders. Nothing may treat
|
|
126
|
+
* this as an authorization verdict.
|
|
127
|
+
*/
|
|
128
|
+
approvalIsSubmitter?: boolean;
|
|
98
129
|
/**
|
|
99
130
|
* Human-readable reason for the approval lock, surfaced as the band's
|
|
100
131
|
* tooltip. Optional — consumers fall back to their own localized default
|
|
@@ -104,7 +135,15 @@ export interface InlineEditContextValue {
|
|
|
104
135
|
/**
|
|
105
136
|
* Draft of user-edited values. Holds ONLY the keys the user actually
|
|
106
137
|
* changed, so the save path never writes computed / read-only / untouched
|
|
107
|
-
* fields. Read a field's live value
|
|
138
|
+
* fields. Read a field's live value from the staged record
|
|
139
|
+
* `{ ...data, ...draft }`, where `data` is the saved record: an OWN draft
|
|
140
|
+
* key wins even when its value is empty (`null`, `undefined`, `''`), and
|
|
141
|
+
* `data[name]` is read only when the key is absent. An own key with an
|
|
142
|
+
* empty value is a field the user, or a cascade clear, emptied, not an
|
|
143
|
+
* untouched one. A read that falls back to the saved value there shows the
|
|
144
|
+
* value the user just removed, and hands an option widget that pruned it
|
|
145
|
+
* the same value to prune again on every render (objectui#7190,
|
|
146
|
+
* objectui#10466).
|
|
108
147
|
*/
|
|
109
148
|
draft: Record<string, any>;
|
|
110
149
|
/** Field to auto-focus when edit was entered from a specific field. */
|
|
@@ -113,6 +152,34 @@ export interface InlineEditContextValue {
|
|
|
113
152
|
saving: boolean;
|
|
114
153
|
/** Last save error message, or null (driven by the save bar). */
|
|
115
154
|
error: string | null;
|
|
155
|
+
/**
|
|
156
|
+
* Per-field reasons from the last rejected save, keyed by field MACHINE NAME
|
|
157
|
+
* — or `null` when the refusal was not field-scoped (objectui#6868).
|
|
158
|
+
*
|
|
159
|
+
* The record-level companion to `error`, and driven by the same component:
|
|
160
|
+
* `<InlineEditSaveBar>` reads the server's `VALIDATION_FAILED` envelope
|
|
161
|
+
* through `extractFieldErrors` and publishes the result here, so the field
|
|
162
|
+
* renderers (`DetailSection`, `HeaderHighlight`) can draw the reason beside
|
|
163
|
+
* the input the server actually refused. Before this slot existed the save
|
|
164
|
+
* bar and the field rows had no channel between them — they are SIBLINGS
|
|
165
|
+
* under this provider in both persistence modes — so an attributed refusal
|
|
166
|
+
* could only be shown record-level, which is not the in-place hint the
|
|
167
|
+
* objectui#6868 ruling asked for.
|
|
168
|
+
*
|
|
169
|
+
* ⚠️ PRESENTATION ONLY. These are the SERVER's verdicts, transported. The
|
|
170
|
+
* ruling makes the server the validation authority on the inline-edit
|
|
171
|
+
* surface: nothing may populate this slot from a client-side rule check, and
|
|
172
|
+
* nothing may read it as an authorization or acceptance decision. It is text
|
|
173
|
+
* to render beside an input, nothing more.
|
|
174
|
+
*
|
|
175
|
+
* Keyed by machine name because that is what both ends already use — the
|
|
176
|
+
* draft key the field renderers set, and the `field` the server names — so
|
|
177
|
+
* no spelling translation sits between the refusal and the input.
|
|
178
|
+
*
|
|
179
|
+
* Cleared by `enter()` and by teardown, exactly like `error`, so an
|
|
180
|
+
* attribution can never outlive the session that produced it.
|
|
181
|
+
*/
|
|
182
|
+
fieldErrors: Record<string, string> | null;
|
|
116
183
|
/** Enter inline-edit mode, optionally focused on `field`. No-op when `!canEdit`. */
|
|
117
184
|
enter: (field?: string) => void;
|
|
118
185
|
/** Stage a single field edit into the draft. */
|
|
@@ -125,6 +192,11 @@ export interface InlineEditContextValue {
|
|
|
125
192
|
setSaving: (saving: boolean) => void;
|
|
126
193
|
/** Set or clear the save error message (used by the save bar). */
|
|
127
194
|
setError: (error: string | null) => void;
|
|
195
|
+
/**
|
|
196
|
+
* Publish or clear the per-field reasons for a rejected save (used by the
|
|
197
|
+
* save bar). Pass `null` when the refusal was not field-scoped.
|
|
198
|
+
*/
|
|
199
|
+
setFieldErrors: (errors: Record<string, string> | null) => void;
|
|
128
200
|
}
|
|
129
201
|
export interface InlineEditProviderProps {
|
|
130
202
|
/**
|
|
@@ -152,6 +224,14 @@ export interface InlineEditProviderProps {
|
|
|
152
224
|
* for `first_response` nodes and for hosts that don't read approvals.
|
|
153
225
|
*/
|
|
154
226
|
approvalProgress?: ApprovalProgress;
|
|
227
|
+
/**
|
|
228
|
+
* Whether the viewer submitted the pending approval request
|
|
229
|
+
* (`830ed5803`). Surfaced verbatim so the recall affordance is offered to
|
|
230
|
+
* the submitter only. Omitted ⇒ `undefined` ⇒ recall is offered exactly as
|
|
231
|
+
* it was before this prop existed (see `InlineEditContextValue`), so a host
|
|
232
|
+
* that resolves no approval identity is unchanged.
|
|
233
|
+
*/
|
|
234
|
+
approvalIsSubmitter?: boolean;
|
|
155
235
|
/** Optional human-readable lock reason, surfaced as the band tooltip. */
|
|
156
236
|
lockedReason?: string;
|
|
157
237
|
children: React.ReactNode;
|
|
@@ -24,7 +24,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
24
24
|
*/
|
|
25
25
|
import React from 'react';
|
|
26
26
|
const InlineEditContext = React.createContext(null);
|
|
27
|
-
export const InlineEditProvider = ({ canEdit = true, locked = false, approvalPending, approvalProgress, lockedReason, children, }) => {
|
|
27
|
+
export const InlineEditProvider = ({ canEdit = true, locked = false, approvalPending, approvalProgress, approvalIsSubmitter, lockedReason, children, }) => {
|
|
28
28
|
// A locked record always has an approval running; the converse is what
|
|
29
29
|
// #2902 added. Defaulting this way means an un-migrated host keeps its
|
|
30
30
|
// previous behavior with no code change.
|
|
@@ -34,6 +34,7 @@ export const InlineEditProvider = ({ canEdit = true, locked = false, approvalPen
|
|
|
34
34
|
const [autoFocusField, setAutoFocusField] = React.useState(null);
|
|
35
35
|
const [saving, setSaving] = React.useState(false);
|
|
36
36
|
const [error, setError] = React.useState(null);
|
|
37
|
+
const [fieldErrors, setFieldErrors] = React.useState(null);
|
|
37
38
|
const enter = React.useCallback((field) => {
|
|
38
39
|
// Object-lifecycle / permission gate: never enter edit on a record the
|
|
39
40
|
// host has marked non-editable, even if a stray affordance fires.
|
|
@@ -42,6 +43,7 @@ export const InlineEditProvider = ({ canEdit = true, locked = false, approvalPen
|
|
|
42
43
|
setAutoFocusField(field ?? null);
|
|
43
44
|
setEditing(true);
|
|
44
45
|
setError(null);
|
|
46
|
+
setFieldErrors(null);
|
|
45
47
|
}, [canEdit]);
|
|
46
48
|
const setField = React.useCallback((field, value) => {
|
|
47
49
|
setDraft((prev) => ({ ...prev, [field]: value }));
|
|
@@ -56,6 +58,7 @@ export const InlineEditProvider = ({ canEdit = true, locked = false, approvalPen
|
|
|
56
58
|
setAutoFocusField(null);
|
|
57
59
|
setSaving(false);
|
|
58
60
|
setError(null);
|
|
61
|
+
setFieldErrors(null);
|
|
59
62
|
}, []);
|
|
60
63
|
const value = React.useMemo(() => ({
|
|
61
64
|
editing,
|
|
@@ -63,18 +66,21 @@ export const InlineEditProvider = ({ canEdit = true, locked = false, approvalPen
|
|
|
63
66
|
locked,
|
|
64
67
|
approvalPending: pending,
|
|
65
68
|
approvalProgress,
|
|
69
|
+
approvalIsSubmitter,
|
|
66
70
|
lockedReason,
|
|
67
71
|
draft,
|
|
68
72
|
autoFocusField,
|
|
69
73
|
saving,
|
|
70
74
|
error,
|
|
75
|
+
fieldErrors,
|
|
71
76
|
enter,
|
|
72
77
|
setField,
|
|
73
78
|
cancel: teardown,
|
|
74
79
|
reset: teardown,
|
|
75
80
|
setSaving,
|
|
76
81
|
setError,
|
|
77
|
-
|
|
82
|
+
setFieldErrors,
|
|
83
|
+
}), [editing, canEdit, locked, pending, approvalProgress, approvalIsSubmitter, lockedReason, draft, autoFocusField, saving, error, fieldErrors, enter, setField, teardown]);
|
|
78
84
|
return _jsx(InlineEditContext.Provider, { value: value, children: children });
|
|
79
85
|
};
|
|
80
86
|
/**
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* receive props from the schema tree.
|
|
14
14
|
*/
|
|
15
15
|
import React from 'react';
|
|
16
|
+
import type { DataSource } from '@object-ui/types';
|
|
16
17
|
export declare const HighlightFieldsProvider: React.FC<{
|
|
17
18
|
children: React.ReactNode;
|
|
18
19
|
}>;
|
|
@@ -27,10 +28,42 @@ export declare function useRegisterHighlightFields(instanceId: string, names: st
|
|
|
27
28
|
export interface RecordContextValue<TData = any, TObjectSchema = any> {
|
|
28
29
|
/** Object machine name, e.g. "crm_opportunity". */
|
|
29
30
|
objectName: string;
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Primary key value of the record being displayed, as a `string` -- the one
|
|
33
|
+
* spelling `@objectstack/spec` declares on every record door (get, update,
|
|
34
|
+
* delete and the batch operation are all `z.string()`), so this consumer
|
|
35
|
+
* type is no longer wider than the protocol (objectui#9333).
|
|
36
|
+
*
|
|
37
|
+
* A host whose primary keys are numeric does NOT stringify at its call
|
|
38
|
+
* sites: `RecordContextProviderProps` still accepts `string | number`, and
|
|
39
|
+
* the provider pays the conversion once, below.
|
|
40
|
+
*
|
|
41
|
+
* `LineItemsPanel` reads this member as a `string` and hands it to a
|
|
42
|
+
* `string` parameter with no assertion in between, which is what
|
|
43
|
+
* objectui#9304 had to leave behind. That is a statement about that one
|
|
44
|
+
* reader, not about every reader: three `record:*` renderers in
|
|
45
|
+
* `@object-ui/plugin-detail` -- `record-details`, `record-quick-actions` and
|
|
46
|
+
* `record-alert` -- still read it through an `as any`. Those casts are
|
|
47
|
+
* redundant now rather than load-bearing, and removing them was left outside
|
|
48
|
+
* objectui#9333 deliberately.
|
|
49
|
+
*/
|
|
50
|
+
recordId: string | null | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* The data adapter the page is bound to, as the host resolved it — the same
|
|
53
|
+
* object the host hands `SchemaRendererProvider`, forwarded so that
|
|
54
|
+
* `record:*` renderers can self-fetch (related lists, activity, the
|
|
55
|
+
* reference rail) without re-plumbing it through the schema tree.
|
|
56
|
+
*
|
|
57
|
+
* Typed `DataSource`, not an id: every producer has always passed the
|
|
58
|
+
* adapter, and every reader has always called adapter methods on it
|
|
59
|
+
* (objectui#9197). `getObjectSchema` is a REQUIRED member of `DataSource`,
|
|
60
|
+
* so a reader gets the compiler's answer about it instead of a cast.
|
|
61
|
+
*
|
|
62
|
+
* Optional: hosts that render a record with no adapter bound (the Studio
|
|
63
|
+
* designer and palette previews) leave it undefined, and renderers treat
|
|
64
|
+
* that as "cannot self-fetch" rather than throwing.
|
|
65
|
+
*/
|
|
66
|
+
dataSource?: DataSource;
|
|
34
67
|
/** Loaded record data (flat record map). Undefined while loading. */
|
|
35
68
|
data?: TData;
|
|
36
69
|
/** Resolved object metadata schema (fields, label, etc.). */
|
|
@@ -68,7 +101,17 @@ export interface RecordContextValue<TData = any, TObjectSchema = any> {
|
|
|
68
101
|
*/
|
|
69
102
|
embedded?: boolean;
|
|
70
103
|
}
|
|
71
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Props of `RecordContextProvider` -- THE injection boundary (objectui#9333).
|
|
106
|
+
*
|
|
107
|
+
* Identical to `RecordContextValue` except for `recordId`, the one member a
|
|
108
|
+
* host may still hand over in the wider shape its backend actually holds. The
|
|
109
|
+
* provider narrows it to the protocol's `string` exactly once, here, so no
|
|
110
|
+
* consumer downstream carries a `String(...)` or a cast.
|
|
111
|
+
*/
|
|
112
|
+
export interface RecordContextProviderProps extends Omit<RecordContextValue, 'recordId'> {
|
|
113
|
+
/** Primary key as the host holds it; narrowed to `string` on the way in. */
|
|
114
|
+
recordId: string | number | null | undefined;
|
|
72
115
|
children: React.ReactNode;
|
|
73
116
|
}
|
|
74
117
|
export declare const RecordContextProvider: React.FC<RecordContextProviderProps>;
|
|
@@ -87,7 +87,16 @@ const NOOP_SUBSCRIBE = (_) => () => { };
|
|
|
87
87
|
const GET_EMPTY = () => EMPTY_SET;
|
|
88
88
|
const EMPTY_SET = new Set();
|
|
89
89
|
const RecordContext = React.createContext(null);
|
|
90
|
-
export const RecordContextProvider = ({ children, ...
|
|
90
|
+
export const RecordContextProvider = ({ children, recordId, ...rest }) => {
|
|
91
|
+
// The whole conversion for this contract, in one typed place. Gated on
|
|
92
|
+
// `typeof` rather than written as an unconditional `String(...)`: `null` and
|
|
93
|
+
// `undefined` mean "no record bound", and stringifying them would hand
|
|
94
|
+
// consumers the literal ids "null" / "undefined". A falsiness gate would be
|
|
95
|
+
// wrong in the other direction -- a numeric `0` is a real primary key.
|
|
96
|
+
const value = {
|
|
97
|
+
...rest,
|
|
98
|
+
recordId: typeof recordId === 'number' ? String(recordId) : recordId,
|
|
99
|
+
};
|
|
91
100
|
// Memoize so consumers that rely on referential equality don't re-render
|
|
92
101
|
// on unrelated parent renders.
|
|
93
102
|
const memo = React.useMemo(() => value, [
|
|
@@ -61,6 +61,21 @@ export interface RelatedRecordHandlers {
|
|
|
61
61
|
* argument is needed here (增).
|
|
62
62
|
*/
|
|
63
63
|
onCreate?: () => void;
|
|
64
|
+
/**
|
|
65
|
+
* [#4646] Offer "+ New", but GREYED — the `userActions.create.disabledWhen`
|
|
66
|
+
* verdict for the scope this toolbar sits in (the host parent record on a
|
|
67
|
+
* record page's related list).
|
|
68
|
+
*
|
|
69
|
+
* A separate channel from omitting {@link onCreate} because hidden and
|
|
70
|
+
* disabled are different answers, exactly as they are for the row Edit/Delete
|
|
71
|
+
* predicates (objectui#2614) and for the record header's (objectui#4419 /
|
|
72
|
+
* PR #4515): `visibleWhen` decides whether the affordance exists at all,
|
|
73
|
+
* `disabledWhen` decides whether the existing affordance can be pressed. The
|
|
74
|
+
* host still supplies `onCreate` in the disabled case, so the button keeps
|
|
75
|
+
* its label and tooltip and the user can see that creating is a thing this
|
|
76
|
+
* list does — just not right now.
|
|
77
|
+
*/
|
|
78
|
+
createDisabled?: boolean;
|
|
64
79
|
/** Open an edit form for an existing child record (改). */
|
|
65
80
|
onEdit?: (recordId: string | number, record?: unknown) => void;
|
|
66
81
|
/**
|
|
@@ -68,7 +83,11 @@ export interface RelatedRecordHandlers {
|
|
|
68
83
|
* the deletion resolves.
|
|
69
84
|
*/
|
|
70
85
|
onDelete?: (recordId: string | number, record?: unknown) => void | Promise<void>;
|
|
71
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* Child object row-level actions, localized: its `list_item` actions, and
|
|
88
|
+
* its `record_related` ones when the list sits inside a parent record
|
|
89
|
+
* (objectui#11270).
|
|
90
|
+
*/
|
|
72
91
|
rowActions?: RelatedRowActionDef[];
|
|
73
92
|
/** Execute one of {@link rowActions} against a specific child row. */
|
|
74
93
|
onRowAction?: (action: RelatedRowActionDef, record: unknown) => void | Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
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
5
|
* Host-provided fetch used for `provider: 'api'` view data sources so custom
|
|
5
6
|
* endpoints carry the same credentials (Authorization, tenant, locale headers)
|
|
@@ -8,7 +9,29 @@ import type { DebugFlags } from '@object-ui/core';
|
|
|
8
9
|
*/
|
|
9
10
|
export type ApiFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
10
11
|
interface SchemaRendererContextType {
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The adapter the host injected, as the published `DataSource` contract
|
|
14
|
+
* declares it (objectui#7912).
|
|
15
|
+
*
|
|
16
|
+
* Typed, not `any`: this is the injection point for the ENTIRE renderer tree,
|
|
17
|
+
* and `useSchemaContext()` hands whatever is declared here to every consumer
|
|
18
|
+
* that reads it back. While it was `any`, a bare string passed where the
|
|
19
|
+
* host's adapter belongs raised nothing at compile time and failed at runtime
|
|
20
|
+
* on the first `find()` — the shape of a config value read from the wrong
|
|
21
|
+
* place. The type was never unknown: `@object-ui/types` exports `DataSource`,
|
|
22
|
+
* `useSettledSchema` in this same package already declares its parameter as
|
|
23
|
+
* `DataSource<any> | null | undefined`, and `app-shell`'s README writes
|
|
24
|
+
* `DataSource` against this very seam.
|
|
25
|
+
*
|
|
26
|
+
* `null | undefined` are part of the contract, not a weakening of it: a host
|
|
27
|
+
* renders with no adapter bound (a Studio preview, a react page before the
|
|
28
|
+
* AdapterProvider connects, a widget test that drives `apiFetch` alone), and
|
|
29
|
+
* every reader in the tree already guards for it. The union states the three
|
|
30
|
+
* states that actually occur and refuses everything else — a string, `{}`, a
|
|
31
|
+
* plain data bag, a partial adapter missing a required member. It is spelled
|
|
32
|
+
* exactly as `useSettledSchema` spells it, one hop away.
|
|
33
|
+
*/
|
|
34
|
+
dataSource: DataSource | null | undefined;
|
|
12
35
|
debug?: boolean;
|
|
13
36
|
debugFlags?: DebugFlags;
|
|
14
37
|
apiFetch?: ApiFetch;
|
|
@@ -17,10 +40,39 @@ declare const SchemaRendererContext: React.Context<SchemaRendererContextType | n
|
|
|
17
40
|
export { SchemaRendererContext };
|
|
18
41
|
export declare const SchemaRendererProvider: ({ children, dataSource, debug, debugFlags, apiFetch, }: {
|
|
19
42
|
children: React.ReactNode;
|
|
20
|
-
dataSource
|
|
43
|
+
/** The host's adapter — see {@link SchemaRendererContextType.dataSource}. */
|
|
44
|
+
dataSource: DataSource | null | undefined;
|
|
21
45
|
debug?: boolean;
|
|
22
46
|
debugFlags?: DebugFlags;
|
|
23
47
|
apiFetch?: ApiFetch;
|
|
24
48
|
}) => React.JSX.Element;
|
|
25
49
|
export declare const useSchemaContext: () => SchemaRendererContextType;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve a `bind` path against the ambient predicate scope.
|
|
52
|
+
*
|
|
53
|
+
* ## What it reads, and what it deliberately does not (objectui#9308)
|
|
54
|
+
*
|
|
55
|
+
* The scope a host publishes through `PredicateScopeProvider` — the same
|
|
56
|
+
* channel `useCondition` / `useExpression` merge under a locally-passed
|
|
57
|
+
* context, and the one app-shell's `ExpressionProvider` already feeds. It is
|
|
58
|
+
* NOT `SchemaRendererContext.dataSource`.
|
|
59
|
+
*
|
|
60
|
+
* `dataSource` is the host's injected ADAPTER, declared as the published
|
|
61
|
+
* `DataSource` contract (objectui#7912). An adapter has no `users` member, no
|
|
62
|
+
* `value` member, no member a `bind` path names — so walking it resolved
|
|
63
|
+
* `undefined` for every conformant host, and the nine production readers of
|
|
64
|
+
* this hook have all been running their fallback (`boundData || schema.items`,
|
|
65
|
+
* `|| schema.nodes`, or a fall-through to their own fetch chain) ever since.
|
|
66
|
+
* The only hosts it answered were ones injecting a data bag through a key the
|
|
67
|
+
* contract says is an adapter, which has been a compile error since
|
|
68
|
+
* objectui#7912.
|
|
69
|
+
*
|
|
70
|
+
* Maintainer ruling 2026-09-13 (option B) points it at the channel that can
|
|
71
|
+
* actually answer it. No published key is added: the provider, the hook and
|
|
72
|
+
* the host wiring all already exist.
|
|
73
|
+
*
|
|
74
|
+
* Returns `undefined` for an absent or empty path, and for any path the scope
|
|
75
|
+
* does not carry — the readers' fallbacks are what run then, exactly as
|
|
76
|
+
* before.
|
|
77
|
+
*/
|
|
26
78
|
export declare const useDataScope: (path?: string) => any;
|