@object-ui/core 17.3.0 → 17.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1224 -0
- package/README.md +2 -12
- package/dist/actions/ActionEngine.d.ts +42 -25
- package/dist/actions/ActionEngine.js +66 -52
- package/dist/actions/ActionRunner.d.ts +199 -21
- package/dist/actions/ActionRunner.js +332 -16
- package/dist/actions/actionKeys.d.ts +96 -20
- package/dist/actions/actionKeys.js +157 -18
- package/dist/data-scope/ViewDataProvider.d.ts +37 -12
- package/dist/data-scope/ViewDataProvider.js +83 -5
- package/dist/data-scope/element-data-source.d.ts +113 -0
- package/dist/data-scope/element-data-source.js +216 -0
- package/dist/data-scope/index.d.ts +2 -1
- package/dist/data-scope/index.js +1 -0
- package/dist/evaluator/declaredPredicate.d.ts +119 -0
- package/dist/evaluator/declaredPredicate.js +152 -0
- package/dist/evaluator/fieldRules.d.ts +16 -0
- package/dist/evaluator/fieldRules.js +51 -16
- package/dist/evaluator/index.d.ts +1 -0
- package/dist/evaluator/index.js +1 -0
- package/dist/evaluator/listConditional.d.ts +5 -2
- package/dist/evaluator/listConditional.js +58 -16
- package/dist/evaluator/predicateInput.d.ts +51 -3
- package/dist/evaluator/predicateInput.js +62 -5
- package/dist/index.d.ts +4 -1
- package/dist/index.js +14 -1
- package/dist/protocols/DndProtocol.d.ts +42 -1
- package/dist/protocols/KeyboardProtocol.d.ts +31 -1
- package/dist/protocols/SharingProtocol.d.ts +11 -1
- package/dist/registry/Registry.d.ts +24 -0
- package/dist/theme/ThemeEngine.d.ts +0 -8
- package/dist/theme/ThemeEngine.js +22 -103
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.js +7 -1
- package/dist/types/index.d.ts +42 -8
- package/dist/utils/chart-series.d.ts +258 -5
- package/dist/utils/chart-series.js +423 -20
- package/dist/utils/compare-to.d.ts +40 -13
- package/dist/utils/compare-to.js +4 -32
- package/dist/utils/dashboard-filters.d.ts +54 -8
- package/dist/utils/dashboard-filters.js +261 -12
- package/dist/utils/dataset-format.d.ts +73 -14
- package/dist/utils/dataset-format.js +130 -9
- package/dist/utils/dataset-pivot.d.ts +28 -7
- package/dist/utils/dataset-pivot.js +27 -6
- package/dist/utils/dom-props.d.ts +137 -0
- package/dist/utils/dom-props.js +153 -0
- package/dist/utils/normalize-list-view.d.ts +26 -6
- package/dist/utils/normalize-list-view.js +30 -8
- package/dist/utils/number-display.d.ts +151 -0
- package/dist/utils/number-display.js +71 -0
- package/dist/utils/resolve-view-id.d.ts +31 -0
- package/dist/utils/resolve-view-id.js +45 -0
- package/dist/utils/sort-query.d.ts +55 -0
- package/dist/utils/sort-query.js +35 -0
- package/package.json +5 -5
- package/dist/utils/merge-views-into-objects.d.ts +0 -19
- package/dist/utils/merge-views-into-objects.js +0 -39
|
@@ -44,7 +44,8 @@ export interface RowPredicateOptions {
|
|
|
44
44
|
* row-action `visible`/`disabled`; `false` (no style) for formatting. */
|
|
45
45
|
fallback?: boolean;
|
|
46
46
|
/** Extra top-level scope merged alongside the row — e.g. the global predicate
|
|
47
|
-
* scope (`features` / `user` / `app`) a host shell provides.
|
|
47
|
+
* scope (`features` / `user` / `app`) a host shell provides. The row wins on
|
|
48
|
+
* collision: a `record` or `data` key here never shadows the row. */
|
|
48
49
|
scope?: Record<string, unknown>;
|
|
49
50
|
/** When true, log a one-time warning if a *present* predicate faults. */
|
|
50
51
|
warnOnError?: boolean;
|
|
@@ -65,7 +66,9 @@ export interface RowPredicateOptions {
|
|
|
65
66
|
* fields are bound three ways so every authoring convention resolves:
|
|
66
67
|
* `record.status` (spec/canonical), bare `status` (row-action shorthand), and
|
|
67
68
|
* `data.status` (legacy). The optional `scope` (host predicate scope) is bound
|
|
68
|
-
* alongside so `features.*` / `user.*` predicates keep working
|
|
69
|
+
* alongside so `features.*` / `user.*` predicates keep working — but the row is
|
|
70
|
+
* the subject: `record` and `data` always name THIS row, on both dialect paths,
|
|
71
|
+
* even when the host scope carries keys of those names (objectui#3796).
|
|
69
72
|
*/
|
|
70
73
|
export declare function evalRowPredicate(pred: FieldRulePredicate | undefined | null, row: Record<string, unknown> | null | undefined, opts?: RowPredicateOptions): boolean;
|
|
71
74
|
/** A conditional-formatting rule in any of the three historical shapes. */
|
|
@@ -54,27 +54,47 @@ function warnLegacyDialect(source) {
|
|
|
54
54
|
if (warnedLegacy.has(source))
|
|
55
55
|
return;
|
|
56
56
|
warnedLegacy.add(source);
|
|
57
|
-
console.warn('[object-ui] A
|
|
57
|
+
console.warn('[object-ui] A conditional predicate uses the legacy expression dialect ' +
|
|
58
58
|
'and is not portable to the server CEL engine: ' +
|
|
59
59
|
JSON.stringify(source) +
|
|
60
60
|
'. Rewrite it as CEL (use "==" not "===", "x in [..]" for membership, ' +
|
|
61
61
|
'".contains()" not ".includes()", and drop template interpolation).');
|
|
62
62
|
}
|
|
63
63
|
const warnedError = new Set();
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
|
+
* One-time fail-closed report for a predicate that could not be evaluated.
|
|
66
|
+
*
|
|
67
|
+
* `reason` is the engine's own description of the failure (`"[runtime] No such
|
|
68
|
+
* key: owner_id"`) — the single most useful line for the author, and precisely
|
|
69
|
+
* what this path used to discard: the fault-probing CEL route silences the
|
|
70
|
+
* canonical helper's warning to avoid double-reporting, so before objectui#3792
|
|
71
|
+
* the more safety-critical the surface, the LESS it said. It is optional
|
|
72
|
+
* because a caller may genuinely have no reason to pass (nothing fabricates
|
|
73
|
+
* one).
|
|
74
|
+
*
|
|
75
|
+
* The wording is deliberately surface-neutral: this function is not
|
|
76
|
+
* list-specific and has not been for a long time — row kebabs, the bulk
|
|
77
|
+
* selection bar, kanban conditional formatting and (since objectui#3521)
|
|
78
|
+
* `page:header` all report through it, and telling a page-header author that
|
|
79
|
+
* "a list conditional predicate" failed sends them to the wrong screen.
|
|
80
|
+
*/
|
|
81
|
+
function warnEvalError(source, label, reason) {
|
|
65
82
|
// One-time-warning identity for the (label, source) pair. JSON-encoded rather
|
|
66
83
|
// than joined on a character the two halves "cannot contain" — that separator
|
|
67
84
|
// was a raw U+0000, which made this whole file binary to grep (objectstack#5450).
|
|
68
85
|
// JSON needs no impossible character, so the boundary is unambiguous for any
|
|
69
86
|
// label and any predicate source, and the literal is plain ASCII.
|
|
87
|
+
// `reason` is deliberately NOT part of the key: one predicate text has one
|
|
88
|
+
// reason, so keying on it could only ever weaken the warn-once contract.
|
|
70
89
|
const key = JSON.stringify([label ?? '', source]);
|
|
71
90
|
if (warnedError.has(key))
|
|
72
91
|
return;
|
|
73
92
|
warnedError.add(key);
|
|
74
|
-
console.warn('[object-ui] A
|
|
93
|
+
console.warn('[object-ui] A conditional predicate failed to evaluate' +
|
|
75
94
|
(label ? ` (${label})` : '') +
|
|
76
95
|
' and was treated as its safe default: ' +
|
|
77
96
|
JSON.stringify(source) +
|
|
97
|
+
(reason ? `. Reason: ${reason}` : '') +
|
|
78
98
|
'. Check the field names and CEL syntax.');
|
|
79
99
|
}
|
|
80
100
|
/**
|
|
@@ -91,11 +111,18 @@ function evalCel(pred, record, scope) {
|
|
|
91
111
|
// verbatim — no second engine — so CEL semantics never drift from B2.
|
|
92
112
|
// `warn: false`: this caller reports the fault itself (the labelled
|
|
93
113
|
// `warnEvalError` in `evalRowPredicate`) — one warning per broken predicate,
|
|
94
|
-
// not two (#5149).
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
// not two (#5149). `onFault` carries the engine's reason across that silence
|
|
115
|
+
// so the labelled report can still name it (objectui#3792); both probes fault
|
|
116
|
+
// identically, so the first reason is the reason.
|
|
117
|
+
let reason;
|
|
118
|
+
const onFault = (r) => {
|
|
119
|
+
reason ?? (reason = r);
|
|
120
|
+
};
|
|
121
|
+
const diagnostic = { warn: false, onFault };
|
|
122
|
+
const asTrue = evalFieldPredicate(pred, record, true, undefined, scope, diagnostic);
|
|
123
|
+
const asFalse = evalFieldPredicate(pred, record, false, undefined, scope, diagnostic);
|
|
97
124
|
if (asTrue !== asFalse)
|
|
98
|
-
return { ok: false, value: false };
|
|
125
|
+
return { ok: false, value: false, reason };
|
|
99
126
|
return { ok: true, value: asTrue };
|
|
100
127
|
}
|
|
101
128
|
/**
|
|
@@ -104,7 +131,9 @@ function evalCel(pred, record, scope) {
|
|
|
104
131
|
* fields are bound three ways so every authoring convention resolves:
|
|
105
132
|
* `record.status` (spec/canonical), bare `status` (row-action shorthand), and
|
|
106
133
|
* `data.status` (legacy). The optional `scope` (host predicate scope) is bound
|
|
107
|
-
* alongside so `features.*` / `user.*` predicates keep working
|
|
134
|
+
* alongside so `features.*` / `user.*` predicates keep working — but the row is
|
|
135
|
+
* the subject: `record` and `data` always name THIS row, on both dialect paths,
|
|
136
|
+
* even when the host scope carries keys of those names (objectui#3796).
|
|
108
137
|
*/
|
|
109
138
|
export function evalRowPredicate(pred, row, opts = {}) {
|
|
110
139
|
const fallback = opts.fallback ?? false;
|
|
@@ -117,20 +146,33 @@ export function evalRowPredicate(pred, row, opts = {}) {
|
|
|
117
146
|
// means one thing whether or not this surface expanded the column
|
|
118
147
|
// (objectui#3501). Returned by reference when there is nothing to collapse.
|
|
119
148
|
const rowObj = toPredicateRecord(row && typeof row === 'object' ? row : {}, opts.fields);
|
|
120
|
-
// Bare fields + `data.*` + the host scope, all top-level
|
|
121
|
-
//
|
|
122
|
-
|
|
149
|
+
// Bare fields + `data.*` + `record.*` + the host scope, all top-level.
|
|
150
|
+
//
|
|
151
|
+
// `data` AND `record` are pinned AFTER the spread, so a host scope that
|
|
152
|
+
// happens to carry either key is background and the ROW stays the subject of
|
|
153
|
+
// this function — on BOTH dialect paths. `data` always had that protection;
|
|
154
|
+
// `record` did not, and relied instead on each engine's own binding, which
|
|
155
|
+
// disagreed: the legacy evaluator re-pinned `record` (row won) while the CEL
|
|
156
|
+
// engine takes `extra` over its `record` binding (host scope won). Same
|
|
157
|
+
// function, same predicate text, opposite subjects — decided by whether the
|
|
158
|
+
// string happens to contain `===`/`${…}`, which no author is choosing
|
|
159
|
+
// deliberately (objectui#3796). Pinning here fixes both paths at the merge,
|
|
160
|
+
// independently of either engine's precedence.
|
|
161
|
+
const scope = { ...(opts.scope ?? {}), ...rowObj, data: rowObj, record: rowObj };
|
|
123
162
|
// Legacy-dialect *strings* route to the legacy engine (back-compat). The
|
|
124
163
|
// `{ dialect: 'cel', source }` envelope is never routed here — it is CEL.
|
|
125
164
|
if (source !== undefined && isLegacyDialectSource(source)) {
|
|
126
165
|
warnLegacyDialect(source);
|
|
127
166
|
try {
|
|
128
|
-
const evaluator = new ExpressionEvaluator(
|
|
167
|
+
const evaluator = new ExpressionEvaluator(scope);
|
|
129
168
|
return evaluator.evaluateCondition(source, { throwOnError: true });
|
|
130
169
|
}
|
|
131
|
-
catch {
|
|
132
|
-
|
|
133
|
-
|
|
170
|
+
catch (err) {
|
|
171
|
+
// The legacy engine reports by throwing, so its message IS the reason —
|
|
172
|
+
// the fail-closed report carries it for the same reason the CEL path does.
|
|
173
|
+
if (opts.warnOnError) {
|
|
174
|
+
warnEvalError(source, opts.label, `[legacy] ${err instanceof Error ? err.message : String(err)}`);
|
|
175
|
+
}
|
|
134
176
|
return fallback;
|
|
135
177
|
}
|
|
136
178
|
}
|
|
@@ -145,7 +187,7 @@ export function evalRowPredicate(pred, row, opts = {}) {
|
|
|
145
187
|
}
|
|
146
188
|
const verdict = evalCel(pred, rowObj, scope);
|
|
147
189
|
if (!verdict.ok) {
|
|
148
|
-
warnEvalError(source ?? '(expression)', opts.label);
|
|
190
|
+
warnEvalError(source ?? '(expression)', opts.label, verdict.reason);
|
|
149
191
|
return fallback;
|
|
150
192
|
}
|
|
151
193
|
return verdict.value;
|
|
@@ -44,7 +44,9 @@ export type EvaluatorPredicateInput = string | boolean | {
|
|
|
44
44
|
*
|
|
45
45
|
* Accepts:
|
|
46
46
|
* - `boolean` → returned as-is (predicate evaluation short-circuits).
|
|
47
|
-
* - `string` → wrapped as `${string}` (legacy DX
|
|
47
|
+
* - `string` → a BARE expression is wrapped as `${string}` (legacy DX
|
|
48
|
+
* shorthand); a string that is already a `${…}` template is ALREADY in the
|
|
49
|
+
* normalized shape and is returned untouched (objectui#3871, below).
|
|
48
50
|
* - `Expression` envelope `{ dialect, source }` (the normalized form
|
|
49
51
|
* `@objectstack/spec`'s `ExpressionInputSchema` produces for every
|
|
50
52
|
* authored predicate, including bare strings) → a `cel` dialect keeps
|
|
@@ -73,8 +75,54 @@ export type EvaluatorPredicateInput = string | boolean | {
|
|
|
73
75
|
* site. `@object-ui/react`'s `toPredicateInput` is a re-export of this function
|
|
74
76
|
* (since #3367; it used to be an independent twin held in step by a 14-shape
|
|
75
77
|
* normalization parity table, which is exactly the arrangement the paragraph
|
|
76
|
-
* above describes the failure mode of).
|
|
77
|
-
*
|
|
78
|
+
* above describes the failure mode of).
|
|
79
|
+
*
|
|
80
|
+
* ## Why an already-`${…}` string is NOT wrapped again (objectui#3871)
|
|
81
|
+
*
|
|
82
|
+
* `${…}` is one of the spellings this repo documents for a predicate
|
|
83
|
+
* (AGENTS.md §4: `hidden?: string; // expression: "${data.role != 'admin'}"`)
|
|
84
|
+
* AND one of the shapes {@link EvaluatorPredicateInput} lists as a normalized
|
|
85
|
+
* OUTPUT. Wrapping unconditionally therefore corrupted every value that was
|
|
86
|
+
* already normalized: `'${x}'` became `'${${x}}'`, which no longer matches
|
|
87
|
+
* `ExpressionEvaluator`'s single-template fast path (`/^\$\{([^}]+)\}$/` —
|
|
88
|
+
* `[^}]+` cannot cross the inner `}`), so it fell to the global replace, whose
|
|
89
|
+
* inner expression `'${x'` does not parse. The verdict then stopped depending
|
|
90
|
+
* on the predicate at all, in a direction set by the CALLER's error policy:
|
|
91
|
+
*
|
|
92
|
+
* - fail-soft callers (every `disabled` / `enabled` leg, and the `visible`
|
|
93
|
+
* legs of `action:group` / `action:icon` / `RelatedList`) got the original
|
|
94
|
+
* non-empty string back from the evaluator's `catch { return match }`, so
|
|
95
|
+
* `Boolean(…)` was a constant `true` — `disabled` greyed out forever,
|
|
96
|
+
* `visible` shown forever, `enabled` never disabling.
|
|
97
|
+
* - fail-closed callers (`throwOnError: true` — `action:button` / `action:bar`
|
|
98
|
+
* / `action:menu` / `DeclaredActionsBar` `visible`, and
|
|
99
|
+
* `ActionEngine.getActionsForLocation`) got a THROW, so the constant was
|
|
100
|
+
* the other one: the action was hidden forever, including when its
|
|
101
|
+
* predicate held.
|
|
102
|
+
*
|
|
103
|
+
* Either way the author's expression was never consulted. The guard is
|
|
104
|
+
* `includes('${')` — "does this string already carry template syntax", the
|
|
105
|
+
* same question `evaluateCondition` itself asks before choosing the template
|
|
106
|
+
* path — not an anchored full match: a multi-part string like `'a ${x} b'` is
|
|
107
|
+
* not a predicate in any spelling, and wrapping it produced the same constant
|
|
108
|
+
* verdict, so the narrower guard would buy nothing and would still corrupt a
|
|
109
|
+
* value the evaluator can read.
|
|
110
|
+
*
|
|
111
|
+
* The same guard covers the envelope branch below, where the unwrapped
|
|
112
|
+
* `source` reaches the identical wrap one line later. It is one defect with two
|
|
113
|
+
* spellings, and `{ dialect: 'template', source: '${x}' }` is the shape a
|
|
114
|
+
* producer that compiles an authored template emits. A bare `source` (what
|
|
115
|
+
* `@objectstack/spec`'s `ExpressionInputSchema` produces today, pinned in
|
|
116
|
+
* `packages/react/src/hooks/__tests__/useExpression.test.ts`) is unaffected.
|
|
117
|
+
*
|
|
118
|
+
* What this does NOT do is decide whether `${…}` SHOULD be authorable on an
|
|
119
|
+
* action predicate: `@objectstack/spec`'s `PredicateInput` models a bare string
|
|
120
|
+
* and a dialect envelope, not the template spelling. Rejecting it loudly at
|
|
121
|
+
* publish time is a spec-side ruling (deferred to the objectstack seat by the
|
|
122
|
+
* objectui#3871 dispatch); until then this normalizer must not silently invent
|
|
123
|
+
* a verdict for a spelling both the docs and this file's own output type bless.
|
|
124
|
+
*
|
|
125
|
+
* What pins the single implementation is the identity assertion in
|
|
78
126
|
* `packages/react/src/hooks/__tests__/actionPredicate.parity.test.tsx` — the
|
|
79
127
|
* react export must BE this function object — alongside the engine-path vs
|
|
80
128
|
* renderer-path verdict parity suite in the same file, which is a separate
|
|
@@ -5,6 +5,15 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* Wrap a BARE expression as a `${…}` template, and leave a string that already
|
|
10
|
+
* carries template syntax alone (objectui#3871 — rationale in
|
|
11
|
+
* {@link toPredicateInput}'s doc below). One rule, so the two string spellings
|
|
12
|
+
* inside the normalizer cannot drift apart.
|
|
13
|
+
*/
|
|
14
|
+
function wrapIfBare(expression) {
|
|
15
|
+
return expression.includes('${') ? expression : `\${${expression}}`;
|
|
16
|
+
}
|
|
8
17
|
/**
|
|
9
18
|
* Normalize a schema-supplied predicate (`visible` / `enabled` / `disabled` /
|
|
10
19
|
* `hidden`) into the form {@link ExpressionEvaluator.evaluateCondition}
|
|
@@ -12,7 +21,9 @@
|
|
|
12
21
|
*
|
|
13
22
|
* Accepts:
|
|
14
23
|
* - `boolean` → returned as-is (predicate evaluation short-circuits).
|
|
15
|
-
* - `string` → wrapped as `${string}` (legacy DX
|
|
24
|
+
* - `string` → a BARE expression is wrapped as `${string}` (legacy DX
|
|
25
|
+
* shorthand); a string that is already a `${…}` template is ALREADY in the
|
|
26
|
+
* normalized shape and is returned untouched (objectui#3871, below).
|
|
16
27
|
* - `Expression` envelope `{ dialect, source }` (the normalized form
|
|
17
28
|
* `@objectstack/spec`'s `ExpressionInputSchema` produces for every
|
|
18
29
|
* authored predicate, including bare strings) → a `cel` dialect keeps
|
|
@@ -41,8 +52,54 @@
|
|
|
41
52
|
* site. `@object-ui/react`'s `toPredicateInput` is a re-export of this function
|
|
42
53
|
* (since #3367; it used to be an independent twin held in step by a 14-shape
|
|
43
54
|
* normalization parity table, which is exactly the arrangement the paragraph
|
|
44
|
-
* above describes the failure mode of).
|
|
45
|
-
*
|
|
55
|
+
* above describes the failure mode of).
|
|
56
|
+
*
|
|
57
|
+
* ## Why an already-`${…}` string is NOT wrapped again (objectui#3871)
|
|
58
|
+
*
|
|
59
|
+
* `${…}` is one of the spellings this repo documents for a predicate
|
|
60
|
+
* (AGENTS.md §4: `hidden?: string; // expression: "${data.role != 'admin'}"`)
|
|
61
|
+
* AND one of the shapes {@link EvaluatorPredicateInput} lists as a normalized
|
|
62
|
+
* OUTPUT. Wrapping unconditionally therefore corrupted every value that was
|
|
63
|
+
* already normalized: `'${x}'` became `'${${x}}'`, which no longer matches
|
|
64
|
+
* `ExpressionEvaluator`'s single-template fast path (`/^\$\{([^}]+)\}$/` —
|
|
65
|
+
* `[^}]+` cannot cross the inner `}`), so it fell to the global replace, whose
|
|
66
|
+
* inner expression `'${x'` does not parse. The verdict then stopped depending
|
|
67
|
+
* on the predicate at all, in a direction set by the CALLER's error policy:
|
|
68
|
+
*
|
|
69
|
+
* - fail-soft callers (every `disabled` / `enabled` leg, and the `visible`
|
|
70
|
+
* legs of `action:group` / `action:icon` / `RelatedList`) got the original
|
|
71
|
+
* non-empty string back from the evaluator's `catch { return match }`, so
|
|
72
|
+
* `Boolean(…)` was a constant `true` — `disabled` greyed out forever,
|
|
73
|
+
* `visible` shown forever, `enabled` never disabling.
|
|
74
|
+
* - fail-closed callers (`throwOnError: true` — `action:button` / `action:bar`
|
|
75
|
+
* / `action:menu` / `DeclaredActionsBar` `visible`, and
|
|
76
|
+
* `ActionEngine.getActionsForLocation`) got a THROW, so the constant was
|
|
77
|
+
* the other one: the action was hidden forever, including when its
|
|
78
|
+
* predicate held.
|
|
79
|
+
*
|
|
80
|
+
* Either way the author's expression was never consulted. The guard is
|
|
81
|
+
* `includes('${')` — "does this string already carry template syntax", the
|
|
82
|
+
* same question `evaluateCondition` itself asks before choosing the template
|
|
83
|
+
* path — not an anchored full match: a multi-part string like `'a ${x} b'` is
|
|
84
|
+
* not a predicate in any spelling, and wrapping it produced the same constant
|
|
85
|
+
* verdict, so the narrower guard would buy nothing and would still corrupt a
|
|
86
|
+
* value the evaluator can read.
|
|
87
|
+
*
|
|
88
|
+
* The same guard covers the envelope branch below, where the unwrapped
|
|
89
|
+
* `source` reaches the identical wrap one line later. It is one defect with two
|
|
90
|
+
* spellings, and `{ dialect: 'template', source: '${x}' }` is the shape a
|
|
91
|
+
* producer that compiles an authored template emits. A bare `source` (what
|
|
92
|
+
* `@objectstack/spec`'s `ExpressionInputSchema` produces today, pinned in
|
|
93
|
+
* `packages/react/src/hooks/__tests__/useExpression.test.ts`) is unaffected.
|
|
94
|
+
*
|
|
95
|
+
* What this does NOT do is decide whether `${…}` SHOULD be authorable on an
|
|
96
|
+
* action predicate: `@objectstack/spec`'s `PredicateInput` models a bare string
|
|
97
|
+
* and a dialect envelope, not the template spelling. Rejecting it loudly at
|
|
98
|
+
* publish time is a spec-side ruling (deferred to the objectstack seat by the
|
|
99
|
+
* objectui#3871 dispatch); until then this normalizer must not silently invent
|
|
100
|
+
* a verdict for a spelling both the docs and this file's own output type bless.
|
|
101
|
+
*
|
|
102
|
+
* What pins the single implementation is the identity assertion in
|
|
46
103
|
* `packages/react/src/hooks/__tests__/actionPredicate.parity.test.tsx` — the
|
|
47
104
|
* react export must BE this function object — alongside the engine-path vs
|
|
48
105
|
* renderer-path verdict parity suite in the same file, which is a separate
|
|
@@ -55,7 +112,7 @@ export function toPredicateInput(value) {
|
|
|
55
112
|
if (typeof value === 'boolean')
|
|
56
113
|
return value;
|
|
57
114
|
if (typeof value === 'string')
|
|
58
|
-
return
|
|
115
|
+
return wrapIfBare(value);
|
|
59
116
|
if (typeof value === 'object' && typeof value.source === 'string') {
|
|
60
117
|
const src = value.source;
|
|
61
118
|
if (!src)
|
|
@@ -66,7 +123,7 @@ export function toPredicateInput(value) {
|
|
|
66
123
|
if (value.dialect === 'cel')
|
|
67
124
|
return { dialect: 'cel', source: src };
|
|
68
125
|
// Every other dialect (template / unset) keeps the legacy `${…}` behavior.
|
|
69
|
-
return
|
|
126
|
+
return wrapIfBare(src);
|
|
70
127
|
}
|
|
71
128
|
return undefined;
|
|
72
129
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,12 +13,15 @@ export * from './registry/PluginScopeImpl.js';
|
|
|
13
13
|
export * from './registry/WidgetRegistry.js';
|
|
14
14
|
export * from './validation/index.js';
|
|
15
15
|
export * from './builder/schema-builder.js';
|
|
16
|
+
export * from './utils/dom-props.js';
|
|
16
17
|
export * from './utils/filter-converter.js';
|
|
17
18
|
export * from './utils/managedBy.js';
|
|
18
19
|
export * from './utils/extract-records.js';
|
|
19
20
|
export * from './utils/expand-fields.js';
|
|
20
21
|
export * from './utils/column-identity.js';
|
|
21
22
|
export * from './utils/sort-values.js';
|
|
23
|
+
export * from './utils/sort-query.js';
|
|
24
|
+
export * from './utils/resolve-view-id.js';
|
|
22
25
|
export * from './evaluator/index.js';
|
|
23
26
|
export * from './actions/index.js';
|
|
24
27
|
export * from './query/index.js';
|
|
@@ -28,7 +31,6 @@ export * from './data-scope/index.js';
|
|
|
28
31
|
export * from './errors/index.js';
|
|
29
32
|
export * from './utils/debug.js';
|
|
30
33
|
export * from './utils/debug-collector.js';
|
|
31
|
-
export * from './utils/merge-views-into-objects.js';
|
|
32
34
|
export * from './utils/freeze-schema.js';
|
|
33
35
|
export * from './protocols/index.js';
|
|
34
36
|
export * from './styling/scoped-styles.js';
|
|
@@ -47,6 +49,7 @@ export * from './utils/dashboard-filters.js';
|
|
|
47
49
|
export * from './utils/merge-filters.js';
|
|
48
50
|
export * from './utils/compare-to.js';
|
|
49
51
|
export * from './utils/chart-series.js';
|
|
52
|
+
export * from './utils/number-display.js';
|
|
50
53
|
export * from './utils/dataset-format.js';
|
|
51
54
|
export * from './utils/dataset-pivot.js';
|
|
52
55
|
export * from './utils/record-title.js';
|
package/dist/index.js
CHANGED
|
@@ -12,12 +12,20 @@ export * from './registry/PluginScopeImpl.js';
|
|
|
12
12
|
export * from './registry/WidgetRegistry.js';
|
|
13
13
|
export * from './validation/index.js';
|
|
14
14
|
export * from './builder/schema-builder.js';
|
|
15
|
+
// The DOM pass-through whitelist of the SDUI widget prop contract
|
|
16
|
+
// (objectui#4425 phase 2): a registered widget's host element receives only
|
|
17
|
+
// what `toDomProps` passes — everything else is consumed or dropped.
|
|
18
|
+
// `@object-ui/fields` executes the SAME mechanism against its own declared key
|
|
19
|
+
// list (`pickDomProps`), so there is one judge, not two.
|
|
20
|
+
export * from './utils/dom-props.js';
|
|
15
21
|
export * from './utils/filter-converter.js';
|
|
16
22
|
export * from './utils/managedBy.js';
|
|
17
23
|
export * from './utils/extract-records.js';
|
|
18
24
|
export * from './utils/expand-fields.js';
|
|
19
25
|
export * from './utils/column-identity.js';
|
|
20
26
|
export * from './utils/sort-values.js';
|
|
27
|
+
export * from './utils/sort-query.js';
|
|
28
|
+
export * from './utils/resolve-view-id.js';
|
|
21
29
|
export * from './evaluator/index.js';
|
|
22
30
|
export * from './actions/index.js';
|
|
23
31
|
export * from './query/index.js';
|
|
@@ -27,7 +35,6 @@ export * from './data-scope/index.js';
|
|
|
27
35
|
export * from './errors/index.js';
|
|
28
36
|
export * from './utils/debug.js';
|
|
29
37
|
export * from './utils/debug-collector.js';
|
|
30
|
-
export * from './utils/merge-views-into-objects.js';
|
|
31
38
|
export * from './utils/freeze-schema.js';
|
|
32
39
|
export * from './protocols/index.js';
|
|
33
40
|
export * from './styling/scoped-styles.js';
|
|
@@ -49,6 +56,12 @@ export * from './utils/dashboard-filters.js';
|
|
|
49
56
|
export * from './utils/merge-filters.js';
|
|
50
57
|
export * from './utils/compare-to.js';
|
|
51
58
|
export * from './utils/chart-series.js';
|
|
59
|
+
// The ONE number-display formatter (objectui#4033) — grouping policy, display
|
|
60
|
+
// locale and the percent convention. It lived in `@object-ui/i18n` until
|
|
61
|
+
// objectui#4576; it is pure, and living above `core` was what kept
|
|
62
|
+
// `dataset-format` below from reaching it (so the two drifted). `@object-ui/i18n`
|
|
63
|
+
// re-exports these names unchanged, so both import paths name the same symbol.
|
|
64
|
+
export * from './utils/number-display.js';
|
|
52
65
|
export * from './utils/dataset-format.js';
|
|
53
66
|
// Pivot lookup-key encoders, shared by every cross-tab renderer so the
|
|
54
67
|
// dashboard widget and the report renderer key their buckets identically
|
|
@@ -14,7 +14,48 @@
|
|
|
14
14
|
* @module protocols/DndProtocol
|
|
15
15
|
* @packageDocumentation
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
/** How a drag is initiated. */
|
|
18
|
+
export type DragHandle = 'element' | 'handle' | 'grip_icon';
|
|
19
|
+
/** The `dropEffect` advertised to assistive technology and DnD libraries. */
|
|
20
|
+
export type DropEffect = 'none' | 'link' | 'move' | 'copy';
|
|
21
|
+
/** Movement constraints applied to a drag. */
|
|
22
|
+
export interface DragConstraint {
|
|
23
|
+
axis: 'x' | 'y' | 'both';
|
|
24
|
+
bounds: 'none' | 'parent' | 'viewport';
|
|
25
|
+
grid?: [number, number];
|
|
26
|
+
}
|
|
27
|
+
/** A draggable item's declaration. */
|
|
28
|
+
export interface DragItem {
|
|
29
|
+
type: string;
|
|
30
|
+
label?: string;
|
|
31
|
+
handle: DragHandle;
|
|
32
|
+
constraint?: DragConstraint;
|
|
33
|
+
preview: 'custom' | 'none' | 'element';
|
|
34
|
+
disabled: boolean;
|
|
35
|
+
ariaLabel?: string;
|
|
36
|
+
ariaDescribedBy?: string;
|
|
37
|
+
role?: string;
|
|
38
|
+
}
|
|
39
|
+
/** A drop target's declaration. */
|
|
40
|
+
export interface DropZone {
|
|
41
|
+
label?: string;
|
|
42
|
+
accept: string[];
|
|
43
|
+
maxItems?: number;
|
|
44
|
+
highlightOnDragOver: boolean;
|
|
45
|
+
dropEffect: DropEffect;
|
|
46
|
+
ariaLabel?: string;
|
|
47
|
+
ariaDescribedBy?: string;
|
|
48
|
+
role?: string;
|
|
49
|
+
}
|
|
50
|
+
/** Top-level drag-and-drop configuration. */
|
|
51
|
+
export interface DndConfig {
|
|
52
|
+
enabled: boolean;
|
|
53
|
+
dragItem?: DragItem;
|
|
54
|
+
dropZone?: DropZone;
|
|
55
|
+
sortable: boolean;
|
|
56
|
+
autoScroll: boolean;
|
|
57
|
+
touchDelay: number;
|
|
58
|
+
}
|
|
18
59
|
/** Fully resolved DnD configuration with all defaults applied. */
|
|
19
60
|
export interface ResolvedDndConfig {
|
|
20
61
|
enabled: boolean;
|
|
@@ -15,7 +15,37 @@
|
|
|
15
15
|
* @module protocols/KeyboardProtocol
|
|
16
16
|
* @packageDocumentation
|
|
17
17
|
*/
|
|
18
|
-
|
|
18
|
+
/** A single keyboard shortcut binding. */
|
|
19
|
+
export interface KeyboardShortcut {
|
|
20
|
+
key: string;
|
|
21
|
+
action: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
scope: 'global' | 'form' | 'modal' | 'view' | 'list';
|
|
24
|
+
}
|
|
25
|
+
/** Focus trap declaration for a modal-like region. */
|
|
26
|
+
export interface FocusTrapConfig {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
initialFocus?: string;
|
|
29
|
+
returnFocus: boolean;
|
|
30
|
+
escapeDeactivates: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** Focus management declaration. */
|
|
33
|
+
export interface FocusManagement {
|
|
34
|
+
tabOrder: 'manual' | 'auto';
|
|
35
|
+
skipLinks: boolean;
|
|
36
|
+
focusVisible: boolean;
|
|
37
|
+
focusTrap?: FocusTrapConfig;
|
|
38
|
+
arrowNavigation: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** Top-level keyboard navigation configuration. */
|
|
41
|
+
export interface KeyboardNavigationConfig {
|
|
42
|
+
shortcuts?: KeyboardShortcut[];
|
|
43
|
+
focusManagement?: FocusManagement;
|
|
44
|
+
rovingTabindex: boolean;
|
|
45
|
+
ariaLabel?: string;
|
|
46
|
+
ariaDescribedBy?: string;
|
|
47
|
+
role?: string;
|
|
48
|
+
}
|
|
19
49
|
/** Fully resolved keyboard navigation configuration. */
|
|
20
50
|
export interface ResolvedKeyboardConfig {
|
|
21
51
|
shortcuts: KeyboardShortcut[];
|
|
@@ -15,7 +15,17 @@
|
|
|
15
15
|
* @module protocols/SharingProtocol
|
|
16
16
|
* @packageDocumentation
|
|
17
17
|
*/
|
|
18
|
-
import type { SharingConfig
|
|
18
|
+
import type { SharingConfig } from '@object-ui/types';
|
|
19
|
+
/** An iframe embed declaration. */
|
|
20
|
+
export interface EmbedConfig {
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
allowedOrigins?: string[];
|
|
23
|
+
width?: string;
|
|
24
|
+
height?: string;
|
|
25
|
+
showHeader?: boolean;
|
|
26
|
+
showNavigation?: boolean;
|
|
27
|
+
responsive?: boolean;
|
|
28
|
+
}
|
|
19
29
|
/** Fully resolved sharing configuration. */
|
|
20
30
|
export interface ResolvedSharingConfig {
|
|
21
31
|
enabled: boolean;
|
|
@@ -43,6 +43,30 @@ export type ComponentMeta = {
|
|
|
43
43
|
* registry.register('form', FormView, { namespace: 'view', skipFallback: true });
|
|
44
44
|
*/
|
|
45
45
|
skipFallback?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* How a HOST must associate its own visible label with what this component
|
|
48
|
+
* renders (objectui#3961). Read by the form renderer; absent ⇒ `'control'`.
|
|
49
|
+
*
|
|
50
|
+
* - `'control'` — the component's outermost rendered element is a LABELABLE
|
|
51
|
+
* HTML element (`input` / `textarea` / `select` / `button` / …), so the host
|
|
52
|
+
* associates its label the plain way: `<label for>` → the id the host handed
|
|
53
|
+
* down. This is the default and covers every single-control widget.
|
|
54
|
+
* - `'group'` — it is NOT labelable: either a real composite (several inputs
|
|
55
|
+
* under one container, e.g. `address`) or a single control that is not a
|
|
56
|
+
* labelable element (a `div[role="button"]` dropzone, e.g. `file`). A
|
|
57
|
+
* `<label for>` pointing at such an element is inert in HTML — it activates
|
|
58
|
+
* nothing and contributes no accessible name (`HTMLLabelElement.control` is
|
|
59
|
+
* `null`) — so the host must instead give its label an `id` and hand the
|
|
60
|
+
* component `aria-labelledby`, which associates by IDREF and works on any
|
|
61
|
+
* element.
|
|
62
|
+
*
|
|
63
|
+
* This is a DECLARATION, not a guess: the host cannot infer it from the DOM a
|
|
64
|
+
* widget happens to render, and a widget that fails to declare it falls back
|
|
65
|
+
* to the `'control'` path where the dangling/inert `for` is caught by the
|
|
66
|
+
* label-association tests (objectui#3952) instead of silently producing an
|
|
67
|
+
* unlabelled group.
|
|
68
|
+
*/
|
|
69
|
+
labelling?: 'control' | 'group';
|
|
46
70
|
inputs?: ComponentInput[];
|
|
47
71
|
defaultProps?: Record<string, any>;
|
|
48
72
|
defaultChildren?: SchemaNode[];
|
|
@@ -43,14 +43,6 @@ export declare function generateBorderRadiusVars(borderRadius: NonNullable<Theme
|
|
|
43
43
|
* Generate CSS custom properties from a Theme's shadow config.
|
|
44
44
|
*/
|
|
45
45
|
export declare function generateShadowVars(shadows: NonNullable<Theme['shadows']>): Record<string, string>;
|
|
46
|
-
/**
|
|
47
|
-
* Generate CSS custom properties from a Theme's animation config.
|
|
48
|
-
*/
|
|
49
|
-
export declare function generateAnimationVars(animation: NonNullable<Theme['animation']>): Record<string, string>;
|
|
50
|
-
/**
|
|
51
|
-
* Generate CSS custom properties from a Theme's z-index config.
|
|
52
|
-
*/
|
|
53
|
-
export declare function generateZIndexVars(zIndex: NonNullable<Theme['zIndex']>): Record<string, string>;
|
|
54
46
|
/**
|
|
55
47
|
* Generate ALL CSS custom properties from a complete Theme.
|
|
56
48
|
* This is the main entry point for theme → CSS conversion.
|