@getodk/xforms-engine 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/body/BodyElementDefinition.d.ts +4 -3
- package/dist/body/RepeatElementDefinition.d.ts +2 -2
- package/dist/body/control/ControlDefinition.d.ts +2 -2
- package/dist/body/control/select/ItemDefinition.d.ts +2 -2
- package/dist/body/control/select/ItemsetDefinition.d.ts +5 -4
- package/dist/body/group/BaseGroupDefinition.d.ts +1 -1
- package/dist/body/group/PresentationGroupDefinition.d.ts +1 -1
- package/dist/client/BaseNode.d.ts +68 -2
- package/dist/client/GroupNode.d.ts +2 -0
- package/dist/client/ModelValueNode.d.ts +37 -0
- package/dist/client/NoteNode.d.ts +53 -0
- package/dist/client/RootNode.d.ts +2 -0
- package/dist/client/SelectNode.d.ts +5 -3
- package/dist/client/StringNode.d.ts +5 -3
- package/dist/client/SubtreeNode.d.ts +2 -0
- package/dist/client/TextRange.d.ts +85 -2
- package/dist/client/constants.d.ts +9 -0
- package/dist/client/hierarchy.d.ts +14 -9
- package/dist/client/node-types.d.ts +2 -1
- package/dist/client/{RepeatRangeNode.d.ts → repeat/BaseRepeatRangeNode.d.ts} +18 -17
- package/dist/client/{RepeatInstanceNode.d.ts → repeat/RepeatInstanceNode.d.ts} +9 -8
- package/dist/client/repeat/RepeatRangeControlledNode.d.ts +19 -0
- package/dist/client/repeat/RepeatRangeUncontrolledNode.d.ts +20 -0
- package/dist/client/validation.d.ts +163 -0
- package/dist/expression/DependentExpression.d.ts +12 -8
- package/dist/index.d.ts +9 -4
- package/dist/index.js +2635 -678
- package/dist/index.js.map +1 -1
- package/dist/instance/Group.d.ts +3 -1
- package/dist/instance/ModelValue.d.ts +40 -0
- package/dist/instance/Note.d.ts +42 -0
- package/dist/instance/Root.d.ts +2 -0
- package/dist/instance/SelectField.d.ts +10 -4
- package/dist/instance/StringField.d.ts +11 -5
- package/dist/instance/Subtree.d.ts +2 -0
- package/dist/instance/abstract/DescendantNode.d.ts +5 -6
- package/dist/instance/abstract/InstanceNode.d.ts +2 -0
- package/dist/instance/hierarchy.d.ts +10 -5
- package/dist/instance/internal-api/ValidationContext.d.ts +21 -0
- package/dist/instance/{RepeatRange.d.ts → repeat/BaseRepeatRange.d.ts} +46 -45
- package/dist/instance/{RepeatInstance.d.ts → repeat/RepeatInstance.d.ts} +13 -12
- package/dist/instance/repeat/RepeatRangeControlled.d.ts +16 -0
- package/dist/instance/repeat/RepeatRangeUncontrolled.d.ts +35 -0
- package/dist/instance/text/TextRange.d.ts +4 -4
- package/dist/lib/reactivity/createComputedExpression.d.ts +6 -1
- package/dist/lib/reactivity/createNoteReadonlyThunk.d.ts +5 -0
- package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +1 -1
- package/dist/lib/reactivity/node-state/createSpecifiedState.d.ts +1 -1
- package/dist/lib/reactivity/text/createFieldHint.d.ts +3 -3
- package/dist/lib/reactivity/text/createNodeLabel.d.ts +2 -2
- package/dist/lib/reactivity/text/createNoteText.d.ts +25 -0
- package/dist/lib/reactivity/text/createTextRange.d.ts +5 -7
- package/dist/lib/reactivity/validation/createAggregatedViolations.d.ts +9 -0
- package/dist/lib/reactivity/validation/createValidation.d.ts +18 -0
- package/dist/model/BindDefinition.d.ts +4 -2
- package/dist/model/BindElement.d.ts +1 -0
- package/dist/model/{ValueNodeDefinition.d.ts → LeafNodeDefinition.d.ts} +2 -2
- package/dist/model/NodeDefinition.d.ts +8 -8
- package/dist/model/RepeatInstanceDefinition.d.ts +2 -2
- package/dist/model/RepeatRangeDefinition.d.ts +14 -4
- package/dist/parse/NoteNodeDefinition.d.ts +31 -0
- package/dist/parse/expression/RepeatCountControlExpression.d.ts +19 -0
- package/dist/parse/text/HintDefinition.d.ts +9 -0
- package/dist/parse/text/ItemLabelDefinition.d.ts +9 -0
- package/dist/parse/text/ItemsetLabelDefinition.d.ts +13 -0
- package/dist/parse/text/LabelDefinition.d.ts +15 -0
- package/dist/parse/text/MessageDefinition.d.ts +15 -0
- package/dist/parse/text/OutputChunkDefinition.d.ts +8 -0
- package/dist/parse/text/ReferenceChunkDefinition.d.ts +8 -0
- package/dist/parse/text/StaticTextChunkDefinition.d.ts +10 -0
- package/dist/parse/text/TranslationChunkDefinition.d.ts +9 -0
- package/dist/parse/text/abstract/TextChunkDefinition.d.ts +18 -0
- package/dist/parse/text/abstract/TextElementDefinition.d.ts +23 -0
- package/dist/parse/text/abstract/TextRangeDefinition.d.ts +35 -0
- package/dist/parse/xpath/dependency-analysis.d.ts +40 -0
- package/dist/parse/xpath/path-resolution.d.ts +70 -0
- package/dist/parse/xpath/predicate-analysis.d.ts +30 -0
- package/dist/parse/xpath/reference-parsing.d.ts +18 -0
- package/dist/parse/xpath/semantic-analysis.d.ts +98 -0
- package/dist/parse/xpath/syntax-traversal.d.ts +69 -0
- package/dist/solid.js +2636 -679
- package/dist/solid.js.map +1 -1
- package/package.json +14 -15
- package/src/body/BodyElementDefinition.ts +4 -3
- package/src/body/RepeatElementDefinition.ts +5 -17
- package/src/body/control/ControlDefinition.ts +4 -3
- package/src/body/control/select/ItemDefinition.ts +3 -3
- package/src/body/control/select/ItemsetDefinition.ts +29 -12
- package/src/body/control/select/ItemsetNodesetExpression.ts +1 -1
- package/src/body/group/BaseGroupDefinition.ts +3 -2
- package/src/body/group/PresentationGroupDefinition.ts +1 -1
- package/src/client/BaseNode.ts +73 -7
- package/src/client/GroupNode.ts +2 -0
- package/src/client/ModelValueNode.ts +40 -0
- package/src/client/NoteNode.ts +74 -0
- package/src/client/README.md +1 -0
- package/src/client/RootNode.ts +2 -0
- package/src/client/SelectNode.ts +5 -3
- package/src/client/StringNode.ts +5 -3
- package/src/client/SubtreeNode.ts +2 -0
- package/src/client/TextRange.ts +99 -2
- package/src/client/constants.ts +10 -0
- package/src/client/hierarchy.ts +30 -14
- package/src/client/node-types.ts +8 -1
- package/src/client/{RepeatRangeNode.ts → repeat/BaseRepeatRangeNode.ts} +18 -19
- package/src/client/{RepeatInstanceNode.ts → repeat/RepeatInstanceNode.ts} +10 -8
- package/src/client/repeat/RepeatRangeControlledNode.ts +20 -0
- package/src/client/repeat/RepeatRangeUncontrolledNode.ts +21 -0
- package/src/client/validation.ts +199 -0
- package/src/expression/DependentExpression.ts +45 -27
- package/src/index.ts +15 -8
- package/src/instance/Group.ts +10 -4
- package/src/instance/ModelValue.ts +104 -0
- package/src/instance/Note.ts +142 -0
- package/src/instance/Root.ts +9 -3
- package/src/instance/SelectField.ts +28 -6
- package/src/instance/StringField.ts +35 -9
- package/src/instance/Subtree.ts +9 -3
- package/src/instance/abstract/DescendantNode.ts +6 -7
- package/src/instance/abstract/InstanceNode.ts +20 -6
- package/src/instance/children.ts +42 -15
- package/src/instance/hierarchy.ts +21 -2
- package/src/instance/internal-api/ValidationContext.ts +23 -0
- package/src/instance/{RepeatRange.ts → repeat/BaseRepeatRange.ts} +114 -99
- package/src/instance/{RepeatInstance.ts → repeat/RepeatInstance.ts} +27 -22
- package/src/instance/repeat/RepeatRangeControlled.ts +82 -0
- package/src/instance/repeat/RepeatRangeUncontrolled.ts +67 -0
- package/src/instance/text/TextRange.ts +10 -4
- package/src/lib/reactivity/createComputedExpression.ts +22 -24
- package/src/lib/reactivity/createNoteReadonlyThunk.ts +33 -0
- package/src/lib/reactivity/createSelectItems.ts +21 -14
- package/src/lib/reactivity/node-state/createSharedNodeState.ts +1 -1
- package/src/lib/reactivity/text/createFieldHint.ts +9 -7
- package/src/lib/reactivity/text/createNodeLabel.ts +7 -5
- package/src/lib/reactivity/text/createNoteText.ts +72 -0
- package/src/lib/reactivity/text/createTextRange.ts +17 -90
- package/src/lib/reactivity/validation/createAggregatedViolations.ts +70 -0
- package/src/lib/reactivity/validation/createValidation.ts +196 -0
- package/src/model/BindComputation.ts +0 -4
- package/src/model/BindDefinition.ts +8 -6
- package/src/model/BindElement.ts +1 -0
- package/src/model/{ValueNodeDefinition.ts → LeafNodeDefinition.ts} +4 -4
- package/src/model/ModelBindMap.ts +4 -0
- package/src/model/NodeDefinition.ts +12 -12
- package/src/model/RepeatInstanceDefinition.ts +2 -2
- package/src/model/RepeatRangeDefinition.ts +49 -8
- package/src/model/RootDefinition.ts +7 -3
- package/src/parse/NoteNodeDefinition.ts +70 -0
- package/src/parse/TODO.md +3 -0
- package/src/parse/expression/RepeatCountControlExpression.ts +44 -0
- package/src/parse/text/HintDefinition.ts +25 -0
- package/src/parse/text/ItemLabelDefinition.ts +25 -0
- package/src/parse/text/ItemsetLabelDefinition.ts +44 -0
- package/src/parse/text/LabelDefinition.ts +61 -0
- package/src/parse/text/MessageDefinition.ts +49 -0
- package/src/parse/text/OutputChunkDefinition.ts +25 -0
- package/src/parse/text/ReferenceChunkDefinition.ts +14 -0
- package/src/parse/text/StaticTextChunkDefinition.ts +19 -0
- package/src/parse/text/TranslationChunkDefinition.ts +38 -0
- package/src/parse/text/abstract/TextChunkDefinition.ts +38 -0
- package/src/parse/text/abstract/TextElementDefinition.ts +71 -0
- package/src/parse/text/abstract/TextRangeDefinition.ts +70 -0
- package/src/parse/xpath/dependency-analysis.ts +105 -0
- package/src/parse/xpath/path-resolution.ts +475 -0
- package/src/parse/xpath/predicate-analysis.ts +61 -0
- package/src/parse/xpath/reference-parsing.ts +90 -0
- package/src/parse/xpath/semantic-analysis.ts +466 -0
- package/src/parse/xpath/syntax-traversal.ts +129 -0
- package/dist/body/text/HintDefinition.d.ts +0 -11
- package/dist/body/text/LabelDefinition.d.ts +0 -22
- package/dist/body/text/TextElementDefinition.d.ts +0 -33
- package/dist/body/text/TextElementOutputPart.d.ts +0 -13
- package/dist/body/text/TextElementPart.d.ts +0 -13
- package/dist/body/text/TextElementReferencePart.d.ts +0 -7
- package/dist/body/text/TextElementStaticPart.d.ts +0 -7
- package/dist/lib/xpath/analysis.d.ts +0 -23
- package/src/body/text/HintDefinition.ts +0 -26
- package/src/body/text/LabelDefinition.ts +0 -68
- package/src/body/text/TextElementDefinition.ts +0 -97
- package/src/body/text/TextElementOutputPart.ts +0 -27
- package/src/body/text/TextElementPart.ts +0 -31
- package/src/body/text/TextElementReferencePart.ts +0 -21
- package/src/body/text/TextElementStaticPart.ts +0 -26
- package/src/lib/xpath/analysis.ts +0 -241
|
@@ -8,10 +8,12 @@ import type {
|
|
|
8
8
|
} from '../../expression/DependentExpression.ts';
|
|
9
9
|
import type { EvaluationContext } from '../../instance/internal-api/EvaluationContext.ts';
|
|
10
10
|
import type { SubscribableDependency } from '../../instance/internal-api/SubscribableDependency.ts';
|
|
11
|
+
import { isConstantExpression } from '../../parse/xpath/semantic-analysis.ts';
|
|
11
12
|
|
|
12
13
|
interface ComputedExpressionResults {
|
|
13
14
|
readonly boolean: boolean;
|
|
14
15
|
readonly nodes: Node[];
|
|
16
|
+
readonly number: number;
|
|
15
17
|
readonly string: string;
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -44,6 +46,11 @@ const expressionEvaluator = <Type extends DependentExpressionResultType>(
|
|
|
44
46
|
return evaluator.evaluateNodes(expression, options);
|
|
45
47
|
}) as ExpressionEvaluator<Type>;
|
|
46
48
|
|
|
49
|
+
case 'number':
|
|
50
|
+
return (() => {
|
|
51
|
+
return evaluator.evaluateNumber(expression, options);
|
|
52
|
+
}) as ExpressionEvaluator<Type>;
|
|
53
|
+
|
|
47
54
|
case 'string':
|
|
48
55
|
return (() => {
|
|
49
56
|
return evaluator.evaluateString(expression, options);
|
|
@@ -54,26 +61,19 @@ const expressionEvaluator = <Type extends DependentExpressionResultType>(
|
|
|
54
61
|
}
|
|
55
62
|
};
|
|
56
63
|
|
|
57
|
-
/**
|
|
58
|
-
* Determines if an XPath expression will always produce the same value.
|
|
59
|
-
*
|
|
60
|
-
* @todo There are quite a few more cases than this, and it also likely belongs
|
|
61
|
-
* in another `lib` module.
|
|
62
|
-
*/
|
|
63
|
-
const isConstantExpression = (expression: string): boolean => {
|
|
64
|
-
const normalized = expression.replaceAll(/\s/g, '');
|
|
65
|
-
|
|
66
|
-
return normalized === 'true()' || normalized === 'false()';
|
|
67
|
-
};
|
|
68
|
-
|
|
69
64
|
// prettier-ignore
|
|
70
65
|
type ComputedExpression<Type extends DependentExpressionResultType> = Accessor<
|
|
71
66
|
EvaluatedExpression<Type>
|
|
72
67
|
>;
|
|
73
68
|
|
|
69
|
+
interface CreateComputedExpressionOptions {
|
|
70
|
+
readonly arbitraryDependencies?: readonly SubscribableDependency[];
|
|
71
|
+
}
|
|
72
|
+
|
|
74
73
|
export const createComputedExpression = <Type extends DependentExpressionResultType>(
|
|
75
74
|
context: EvaluationContext,
|
|
76
|
-
dependentExpression: DependentExpression<Type
|
|
75
|
+
dependentExpression: DependentExpression<Type>,
|
|
76
|
+
options: CreateComputedExpressionOptions = {}
|
|
77
77
|
): ComputedExpression<Type> => {
|
|
78
78
|
const { contextNode, evaluator, root, scope } = context;
|
|
79
79
|
const { expression, isTranslated, resultType } = dependentExpression;
|
|
@@ -85,26 +85,24 @@ export const createComputedExpression = <Type extends DependentExpressionResultT
|
|
|
85
85
|
return createMemo(evaluateExpression);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
const { arbitraryDependencies = [] } = options;
|
|
89
|
+
|
|
88
90
|
const getReferencedDependencies = createMemo(() => {
|
|
89
91
|
return dependencyReferences.flatMap((reference) => {
|
|
90
92
|
return context.getSubscribableDependenciesByReference(reference) ?? [];
|
|
91
93
|
});
|
|
92
94
|
});
|
|
93
95
|
|
|
94
|
-
|
|
96
|
+
return createMemo(() => {
|
|
97
|
+
if (isTranslated) {
|
|
98
|
+
root.subscribe();
|
|
99
|
+
}
|
|
95
100
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return [root, ...getReferencedDependencies()];
|
|
101
|
+
arbitraryDependencies.forEach((dependency) => {
|
|
102
|
+
dependency.subscribe();
|
|
99
103
|
});
|
|
100
|
-
} else {
|
|
101
|
-
getDependencies = getReferencedDependencies;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return createMemo(() => {
|
|
105
|
-
const dependencies = getDependencies();
|
|
106
104
|
|
|
107
|
-
|
|
105
|
+
getReferencedDependencies().forEach((dependency) => {
|
|
108
106
|
dependency.subscribe();
|
|
109
107
|
});
|
|
110
108
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Accessor } from 'solid-js';
|
|
2
|
+
import type { EvaluationContext } from '../../instance/internal-api/EvaluationContext.ts';
|
|
3
|
+
import type { BindComputation } from '../../model/BindComputation.ts';
|
|
4
|
+
import { createComputedExpression } from './createComputedExpression.ts';
|
|
5
|
+
|
|
6
|
+
export const createNoteReadonlyThunk = (
|
|
7
|
+
context: EvaluationContext,
|
|
8
|
+
readonlyDefinition: BindComputation<'readonly'>
|
|
9
|
+
): Accessor<true> => {
|
|
10
|
+
if (!readonlyDefinition.isConstantTruthyExpression()) {
|
|
11
|
+
throw new Error('Expected a static readonly expression');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let result = true;
|
|
15
|
+
|
|
16
|
+
if (import.meta.env.DEV) {
|
|
17
|
+
const { expression } = readonlyDefinition;
|
|
18
|
+
|
|
19
|
+
if (readonlyDefinition.dependencyReferences.size > 0) {
|
|
20
|
+
throw new Error(`Expected expression ${expression} to have no dependencies`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const computedExpression = createComputedExpression(context, readonlyDefinition);
|
|
24
|
+
|
|
25
|
+
result = computedExpression();
|
|
26
|
+
|
|
27
|
+
if (result !== true) {
|
|
28
|
+
throw new Error(`Expected expression ${readonlyDefinition.expression} to return true`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return () => result;
|
|
33
|
+
};
|
|
@@ -4,6 +4,7 @@ import type { Accessor } from 'solid-js';
|
|
|
4
4
|
import { createMemo } from 'solid-js';
|
|
5
5
|
import type { ItemDefinition } from '../../body/control/select/ItemDefinition.ts';
|
|
6
6
|
import type { ItemsetDefinition } from '../../body/control/select/ItemsetDefinition.ts';
|
|
7
|
+
import type { TextRange as ClientTextRange } from '../../client/TextRange.ts';
|
|
7
8
|
import type { SelectItem } from '../../index.ts';
|
|
8
9
|
import type { SelectField } from '../../instance/SelectField.ts';
|
|
9
10
|
import type {
|
|
@@ -11,20 +12,31 @@ import type {
|
|
|
11
12
|
EvaluationContextRoot,
|
|
12
13
|
} from '../../instance/internal-api/EvaluationContext.ts';
|
|
13
14
|
import type { SubscribableDependency } from '../../instance/internal-api/SubscribableDependency.ts';
|
|
14
|
-
import
|
|
15
|
+
import { TextChunk } from '../../instance/text/TextChunk.ts';
|
|
16
|
+
import { TextRange } from '../../instance/text/TextRange.ts';
|
|
15
17
|
import { createComputedExpression } from './createComputedExpression.ts';
|
|
16
18
|
import type { ReactiveScope } from './scope.ts';
|
|
17
19
|
import { createTextRange } from './text/createTextRange.ts';
|
|
18
20
|
|
|
21
|
+
type DerivedItemLabel = ClientTextRange<'item-label', 'form-derived'>;
|
|
22
|
+
|
|
23
|
+
const derivedItemLabel = (context: EvaluationContext, value: string): DerivedItemLabel => {
|
|
24
|
+
const chunk = new TextChunk(context.root, 'static', value);
|
|
25
|
+
|
|
26
|
+
return new TextRange('form-derived', 'item-label', [chunk]);
|
|
27
|
+
};
|
|
28
|
+
|
|
19
29
|
const createSelectItemLabel = (
|
|
20
30
|
context: EvaluationContext,
|
|
21
31
|
definition: ItemDefinition
|
|
22
|
-
): Accessor<
|
|
32
|
+
): Accessor<ClientTextRange<'item-label'>> => {
|
|
23
33
|
const { label, value } = definition;
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
35
|
+
if (label == null) {
|
|
36
|
+
return () => derivedItemLabel(context, value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return createTextRange(context, 'item-label', label);
|
|
28
40
|
};
|
|
29
41
|
|
|
30
42
|
const createTranslatedStaticSelectItems = (
|
|
@@ -73,25 +85,20 @@ const createSelectItemsetItemLabel = (
|
|
|
73
85
|
context: EvaluationContext,
|
|
74
86
|
definition: ItemsetDefinition,
|
|
75
87
|
itemValue: Accessor<string>
|
|
76
|
-
): Accessor<
|
|
88
|
+
): Accessor<ClientTextRange<'item-label'>> => {
|
|
77
89
|
const { label } = definition;
|
|
78
90
|
|
|
79
91
|
if (label == null) {
|
|
80
92
|
return createMemo(() => {
|
|
81
|
-
|
|
82
|
-
const staticValueLabel = createTextRange(context, 'label', label, {
|
|
83
|
-
fallbackValue: value,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
return staticValueLabel();
|
|
93
|
+
return derivedItemLabel(context, itemValue());
|
|
87
94
|
});
|
|
88
95
|
}
|
|
89
96
|
|
|
90
|
-
return createTextRange(context, 'label', label);
|
|
97
|
+
return createTextRange(context, 'item-label', label);
|
|
91
98
|
};
|
|
92
99
|
|
|
93
100
|
interface ItemsetItem {
|
|
94
|
-
label():
|
|
101
|
+
label(): ClientTextRange<'item-label'>;
|
|
95
102
|
value(): string;
|
|
96
103
|
}
|
|
97
104
|
|
|
@@ -31,7 +31,7 @@ export interface SharedNodeState<Spec extends StateSpec> {
|
|
|
31
31
|
readonly setProperty: SetEnginePropertyState<Spec>;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
interface SharedNodeStateOptions<
|
|
34
|
+
export interface SharedNodeStateOptions<
|
|
35
35
|
Factory extends OpaqueReactiveObjectFactory,
|
|
36
36
|
Spec extends StateSpec,
|
|
37
37
|
> {
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { type Accessor } from 'solid-js';
|
|
2
|
+
import type { TextRange } from '../../../client/TextRange.ts';
|
|
2
3
|
import type { EvaluationContext } from '../../../instance/internal-api/EvaluationContext.ts';
|
|
3
|
-
import {
|
|
4
|
-
import type { ValueNodeDefinition } from '../../../model/ValueNodeDefinition.ts';
|
|
4
|
+
import type { LeafNodeDefinition } from '../../../model/LeafNodeDefinition.ts';
|
|
5
5
|
import { createTextRange } from './createTextRange.ts';
|
|
6
6
|
|
|
7
7
|
export const createFieldHint = (
|
|
8
8
|
context: EvaluationContext,
|
|
9
|
-
definition:
|
|
10
|
-
): Accessor<TextRange<'hint'> | null> => {
|
|
9
|
+
definition: LeafNodeDefinition
|
|
10
|
+
): Accessor<TextRange<'hint', 'form'> | null> => {
|
|
11
11
|
const hintDefinition = definition.bodyElement?.hint ?? null;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
13
|
+
if (hintDefinition == null) {
|
|
14
|
+
return () => null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return createTextRange(context, 'hint', hintDefinition);
|
|
16
18
|
};
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { type Accessor } from 'solid-js';
|
|
2
|
+
import type { TextRange } from '../../../client/TextRange.ts';
|
|
2
3
|
import type { EvaluationContext } from '../../../instance/internal-api/EvaluationContext.ts';
|
|
3
|
-
import { TextRange } from '../../../instance/text/TextRange.ts';
|
|
4
4
|
import type { AnyNodeDefinition } from '../../../model/NodeDefinition.ts';
|
|
5
5
|
import { createTextRange } from './createTextRange.ts';
|
|
6
6
|
|
|
7
7
|
export const createNodeLabel = (
|
|
8
8
|
context: EvaluationContext,
|
|
9
9
|
definition: AnyNodeDefinition
|
|
10
|
-
): Accessor<TextRange<'label'> | null> => {
|
|
10
|
+
): Accessor<TextRange<'label', 'form'> | null> => {
|
|
11
11
|
const labelDefinition = definition.bodyElement?.label ?? null;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
13
|
+
if (labelDefinition == null) {
|
|
14
|
+
return () => null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return createTextRange(context, 'label', labelDefinition);
|
|
16
18
|
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
|
|
2
|
+
import type { Accessor } from 'solid-js';
|
|
3
|
+
import type { TextRange } from '../../../client/TextRange.ts';
|
|
4
|
+
import type { EvaluationContext } from '../../../instance/internal-api/EvaluationContext.ts';
|
|
5
|
+
import type { NoteTextDefinition } from '../../../parse/NoteNodeDefinition.ts';
|
|
6
|
+
import { createTextRange } from './createTextRange.ts';
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
9
|
+
export type NoteTextRole = 'label' | 'hint';
|
|
10
|
+
|
|
11
|
+
export type ComputedNoteText<Role extends NoteTextRole = NoteTextRole> = Accessor<
|
|
12
|
+
TextRange<Role, 'form'>
|
|
13
|
+
>;
|
|
14
|
+
|
|
15
|
+
interface BaseNoteText {
|
|
16
|
+
readonly role: NoteTextRole;
|
|
17
|
+
readonly label: ComputedNoteText<'label'> | null;
|
|
18
|
+
readonly hint: ComputedNoteText<'hint'> | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface LabelNoteText extends BaseNoteText {
|
|
22
|
+
readonly role: 'label';
|
|
23
|
+
readonly label: ComputedNoteText<'label'>;
|
|
24
|
+
readonly hint: null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface HintNoteText extends BaseNoteText {
|
|
28
|
+
readonly role: 'hint';
|
|
29
|
+
readonly label: null;
|
|
30
|
+
readonly hint: ComputedNoteText<'hint'>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// prettier-ignore
|
|
34
|
+
export type NoteTextComputation =
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
36
|
+
| LabelNoteText
|
|
37
|
+
| HintNoteText;
|
|
38
|
+
|
|
39
|
+
export const createNoteText = (
|
|
40
|
+
context: EvaluationContext,
|
|
41
|
+
noteTextDefinition: NoteTextDefinition
|
|
42
|
+
): NoteTextComputation => {
|
|
43
|
+
const { scope } = context;
|
|
44
|
+
const { role } = noteTextDefinition;
|
|
45
|
+
|
|
46
|
+
return scope.runTask(() => {
|
|
47
|
+
switch (role) {
|
|
48
|
+
case 'label': {
|
|
49
|
+
const label = createTextRange(context, role, noteTextDefinition);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
role,
|
|
53
|
+
label,
|
|
54
|
+
hint: null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
case 'hint': {
|
|
59
|
+
const hint = createTextRange(context, role, noteTextDefinition);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
role,
|
|
63
|
+
label: null,
|
|
64
|
+
hint,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
default:
|
|
69
|
+
throw new UnreachableError(noteTextDefinition);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
};
|
|
@@ -1,23 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { createMemo
|
|
3
|
-
import type {
|
|
4
|
-
TextElementChild,
|
|
5
|
-
TextElementDefinition,
|
|
6
|
-
} from '../../../body/text/TextElementDefinition.ts';
|
|
7
|
-
import type { TextElementReferencePart } from '../../../body/text/TextElementReferencePart.ts';
|
|
8
|
-
import type { TextChunkSource } from '../../../client/TextRange.ts';
|
|
1
|
+
import type { Accessor } from 'solid-js';
|
|
2
|
+
import { createMemo } from 'solid-js';
|
|
3
|
+
import type { TextChunkSource, TextRole } from '../../../client/TextRange.ts';
|
|
9
4
|
import type { EvaluationContext } from '../../../instance/internal-api/EvaluationContext.ts';
|
|
10
5
|
import { TextChunk } from '../../../instance/text/TextChunk.ts';
|
|
11
|
-
import { TextRange
|
|
6
|
+
import { TextRange } from '../../../instance/text/TextRange.ts';
|
|
7
|
+
import type { AnyTextChunkDefinition } from '../../../parse/text/abstract/TextChunkDefinition.ts';
|
|
8
|
+
import type { TextRangeDefinition } from '../../../parse/text/abstract/TextRangeDefinition.ts';
|
|
12
9
|
import { createComputedExpression } from '../createComputedExpression.ts';
|
|
13
10
|
|
|
14
|
-
// prettier-ignore
|
|
15
|
-
type TextSources =
|
|
16
|
-
| readonly [TextElementReferencePart]
|
|
17
|
-
| readonly TextElementChild[];
|
|
18
|
-
|
|
19
|
-
type TextSource = CollectionValues<TextSources>;
|
|
20
|
-
|
|
21
11
|
interface TextChunkComputation {
|
|
22
12
|
readonly source: TextChunkSource;
|
|
23
13
|
readonly getText: Accessor<string>;
|
|
@@ -25,21 +15,20 @@ interface TextChunkComputation {
|
|
|
25
15
|
|
|
26
16
|
const createComputedTextChunk = (
|
|
27
17
|
context: EvaluationContext,
|
|
28
|
-
textSource:
|
|
18
|
+
textSource: AnyTextChunkDefinition
|
|
29
19
|
): TextChunkComputation => {
|
|
30
|
-
const {
|
|
20
|
+
const { source } = textSource;
|
|
31
21
|
|
|
32
|
-
if (
|
|
22
|
+
if (source === 'static') {
|
|
33
23
|
const { stringValue } = textSource;
|
|
34
24
|
|
|
35
25
|
return {
|
|
36
|
-
source
|
|
26
|
+
source,
|
|
37
27
|
getText: () => stringValue,
|
|
38
28
|
};
|
|
39
29
|
}
|
|
40
30
|
|
|
41
31
|
return context.scope.runTask(() => {
|
|
42
|
-
const source: TextChunkSource = type === 'reference' ? 'itext' : type;
|
|
43
32
|
const getText = createComputedExpression(context, textSource);
|
|
44
33
|
|
|
45
34
|
return {
|
|
@@ -51,7 +40,7 @@ const createComputedTextChunk = (
|
|
|
51
40
|
|
|
52
41
|
const createTextChunks = (
|
|
53
42
|
context: EvaluationContext,
|
|
54
|
-
textSources:
|
|
43
|
+
textSources: readonly AnyTextChunkDefinition[]
|
|
55
44
|
): Accessor<readonly TextChunk[]> => {
|
|
56
45
|
return context.scope.runTask(() => {
|
|
57
46
|
const { root } = context;
|
|
@@ -67,45 +56,7 @@ const createTextChunks = (
|
|
|
67
56
|
});
|
|
68
57
|
};
|
|
69
58
|
|
|
70
|
-
|
|
71
|
-
readonly fallbackValue?: FallbackValue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// prettier-ignore
|
|
75
|
-
type ComputedTextRange<
|
|
76
|
-
Role extends TextRole,
|
|
77
|
-
Definition extends TextElementDefinition<Role> | null,
|
|
78
|
-
FallbackValue extends string | null
|
|
79
|
-
> = Accessor<
|
|
80
|
-
Definition extends null
|
|
81
|
-
? FallbackValue extends null
|
|
82
|
-
? TextRange<Role> | null
|
|
83
|
-
: TextRange<Role>
|
|
84
|
-
: TextRange<Role>
|
|
85
|
-
>;
|
|
86
|
-
|
|
87
|
-
// prettier-ignore
|
|
88
|
-
type FallbackTextRange<
|
|
89
|
-
Role extends TextRole,
|
|
90
|
-
FallbackValue extends string | null
|
|
91
|
-
> =
|
|
92
|
-
FallbackValue extends null
|
|
93
|
-
? TextRange<Role> | null
|
|
94
|
-
: TextRange<Role>;
|
|
95
|
-
|
|
96
|
-
const createFallbackTextRange = <Role extends TextRole, FallbackValue extends string | null>(
|
|
97
|
-
context: EvaluationContext,
|
|
98
|
-
role: Role,
|
|
99
|
-
fallbackValue: FallbackValue
|
|
100
|
-
): FallbackTextRange<Role, FallbackValue> => {
|
|
101
|
-
if (fallbackValue == null) {
|
|
102
|
-
return null as FallbackTextRange<Role, FallbackValue>;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const staticChunk = new TextChunk(context.root, 'static', fallbackValue);
|
|
106
|
-
|
|
107
|
-
return new TextRange(role, [staticChunk]);
|
|
108
|
-
};
|
|
59
|
+
type ComputedFormTextRange<Role extends TextRole> = Accessor<TextRange<Role, 'form'>>;
|
|
109
60
|
|
|
110
61
|
/**
|
|
111
62
|
* Creates a text range (e.g. label or hint) from the provided definition,
|
|
@@ -116,40 +67,16 @@ const createFallbackTextRange = <Role extends TextRole, FallbackValue extends st
|
|
|
116
67
|
*
|
|
117
68
|
* @todo This does not yet handle itext translations **with** outputs!
|
|
118
69
|
*/
|
|
119
|
-
export const createTextRange = <
|
|
120
|
-
Role extends TextRole,
|
|
121
|
-
Definition extends TextElementDefinition<Role> | null,
|
|
122
|
-
FallbackValue extends string | null = null,
|
|
123
|
-
>(
|
|
70
|
+
export const createTextRange = <Role extends TextRole>(
|
|
124
71
|
context: EvaluationContext,
|
|
125
72
|
role: Role,
|
|
126
|
-
definition:
|
|
127
|
-
|
|
128
|
-
): ComputedTextRange<Role, Definition, FallbackValue> => {
|
|
73
|
+
definition: TextRangeDefinition<Role>
|
|
74
|
+
): ComputedFormTextRange<Role> => {
|
|
129
75
|
return context.scope.runTask(() => {
|
|
130
|
-
|
|
131
|
-
const textRange = createFallbackTextRange(
|
|
132
|
-
context,
|
|
133
|
-
role,
|
|
134
|
-
options?.fallbackValue ?? (null as FallbackValue)
|
|
135
|
-
);
|
|
136
|
-
const getTextRange = () => textRange;
|
|
137
|
-
|
|
138
|
-
return getTextRange as ComputedTextRange<Role, Definition, FallbackValue>;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const { children, referenceExpression } = definition;
|
|
142
|
-
|
|
143
|
-
let getTextChunks: Accessor<readonly TextChunk[]>;
|
|
144
|
-
|
|
145
|
-
if (referenceExpression == null) {
|
|
146
|
-
getTextChunks = createTextChunks(context, children);
|
|
147
|
-
} else {
|
|
148
|
-
getTextChunks = createTextChunks(context, [referenceExpression]);
|
|
149
|
-
}
|
|
76
|
+
const getTextChunks = createTextChunks(context, definition.chunks);
|
|
150
77
|
|
|
151
78
|
return createMemo(() => {
|
|
152
|
-
return new TextRange(role, getTextChunks());
|
|
79
|
+
return new TextRange('form', role, getTextChunks());
|
|
153
80
|
});
|
|
154
81
|
});
|
|
155
82
|
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createMemo } from 'solid-js';
|
|
2
|
+
import type { OpaqueReactiveObjectFactory } from '../../../client/OpaqueReactiveObjectFactory.ts';
|
|
3
|
+
import type {
|
|
4
|
+
AncestorNodeValidationState,
|
|
5
|
+
DescendantNodeViolationReference,
|
|
6
|
+
} from '../../../client/validation.ts';
|
|
7
|
+
import type { AnyParentNode, AnyValueNode } from '../../../instance/hierarchy.ts';
|
|
8
|
+
import { createSharedNodeState } from '../node-state/createSharedNodeState.ts';
|
|
9
|
+
|
|
10
|
+
const violationReference = (node: AnyValueNode): DescendantNodeViolationReference | null => {
|
|
11
|
+
const violation = node.getViolation();
|
|
12
|
+
|
|
13
|
+
if (violation == null) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { nodeId } = node;
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
nodeId,
|
|
21
|
+
get reference() {
|
|
22
|
+
return node.currentState.reference;
|
|
23
|
+
},
|
|
24
|
+
violation,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const collectViolationReferences = (
|
|
29
|
+
context: AnyParentNode
|
|
30
|
+
): readonly DescendantNodeViolationReference[] => {
|
|
31
|
+
return context.getChildren().flatMap((child) => {
|
|
32
|
+
switch (child.nodeType) {
|
|
33
|
+
case 'model-value':
|
|
34
|
+
case 'note':
|
|
35
|
+
case 'string':
|
|
36
|
+
case 'select': {
|
|
37
|
+
const reference = violationReference(child);
|
|
38
|
+
|
|
39
|
+
if (reference == null) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return [reference];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
default:
|
|
47
|
+
return collectViolationReferences(child);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
interface AggregatedViolationsOptions {
|
|
53
|
+
readonly clientStateFactory: OpaqueReactiveObjectFactory<AncestorNodeValidationState>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const createAggregatedViolations = (
|
|
57
|
+
context: AnyParentNode,
|
|
58
|
+
options: AggregatedViolationsOptions
|
|
59
|
+
): AncestorNodeValidationState => {
|
|
60
|
+
const { scope } = context;
|
|
61
|
+
|
|
62
|
+
return scope.runTask(() => {
|
|
63
|
+
const violations = createMemo(() => {
|
|
64
|
+
return collectViolationReferences(context);
|
|
65
|
+
});
|
|
66
|
+
const spec = { violations };
|
|
67
|
+
|
|
68
|
+
return createSharedNodeState(scope, spec, options).currentState;
|
|
69
|
+
});
|
|
70
|
+
};
|