@object-ui/react 17.2.0 → 17.3.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 +48 -0
- package/dist/hooks/useExpression.d.ts +34 -15
- package/dist/hooks/useExpression.js +30 -33
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @object-ui/react
|
|
2
2
|
|
|
3
|
+
## 17.3.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d915c47: Relation fields (`lookup` / `master_detail` / `user` / `tree`) are now usable in action and conditional-formatting predicates: they bind as the stored foreign key on every surface, and the fields a predicate reads are included in the query projection (#3501).
|
|
8
|
+
|
|
9
|
+
Before this, one predicate over one relation field had four different fates, decided by things its author does not control. `$expand` **replaces** the id in place with the whole related record, and a view expands exactly the relations it shows as COLUMNS — so `record.owner == "U1"` was **true** where the column was absent, **false** where it was displayed, and a **fault** where the field was neither displayed nor projected (a list's `$select` was built from its columns alone, and CEL treats an absent key as a fault, not as null). A fault is fail-CLOSED on the row kebab and the selection bar and fail-OPEN on the lenient paths, so the same authoring mistake hid the button from everyone on one surface and showed it to everyone on the next, with nothing on screen to point at either. The server, meanwhile, only ever sees the id — so client and server could not agree, which is the one thing ADR-0036 / ADR-0058 exist to guarantee.
|
|
10
|
+
|
|
11
|
+
Two changes close it. `toPredicateRecord` (new, `@object-ui/core`) collapses expanded relation values back to their ids when a record is bound for evaluation — driven by the object's own field types, not by sniffing for an `id` key, so a `json` field that happens to carry one is untouched. It is threaded through `evalRowPredicate` / `resolveConditionalFormatting` (via a new `fields` option), `useRowPredicate`, `partitionBulkRows`, and both `page:header` evaluators, with the object schema supplied by `ObjectGrid` / `ListView` / `ObjectKanban` / the record context. Kanban card formatting is threaded the same way, so a rule cannot match on the grid view of a list and silently never match on its board. Display is unaffected — a detail-page title still renders the related record's name, and the schema-only `kanban-ui` entry point (which has no object schema to offer) keeps using the payload verbatim. `collectPredicateFieldRefs` / `listViewPredicates` (new) harvest the `record.x` / `data.x` references out of a view's conditional formatting, row-action defs, bulk-action defs, promoted object actions and `userActions` overrides, and add them to `$select` — intersected with the object's declared fields plus the platform columns every object carries (`isProjectableField`), because an unknown key is not ignored by every backend. No `$expand` is added: a predicate wants the foreign key, which is what an unexpanded relation already is.
|
|
12
|
+
|
|
13
|
+
- 175bd79: `toPredicateInput` is now re-exported from `@object-ui/core` instead of being reimplemented in `@object-ui/react`. Behaviour is byte-for-byte identical — the renderer-side copy in `packages/react/src/hooks/useExpression.ts` had item-for-item the same semantics as the canonical `packages/core/src/evaluator/predicateInput.ts` (booleans short-circuit, bare strings and non-`cel` dialects become `${…}`, a `{ dialect: 'cel', source }` envelope survives so `useCondition` routes it to the canonical `@objectstack/formula` engine, empty/absent predicates become `undefined`), and every existing import path (`import { toPredicateInput } from '@object-ui/react'`) keeps working with an unchanged signature. What changes is that there is now ONE implementation rather than two held in step by a parity table: #3314 is the record of what two normalizations do when left alone — they drift, and the same `visible:` predicate reaches different verdicts depending on whether the action was surfaced by `ActionEngine.getActionsForLocation` or rendered standalone. The 14-shape normalization parity table degenerated once both of its columns called the same function, so it is replaced by a single identity assertion (`react`'s export IS `core`'s function object); the engine-path-vs-renderer-path verdict parity suite is untouched and still proves the two call paths agree (#3367).
|
|
14
|
+
- Updated dependencies [18cd432]
|
|
15
|
+
- Updated dependencies [d915c47]
|
|
16
|
+
- Updated dependencies [b71fc92]
|
|
17
|
+
- Updated dependencies [65516ba]
|
|
18
|
+
- Updated dependencies [94c5b7c]
|
|
19
|
+
- Updated dependencies [ca0fa8f]
|
|
20
|
+
- Updated dependencies [3889ffb]
|
|
21
|
+
- Updated dependencies [5781fb1]
|
|
22
|
+
- Updated dependencies [7e2406a]
|
|
23
|
+
- Updated dependencies [9e9e9a9]
|
|
24
|
+
- Updated dependencies [4eeb932]
|
|
25
|
+
- Updated dependencies [5c856ec]
|
|
26
|
+
- Updated dependencies [23018cc]
|
|
27
|
+
- Updated dependencies [68b6a28]
|
|
28
|
+
- Updated dependencies [0554e88]
|
|
29
|
+
- Updated dependencies [d915c47]
|
|
30
|
+
- Updated dependencies [f44d872]
|
|
31
|
+
- Updated dependencies [28b2e65]
|
|
32
|
+
- Updated dependencies [509104a]
|
|
33
|
+
- Updated dependencies [825bbe3]
|
|
34
|
+
- Updated dependencies [6195841]
|
|
35
|
+
- Updated dependencies [5dd0127]
|
|
36
|
+
- Updated dependencies [a415684]
|
|
37
|
+
- Updated dependencies [a4cff5b]
|
|
38
|
+
- Updated dependencies [5af2852]
|
|
39
|
+
- Updated dependencies [f833d3a]
|
|
40
|
+
- Updated dependencies [a6ec93d]
|
|
41
|
+
- Updated dependencies [2a9513d]
|
|
42
|
+
- Updated dependencies [d22ae31]
|
|
43
|
+
- Updated dependencies [c7ed4c3]
|
|
44
|
+
- Updated dependencies [2409e1d]
|
|
45
|
+
- Updated dependencies [789fe3e]
|
|
46
|
+
- @object-ui/core@17.3.0
|
|
47
|
+
- @object-ui/types@17.3.0
|
|
48
|
+
- @object-ui/i18n@17.3.0
|
|
49
|
+
- @object-ui/data-objectstack@17.3.0
|
|
50
|
+
|
|
3
51
|
## 17.2.0
|
|
4
52
|
|
|
5
53
|
### Minor Changes
|
|
@@ -26,24 +26,36 @@ export declare function PredicateScopeProvider({ scope, children, }: {
|
|
|
26
26
|
export declare function usePredicateScope(): Record<string, any>;
|
|
27
27
|
/**
|
|
28
28
|
* Normalize a schema-supplied predicate (`visible` / `enabled` / `disabled` /
|
|
29
|
-
* `hidden`) into the
|
|
29
|
+
* `hidden`) into the form `useCondition` expects — the canonical helper to use
|
|
30
|
+
* in renderers so we never end up with `${[object Object]}` after JS
|
|
31
|
+
* template-literal interpolation.
|
|
30
32
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* compatible variable syntax (`record.x`, etc.).
|
|
38
|
-
* - `null` / `undefined` / empty → `undefined` (default visible/enabled).
|
|
33
|
+
* **Re-exported from `@object-ui/core`, not reimplemented here.** There is
|
|
34
|
+
* exactly ONE implementation — `packages/core/src/evaluator/predicateInput.ts`
|
|
35
|
+
* — shared by the engine path (`ActionEngine.getActionsForLocation`) and the
|
|
36
|
+
* renderer path (`action-button` / `action-menu` / `action-bar` / …). See that
|
|
37
|
+
* file for the accepted input shapes and for why a `cel` envelope must survive
|
|
38
|
+
* normalization (#2661 / #3314).
|
|
39
39
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
40
|
+
* ## Why a re-export and not a twin (#3367)
|
|
41
|
+
*
|
|
42
|
+
* This module used to carry an independent renderer-side implementation with
|
|
43
|
+
* item-for-item identical semantics, held in step with the canonical one by a
|
|
44
|
+
* 14-shape normalization parity table. That table was a *guardrail against
|
|
45
|
+
* drift*, not a single source of truth — and #3314 is the record of what two
|
|
46
|
+
* normalizations do when left alone: they drift, and the same `visible:`
|
|
47
|
+
* predicate reaches different verdicts depending on which path surfaced the
|
|
48
|
+
* action. A re-export cannot drift, so the parity suite now pins identity
|
|
49
|
+
* (`react`'s export IS `core`'s function) instead of enumerating shapes, and
|
|
50
|
+
* keeps pinning the two PATHS verdict-for-verdict.
|
|
51
|
+
*
|
|
52
|
+
* Routing hook code through the `@object-ui/core` barrel is not a new coupling:
|
|
53
|
+
* this module already imports `ExpressionEvaluator` / `evalRowPredicate` from
|
|
54
|
+
* it, and `@object-ui/core` is a declared dependency of `@object-ui/react`
|
|
55
|
+
* (the reverse direction is the forbidden one — core declares "Zero React
|
|
56
|
+
* dependencies").
|
|
42
57
|
*/
|
|
43
|
-
export
|
|
44
|
-
dialect: 'cel';
|
|
45
|
-
source: string;
|
|
46
|
-
} | undefined;
|
|
58
|
+
export { toPredicateInput } from '@object-ui/core';
|
|
47
59
|
/**
|
|
48
60
|
* Hook for evaluating expressions with dynamic context
|
|
49
61
|
*
|
|
@@ -85,4 +97,11 @@ export declare function useRowPredicate(pred: unknown, row: Record<string, any>
|
|
|
85
97
|
fallback?: boolean;
|
|
86
98
|
warnOnError?: boolean;
|
|
87
99
|
label?: string;
|
|
100
|
+
/**
|
|
101
|
+
* The object's field definitions. Supplying them binds a relation field as
|
|
102
|
+
* the stored foreign key instead of whatever `$expand` substituted for it
|
|
103
|
+
* on this surface, so `record.owner == os.user.id` reaches the server's
|
|
104
|
+
* verdict — see `toPredicateRecord`.
|
|
105
|
+
*/
|
|
106
|
+
fields?: unknown;
|
|
88
107
|
}): boolean;
|
|
@@ -40,41 +40,36 @@ export function usePredicateScope() {
|
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
42
|
* Normalize a schema-supplied predicate (`visible` / `enabled` / `disabled` /
|
|
43
|
-
* `hidden`) into the
|
|
43
|
+
* `hidden`) into the form `useCondition` expects — the canonical helper to use
|
|
44
|
+
* in renderers so we never end up with `${[object Object]}` after JS
|
|
45
|
+
* template-literal interpolation.
|
|
44
46
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* compatible variable syntax (`record.x`, etc.).
|
|
52
|
-
* - `null` / `undefined` / empty → `undefined` (default visible/enabled).
|
|
47
|
+
* **Re-exported from `@object-ui/core`, not reimplemented here.** There is
|
|
48
|
+
* exactly ONE implementation — `packages/core/src/evaluator/predicateInput.ts`
|
|
49
|
+
* — shared by the engine path (`ActionEngine.getActionsForLocation`) and the
|
|
50
|
+
* renderer path (`action-button` / `action-menu` / `action-bar` / …). See that
|
|
51
|
+
* file for the accepted input shapes and for why a `cel` envelope must survive
|
|
52
|
+
* normalization (#2661 / #3314).
|
|
53
53
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
54
|
+
* ## Why a re-export and not a twin (#3367)
|
|
55
|
+
*
|
|
56
|
+
* This module used to carry an independent renderer-side implementation with
|
|
57
|
+
* item-for-item identical semantics, held in step with the canonical one by a
|
|
58
|
+
* 14-shape normalization parity table. That table was a *guardrail against
|
|
59
|
+
* drift*, not a single source of truth — and #3314 is the record of what two
|
|
60
|
+
* normalizations do when left alone: they drift, and the same `visible:`
|
|
61
|
+
* predicate reaches different verdicts depending on which path surfaced the
|
|
62
|
+
* action. A re-export cannot drift, so the parity suite now pins identity
|
|
63
|
+
* (`react`'s export IS `core`'s function) instead of enumerating shapes, and
|
|
64
|
+
* keeps pinning the two PATHS verdict-for-verdict.
|
|
65
|
+
*
|
|
66
|
+
* Routing hook code through the `@object-ui/core` barrel is not a new coupling:
|
|
67
|
+
* this module already imports `ExpressionEvaluator` / `evalRowPredicate` from
|
|
68
|
+
* it, and `@object-ui/core` is a declared dependency of `@object-ui/react`
|
|
69
|
+
* (the reverse direction is the forbidden one — core declares "Zero React
|
|
70
|
+
* dependencies").
|
|
56
71
|
*/
|
|
57
|
-
export
|
|
58
|
-
if (value === null || value === undefined || value === '')
|
|
59
|
-
return undefined;
|
|
60
|
-
if (typeof value === 'boolean')
|
|
61
|
-
return value;
|
|
62
|
-
if (typeof value === 'string')
|
|
63
|
-
return `\${${value}}`;
|
|
64
|
-
if (typeof value === 'object' && typeof value.source === 'string') {
|
|
65
|
-
const src = value.source;
|
|
66
|
-
if (!src)
|
|
67
|
-
return undefined;
|
|
68
|
-
// #2661 — preserve a CEL-dialect envelope so `useCondition` routes it to the
|
|
69
|
-
// canonical `@objectstack/formula` engine (identical verdict to the server),
|
|
70
|
-
// instead of collapsing it to a `${source}` string on the legacy JS path.
|
|
71
|
-
// Every other dialect (template / unset) keeps the legacy `${…}` behavior.
|
|
72
|
-
if (value.dialect === 'cel')
|
|
73
|
-
return { dialect: 'cel', source: src };
|
|
74
|
-
return `\${${src}}`;
|
|
75
|
-
}
|
|
76
|
-
return undefined;
|
|
77
|
-
}
|
|
72
|
+
export { toPredicateInput } from '@object-ui/core';
|
|
78
73
|
/**
|
|
79
74
|
* Hook for evaluating expressions with dynamic context
|
|
80
75
|
*
|
|
@@ -160,6 +155,7 @@ export function useCondition(condition, context = {}, options) {
|
|
|
160
155
|
export function useRowPredicate(pred, row, options) {
|
|
161
156
|
const scope = usePredicateScope();
|
|
162
157
|
const fallback = options?.fallback ?? true;
|
|
158
|
+
const fields = options?.fields;
|
|
163
159
|
return useMemo(() => {
|
|
164
160
|
// A boolean predicate short-circuits (no expression to evaluate).
|
|
165
161
|
if (typeof pred === 'boolean')
|
|
@@ -171,6 +167,7 @@ export function useRowPredicate(pred, row, options) {
|
|
|
171
167
|
scope,
|
|
172
168
|
warnOnError: options?.warnOnError,
|
|
173
169
|
label: options?.label,
|
|
170
|
+
fields: fields,
|
|
174
171
|
});
|
|
175
|
-
}, [pred, row, scope, fallback, options?.warnOnError, options?.label]);
|
|
172
|
+
}, [pred, row, scope, fallback, options?.warnOnError, options?.label, fields]);
|
|
176
173
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/react",
|
|
3
|
-
"version": "17.
|
|
3
|
+
"version": "17.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "React bindings and SchemaRenderer component for Object UI",
|
|
@@ -24,20 +24,20 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@objectstack/spec": "^17.0.0-rc.
|
|
27
|
+
"@objectstack/spec": "^17.0.0-rc.2",
|
|
28
28
|
"react-hook-form": "^7.83.0",
|
|
29
|
-
"@object-ui/core": "17.
|
|
30
|
-
"@object-ui/data-objectstack": "17.
|
|
31
|
-
"@object-ui/i18n": "17.
|
|
32
|
-
"@object-ui/types": "17.
|
|
29
|
+
"@object-ui/core": "17.3.0",
|
|
30
|
+
"@object-ui/data-objectstack": "17.3.0",
|
|
31
|
+
"@object-ui/i18n": "17.3.0",
|
|
32
|
+
"@object-ui/types": "17.3.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"react": "^18.0.0 || ^19.0.0",
|
|
36
36
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@types/react": "19.2.
|
|
40
|
-
"@types/react-dom": "19.2.
|
|
39
|
+
"@types/react": "19.2.18",
|
|
40
|
+
"@types/react-dom": "19.2.4",
|
|
41
41
|
"typescript": "^6.0.3"
|
|
42
42
|
},
|
|
43
43
|
"keywords": [
|