@object-ui/core 17.2.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 +1436 -0
- package/README.md +31 -12
- package/dist/actions/ActionEngine.d.ts +53 -31
- package/dist/actions/ActionEngine.js +87 -66
- package/dist/actions/ActionRunner.d.ts +215 -24
- package/dist/actions/ActionRunner.js +334 -17
- package/dist/actions/actionErrorDetail.d.ts +26 -0
- package/dist/actions/actionErrorDetail.js +37 -0
- package/dist/actions/actionKeys.d.ts +96 -20
- package/dist/actions/actionKeys.js +168 -18
- package/dist/actions/actionResponse.d.ts +42 -0
- package/dist/actions/actionResponse.js +104 -0
- package/dist/actions/index.d.ts +3 -0
- package/dist/actions/index.js +3 -0
- package/dist/actions/serverActionHandler.d.ts +143 -0
- package/dist/actions/serverActionHandler.js +137 -0
- 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/ExpressionEvaluator.js +4 -2
- package/dist/evaluator/declaredPredicate.d.ts +119 -0
- package/dist/evaluator/declaredPredicate.js +152 -0
- package/dist/evaluator/fieldRules.d.ts +42 -2
- package/dist/evaluator/fieldRules.js +101 -11
- package/dist/evaluator/index.d.ts +2 -0
- package/dist/evaluator/index.js +2 -0
- package/dist/evaluator/listConditional.d.ts +18 -3
- package/dist/evaluator/listConditional.js +0 -0
- package/dist/evaluator/optionRules.js +5 -1
- package/dist/evaluator/predicateInput.d.ts +132 -0
- package/dist/evaluator/predicateInput.js +129 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +26 -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/protocols/index.d.ts +0 -1
- package/dist/protocols/index.js +8 -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 +79 -15
- package/dist/utils/dataset-format.js +130 -9
- package/dist/utils/dataset-pivot.d.ts +78 -0
- package/dist/utils/dataset-pivot.js +78 -0
- package/dist/utils/dom-props.d.ts +137 -0
- package/dist/utils/dom-props.js +153 -0
- package/dist/utils/filter-converter.d.ts +39 -11
- package/dist/utils/filter-converter.js +87 -7
- 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/predicate-fields.d.ts +96 -0
- package/dist/utils/predicate-fields.js +163 -0
- package/dist/utils/predicate-record.d.ts +32 -0
- package/dist/utils/predicate-record.js +120 -0
- package/dist/utils/record-title.js +0 -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/protocols/NotificationProtocol.d.ts +0 -71
- package/dist/protocols/NotificationProtocol.js +0 -89
- package/dist/utils/merge-views-into-objects.d.ts +0 -19
- package/dist/utils/merge-views-into-objects.js +0 -39
|
@@ -22,8 +22,46 @@
|
|
|
22
22
|
* CEL (mirrors the server's `toExpression`). Evaluation is *fail-open* for
|
|
23
23
|
* visibility/required (a broken predicate must not hide a field or wrongly
|
|
24
24
|
* block submit) and *fail-open* for readonly (a broken predicate leaves the
|
|
25
|
-
* field editable)
|
|
26
|
-
*
|
|
25
|
+
* field editable).
|
|
26
|
+
*
|
|
27
|
+
* Fail-open agrees with the server for MOST faults — but not for all, and the
|
|
28
|
+
* exception is the half worth knowing. Since objectstack#4889 a `readonlyWhen`
|
|
29
|
+
* predicate that faults because it names a scope ROOT the write never bound —
|
|
30
|
+
* `parent.status == 'paid'` with no master-detail header in hand — is
|
|
31
|
+
* fail-CLOSED on the server: `isReadonlyWhenLocked` warns and then returns
|
|
32
|
+
* TRUE, i.e. LOCKED (a declared lock is not waived merely because it could not
|
|
33
|
+
* be evaluated), and `stripReadonlyWhenFields` — plus
|
|
34
|
+
* `stripReadonlyWhenFieldsMulti` on the bulk path — DELETES that key from the
|
|
35
|
+
* UPDATE payload. This file keeps the same fault fail-OPEN. For that one class
|
|
36
|
+
* the two ends therefore point in OPPOSITE directions, deliberately: the
|
|
37
|
+
* framework's ADR-0057 D10 — server enforces, client is courtesy (framework
|
|
38
|
+
* numbering; this repo's own ADR-0057 is an unrelated document) — makes the
|
|
39
|
+
* server the authority, so the courtesy layer does not get to guess "locked"
|
|
40
|
+
* and grey out a field the server might have accepted.
|
|
41
|
+
*
|
|
42
|
+
* The caller-visible consequence, spelled out because the symptom is silent:
|
|
43
|
+
* the form renders the field EDITABLE, the user edits it, the save reports
|
|
44
|
+
* SUCCESS, and the new value never lands — the server dropped the key and kept
|
|
45
|
+
* the persisted one. Debug that toward the SERVER-side lock (its `… treating
|
|
46
|
+
* the field as LOCKED` warning, and the write response's `droppedFields`), not
|
|
47
|
+
* toward the client predicate, which did exactly what it was asked to. The
|
|
48
|
+
* other two halves carry no such carve-out: `requiredWhen` binds the same
|
|
49
|
+
* `parent` scope but deliberately kept fail-open semantics (objectstack#4977),
|
|
50
|
+
* so an unevaluable requirement is skipped on BOTH ends, and field-level
|
|
51
|
+
* `visibleWhen` is not a server concept at all.
|
|
52
|
+
*
|
|
53
|
+
* Fail-open is **loud**, not silent (objectstack#5149): a predicate that
|
|
54
|
+
* cannot be evaluated — parse error, unbound identifier, engine fault — logs
|
|
55
|
+
* one `console.warn` per predicate text with the source and the failure
|
|
56
|
+
* reason, then still returns the caller's fallback. Without the warning a
|
|
57
|
+
* broken predicate is indistinguishable from an absent one, so
|
|
58
|
+
* conditional-visibility bugs survive inspection indefinitely. This is the
|
|
59
|
+
* client half of the server's "log and allow" convention for the faults where
|
|
60
|
+
* the two ends DO agree (`readonlyWhen for 'x' failed to evaluate — change
|
|
61
|
+
* allowed through` — the GENERIC-fault message; the unbound-root fault above
|
|
62
|
+
* logs `treating the field as LOCKED` instead). The *default* stays
|
|
63
|
+
* fail-open on purpose — flipping it is a shipped-behavior change tracked
|
|
64
|
+
* separately in objectstack#5149 (appeal 1, undecided).
|
|
27
65
|
*/
|
|
28
66
|
import { ExpressionEngine } from '@objectstack/formula';
|
|
29
67
|
/** Normalize a predicate into the `Expression` shape the engine expects. */
|
|
@@ -32,6 +70,26 @@ function toExpression(pred) {
|
|
|
32
70
|
return { dialect: 'cel', source: pred };
|
|
33
71
|
return { dialect: (pred.dialect ?? 'cel'), source: pred.source };
|
|
34
72
|
}
|
|
73
|
+
const warnedPredicates = new Set();
|
|
74
|
+
/**
|
|
75
|
+
* One-time warning for a predicate that could not be evaluated. Deduped per
|
|
76
|
+
* predicate TEXT (dialect + source): a broken predicate is re-evaluated on
|
|
77
|
+
* every render/keystroke, and the point is one loud line, not a scrolling
|
|
78
|
+
* wall. The dedupe key is JSON-encoded — never a control-character separator
|
|
79
|
+
* (objectstack#5450 made a sibling of this file binary to grep that way).
|
|
80
|
+
*/
|
|
81
|
+
function warnPredicateFailure(expr, fallback, reason, context) {
|
|
82
|
+
const key = JSON.stringify([expr.dialect, expr.source]);
|
|
83
|
+
if (warnedPredicates.has(key))
|
|
84
|
+
return;
|
|
85
|
+
warnedPredicates.add(key);
|
|
86
|
+
console.warn('[object-ui] A conditional predicate failed to evaluate' +
|
|
87
|
+
(context ? ` (${context})` : '') +
|
|
88
|
+
` and was treated as its safe default (${String(fallback)}): ` +
|
|
89
|
+
JSON.stringify(expr.source) +
|
|
90
|
+
`. Reason: ${reason}. ` +
|
|
91
|
+
"Values are bound under 'record.' (e.g. record.status) — check the field names and CEL syntax.");
|
|
92
|
+
}
|
|
35
93
|
/**
|
|
36
94
|
* Evaluate a field-rule CEL predicate against a record.
|
|
37
95
|
*
|
|
@@ -46,23 +104,47 @@ function toExpression(pred) {
|
|
|
46
104
|
* e.g. `{ parent }` so an inline line-item cell can reference
|
|
47
105
|
* its header (`parent.status == 'paid'`) as well as its own
|
|
48
106
|
* row (`record.quantity`). Bound via the engine's `extra`.
|
|
107
|
+
* @param diagnostic Failure-warning options: a `context` locator for the
|
|
108
|
+
* one-time warning, and `warn: false` for callers that probe
|
|
109
|
+
* for faults and report them themselves. The returned value
|
|
110
|
+
* is unaffected either way.
|
|
49
111
|
*/
|
|
50
|
-
export function evalFieldPredicate(pred, record, fallback, previous, scope) {
|
|
112
|
+
export function evalFieldPredicate(pred, record, fallback, previous, scope, diagnostic) {
|
|
51
113
|
if (pred == null || (typeof pred === 'string' && !pred.trim()))
|
|
52
114
|
return fallback;
|
|
115
|
+
const expr = toExpression(pred);
|
|
116
|
+
// The two fault sources — a not-ok verdict and a throw that slipped past the
|
|
117
|
+
// engine's "never throws" contract — converge on one reason string and one
|
|
118
|
+
// reporting site below, so the built-in warning and the `onFault` passback
|
|
119
|
+
// can never describe the failure differently.
|
|
120
|
+
let reason;
|
|
121
|
+
let value = fallback;
|
|
53
122
|
try {
|
|
54
|
-
const res = ExpressionEngine.evaluate(
|
|
123
|
+
const res = ExpressionEngine.evaluate(expr, {
|
|
55
124
|
record,
|
|
56
125
|
previous,
|
|
57
126
|
...(scope ? { extra: scope } : {}),
|
|
58
127
|
});
|
|
128
|
+
// Parse error, type error, unbound identifier, engine fault … — every
|
|
129
|
+
// not-ok verdict resolves to the fallback, but never silently (#5149).
|
|
59
130
|
if (!res.ok)
|
|
60
|
-
|
|
61
|
-
|
|
131
|
+
reason = `[${res.error.kind}] ${res.error.message}`;
|
|
132
|
+
else
|
|
133
|
+
value = res.value === true;
|
|
62
134
|
}
|
|
63
|
-
catch {
|
|
135
|
+
catch (err) {
|
|
136
|
+
reason = `[throw] ${err instanceof Error ? err.message : String(err)}`;
|
|
137
|
+
}
|
|
138
|
+
if (reason !== undefined) {
|
|
139
|
+
if (diagnostic?.warn !== false) {
|
|
140
|
+
warnPredicateFailure(expr, fallback, reason, diagnostic?.context);
|
|
141
|
+
}
|
|
142
|
+
// Outside the try on purpose: a throwing callback is a caller bug and must
|
|
143
|
+
// surface as one, not be swallowed and re-reported as an engine fault.
|
|
144
|
+
diagnostic?.onFault?.(reason);
|
|
64
145
|
return fallback;
|
|
65
146
|
}
|
|
147
|
+
return value;
|
|
66
148
|
}
|
|
67
149
|
/**
|
|
68
150
|
* Resolve the effective `{ visible, readonly, required }` state for a field
|
|
@@ -70,18 +152,26 @@ export function evalFieldPredicate(pred, record, fallback, previous, scope) {
|
|
|
70
152
|
* present, *overrides* the static flag. A static `true` is never weakened by a
|
|
71
153
|
* `false` predicate result for required/readonly — but `visibleWhen` is
|
|
72
154
|
* authoritative when present (so a field can be conditionally shown/hidden).
|
|
155
|
+
*
|
|
156
|
+
* @param fieldContext Optional locator for failure warnings — e.g.
|
|
157
|
+
* `"field 'amount'"`. The warning then reads
|
|
158
|
+
* `visibleWhen of field 'amount' …`; without it, the rule
|
|
159
|
+
* kind alone is reported.
|
|
73
160
|
*/
|
|
74
|
-
export function resolveFieldRuleState(rules, record, statics, previous, scope) {
|
|
161
|
+
export function resolveFieldRuleState(rules, record, statics, previous, scope, fieldContext) {
|
|
162
|
+
const diag = (rule) => ({
|
|
163
|
+
context: fieldContext ? `${rule} of ${fieldContext}` : rule,
|
|
164
|
+
});
|
|
75
165
|
const visible = rules.visibleWhen != null
|
|
76
|
-
? evalFieldPredicate(rules.visibleWhen, record, true, previous, scope)
|
|
166
|
+
? evalFieldPredicate(rules.visibleWhen, record, true, previous, scope, diag('visibleWhen'))
|
|
77
167
|
: true;
|
|
78
168
|
const readonly = statics.readonly === true ||
|
|
79
169
|
(rules.readonlyWhen != null
|
|
80
|
-
? evalFieldPredicate(rules.readonlyWhen, record, false, previous, scope)
|
|
170
|
+
? evalFieldPredicate(rules.readonlyWhen, record, false, previous, scope, diag('readonlyWhen'))
|
|
81
171
|
: false);
|
|
82
172
|
const required = statics.required === true ||
|
|
83
173
|
(rules.requiredWhen != null
|
|
84
|
-
? evalFieldPredicate(rules.requiredWhen, record, false, previous, scope)
|
|
174
|
+
? evalFieldPredicate(rules.requiredWhen, record, false, previous, scope, diag('requiredWhen'))
|
|
85
175
|
: false);
|
|
86
176
|
return { visible, readonly, required };
|
|
87
177
|
}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export * from './ExpressionContext.js';
|
|
9
9
|
export * from './ExpressionEvaluator.js';
|
|
10
|
+
export * from './predicateInput.js';
|
|
11
|
+
export * from './declaredPredicate.js';
|
|
10
12
|
export * from './fieldRules.js';
|
|
11
13
|
export * from './listConditional.js';
|
|
12
14
|
export * from './optionRules.js';
|
package/dist/evaluator/index.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export * from './ExpressionContext.js';
|
|
9
9
|
export * from './ExpressionEvaluator.js';
|
|
10
|
+
export * from './predicateInput.js';
|
|
11
|
+
export * from './declaredPredicate.js';
|
|
10
12
|
export * from './fieldRules.js';
|
|
11
13
|
export * from './listConditional.js';
|
|
12
14
|
export * from './optionRules.js';
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
* (or nothing) to the server's CEL engine.
|
|
35
35
|
*/
|
|
36
36
|
import { type FieldRulePredicate } from './fieldRules.js';
|
|
37
|
+
import { type FieldContainerLike } from '../utils/predicate-record.js';
|
|
37
38
|
/** True when a *string* predicate uses legacy-only (non-CEL) syntax. */
|
|
38
39
|
export declare function isLegacyDialectSource(source: string): boolean;
|
|
39
40
|
/** Options for {@link evalRowPredicate}. */
|
|
@@ -43,12 +44,21 @@ export interface RowPredicateOptions {
|
|
|
43
44
|
* row-action `visible`/`disabled`; `false` (no style) for formatting. */
|
|
44
45
|
fallback?: boolean;
|
|
45
46
|
/** Extra top-level scope merged alongside the row — e.g. the global predicate
|
|
46
|
-
* 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. */
|
|
47
49
|
scope?: Record<string, unknown>;
|
|
48
50
|
/** When true, log a one-time warning if a *present* predicate faults. */
|
|
49
51
|
warnOnError?: boolean;
|
|
50
52
|
/** Label used in warning messages (e.g. an action name or rule index). */
|
|
51
53
|
label?: string;
|
|
54
|
+
/**
|
|
55
|
+
* The object's field definitions (`objectSchema.fields`). Supplying them lets
|
|
56
|
+
* a relation field be bound as the FOREIGN KEY the server stores, rather than
|
|
57
|
+
* as whatever `$expand` happened to substitute for it on this surface — see
|
|
58
|
+
* {@link toPredicateRecord}. Omitting them keeps the payload verbatim, so a
|
|
59
|
+
* caller with no schema at hand never guesses which fields are relations.
|
|
60
|
+
*/
|
|
61
|
+
fields?: FieldContainerLike;
|
|
52
62
|
}
|
|
53
63
|
/**
|
|
54
64
|
* Evaluate a single boolean predicate against a row record on the canonical CEL
|
|
@@ -56,7 +66,9 @@ export interface RowPredicateOptions {
|
|
|
56
66
|
* fields are bound three ways so every authoring convention resolves:
|
|
57
67
|
* `record.status` (spec/canonical), bare `status` (row-action shorthand), and
|
|
58
68
|
* `data.status` (legacy). The optional `scope` (host predicate scope) is bound
|
|
59
|
-
* 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).
|
|
60
72
|
*/
|
|
61
73
|
export declare function evalRowPredicate(pred: FieldRulePredicate | undefined | null, row: Record<string, unknown> | null | undefined, opts?: RowPredicateOptions): boolean;
|
|
62
74
|
/** A conditional-formatting rule in any of the three historical shapes. */
|
|
@@ -89,5 +101,8 @@ export type ConditionalFormattingRuleLike = {
|
|
|
89
101
|
*
|
|
90
102
|
* @param scope Extra top-level scope (host predicate scope) bound alongside
|
|
91
103
|
* the row, so `features.*` predicates keep resolving.
|
|
104
|
+
* @param fields The object's field definitions, so a rule comparing a relation
|
|
105
|
+
* field sees the stored foreign key rather than the expanded
|
|
106
|
+
* record (see {@link RowPredicateOptions.fields}).
|
|
92
107
|
*/
|
|
93
|
-
export declare function resolveConditionalFormatting(record: Record<string, unknown> | null | undefined, rules?: readonly ConditionalFormattingRuleLike[] | null, scope?: Record<string, unknown
|
|
108
|
+
export declare function resolveConditionalFormatting(record: Record<string, unknown> | null | undefined, rules?: readonly ConditionalFormattingRuleLike[] | null, scope?: Record<string, unknown>, fields?: FieldContainerLike): Record<string, string>;
|
|
Binary file
|
|
@@ -39,7 +39,11 @@ export function isOptionGroupGated(dependsOn, record) {
|
|
|
39
39
|
export function resolveVisibleOptions(options, record, scope) {
|
|
40
40
|
if (!options || options.length === 0)
|
|
41
41
|
return [];
|
|
42
|
-
return options.filter((o) => o?.visibleWhen == null
|
|
42
|
+
return options.filter((o) => o?.visibleWhen == null
|
|
43
|
+
? true
|
|
44
|
+
: evalFieldPredicate(o.visibleWhen, record, true, undefined, scope, {
|
|
45
|
+
context: `visibleWhen of option '${String(o.value)}'`,
|
|
46
|
+
}));
|
|
43
47
|
}
|
|
44
48
|
/**
|
|
45
49
|
* Whether a scalar field value is still valid given the currently-visible
|
|
@@ -0,0 +1,132 @@
|
|
|
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
|
+
* The normalized shape {@link ExpressionEvaluator.evaluateCondition} consumes:
|
|
10
|
+
* a boolean (short-circuits), a `${…}` template string (legacy JS path), a
|
|
11
|
+
* `{ dialect: 'cel', source }` envelope (canonical `@objectstack/formula`
|
|
12
|
+
* path), or `undefined` for "no predicate declared".
|
|
13
|
+
*
|
|
14
|
+
* ## Why not `PredicateInput` (objectstack#4115 / objectui#3074)
|
|
15
|
+
*
|
|
16
|
+
* `@objectstack/spec` owns `PredicateInput` — and it is a genuinely DIFFERENT
|
|
17
|
+
* concept, so wearing that name would be the exact drift this repo's
|
|
18
|
+
* `check:spec-symbols` gate exists to stop. The spec's is
|
|
19
|
+
* `z.input<typeof PredicateInputSchema>`: what an author WRITES, before
|
|
20
|
+
* normalization — a bare string, or an envelope over the full dialect set
|
|
21
|
+
* (`cel` | `cron` | `template`) with optional `source` / `ast` / `meta`.
|
|
22
|
+
*
|
|
23
|
+
* This one is the OUTPUT of normalization: what the evaluator ACCEPTS. It
|
|
24
|
+
* admits `boolean` and the `${…}` template spelling (neither of which the spec
|
|
25
|
+
* models as a predicate), narrows `dialect` to `cel` alone (every other dialect
|
|
26
|
+
* has already been flattened onto the legacy path by `toPredicateInput`),
|
|
27
|
+
* requires a non-empty `source`, and carries no `ast` / `meta`. The two unions
|
|
28
|
+
* are not mutually assignable in either direction.
|
|
29
|
+
*
|
|
30
|
+
* The rename is pinned from both sides by the tripwire in
|
|
31
|
+
* `packages/core/src/utils/__tests__/spec-symbol-batch4.test.ts`: the spec must
|
|
32
|
+
* still own `PredicateInput` (else this rename has lost its reason), and must
|
|
33
|
+
* still NOT own `EvaluatorPredicateInput` (the objectui#3074 mistake of
|
|
34
|
+
* renaming onto another name the spec already holds).
|
|
35
|
+
*/
|
|
36
|
+
export type EvaluatorPredicateInput = string | boolean | {
|
|
37
|
+
dialect: 'cel';
|
|
38
|
+
source: string;
|
|
39
|
+
} | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Normalize a schema-supplied predicate (`visible` / `enabled` / `disabled` /
|
|
42
|
+
* `hidden`) into the form {@link ExpressionEvaluator.evaluateCondition}
|
|
43
|
+
* expects.
|
|
44
|
+
*
|
|
45
|
+
* Accepts:
|
|
46
|
+
* - `boolean` → returned as-is (predicate evaluation short-circuits).
|
|
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).
|
|
50
|
+
* - `Expression` envelope `{ dialect, source }` (the normalized form
|
|
51
|
+
* `@objectstack/spec`'s `ExpressionInputSchema` produces for every
|
|
52
|
+
* authored predicate, including bare strings) → a `cel` dialect keeps
|
|
53
|
+
* its envelope; every other dialect is unwrapped and wrapped as
|
|
54
|
+
* `${source}`.
|
|
55
|
+
* - `null` / `undefined` / empty / anything else → `undefined`
|
|
56
|
+
* (default visible/enabled).
|
|
57
|
+
*
|
|
58
|
+
* ## Why the `cel` envelope must survive normalization (#2661 / #3314)
|
|
59
|
+
*
|
|
60
|
+
* `evaluateCondition` routes to the canonical `@objectstack/formula` engine —
|
|
61
|
+
* the one the SERVER enforces with — only while the argument is still a
|
|
62
|
+
* `{ dialect: 'cel' }` object. Collapse it to `${source}` first and the
|
|
63
|
+
* predicate silently falls to the legacy JS evaluator, whose semantics
|
|
64
|
+
* differ: CEL has no `<` overload for `null`, so `null < null` faults and
|
|
65
|
+
* fail-closed hides, while JS quietly yields `false`; CEL-only builtins
|
|
66
|
+
* (`today()`, objectstack#3205) do not exist on the JS path at all. Two
|
|
67
|
+
* normalizers that disagree on this one branch therefore make the SAME
|
|
68
|
+
* `visible:` predicate reach different verdicts depending on whether the
|
|
69
|
+
* action was surfaced by `ActionEngine.getActionsForLocation` or rendered
|
|
70
|
+
* standalone (#3314).
|
|
71
|
+
*
|
|
72
|
+
* This is THE implementation, not one of two — `@object-ui/core` is the common
|
|
73
|
+
* dependency of every consumer, so engine-side code and renderer-side code
|
|
74
|
+
* share one normalization instead of hand-rolling envelope unwrapping per call
|
|
75
|
+
* site. `@object-ui/react`'s `toPredicateInput` is a re-export of this function
|
|
76
|
+
* (since #3367; it used to be an independent twin held in step by a 14-shape
|
|
77
|
+
* normalization parity table, which is exactly the arrangement the paragraph
|
|
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
|
|
126
|
+
* `packages/react/src/hooks/__tests__/actionPredicate.parity.test.tsx` — the
|
|
127
|
+
* react export must BE this function object — alongside the engine-path vs
|
|
128
|
+
* renderer-path verdict parity suite in the same file, which is a separate
|
|
129
|
+
* claim and still earns its keep: sharing a normalizer does not by itself
|
|
130
|
+
* prove the two call paths reach the same verdict.
|
|
131
|
+
*/
|
|
132
|
+
export declare function toPredicateInput(value: unknown): EvaluatorPredicateInput;
|
|
@@ -0,0 +1,129 @@
|
|
|
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
|
+
* 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
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Normalize a schema-supplied predicate (`visible` / `enabled` / `disabled` /
|
|
19
|
+
* `hidden`) into the form {@link ExpressionEvaluator.evaluateCondition}
|
|
20
|
+
* expects.
|
|
21
|
+
*
|
|
22
|
+
* Accepts:
|
|
23
|
+
* - `boolean` → returned as-is (predicate evaluation short-circuits).
|
|
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).
|
|
27
|
+
* - `Expression` envelope `{ dialect, source }` (the normalized form
|
|
28
|
+
* `@objectstack/spec`'s `ExpressionInputSchema` produces for every
|
|
29
|
+
* authored predicate, including bare strings) → a `cel` dialect keeps
|
|
30
|
+
* its envelope; every other dialect is unwrapped and wrapped as
|
|
31
|
+
* `${source}`.
|
|
32
|
+
* - `null` / `undefined` / empty / anything else → `undefined`
|
|
33
|
+
* (default visible/enabled).
|
|
34
|
+
*
|
|
35
|
+
* ## Why the `cel` envelope must survive normalization (#2661 / #3314)
|
|
36
|
+
*
|
|
37
|
+
* `evaluateCondition` routes to the canonical `@objectstack/formula` engine —
|
|
38
|
+
* the one the SERVER enforces with — only while the argument is still a
|
|
39
|
+
* `{ dialect: 'cel' }` object. Collapse it to `${source}` first and the
|
|
40
|
+
* predicate silently falls to the legacy JS evaluator, whose semantics
|
|
41
|
+
* differ: CEL has no `<` overload for `null`, so `null < null` faults and
|
|
42
|
+
* fail-closed hides, while JS quietly yields `false`; CEL-only builtins
|
|
43
|
+
* (`today()`, objectstack#3205) do not exist on the JS path at all. Two
|
|
44
|
+
* normalizers that disagree on this one branch therefore make the SAME
|
|
45
|
+
* `visible:` predicate reach different verdicts depending on whether the
|
|
46
|
+
* action was surfaced by `ActionEngine.getActionsForLocation` or rendered
|
|
47
|
+
* standalone (#3314).
|
|
48
|
+
*
|
|
49
|
+
* This is THE implementation, not one of two — `@object-ui/core` is the common
|
|
50
|
+
* dependency of every consumer, so engine-side code and renderer-side code
|
|
51
|
+
* share one normalization instead of hand-rolling envelope unwrapping per call
|
|
52
|
+
* site. `@object-ui/react`'s `toPredicateInput` is a re-export of this function
|
|
53
|
+
* (since #3367; it used to be an independent twin held in step by a 14-shape
|
|
54
|
+
* normalization parity table, which is exactly the arrangement the paragraph
|
|
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
|
|
103
|
+
* `packages/react/src/hooks/__tests__/actionPredicate.parity.test.tsx` — the
|
|
104
|
+
* react export must BE this function object — alongside the engine-path vs
|
|
105
|
+
* renderer-path verdict parity suite in the same file, which is a separate
|
|
106
|
+
* claim and still earns its keep: sharing a normalizer does not by itself
|
|
107
|
+
* prove the two call paths reach the same verdict.
|
|
108
|
+
*/
|
|
109
|
+
export function toPredicateInput(value) {
|
|
110
|
+
if (value === null || value === undefined || value === '')
|
|
111
|
+
return undefined;
|
|
112
|
+
if (typeof value === 'boolean')
|
|
113
|
+
return value;
|
|
114
|
+
if (typeof value === 'string')
|
|
115
|
+
return wrapIfBare(value);
|
|
116
|
+
if (typeof value === 'object' && typeof value.source === 'string') {
|
|
117
|
+
const src = value.source;
|
|
118
|
+
if (!src)
|
|
119
|
+
return undefined;
|
|
120
|
+
// Preserve a CEL-dialect envelope so `evaluateCondition` routes it to the
|
|
121
|
+
// canonical `@objectstack/formula` engine (identical verdict to the
|
|
122
|
+
// server) instead of collapsing it onto the legacy JS path.
|
|
123
|
+
if (value.dialect === 'cel')
|
|
124
|
+
return { dialect: 'cel', source: src };
|
|
125
|
+
// Every other dialect (template / unset) keeps the legacy `${…}` behavior.
|
|
126
|
+
return wrapIfBare(src);
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
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,8 +49,12 @@ 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';
|
|
54
|
+
export * from './utils/dataset-pivot.js';
|
|
51
55
|
export * from './utils/record-title.js';
|
|
52
56
|
export * from './utils/export-filename.js';
|
|
53
57
|
export * from './utils/reference-keys.js';
|
|
58
|
+
export * from './utils/predicate-record.js';
|
|
59
|
+
export * from './utils/predicate-fields.js';
|
|
54
60
|
export * from './utils/normalize-list-view.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,8 +56,26 @@ 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';
|
|
66
|
+
// Pivot lookup-key encoders, shared by every cross-tab renderer so the
|
|
67
|
+
// dashboard widget and the report renderer key their buckets identically
|
|
68
|
+
// (objectstack#5473 / objectstack#5665).
|
|
69
|
+
export * from './utils/dataset-pivot.js';
|
|
53
70
|
export * from './utils/record-title.js';
|
|
54
71
|
export * from './utils/export-filename.js';
|
|
55
72
|
export * from './utils/reference-keys.js';
|
|
73
|
+
// Binds a fetched record into an expression scope the way the SERVER binds it
|
|
74
|
+
// (a relation is its foreign key, never the expanded record) — see
|
|
75
|
+
// `toPredicateRecord` for why an unnormalized one gives the same predicate
|
|
76
|
+
// different verdicts on different surfaces.
|
|
77
|
+
export * from './utils/predicate-record.js';
|
|
78
|
+
// The other half of a view's field appetite: the fields its PREDICATES read,
|
|
79
|
+
// which the column-derived `$select` never asked the server for.
|
|
80
|
+
export * from './utils/predicate-fields.js';
|
|
56
81
|
export * from './utils/normalize-list-view.js';
|
|
@@ -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;
|