@player-ui/player 0.3.1-next.0 → 0.3.1
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/dist/index.cjs.js +899 -336
- package/dist/index.d.ts +275 -93
- package/dist/index.esm.js +890 -334
- package/dist/player.dev.js +11429 -0
- package/dist/player.prod.js +2 -0
- package/package.json +16 -5
- package/src/binding/binding.ts +8 -0
- package/src/binding/index.ts +14 -4
- package/src/binding/resolver.ts +2 -4
- package/src/binding-grammar/custom/index.ts +17 -9
- package/src/controllers/constants/index.ts +9 -5
- package/src/controllers/{data.ts → data/controller.ts} +62 -61
- package/src/controllers/data/index.ts +1 -0
- package/src/controllers/data/utils.ts +42 -0
- package/src/controllers/flow/controller.ts +16 -12
- package/src/controllers/flow/flow.ts +6 -1
- package/src/controllers/index.ts +1 -1
- package/src/controllers/validation/binding-tracker.ts +42 -19
- package/src/controllers/validation/controller.ts +375 -148
- package/src/controllers/view/asset-transform.ts +4 -1
- package/src/controllers/view/controller.ts +20 -3
- package/src/data/dependency-tracker.ts +14 -0
- package/src/data/local-model.ts +25 -1
- package/src/data/model.ts +60 -8
- package/src/data/noop-model.ts +2 -0
- package/src/expressions/evaluator-functions.ts +24 -2
- package/src/expressions/evaluator.ts +38 -34
- package/src/expressions/index.ts +1 -0
- package/src/expressions/parser.ts +116 -44
- package/src/expressions/types.ts +50 -17
- package/src/expressions/utils.ts +143 -1
- package/src/player.ts +60 -46
- package/src/plugins/default-exp-plugin.ts +57 -0
- package/src/plugins/flow-exp-plugin.ts +2 -2
- package/src/schema/schema.ts +28 -9
- package/src/string-resolver/index.ts +26 -9
- package/src/types.ts +6 -3
- package/src/validator/binding-map-splice.ts +59 -0
- package/src/validator/index.ts +1 -0
- package/src/validator/types.ts +11 -3
- package/src/validator/validation-middleware.ts +58 -6
- package/src/view/parser/index.ts +51 -3
- package/src/view/plugins/applicability.ts +1 -1
- package/src/view/plugins/string-resolver.ts +35 -9
- package/src/view/plugins/template-plugin.ts +1 -6
- package/src/view/resolver/index.ts +119 -54
- package/src/view/resolver/types.ts +48 -7
|
@@ -26,7 +26,7 @@ const DOUBLE_QUOTE = '"';
|
|
|
26
26
|
const BACK_TICK = '`';
|
|
27
27
|
// const IDENTIFIER_REGEX = /[\w\-@]+/;
|
|
28
28
|
|
|
29
|
-
/** A _faster_ way to match chars instead of a regex
|
|
29
|
+
/** A _faster_ way to match chars instead of a regex. */
|
|
30
30
|
const isIdentifierChar = (char?: string): boolean => {
|
|
31
31
|
if (!char) {
|
|
32
32
|
return false;
|
|
@@ -34,14 +34,22 @@ const isIdentifierChar = (char?: string): boolean => {
|
|
|
34
34
|
|
|
35
35
|
const charCode = char.charCodeAt(0);
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
charCode ===
|
|
42
|
-
charCode ===
|
|
43
|
-
charCode ===
|
|
44
|
-
|
|
37
|
+
const matches =
|
|
38
|
+
charCode === 32 || // ' '
|
|
39
|
+
charCode === 34 || // "
|
|
40
|
+
charCode === 39 || // '
|
|
41
|
+
charCode === 40 || // (
|
|
42
|
+
charCode === 41 || // )
|
|
43
|
+
charCode === 42 || // *
|
|
44
|
+
charCode === 46 || // .
|
|
45
|
+
charCode === 61 || // =
|
|
46
|
+
charCode === 91 || // [
|
|
47
|
+
charCode === 93 || // ]
|
|
48
|
+
charCode === 96 || // `
|
|
49
|
+
charCode === 123 || // {
|
|
50
|
+
charCode === 125; // }
|
|
51
|
+
|
|
52
|
+
return !matches;
|
|
45
53
|
};
|
|
46
54
|
|
|
47
55
|
/** Parse out a binding AST from a path */
|
|
@@ -10,7 +10,7 @@ export interface ConstantsProvider {
|
|
|
10
10
|
addConstants(data: Record<string, any>, namespace: string): void;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Function to
|
|
13
|
+
* Function to retrieve constants from the providers store
|
|
14
14
|
* - @param key Key used for the store access
|
|
15
15
|
* - @param namespace namespace values were loaded under (defined in the plugin)
|
|
16
16
|
* - @param fallback Optional - if key doesn't exist in namespace what to return (will return unknown if not provided)
|
|
@@ -77,9 +77,13 @@ export class ConstantsController implements ConstantsProvider {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
clearTemporaryValues(): void {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
80
|
+
clearTemporaryValues(namespace?: string): void {
|
|
81
|
+
if (namespace) {
|
|
82
|
+
this.tempStore.get(namespace)?.reset();
|
|
83
|
+
} else {
|
|
84
|
+
this.tempStore.forEach((value: LocalModel) => {
|
|
85
|
+
value.reset();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
84
88
|
}
|
|
85
89
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { SyncHook, SyncWaterfallHook, SyncBailHook } from 'tapable-ts';
|
|
2
|
-
import { omit, removeAt } from 'timm';
|
|
3
2
|
import { dequal } from 'dequal';
|
|
4
|
-
import type { Logger } from '
|
|
5
|
-
import type { BindingParser, BindingLike } from '
|
|
6
|
-
import { BindingInstance } from '
|
|
3
|
+
import type { Logger } from '../../logger';
|
|
4
|
+
import type { BindingParser, BindingLike } from '../../binding';
|
|
5
|
+
import { BindingInstance } from '../../binding';
|
|
7
6
|
import type {
|
|
8
7
|
BatchSetTransaction,
|
|
9
8
|
Updates,
|
|
@@ -11,9 +10,10 @@ import type {
|
|
|
11
10
|
DataModelWithParser,
|
|
12
11
|
DataPipeline,
|
|
13
12
|
DataModelMiddleware,
|
|
14
|
-
} from '
|
|
15
|
-
import { PipelinedDataModel, LocalModel } from '
|
|
16
|
-
import type { RawSetTransaction } from '
|
|
13
|
+
} from '../../data';
|
|
14
|
+
import { PipelinedDataModel, LocalModel } from '../../data';
|
|
15
|
+
import type { RawSetTransaction } from '../../types';
|
|
16
|
+
import { ReadOnlyDataController } from './utils';
|
|
17
17
|
|
|
18
18
|
/** The orchestrator for player data */
|
|
19
19
|
export class DataController implements DataModelWithParser<DataModelOptions> {
|
|
@@ -25,11 +25,15 @@ export class DataController implements DataModelWithParser<DataModelOptions> {
|
|
|
25
25
|
resolveDefaultValue: new SyncBailHook<[BindingInstance], any>(),
|
|
26
26
|
|
|
27
27
|
onDelete: new SyncHook<[any]>(),
|
|
28
|
+
|
|
28
29
|
onSet: new SyncHook<[BatchSetTransaction]>(),
|
|
30
|
+
|
|
29
31
|
onGet: new SyncHook<[any, any]>(),
|
|
30
|
-
|
|
32
|
+
|
|
33
|
+
onUpdate: new SyncHook<[Updates, DataModelOptions | undefined]>(),
|
|
31
34
|
|
|
32
35
|
format: new SyncWaterfallHook<[any, BindingInstance]>(),
|
|
36
|
+
|
|
33
37
|
deformat: new SyncWaterfallHook<[any, BindingInstance]>(),
|
|
34
38
|
|
|
35
39
|
serialize: new SyncWaterfallHook<[any]>(),
|
|
@@ -119,18 +123,24 @@ export class DataController implements DataModelWithParser<DataModelOptions> {
|
|
|
119
123
|
(updates, [binding, newVal]) => {
|
|
120
124
|
const oldVal = this.get(binding, { includeInvalid: true });
|
|
121
125
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
const update = {
|
|
127
|
+
binding,
|
|
128
|
+
newValue: newVal,
|
|
129
|
+
oldValue: oldVal,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
if (dequal(oldVal, newVal)) {
|
|
133
|
+
this.logger?.debug(
|
|
134
|
+
`Skipping update for path: ${binding.asString()}. Value was unchanged: ${oldVal}`
|
|
135
|
+
);
|
|
136
|
+
} else {
|
|
137
|
+
updates.push(update);
|
|
138
|
+
|
|
139
|
+
this.logger?.debug(
|
|
140
|
+
`Setting path: ${binding.asString()} from: ${oldVal} to: ${newVal}`
|
|
141
|
+
);
|
|
128
142
|
}
|
|
129
143
|
|
|
130
|
-
this.logger?.debug(
|
|
131
|
-
`Setting path: ${binding.asString()} from: ${oldVal} to: ${newVal}`
|
|
132
|
-
);
|
|
133
|
-
|
|
134
144
|
return updates;
|
|
135
145
|
},
|
|
136
146
|
[]
|
|
@@ -158,21 +168,23 @@ export class DataController implements DataModelWithParser<DataModelOptions> {
|
|
|
158
168
|
this.hooks.onSet.call(normalizedTransaction);
|
|
159
169
|
|
|
160
170
|
if (setUpdates.length > 0) {
|
|
161
|
-
this.hooks.onUpdate.call(setUpdates);
|
|
171
|
+
this.hooks.onUpdate.call(setUpdates, options);
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
return result;
|
|
165
175
|
}
|
|
166
176
|
|
|
167
|
-
private resolve(binding: BindingLike): BindingInstance {
|
|
177
|
+
private resolve(binding: BindingLike, readOnly: boolean): BindingInstance {
|
|
168
178
|
return Array.isArray(binding) || typeof binding === 'string'
|
|
169
|
-
? this.pathResolver.parse(binding)
|
|
179
|
+
? this.pathResolver.parse(binding, { readOnly })
|
|
170
180
|
: binding;
|
|
171
181
|
}
|
|
172
182
|
|
|
173
183
|
public get(binding: BindingLike, options?: DataModelOptions) {
|
|
174
184
|
const resolved =
|
|
175
|
-
binding instanceof BindingInstance
|
|
185
|
+
binding instanceof BindingInstance
|
|
186
|
+
? binding
|
|
187
|
+
: this.resolve(binding, true);
|
|
176
188
|
let result = this.getModel().get(resolved, options);
|
|
177
189
|
|
|
178
190
|
if (result === undefined && !options?.ignoreDefaultValue) {
|
|
@@ -185,6 +197,8 @@ export class DataController implements DataModelWithParser<DataModelOptions> {
|
|
|
185
197
|
|
|
186
198
|
if (options?.formatted) {
|
|
187
199
|
result = this.hooks.format.call(result, resolved);
|
|
200
|
+
} else if (options?.formatted === false) {
|
|
201
|
+
result = this.hooks.deformat.call(result, resolved);
|
|
188
202
|
}
|
|
189
203
|
|
|
190
204
|
this.hooks.onGet.call(binding, result);
|
|
@@ -192,56 +206,43 @@ export class DataController implements DataModelWithParser<DataModelOptions> {
|
|
|
192
206
|
return result;
|
|
193
207
|
}
|
|
194
208
|
|
|
195
|
-
public delete(binding: BindingLike) {
|
|
196
|
-
if (
|
|
197
|
-
|
|
209
|
+
public delete(binding: BindingLike, options?: DataModelOptions) {
|
|
210
|
+
if (
|
|
211
|
+
typeof binding !== 'string' &&
|
|
212
|
+
!Array.isArray(binding) &&
|
|
213
|
+
!(binding instanceof BindingInstance)
|
|
214
|
+
) {
|
|
215
|
+
throw new Error('Invalid arguments: delete expects a data path (string)');
|
|
198
216
|
}
|
|
199
217
|
|
|
200
|
-
const resolved =
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
public getTrash(): Set<BindingInstance> {
|
|
206
|
-
return this.trash;
|
|
207
|
-
}
|
|
218
|
+
const resolved =
|
|
219
|
+
binding instanceof BindingInstance
|
|
220
|
+
? binding
|
|
221
|
+
: this.resolve(binding, false);
|
|
208
222
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
223
|
+
const parentBinding = resolved.parent();
|
|
224
|
+
const property = resolved.key();
|
|
225
|
+
const parentValue = this.get(parentBinding);
|
|
212
226
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
227
|
+
const existedBeforeDelete =
|
|
228
|
+
typeof parentValue === 'object' &&
|
|
229
|
+
parentValue !== null &&
|
|
230
|
+
Object.prototype.hasOwnProperty.call(parentValue, property);
|
|
217
231
|
|
|
218
|
-
|
|
219
|
-
this.get(parentBinding),
|
|
220
|
-
property
|
|
221
|
-
);
|
|
232
|
+
this.getModel().delete(resolved, options);
|
|
222
233
|
|
|
223
|
-
if (
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// If we're deleting an item in an array, we just splice it out
|
|
227
|
-
// Don't add it to the trash
|
|
228
|
-
if (parentPath && Array.isArray(parent)) {
|
|
229
|
-
if (parent.length > property) {
|
|
230
|
-
this.set([[parentBinding, removeAt(parent, property as number)]]);
|
|
231
|
-
}
|
|
232
|
-
} else if (parentPath && parent[property]) {
|
|
233
|
-
this.set([[parentBinding, omit(parent, property as string)]]);
|
|
234
|
-
} else if (!parentPath) {
|
|
235
|
-
this.getModel().reset(omit(this.get(''), property as string));
|
|
236
|
-
}
|
|
234
|
+
if (existedBeforeDelete && !this.get(resolved)) {
|
|
235
|
+
this.trash.add(resolved);
|
|
237
236
|
}
|
|
238
237
|
|
|
239
|
-
|
|
240
|
-
this.addToTrash(binding);
|
|
241
|
-
}
|
|
238
|
+
this.hooks.onDelete.call(resolved);
|
|
242
239
|
}
|
|
243
240
|
|
|
244
241
|
public serialize(): object {
|
|
245
242
|
return this.hooks.serialize.call(this.get(''));
|
|
246
243
|
}
|
|
244
|
+
|
|
245
|
+
public makeReadOnly(): ReadOnlyDataController {
|
|
246
|
+
return new ReadOnlyDataController(this, this.logger);
|
|
247
|
+
}
|
|
247
248
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './controller';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
import type { DataController } from '.';
|
|
3
|
+
import type { Logger } from '../../logger';
|
|
4
|
+
import type { BindingLike } from '../../binding';
|
|
5
|
+
import type {
|
|
6
|
+
DataModelWithParser,
|
|
7
|
+
DataModelOptions,
|
|
8
|
+
Updates,
|
|
9
|
+
} from '../../data';
|
|
10
|
+
|
|
11
|
+
/** Wrapper for the Data Controller Class that prevents writes */
|
|
12
|
+
export class ReadOnlyDataController
|
|
13
|
+
implements DataModelWithParser<DataModelOptions>
|
|
14
|
+
{
|
|
15
|
+
private controller: DataController;
|
|
16
|
+
private logger?: Logger;
|
|
17
|
+
|
|
18
|
+
constructor(controller: DataController, logger?: Logger) {
|
|
19
|
+
this.controller = controller;
|
|
20
|
+
this.logger = logger;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get(binding: BindingLike, options?: DataModelOptions | undefined) {
|
|
24
|
+
return this.controller.get(binding, options);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
set(
|
|
28
|
+
transaction: [BindingLike, any][],
|
|
29
|
+
options?: DataModelOptions | undefined
|
|
30
|
+
): Updates {
|
|
31
|
+
this.logger?.error(
|
|
32
|
+
'Error: Tried to set in a read only instance of the DataController'
|
|
33
|
+
);
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
delete(binding: BindingLike, options?: DataModelOptions | undefined): void {
|
|
38
|
+
this.logger?.error(
|
|
39
|
+
'Error: Tried to delete in a read only instance of the DataController'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -29,6 +29,7 @@ export class FlowController {
|
|
|
29
29
|
this.start = this.start.bind(this);
|
|
30
30
|
this.run = this.run.bind(this);
|
|
31
31
|
this.transition = this.transition.bind(this);
|
|
32
|
+
this.addNewFlow = this.addNewFlow.bind(this);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/** Navigate to another state in the state-machine */
|
|
@@ -40,20 +41,9 @@ export class FlowController {
|
|
|
40
41
|
this.current.transition(stateTransition, options);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
private
|
|
44
|
+
private addNewFlow(flow: FlowInstance) {
|
|
44
45
|
this.navStack.push(flow);
|
|
45
46
|
this.current = flow;
|
|
46
|
-
flow.hooks.transition.tap(
|
|
47
|
-
'flow-controller',
|
|
48
|
-
async (_oldState, newState: NamedState) => {
|
|
49
|
-
if (newState.value.state_type === 'FLOW') {
|
|
50
|
-
this.log?.debug(`Got FLOW state. Loading flow ${newState.value.ref}`);
|
|
51
|
-
const endState = await this.run(newState.value.ref);
|
|
52
|
-
this.log?.debug(`Flow ended. Using outcome: ${endState.outcome}`);
|
|
53
|
-
flow.transition(endState.outcome);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
47
|
this.hooks.flow.call(flow);
|
|
58
48
|
}
|
|
59
49
|
|
|
@@ -74,6 +64,20 @@ export class FlowController {
|
|
|
74
64
|
|
|
75
65
|
const flow = new FlowInstance(startState, startFlow, { logger: this.log });
|
|
76
66
|
this.addNewFlow(flow);
|
|
67
|
+
|
|
68
|
+
flow.hooks.afterTransition.tap('flow-controller', (flowInstance) => {
|
|
69
|
+
if (flowInstance.currentState?.value.state_type === 'FLOW') {
|
|
70
|
+
const subflowId = flowInstance.currentState?.value.ref;
|
|
71
|
+
this.log?.debug(`Loading subflow ${subflowId}`);
|
|
72
|
+
this.run(subflowId).then((subFlowEndState) => {
|
|
73
|
+
this.log?.debug(
|
|
74
|
+
`Subflow ended. Using outcome: ${subFlowEndState.outcome}`
|
|
75
|
+
);
|
|
76
|
+
flowInstance.transition(subFlowEndState?.outcome);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
77
81
|
const end = await flow.start();
|
|
78
82
|
this.navStack.pop();
|
|
79
83
|
|
|
@@ -58,6 +58,9 @@ export class FlowInstance {
|
|
|
58
58
|
|
|
59
59
|
/** A callback when a transition from 1 state to another was made */
|
|
60
60
|
transition: new SyncHook<[NamedState | undefined, NamedState]>(),
|
|
61
|
+
|
|
62
|
+
/** A callback to run actions after a transition occurs */
|
|
63
|
+
afterTransition: new SyncHook<[FlowInstance]>(),
|
|
61
64
|
};
|
|
62
65
|
|
|
63
66
|
constructor(
|
|
@@ -131,7 +134,7 @@ export class FlowInstance {
|
|
|
131
134
|
|
|
132
135
|
if (skipTransition) {
|
|
133
136
|
this.log?.debug(
|
|
134
|
-
`Skipping transition from ${this.currentState} b/c hook told us to`
|
|
137
|
+
`Skipping transition from ${this.currentState.name} b/c hook told us to`
|
|
135
138
|
);
|
|
136
139
|
return;
|
|
137
140
|
}
|
|
@@ -201,5 +204,7 @@ export class FlowInstance {
|
|
|
201
204
|
this.hooks.transition.call(prevState, {
|
|
202
205
|
...newCurrentState,
|
|
203
206
|
});
|
|
207
|
+
|
|
208
|
+
this.hooks.afterTransition.call(this);
|
|
204
209
|
}
|
|
205
210
|
}
|
package/src/controllers/index.ts
CHANGED
|
@@ -13,6 +13,9 @@ const CONTEXT = 'validation-binding-tracker';
|
|
|
13
13
|
export interface BindingTracker {
|
|
14
14
|
/** Get the bindings currently being tracked for validation */
|
|
15
15
|
getBindings(): Set<BindingInstance>;
|
|
16
|
+
|
|
17
|
+
/** Add a binding to the tracked set */
|
|
18
|
+
trackBinding(binding: BindingInstance): void;
|
|
16
19
|
}
|
|
17
20
|
interface Options {
|
|
18
21
|
/** Parse a binding from a view */
|
|
@@ -42,6 +45,16 @@ export class ValidationBindingTrackerViewPlugin
|
|
|
42
45
|
return this.trackedBindings;
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
/** Add a binding to the tracked set */
|
|
49
|
+
trackBinding(binding: BindingInstance) {
|
|
50
|
+
if (this.trackedBindings.has(binding)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.trackedBindings.add(binding);
|
|
55
|
+
this.options.callbacks?.onAdd?.(binding);
|
|
56
|
+
}
|
|
57
|
+
|
|
45
58
|
/** Attach hooks to the given resolver */
|
|
46
59
|
applyResolver(resolver: Resolver) {
|
|
47
60
|
this.trackedBindings.clear();
|
|
@@ -52,9 +65,6 @@ export class ValidationBindingTrackerViewPlugin
|
|
|
52
65
|
/** Each Node is a registered section or page that maps to a set of nodes in its section */
|
|
53
66
|
const sections = new Map<Node.Node, Set<Node.Node>>();
|
|
54
67
|
|
|
55
|
-
/** Keep track of all seen bindings so we can notify people when we hit one for the first time */
|
|
56
|
-
const seenBindings = new Set<BindingInstance>();
|
|
57
|
-
|
|
58
68
|
let lastViewUpdateChangeSet: Set<BindingInstance> | undefined;
|
|
59
69
|
|
|
60
70
|
const nodeTree = new Map<Node.Node, Set<Node.Node>>();
|
|
@@ -129,10 +139,8 @@ export class ValidationBindingTrackerViewPlugin
|
|
|
129
139
|
}
|
|
130
140
|
}
|
|
131
141
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
this.options.callbacks?.onAdd?.(parsed);
|
|
135
|
-
}
|
|
142
|
+
this.trackedBindings.add(parsed);
|
|
143
|
+
this.options.callbacks?.onAdd?.(parsed);
|
|
136
144
|
};
|
|
137
145
|
|
|
138
146
|
return {
|
|
@@ -144,23 +152,36 @@ export class ValidationBindingTrackerViewPlugin
|
|
|
144
152
|
track(binding);
|
|
145
153
|
}
|
|
146
154
|
|
|
147
|
-
const
|
|
155
|
+
const eows = options.validation
|
|
156
|
+
?._getValidationForBinding(binding)
|
|
157
|
+
?.getAll(getOptions);
|
|
158
|
+
|
|
159
|
+
const firstFieldEOW = eows?.find(
|
|
160
|
+
(eow) =>
|
|
161
|
+
eow.displayTarget === 'field' || eow.displayTarget === undefined
|
|
162
|
+
);
|
|
148
163
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
) {
|
|
153
|
-
|
|
164
|
+
return firstFieldEOW;
|
|
165
|
+
},
|
|
166
|
+
getValidationsForBinding(binding, getOptions) {
|
|
167
|
+
if (getOptions?.track) {
|
|
168
|
+
track(binding);
|
|
154
169
|
}
|
|
155
170
|
|
|
156
|
-
return
|
|
171
|
+
return (
|
|
172
|
+
options.validation
|
|
173
|
+
?._getValidationForBinding(binding)
|
|
174
|
+
?.getAll(getOptions) ?? []
|
|
175
|
+
);
|
|
157
176
|
},
|
|
158
|
-
getChildren: (type
|
|
177
|
+
getChildren: (type?: Validation.DisplayTarget) => {
|
|
159
178
|
const validations = new Array<ValidationResponse>();
|
|
160
179
|
lastComputedBindingTree.get(node)?.forEach((binding) => {
|
|
161
|
-
const eow = options.validation
|
|
180
|
+
const eow = options.validation
|
|
181
|
+
?._getValidationForBinding(binding)
|
|
182
|
+
?.get();
|
|
162
183
|
|
|
163
|
-
if (eow && type === eow.displayTarget) {
|
|
184
|
+
if (eow && (type === undefined || type === eow.displayTarget)) {
|
|
164
185
|
validations.push(eow);
|
|
165
186
|
}
|
|
166
187
|
});
|
|
@@ -170,7 +191,9 @@ export class ValidationBindingTrackerViewPlugin
|
|
|
170
191
|
getValidationsForSection: () => {
|
|
171
192
|
const validations = new Array<ValidationResponse>();
|
|
172
193
|
lastSectionBindingTree.get(node)?.forEach((binding) => {
|
|
173
|
-
const eow = options.validation
|
|
194
|
+
const eow = options.validation
|
|
195
|
+
?._getValidationForBinding(binding)
|
|
196
|
+
?.get();
|
|
174
197
|
|
|
175
198
|
if (eow && eow.displayTarget === 'section') {
|
|
176
199
|
validations.push(eow);
|
|
@@ -213,7 +236,7 @@ export class ValidationBindingTrackerViewPlugin
|
|
|
213
236
|
}
|
|
214
237
|
|
|
215
238
|
if (node === resolver.root) {
|
|
216
|
-
this.trackedBindings = currentBindingTree.get(node)
|
|
239
|
+
this.trackedBindings = new Set(currentBindingTree.get(node));
|
|
217
240
|
lastComputedBindingTree = currentBindingTree;
|
|
218
241
|
|
|
219
242
|
lastSectionBindingTree.clear();
|