@getodk/xforms-engine 0.1.1 → 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/BodyDefinition.d.ts +24 -7
- package/dist/body/BodyElementDefinition.d.ts +4 -3
- package/dist/body/RepeatElementDefinition.d.ts +19 -0
- package/dist/body/appearance/inputAppearanceParser.d.ts +4 -0
- package/dist/body/appearance/selectAppearanceParser.d.ts +4 -0
- package/dist/body/appearance/structureElementAppearanceParser.d.ts +4 -0
- package/dist/body/control/ControlDefinition.d.ts +4 -2
- package/dist/body/control/InputDefinition.d.ts +5 -0
- package/dist/body/control/select/ItemDefinition.d.ts +2 -2
- package/dist/body/control/select/ItemsetDefinition.d.ts +5 -4
- package/dist/body/control/select/SelectDefinition.d.ts +11 -1
- package/dist/body/group/BaseGroupDefinition.d.ts +4 -9
- package/dist/body/group/PresentationGroupDefinition.d.ts +1 -1
- package/dist/client/BaseNode.d.ts +74 -3
- package/dist/client/GroupNode.d.ts +7 -2
- package/dist/client/ModelValueNode.d.ts +37 -0
- package/dist/client/NodeAppearances.d.ts +15 -0
- package/dist/client/NoteNode.d.ts +53 -0
- package/dist/client/RootNode.d.ts +21 -0
- package/dist/client/SelectNode.d.ts +8 -3
- package/dist/client/StringNode.d.ts +8 -3
- package/dist/client/SubtreeNode.d.ts +3 -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} +19 -15
- package/dist/client/{RepeatInstanceNode.d.ts → repeat/RepeatInstanceNode.d.ts} +11 -7
- 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 +3173 -960
- package/dist/index.js.map +1 -1
- package/dist/instance/Group.d.ts +6 -4
- package/dist/instance/ModelValue.d.ts +40 -0
- package/dist/instance/Note.d.ts +42 -0
- package/dist/instance/Root.d.ts +10 -23
- package/dist/instance/SelectField.d.ts +12 -6
- package/dist/instance/StringField.d.ts +13 -7
- package/dist/instance/Subtree.d.ts +3 -1
- package/dist/instance/abstract/DescendantNode.d.ts +16 -9
- package/dist/instance/abstract/InstanceNode.d.ts +28 -29
- package/dist/instance/hierarchy.d.ts +10 -5
- package/dist/instance/internal-api/EvaluationContext.d.ts +5 -4
- package/dist/instance/internal-api/ValidationContext.d.ts +21 -0
- package/dist/instance/internal-api/ValueContext.d.ts +2 -2
- package/dist/instance/repeat/BaseRepeatRange.d.ts +160 -0
- package/dist/instance/{RepeatInstance.d.ts → repeat/RepeatInstance.d.ts} +38 -13
- 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/TokenListParser.d.ts +84 -0
- package/dist/lib/dom/query.d.ts +5 -0
- package/dist/lib/reactivity/createComputedExpression.d.ts +6 -1
- package/dist/lib/reactivity/createNoteReadonlyThunk.d.ts +5 -0
- package/dist/lib/reactivity/materializeCurrentStateChildren.d.ts +2 -1
- 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/DescendentNodeDefinition.d.ts +1 -2
- package/dist/model/{ValueNodeDefinition.d.ts → LeafNodeDefinition.d.ts} +3 -4
- package/dist/model/NodeDefinition.d.ts +16 -16
- package/dist/model/RepeatInstanceDefinition.d.ts +5 -6
- package/dist/model/RepeatRangeDefinition.d.ts +30 -0
- package/dist/model/RepeatTemplateDefinition.d.ts +6 -7
- package/dist/model/RootDefinition.d.ts +3 -1
- package/dist/model/SubtreeDefinition.d.ts +2 -2
- 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 +3174 -961
- package/dist/solid.js.map +1 -1
- package/package.json +14 -15
- package/src/XFormDOM.ts +81 -8
- package/src/body/BodyDefinition.ts +38 -23
- package/src/body/BodyElementDefinition.ts +4 -3
- package/src/body/RepeatElementDefinition.ts +58 -0
- package/src/body/appearance/inputAppearanceParser.ts +39 -0
- package/src/body/appearance/selectAppearanceParser.ts +38 -0
- package/src/body/appearance/structureElementAppearanceParser.ts +7 -0
- package/src/body/control/ControlDefinition.ts +8 -3
- package/src/body/control/InputDefinition.ts +13 -0
- 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/control/select/SelectDefinition.ts +14 -5
- package/src/body/group/BaseGroupDefinition.ts +14 -51
- package/src/body/group/PresentationGroupDefinition.ts +1 -1
- package/src/client/BaseNode.ts +82 -8
- package/src/client/GroupNode.ts +8 -2
- package/src/client/ModelValueNode.ts +40 -0
- package/src/client/NodeAppearances.ts +22 -0
- package/src/client/NoteNode.ts +74 -0
- package/src/client/README.md +1 -0
- package/src/client/RootNode.ts +24 -0
- package/src/client/SelectNode.ts +9 -3
- package/src/client/StringNode.ts +9 -3
- package/src/client/SubtreeNode.ts +3 -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} +20 -17
- package/src/client/{RepeatInstanceNode.ts → repeat/RepeatInstanceNode.ts} +13 -7
- 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 +24 -13
- package/src/instance/ModelValue.ts +104 -0
- package/src/instance/Note.ts +142 -0
- package/src/instance/Root.ts +29 -67
- package/src/instance/SelectField.ts +35 -13
- package/src/instance/StringField.ts +40 -13
- package/src/instance/Subtree.ts +19 -10
- package/src/instance/abstract/DescendantNode.ts +50 -49
- package/src/instance/abstract/InstanceNode.ts +89 -92
- package/src/instance/children.ts +47 -10
- package/src/instance/hierarchy.ts +21 -2
- package/src/instance/index.ts +1 -1
- package/src/instance/internal-api/EvaluationContext.ts +5 -6
- package/src/instance/internal-api/ValidationContext.ts +23 -0
- package/src/instance/internal-api/ValueContext.ts +2 -2
- package/src/instance/repeat/BaseRepeatRange.ts +347 -0
- package/src/instance/{RepeatInstance.ts → repeat/RepeatInstance.ts} +85 -36
- 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/TokenListParser.ts +156 -0
- package/src/lib/dom/query.ts +13 -0
- package/src/lib/reactivity/createChildrenState.ts +51 -6
- package/src/lib/reactivity/createComputedExpression.ts +23 -25
- package/src/lib/reactivity/createNoteReadonlyThunk.ts +33 -0
- package/src/lib/reactivity/createSelectItems.ts +25 -20
- package/src/lib/reactivity/createValueState.ts +6 -6
- package/src/lib/reactivity/materializeCurrentStateChildren.ts +3 -1
- 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/DescendentNodeDefinition.ts +1 -2
- package/src/model/{ValueNodeDefinition.ts → LeafNodeDefinition.ts} +5 -6
- package/src/model/ModelBindMap.ts +4 -0
- package/src/model/ModelDefinition.ts +1 -1
- package/src/model/NodeDefinition.ts +21 -21
- package/src/model/RepeatInstanceDefinition.ts +8 -13
- package/src/model/RepeatRangeDefinition.ts +94 -0
- package/src/model/RepeatTemplateDefinition.ts +10 -15
- package/src/model/RootDefinition.ts +12 -14
- package/src/model/SubtreeDefinition.ts +3 -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/RepeatDefinition.d.ts +0 -16
- package/dist/body/group/RepeatGroupDefinition.d.ts +0 -13
- package/dist/body/text/HintDefinition.d.ts +0 -11
- package/dist/body/text/LabelDefinition.d.ts +0 -20
- 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/instance/RepeatRange.d.ts +0 -80
- package/dist/lib/xpath/analysis.d.ts +0 -23
- package/dist/model/RepeatSequenceDefinition.d.ts +0 -20
- package/src/body/RepeatDefinition.ts +0 -54
- package/src/body/group/RepeatGroupDefinition.ts +0 -91
- package/src/body/text/HintDefinition.ts +0 -26
- package/src/body/text/LabelDefinition.ts +0 -54
- 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/instance/RepeatRange.ts +0 -214
- package/src/lib/xpath/analysis.ts +0 -241
- package/src/model/RepeatSequenceDefinition.ts +0 -53
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { Accessor } from 'solid-js';
|
|
2
|
+
import { createMemo } from 'solid-js';
|
|
3
|
+
import type { OpaqueReactiveObjectFactory } from '../../../client/OpaqueReactiveObjectFactory.ts';
|
|
4
|
+
import type {
|
|
5
|
+
TextRange as ClientTextRange,
|
|
6
|
+
ValidationTextRole,
|
|
7
|
+
} from '../../../client/TextRange.ts';
|
|
8
|
+
import { VALIDATION_TEXT } from '../../../client/constants.ts';
|
|
9
|
+
import type {
|
|
10
|
+
AnyViolation,
|
|
11
|
+
ConditionSatisfied,
|
|
12
|
+
ConditionValidation,
|
|
13
|
+
ConditionViolation,
|
|
14
|
+
ValidationCondition,
|
|
15
|
+
} from '../../../client/validation.ts';
|
|
16
|
+
import type { ValidationContext } from '../../../instance/internal-api/ValidationContext.ts';
|
|
17
|
+
import { TextChunk } from '../../../instance/text/TextChunk.ts';
|
|
18
|
+
import { TextRange } from '../../../instance/text/TextRange.ts';
|
|
19
|
+
import type { MessageDefinition } from '../../../parse/text/MessageDefinition.ts';
|
|
20
|
+
import { createComputedExpression } from '../createComputedExpression.ts';
|
|
21
|
+
import type {
|
|
22
|
+
SharedNodeState,
|
|
23
|
+
SharedNodeStateOptions,
|
|
24
|
+
} from '../node-state/createSharedNodeState.ts';
|
|
25
|
+
import { createSharedNodeState } from '../node-state/createSharedNodeState.ts';
|
|
26
|
+
import type { ReactiveScope } from '../scope.ts';
|
|
27
|
+
import { createTextRange } from '../text/createTextRange.ts';
|
|
28
|
+
|
|
29
|
+
type EngineViolationMessage<Role extends ValidationTextRole> = ClientTextRange<Role, 'engine'>;
|
|
30
|
+
|
|
31
|
+
const engineViolationMessage = <Role extends ValidationTextRole>(
|
|
32
|
+
context: ValidationContext,
|
|
33
|
+
role: Role
|
|
34
|
+
): Accessor<EngineViolationMessage<Role>> => {
|
|
35
|
+
const messageText = VALIDATION_TEXT[role];
|
|
36
|
+
const chunk = new TextChunk(context.root, 'static', messageText);
|
|
37
|
+
const message = new TextRange('engine', role, [chunk]);
|
|
38
|
+
|
|
39
|
+
return () => message;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const createViolationMessage = <Role extends ValidationTextRole>(
|
|
43
|
+
context: ValidationContext,
|
|
44
|
+
role: Role,
|
|
45
|
+
definition: MessageDefinition<Role> | null
|
|
46
|
+
) => {
|
|
47
|
+
if (definition == null) {
|
|
48
|
+
return engineViolationMessage(context, role);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return createTextRange(context, role, definition);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// prettier-ignore
|
|
55
|
+
type ComputedConditionValidation<
|
|
56
|
+
Condition extends ValidationCondition
|
|
57
|
+
> = Accessor<ConditionValidation<Condition>>;
|
|
58
|
+
|
|
59
|
+
const constraintValid = (): ConditionSatisfied<'constraint'> => {
|
|
60
|
+
return {
|
|
61
|
+
condition: 'constraint',
|
|
62
|
+
valid: true,
|
|
63
|
+
message: null,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const createConstraintValidation = (
|
|
68
|
+
context: ValidationContext
|
|
69
|
+
): ComputedConditionValidation<'constraint'> => {
|
|
70
|
+
return context.scope.runTask(() => {
|
|
71
|
+
const { constraint, constraintMsg } = context.definition.bind;
|
|
72
|
+
|
|
73
|
+
if (constraint == null) {
|
|
74
|
+
return constraintValid;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const isValid = createComputedExpression(context, constraint, {
|
|
78
|
+
arbitraryDependencies: [context],
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const message = createViolationMessage(context, 'constraintMsg', constraintMsg);
|
|
82
|
+
|
|
83
|
+
return createMemo(() => {
|
|
84
|
+
if (!context.isRelevant() || context.isBlank() || isValid()) {
|
|
85
|
+
return constraintValid();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
condition: 'constraint',
|
|
90
|
+
valid: false,
|
|
91
|
+
message: message(),
|
|
92
|
+
} as const;
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const requiredValid = (): ConditionSatisfied<'required'> => {
|
|
98
|
+
return {
|
|
99
|
+
condition: 'required',
|
|
100
|
+
valid: true,
|
|
101
|
+
message: null,
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const createRequiredValidation = (
|
|
106
|
+
context: ValidationContext
|
|
107
|
+
): ComputedConditionValidation<'required'> => {
|
|
108
|
+
return context.scope.runTask(() => {
|
|
109
|
+
const { required, requiredMsg } = context.definition.bind;
|
|
110
|
+
|
|
111
|
+
if (required.isDefaultExpression) {
|
|
112
|
+
return requiredValid;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const isValid = () => {
|
|
116
|
+
if (context.isRequired()) {
|
|
117
|
+
return !context.isBlank();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return true;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const message = createViolationMessage(context, 'requiredMsg', requiredMsg);
|
|
124
|
+
|
|
125
|
+
return createMemo(() => {
|
|
126
|
+
if (!context.isRelevant() || isValid()) {
|
|
127
|
+
return requiredValid();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
condition: 'required',
|
|
132
|
+
valid: false,
|
|
133
|
+
message: message(),
|
|
134
|
+
} as const;
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
type OptionalViolation<Condition extends ValidationCondition> =
|
|
140
|
+
Accessor<ConditionViolation<Condition> | null>;
|
|
141
|
+
|
|
142
|
+
const createComputedViolation = <Condition extends ValidationCondition>(
|
|
143
|
+
scope: ReactiveScope,
|
|
144
|
+
validateCondition: ComputedConditionValidation<Condition>
|
|
145
|
+
): OptionalViolation<Condition> => {
|
|
146
|
+
return scope.runTask(() => {
|
|
147
|
+
return createMemo(() => {
|
|
148
|
+
const validation = validateCondition();
|
|
149
|
+
|
|
150
|
+
if (validation.valid) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return validation;
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
type ComputedViolation = Accessor<AnyViolation | null>;
|
|
160
|
+
|
|
161
|
+
interface ValidationStateSpec {
|
|
162
|
+
readonly constraint: ComputedConditionValidation<'constraint'>;
|
|
163
|
+
readonly required: ComputedConditionValidation<'required'>;
|
|
164
|
+
readonly violation: ComputedViolation;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export type SharedValidationState = SharedNodeState<ValidationStateSpec>;
|
|
168
|
+
|
|
169
|
+
interface ValidationStateOptions<Factory extends OpaqueReactiveObjectFactory>
|
|
170
|
+
extends SharedNodeStateOptions<Factory, ValidationStateSpec> {}
|
|
171
|
+
|
|
172
|
+
export const createValidationState = <Factory extends OpaqueReactiveObjectFactory>(
|
|
173
|
+
context: ValidationContext,
|
|
174
|
+
options: ValidationStateOptions<Factory>
|
|
175
|
+
): SharedValidationState => {
|
|
176
|
+
const { scope } = context;
|
|
177
|
+
|
|
178
|
+
return scope.runTask(() => {
|
|
179
|
+
const constraint = createConstraintValidation(context);
|
|
180
|
+
const constraintViolation = createComputedViolation(scope, constraint);
|
|
181
|
+
const required = createRequiredValidation(context);
|
|
182
|
+
const requiredViolation = createComputedViolation(scope, required);
|
|
183
|
+
|
|
184
|
+
const violation = createMemo(() => {
|
|
185
|
+
return constraintViolation() ?? requiredViolation();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const spec: ValidationStateSpec = {
|
|
189
|
+
constraint,
|
|
190
|
+
required,
|
|
191
|
+
violation,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
return createSharedNodeState(scope, spec, options);
|
|
195
|
+
});
|
|
196
|
+
};
|
|
@@ -57,7 +57,6 @@ export class BindComputation<Computation extends BindComputationType> extends De
|
|
|
57
57
|
readonly computation: Computation,
|
|
58
58
|
expression: string | null
|
|
59
59
|
) {
|
|
60
|
-
const isInherited = computation === 'readonly' || computation === 'relevant';
|
|
61
60
|
const ignoreContextReference = computation === 'constraint';
|
|
62
61
|
|
|
63
62
|
let isDefaultExpression: boolean;
|
|
@@ -78,9 +77,6 @@ export class BindComputation<Computation extends BindComputationType> extends De
|
|
|
78
77
|
|
|
79
78
|
super(bind, bindComputationResultTypes[computation], resolvedExpression, {
|
|
80
79
|
ignoreContextReference,
|
|
81
|
-
semanticDependencies: {
|
|
82
|
-
parentContext: isInherited,
|
|
83
|
-
},
|
|
84
80
|
});
|
|
85
81
|
|
|
86
82
|
this.isDefaultExpression = isDefaultExpression;
|
|
@@ -3,6 +3,7 @@ import { bindDataType } from '../XFormDataType.ts';
|
|
|
3
3
|
import type { XFormDefinition } from '../XFormDefinition.ts';
|
|
4
4
|
import { DependencyContext } from '../expression/DependencyContext.ts';
|
|
5
5
|
import type { DependentExpression } from '../expression/DependentExpression.ts';
|
|
6
|
+
import { MessageDefinition } from '../parse/text/MessageDefinition.ts';
|
|
6
7
|
import { BindComputation } from './BindComputation.ts';
|
|
7
8
|
import type { BindElement } from './BindElement.ts';
|
|
8
9
|
import type { ModelDefinition } from './ModelDefinition.ts';
|
|
@@ -25,15 +26,16 @@ export class BindDefinition extends DependencyContext {
|
|
|
25
26
|
/**
|
|
26
27
|
* Diverges from {@link https://github.com/getodk/javarosa/blob/059321160e6f8dbb3e81d9add61d68dd35b13cc8/dag.md | JavaRosa's}, which excludes `constraint` expressions. We compute `constraint` dependencies like the other <bind> computation expressions, but explicitly ignore self-references (this is currently handled by {@link BindComputation}, via its {@link DependentExpression} parent class).
|
|
27
28
|
*/
|
|
28
|
-
readonly constraint: BindComputation<'constraint'
|
|
29
|
+
readonly constraint: BindComputation<'constraint'>;
|
|
30
|
+
|
|
31
|
+
readonly constraintMsg: MessageDefinition<'constraintMsg'> | null;
|
|
32
|
+
readonly requiredMsg: MessageDefinition<'requiredMsg'> | null;
|
|
29
33
|
|
|
30
34
|
// TODO: it is unclear whether this will need to be supported.
|
|
31
35
|
// https://github.com/getodk/collect/issues/3758 mentions deprecation.
|
|
32
36
|
readonly saveIncomplete: BindComputation<'saveIncomplete'>;
|
|
33
37
|
|
|
34
|
-
// TODO: these are deferred
|
|
35
|
-
// readonly requiredMsg: string | null;
|
|
36
|
-
// readonly constraintMsg: string | null;
|
|
38
|
+
// TODO: these are deferred until prioritized
|
|
37
39
|
// readonly preload: string | null;
|
|
38
40
|
// readonly preloadParams: string | null;
|
|
39
41
|
// readonly 'max-pixels': string | null;
|
|
@@ -88,9 +90,9 @@ export class BindDefinition extends DependencyContext {
|
|
|
88
90
|
this.required = BindComputation.forExpression(this, 'required');
|
|
89
91
|
this.constraint = BindComputation.forExpression(this, 'constraint');
|
|
90
92
|
this.saveIncomplete = BindComputation.forExpression(this, 'saveIncomplete');
|
|
93
|
+
this.constraintMsg = MessageDefinition.from(this, 'constraintMsg');
|
|
94
|
+
this.requiredMsg = MessageDefinition.from(this, 'requiredMsg');
|
|
91
95
|
|
|
92
|
-
// this.requiredMsg = BindComputation.forExpression(this, 'requiredMsg');
|
|
93
|
-
// this.constraintMsg = BindComputation.forExpression(this, 'constraintMsg');
|
|
94
96
|
// this.preload = BindComputation.forExpression(this, 'preload');
|
|
95
97
|
// this.preloadParams = BindComputation.forExpression(this, 'preloadParams');
|
|
96
98
|
// this['max-pixels'] = BindComputation.forExpression(this, 'max-pixels');
|
package/src/model/BindElement.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
|
|
2
|
-
import type { RepeatDefinition } from '../body/RepeatDefinition.ts';
|
|
3
2
|
import type { BindDefinition } from './BindDefinition.ts';
|
|
4
3
|
import type {
|
|
5
4
|
ModelNode,
|
|
@@ -14,7 +13,7 @@ import type { RootDefinition } from './RootDefinition.ts';
|
|
|
14
13
|
|
|
15
14
|
export type DescendentNodeType = Exclude<NodeDefinitionType, 'root'>;
|
|
16
15
|
|
|
17
|
-
type DescendentNodeBodyElement = AnyBodyElementDefinition
|
|
16
|
+
type DescendentNodeBodyElement = AnyBodyElementDefinition;
|
|
18
17
|
|
|
19
18
|
export abstract class DescendentNodeDefinition<
|
|
20
19
|
Type extends DescendentNodeType,
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
|
|
2
|
-
import type { AnyControlDefinition } from '../body/control/ControlDefinition.ts';
|
|
1
|
+
import type { AnyBodyElementDefinition, ControlElementDefinition } from '../body/BodyDefinition.ts';
|
|
3
2
|
import type { BindDefinition } from './BindDefinition.ts';
|
|
4
3
|
import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
|
|
5
4
|
import type { NodeDefinition, ParentNodeDefinition } from './NodeDefinition.ts';
|
|
6
5
|
|
|
7
|
-
export class
|
|
8
|
-
extends DescendentNodeDefinition<'
|
|
9
|
-
implements NodeDefinition<'
|
|
6
|
+
export class LeafNodeDefinition
|
|
7
|
+
extends DescendentNodeDefinition<'leaf-node', ControlElementDefinition | null>
|
|
8
|
+
implements NodeDefinition<'leaf-node'>
|
|
10
9
|
{
|
|
11
|
-
readonly type = '
|
|
10
|
+
readonly type = 'leaf-node';
|
|
12
11
|
|
|
13
12
|
readonly nodeName: string;
|
|
14
13
|
readonly children = null;
|