@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.
Files changed (228) hide show
  1. package/dist/body/BodyDefinition.d.ts +24 -7
  2. package/dist/body/BodyElementDefinition.d.ts +4 -3
  3. package/dist/body/RepeatElementDefinition.d.ts +19 -0
  4. package/dist/body/appearance/inputAppearanceParser.d.ts +4 -0
  5. package/dist/body/appearance/selectAppearanceParser.d.ts +4 -0
  6. package/dist/body/appearance/structureElementAppearanceParser.d.ts +4 -0
  7. package/dist/body/control/ControlDefinition.d.ts +4 -2
  8. package/dist/body/control/InputDefinition.d.ts +5 -0
  9. package/dist/body/control/select/ItemDefinition.d.ts +2 -2
  10. package/dist/body/control/select/ItemsetDefinition.d.ts +5 -4
  11. package/dist/body/control/select/SelectDefinition.d.ts +11 -1
  12. package/dist/body/group/BaseGroupDefinition.d.ts +4 -9
  13. package/dist/body/group/PresentationGroupDefinition.d.ts +1 -1
  14. package/dist/client/BaseNode.d.ts +74 -3
  15. package/dist/client/GroupNode.d.ts +7 -2
  16. package/dist/client/ModelValueNode.d.ts +37 -0
  17. package/dist/client/NodeAppearances.d.ts +15 -0
  18. package/dist/client/NoteNode.d.ts +53 -0
  19. package/dist/client/RootNode.d.ts +21 -0
  20. package/dist/client/SelectNode.d.ts +8 -3
  21. package/dist/client/StringNode.d.ts +8 -3
  22. package/dist/client/SubtreeNode.d.ts +3 -0
  23. package/dist/client/TextRange.d.ts +85 -2
  24. package/dist/client/constants.d.ts +9 -0
  25. package/dist/client/hierarchy.d.ts +14 -9
  26. package/dist/client/node-types.d.ts +2 -1
  27. package/dist/client/{RepeatRangeNode.d.ts → repeat/BaseRepeatRangeNode.d.ts} +19 -15
  28. package/dist/client/{RepeatInstanceNode.d.ts → repeat/RepeatInstanceNode.d.ts} +11 -7
  29. package/dist/client/repeat/RepeatRangeControlledNode.d.ts +19 -0
  30. package/dist/client/repeat/RepeatRangeUncontrolledNode.d.ts +20 -0
  31. package/dist/client/validation.d.ts +163 -0
  32. package/dist/expression/DependentExpression.d.ts +12 -8
  33. package/dist/index.d.ts +9 -4
  34. package/dist/index.js +3173 -960
  35. package/dist/index.js.map +1 -1
  36. package/dist/instance/Group.d.ts +6 -4
  37. package/dist/instance/ModelValue.d.ts +40 -0
  38. package/dist/instance/Note.d.ts +42 -0
  39. package/dist/instance/Root.d.ts +10 -23
  40. package/dist/instance/SelectField.d.ts +12 -6
  41. package/dist/instance/StringField.d.ts +13 -7
  42. package/dist/instance/Subtree.d.ts +3 -1
  43. package/dist/instance/abstract/DescendantNode.d.ts +16 -9
  44. package/dist/instance/abstract/InstanceNode.d.ts +28 -29
  45. package/dist/instance/hierarchy.d.ts +10 -5
  46. package/dist/instance/internal-api/EvaluationContext.d.ts +5 -4
  47. package/dist/instance/internal-api/ValidationContext.d.ts +21 -0
  48. package/dist/instance/internal-api/ValueContext.d.ts +2 -2
  49. package/dist/instance/repeat/BaseRepeatRange.d.ts +160 -0
  50. package/dist/instance/{RepeatInstance.d.ts → repeat/RepeatInstance.d.ts} +38 -13
  51. package/dist/instance/repeat/RepeatRangeControlled.d.ts +16 -0
  52. package/dist/instance/repeat/RepeatRangeUncontrolled.d.ts +35 -0
  53. package/dist/instance/text/TextRange.d.ts +4 -4
  54. package/dist/lib/TokenListParser.d.ts +84 -0
  55. package/dist/lib/dom/query.d.ts +5 -0
  56. package/dist/lib/reactivity/createComputedExpression.d.ts +6 -1
  57. package/dist/lib/reactivity/createNoteReadonlyThunk.d.ts +5 -0
  58. package/dist/lib/reactivity/materializeCurrentStateChildren.d.ts +2 -1
  59. package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +1 -1
  60. package/dist/lib/reactivity/node-state/createSpecifiedState.d.ts +1 -1
  61. package/dist/lib/reactivity/text/createFieldHint.d.ts +3 -3
  62. package/dist/lib/reactivity/text/createNodeLabel.d.ts +2 -2
  63. package/dist/lib/reactivity/text/createNoteText.d.ts +25 -0
  64. package/dist/lib/reactivity/text/createTextRange.d.ts +5 -7
  65. package/dist/lib/reactivity/validation/createAggregatedViolations.d.ts +9 -0
  66. package/dist/lib/reactivity/validation/createValidation.d.ts +18 -0
  67. package/dist/model/BindDefinition.d.ts +4 -2
  68. package/dist/model/BindElement.d.ts +1 -0
  69. package/dist/model/DescendentNodeDefinition.d.ts +1 -2
  70. package/dist/model/{ValueNodeDefinition.d.ts → LeafNodeDefinition.d.ts} +3 -4
  71. package/dist/model/NodeDefinition.d.ts +16 -16
  72. package/dist/model/RepeatInstanceDefinition.d.ts +5 -6
  73. package/dist/model/RepeatRangeDefinition.d.ts +30 -0
  74. package/dist/model/RepeatTemplateDefinition.d.ts +6 -7
  75. package/dist/model/RootDefinition.d.ts +3 -1
  76. package/dist/model/SubtreeDefinition.d.ts +2 -2
  77. package/dist/parse/NoteNodeDefinition.d.ts +31 -0
  78. package/dist/parse/expression/RepeatCountControlExpression.d.ts +19 -0
  79. package/dist/parse/text/HintDefinition.d.ts +9 -0
  80. package/dist/parse/text/ItemLabelDefinition.d.ts +9 -0
  81. package/dist/parse/text/ItemsetLabelDefinition.d.ts +13 -0
  82. package/dist/parse/text/LabelDefinition.d.ts +15 -0
  83. package/dist/parse/text/MessageDefinition.d.ts +15 -0
  84. package/dist/parse/text/OutputChunkDefinition.d.ts +8 -0
  85. package/dist/parse/text/ReferenceChunkDefinition.d.ts +8 -0
  86. package/dist/parse/text/StaticTextChunkDefinition.d.ts +10 -0
  87. package/dist/parse/text/TranslationChunkDefinition.d.ts +9 -0
  88. package/dist/parse/text/abstract/TextChunkDefinition.d.ts +18 -0
  89. package/dist/parse/text/abstract/TextElementDefinition.d.ts +23 -0
  90. package/dist/parse/text/abstract/TextRangeDefinition.d.ts +35 -0
  91. package/dist/parse/xpath/dependency-analysis.d.ts +40 -0
  92. package/dist/parse/xpath/path-resolution.d.ts +70 -0
  93. package/dist/parse/xpath/predicate-analysis.d.ts +30 -0
  94. package/dist/parse/xpath/reference-parsing.d.ts +18 -0
  95. package/dist/parse/xpath/semantic-analysis.d.ts +98 -0
  96. package/dist/parse/xpath/syntax-traversal.d.ts +69 -0
  97. package/dist/solid.js +3174 -961
  98. package/dist/solid.js.map +1 -1
  99. package/package.json +14 -15
  100. package/src/XFormDOM.ts +81 -8
  101. package/src/body/BodyDefinition.ts +38 -23
  102. package/src/body/BodyElementDefinition.ts +4 -3
  103. package/src/body/RepeatElementDefinition.ts +58 -0
  104. package/src/body/appearance/inputAppearanceParser.ts +39 -0
  105. package/src/body/appearance/selectAppearanceParser.ts +38 -0
  106. package/src/body/appearance/structureElementAppearanceParser.ts +7 -0
  107. package/src/body/control/ControlDefinition.ts +8 -3
  108. package/src/body/control/InputDefinition.ts +13 -0
  109. package/src/body/control/select/ItemDefinition.ts +3 -3
  110. package/src/body/control/select/ItemsetDefinition.ts +29 -12
  111. package/src/body/control/select/ItemsetNodesetExpression.ts +1 -1
  112. package/src/body/control/select/SelectDefinition.ts +14 -5
  113. package/src/body/group/BaseGroupDefinition.ts +14 -51
  114. package/src/body/group/PresentationGroupDefinition.ts +1 -1
  115. package/src/client/BaseNode.ts +82 -8
  116. package/src/client/GroupNode.ts +8 -2
  117. package/src/client/ModelValueNode.ts +40 -0
  118. package/src/client/NodeAppearances.ts +22 -0
  119. package/src/client/NoteNode.ts +74 -0
  120. package/src/client/README.md +1 -0
  121. package/src/client/RootNode.ts +24 -0
  122. package/src/client/SelectNode.ts +9 -3
  123. package/src/client/StringNode.ts +9 -3
  124. package/src/client/SubtreeNode.ts +3 -0
  125. package/src/client/TextRange.ts +99 -2
  126. package/src/client/constants.ts +10 -0
  127. package/src/client/hierarchy.ts +30 -14
  128. package/src/client/node-types.ts +8 -1
  129. package/src/client/{RepeatRangeNode.ts → repeat/BaseRepeatRangeNode.ts} +20 -17
  130. package/src/client/{RepeatInstanceNode.ts → repeat/RepeatInstanceNode.ts} +13 -7
  131. package/src/client/repeat/RepeatRangeControlledNode.ts +20 -0
  132. package/src/client/repeat/RepeatRangeUncontrolledNode.ts +21 -0
  133. package/src/client/validation.ts +199 -0
  134. package/src/expression/DependentExpression.ts +45 -27
  135. package/src/index.ts +15 -8
  136. package/src/instance/Group.ts +24 -13
  137. package/src/instance/ModelValue.ts +104 -0
  138. package/src/instance/Note.ts +142 -0
  139. package/src/instance/Root.ts +29 -67
  140. package/src/instance/SelectField.ts +35 -13
  141. package/src/instance/StringField.ts +40 -13
  142. package/src/instance/Subtree.ts +19 -10
  143. package/src/instance/abstract/DescendantNode.ts +50 -49
  144. package/src/instance/abstract/InstanceNode.ts +89 -92
  145. package/src/instance/children.ts +47 -10
  146. package/src/instance/hierarchy.ts +21 -2
  147. package/src/instance/index.ts +1 -1
  148. package/src/instance/internal-api/EvaluationContext.ts +5 -6
  149. package/src/instance/internal-api/ValidationContext.ts +23 -0
  150. package/src/instance/internal-api/ValueContext.ts +2 -2
  151. package/src/instance/repeat/BaseRepeatRange.ts +347 -0
  152. package/src/instance/{RepeatInstance.ts → repeat/RepeatInstance.ts} +85 -36
  153. package/src/instance/repeat/RepeatRangeControlled.ts +82 -0
  154. package/src/instance/repeat/RepeatRangeUncontrolled.ts +67 -0
  155. package/src/instance/text/TextRange.ts +10 -4
  156. package/src/lib/TokenListParser.ts +156 -0
  157. package/src/lib/dom/query.ts +13 -0
  158. package/src/lib/reactivity/createChildrenState.ts +51 -6
  159. package/src/lib/reactivity/createComputedExpression.ts +23 -25
  160. package/src/lib/reactivity/createNoteReadonlyThunk.ts +33 -0
  161. package/src/lib/reactivity/createSelectItems.ts +25 -20
  162. package/src/lib/reactivity/createValueState.ts +6 -6
  163. package/src/lib/reactivity/materializeCurrentStateChildren.ts +3 -1
  164. package/src/lib/reactivity/node-state/createSharedNodeState.ts +1 -1
  165. package/src/lib/reactivity/text/createFieldHint.ts +9 -7
  166. package/src/lib/reactivity/text/createNodeLabel.ts +7 -5
  167. package/src/lib/reactivity/text/createNoteText.ts +72 -0
  168. package/src/lib/reactivity/text/createTextRange.ts +17 -90
  169. package/src/lib/reactivity/validation/createAggregatedViolations.ts +70 -0
  170. package/src/lib/reactivity/validation/createValidation.ts +196 -0
  171. package/src/model/BindComputation.ts +0 -4
  172. package/src/model/BindDefinition.ts +8 -6
  173. package/src/model/BindElement.ts +1 -0
  174. package/src/model/DescendentNodeDefinition.ts +1 -2
  175. package/src/model/{ValueNodeDefinition.ts → LeafNodeDefinition.ts} +5 -6
  176. package/src/model/ModelBindMap.ts +4 -0
  177. package/src/model/ModelDefinition.ts +1 -1
  178. package/src/model/NodeDefinition.ts +21 -21
  179. package/src/model/RepeatInstanceDefinition.ts +8 -13
  180. package/src/model/RepeatRangeDefinition.ts +94 -0
  181. package/src/model/RepeatTemplateDefinition.ts +10 -15
  182. package/src/model/RootDefinition.ts +12 -14
  183. package/src/model/SubtreeDefinition.ts +3 -3
  184. package/src/parse/NoteNodeDefinition.ts +70 -0
  185. package/src/parse/TODO.md +3 -0
  186. package/src/parse/expression/RepeatCountControlExpression.ts +44 -0
  187. package/src/parse/text/HintDefinition.ts +25 -0
  188. package/src/parse/text/ItemLabelDefinition.ts +25 -0
  189. package/src/parse/text/ItemsetLabelDefinition.ts +44 -0
  190. package/src/parse/text/LabelDefinition.ts +61 -0
  191. package/src/parse/text/MessageDefinition.ts +49 -0
  192. package/src/parse/text/OutputChunkDefinition.ts +25 -0
  193. package/src/parse/text/ReferenceChunkDefinition.ts +14 -0
  194. package/src/parse/text/StaticTextChunkDefinition.ts +19 -0
  195. package/src/parse/text/TranslationChunkDefinition.ts +38 -0
  196. package/src/parse/text/abstract/TextChunkDefinition.ts +38 -0
  197. package/src/parse/text/abstract/TextElementDefinition.ts +71 -0
  198. package/src/parse/text/abstract/TextRangeDefinition.ts +70 -0
  199. package/src/parse/xpath/dependency-analysis.ts +105 -0
  200. package/src/parse/xpath/path-resolution.ts +475 -0
  201. package/src/parse/xpath/predicate-analysis.ts +61 -0
  202. package/src/parse/xpath/reference-parsing.ts +90 -0
  203. package/src/parse/xpath/semantic-analysis.ts +466 -0
  204. package/src/parse/xpath/syntax-traversal.ts +129 -0
  205. package/dist/body/RepeatDefinition.d.ts +0 -16
  206. package/dist/body/group/RepeatGroupDefinition.d.ts +0 -13
  207. package/dist/body/text/HintDefinition.d.ts +0 -11
  208. package/dist/body/text/LabelDefinition.d.ts +0 -20
  209. package/dist/body/text/TextElementDefinition.d.ts +0 -33
  210. package/dist/body/text/TextElementOutputPart.d.ts +0 -13
  211. package/dist/body/text/TextElementPart.d.ts +0 -13
  212. package/dist/body/text/TextElementReferencePart.d.ts +0 -7
  213. package/dist/body/text/TextElementStaticPart.d.ts +0 -7
  214. package/dist/instance/RepeatRange.d.ts +0 -80
  215. package/dist/lib/xpath/analysis.d.ts +0 -23
  216. package/dist/model/RepeatSequenceDefinition.d.ts +0 -20
  217. package/src/body/RepeatDefinition.ts +0 -54
  218. package/src/body/group/RepeatGroupDefinition.ts +0 -91
  219. package/src/body/text/HintDefinition.ts +0 -26
  220. package/src/body/text/LabelDefinition.ts +0 -54
  221. package/src/body/text/TextElementDefinition.ts +0 -97
  222. package/src/body/text/TextElementOutputPart.ts +0 -27
  223. package/src/body/text/TextElementPart.ts +0 -31
  224. package/src/body/text/TextElementReferencePart.ts +0 -21
  225. package/src/body/text/TextElementStaticPart.ts +0 -26
  226. package/src/instance/RepeatRange.ts +0 -214
  227. package/src/lib/xpath/analysis.ts +0 -241
  228. package/src/model/RepeatSequenceDefinition.ts +0 -53
@@ -2,8 +2,9 @@ import { xmlXPathWhitespaceSeparatedList } from '@getodk/common/lib/string/white
2
2
  import type { Accessor } from 'solid-js';
3
3
  import { untrack } from 'solid-js';
4
4
  import type { AnySelectDefinition } from '../body/control/select/SelectDefinition.ts';
5
- import type { SelectItem, SelectNode } from '../client/SelectNode.ts';
6
- import type { TextRange } from '../index.ts';
5
+ import type { SelectItem, SelectNode, SelectNodeAppearances } from '../client/SelectNode.ts';
6
+ import type { TextRange } from '../client/TextRange.ts';
7
+ import type { AnyViolation, LeafNodeValidationState } from '../client/validation.ts';
7
8
  import { createSelectItems } from '../lib/reactivity/createSelectItems.ts';
8
9
  import { createValueState } from '../lib/reactivity/createValueState.ts';
9
10
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
@@ -13,16 +14,19 @@ import { createSharedNodeState } from '../lib/reactivity/node-state/createShared
13
14
  import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
14
15
  import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
15
16
  import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
16
- import type { ValueNodeDefinition } from '../model/ValueNodeDefinition.ts';
17
+ import type { SharedValidationState } from '../lib/reactivity/validation/createValidation.ts';
18
+ import { createValidationState } from '../lib/reactivity/validation/createValidation.ts';
19
+ import type { LeafNodeDefinition } from '../model/LeafNodeDefinition.ts';
17
20
  import type { Root } from './Root.ts';
18
21
  import type { DescendantNodeStateSpec } from './abstract/DescendantNode.ts';
19
22
  import { DescendantNode } from './abstract/DescendantNode.ts';
20
23
  import type { GeneralParentNode } from './hierarchy.ts';
21
24
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
22
25
  import type { SubscribableDependency } from './internal-api/SubscribableDependency.ts';
26
+ import type { ValidationContext } from './internal-api/ValidationContext.ts';
23
27
  import type { ValueContext } from './internal-api/ValueContext.ts';
24
28
 
25
- export interface SelectFieldDefinition extends ValueNodeDefinition {
29
+ export interface SelectFieldDefinition extends LeafNodeDefinition {
26
30
  readonly bodyElement: AnySelectDefinition;
27
31
  }
28
32
 
@@ -40,9 +44,11 @@ export class SelectField
40
44
  SelectNode,
41
45
  EvaluationContext,
42
46
  SubscribableDependency,
47
+ ValidationContext,
43
48
  ValueContext<readonly SelectItem[]>
44
49
  {
45
50
  private readonly selectExclusive: boolean;
51
+ private readonly validation: SharedValidationState;
46
52
 
47
53
  // InstanceNode
48
54
  protected readonly state: SharedNodeState<SelectFieldStateSpec>;
@@ -50,9 +56,13 @@ export class SelectField
50
56
 
51
57
  // SelectNode
52
58
  readonly nodeType = 'select';
53
-
59
+ readonly appearances: SelectNodeAppearances;
54
60
  readonly currentState: CurrentState<SelectFieldStateSpec>;
55
61
 
62
+ get validationState(): LeafNodeValidationState {
63
+ return this.validation.currentState;
64
+ }
65
+
56
66
  // ValueContext
57
67
  readonly encodeValue = (runtimeValue: readonly SelectItem[]): string => {
58
68
  const itemValues = new Set(runtimeValue.map(({ value }) => value));
@@ -83,16 +93,24 @@ export class SelectField
83
93
  constructor(parent: GeneralParentNode, definition: SelectFieldDefinition) {
84
94
  super(parent, definition);
85
95
 
96
+ this.appearances = definition.bodyElement.appearances;
86
97
  this.selectExclusive = definition.bodyElement.type === 'select1';
87
98
 
88
99
  const valueOptions = createSelectItems(this);
89
100
 
90
101
  this.getValueOptions = valueOptions;
91
102
 
103
+ const sharedStateOptions = {
104
+ clientStateFactory: this.engineConfig.stateFactory,
105
+ };
106
+
92
107
  const state = createSharedNodeState(
93
108
  this.scope,
94
109
  {
95
- ...this.buildSharedStateSpec(parent, definition),
110
+ reference: this.contextReference,
111
+ readonly: this.isReadonly,
112
+ relevant: this.isRelevant,
113
+ required: this.isRequired,
96
114
 
97
115
  label: createNodeLabel(this, definition),
98
116
  hint: createFieldHint(this, definition),
@@ -100,14 +118,17 @@ export class SelectField
100
118
  value: createValueState(this),
101
119
  valueOptions,
102
120
  },
103
- {
104
- clientStateFactory: this.engineConfig.stateFactory,
105
- }
121
+ sharedStateOptions
106
122
  );
107
123
 
108
124
  this.state = state;
109
125
  this.engineState = state.engineState;
110
126
  this.currentState = state.currentState;
127
+ this.validation = createValidationState(this, sharedStateOptions);
128
+ }
129
+
130
+ getViolation(): AnyViolation | null {
131
+ return this.validation.engineState.violation;
111
132
  }
112
133
 
113
134
  protected getSelectItemsByValue(
@@ -120,10 +141,6 @@ export class SelectField
120
141
  );
121
142
  }
122
143
 
123
- protected computeReference(parent: GeneralParentNode): string {
124
- return this.computeChildStepReference(parent);
125
- }
126
-
127
144
  protected updateSelectedItemValues(values: readonly string[]) {
128
145
  const itemsByValue = untrack(() => this.getSelectItemsByValue());
129
146
 
@@ -201,4 +218,9 @@ export class SelectField
201
218
  getChildren(): readonly [] {
202
219
  return [];
203
220
  }
221
+
222
+ // ValidationContext
223
+ isBlank(): boolean {
224
+ return this.engineState.value.length === 0;
225
+ }
204
226
  }
@@ -1,8 +1,9 @@
1
1
  import { identity } from '@getodk/common/lib/identity.ts';
2
2
  import type { Accessor } from 'solid-js';
3
3
  import type { InputDefinition } from '../body/control/InputDefinition.ts';
4
- import type { StringNode } from '../client/StringNode.ts';
5
- import type { TextRange } from '../index.ts';
4
+ import type { StringNode, StringNodeAppearances } from '../client/StringNode.ts';
5
+ import type { TextRange } from '../client/TextRange.ts';
6
+ import type { AnyViolation, LeafNodeValidationState } from '../client/validation.ts';
6
7
  import { createValueState } from '../lib/reactivity/createValueState.ts';
7
8
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
8
9
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
@@ -11,17 +12,20 @@ import { createSharedNodeState } from '../lib/reactivity/node-state/createShared
11
12
  import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
12
13
  import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
13
14
  import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
14
- import type { ValueNodeDefinition } from '../model/ValueNodeDefinition.ts';
15
+ import type { SharedValidationState } from '../lib/reactivity/validation/createValidation.ts';
16
+ import { createValidationState } from '../lib/reactivity/validation/createValidation.ts';
17
+ import type { LeafNodeDefinition } from '../model/LeafNodeDefinition.ts';
15
18
  import type { Root } from './Root.ts';
16
19
  import type { DescendantNodeStateSpec } from './abstract/DescendantNode.ts';
17
20
  import { DescendantNode } from './abstract/DescendantNode.ts';
18
21
  import type { GeneralParentNode } from './hierarchy.ts';
19
22
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
20
23
  import type { SubscribableDependency } from './internal-api/SubscribableDependency.ts';
24
+ import type { ValidationContext } from './internal-api/ValidationContext.ts';
21
25
  import type { ValueContext } from './internal-api/ValueContext.ts';
22
26
 
23
- export interface StringFieldDefinition extends ValueNodeDefinition {
24
- readonly bodyElement: InputDefinition | null;
27
+ export interface StringFieldDefinition extends LeafNodeDefinition {
28
+ readonly bodyElement: InputDefinition;
25
29
  }
26
30
 
27
31
  interface StringFieldStateSpec extends DescendantNodeStateSpec<string> {
@@ -34,8 +38,14 @@ interface StringFieldStateSpec extends DescendantNodeStateSpec<string> {
34
38
 
35
39
  export class StringField
36
40
  extends DescendantNode<StringFieldDefinition, StringFieldStateSpec, null>
37
- implements StringNode, EvaluationContext, SubscribableDependency, ValueContext<string>
41
+ implements
42
+ StringNode,
43
+ EvaluationContext,
44
+ SubscribableDependency,
45
+ ValidationContext,
46
+ ValueContext<string>
38
47
  {
48
+ private readonly validation: SharedValidationState;
39
49
  protected readonly state: SharedNodeState<StringFieldStateSpec>;
40
50
 
41
51
  // InstanceNode
@@ -43,9 +53,13 @@ export class StringField
43
53
 
44
54
  // StringNode
45
55
  readonly nodeType = 'string';
46
-
56
+ readonly appearances: StringNodeAppearances;
47
57
  readonly currentState: CurrentState<StringFieldStateSpec>;
48
58
 
59
+ get validationState(): LeafNodeValidationState {
60
+ return this.validation.currentState;
61
+ }
62
+
49
63
  // ValueContext
50
64
  readonly encodeValue = identity<string>;
51
65
 
@@ -54,10 +68,19 @@ export class StringField
54
68
  constructor(parent: GeneralParentNode, definition: StringFieldDefinition) {
55
69
  super(parent, definition);
56
70
 
71
+ this.appearances = definition.bodyElement.appearances;
72
+
73
+ const sharedStateOptions = {
74
+ clientStateFactory: this.engineConfig.stateFactory,
75
+ };
76
+
57
77
  const state = createSharedNodeState(
58
78
  this.scope,
59
79
  {
60
- ...this.buildSharedStateSpec(parent, definition),
80
+ reference: this.contextReference,
81
+ readonly: this.isReadonly,
82
+ relevant: this.isRelevant,
83
+ required: this.isRequired,
61
84
 
62
85
  label: createNodeLabel(this, definition),
63
86
  hint: createFieldHint(this, definition),
@@ -65,18 +88,22 @@ export class StringField
65
88
  valueOptions: null,
66
89
  value: createValueState(this),
67
90
  },
68
- {
69
- clientStateFactory: this.engineConfig.stateFactory,
70
- }
91
+ sharedStateOptions
71
92
  );
72
93
 
73
94
  this.state = state;
74
95
  this.engineState = state.engineState;
75
96
  this.currentState = state.currentState;
97
+ this.validation = createValidationState(this, sharedStateOptions);
98
+ }
99
+
100
+ getViolation(): AnyViolation | null {
101
+ return this.validation.engineState.violation;
76
102
  }
77
103
 
78
- protected computeReference(parent: GeneralParentNode): string {
79
- return this.computeChildStepReference(parent);
104
+ // ValidationContext
105
+ isBlank(): boolean {
106
+ return this.engineState.value === '';
80
107
  }
81
108
 
82
109
  // InstanceNode
@@ -1,5 +1,6 @@
1
1
  import { type Accessor } from 'solid-js';
2
2
  import type { SubtreeDefinition, SubtreeNode } from '../client/SubtreeNode.ts';
3
+ import type { AncestorNodeValidationState } from '../client/validation.ts';
3
4
  import type { ChildrenState } from '../lib/reactivity/createChildrenState.ts';
4
5
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
5
6
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -8,6 +9,7 @@ import type { CurrentState } from '../lib/reactivity/node-state/createCurrentSta
8
9
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
9
10
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
10
11
  import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
12
+ import { createAggregatedViolations } from '../lib/reactivity/validation/createAggregatedViolations.ts';
11
13
  import type { DescendantNodeSharedStateSpec } from './abstract/DescendantNode.ts';
12
14
  import { DescendantNode } from './abstract/DescendantNode.ts';
13
15
  import { buildChildren } from './children.ts';
@@ -36,8 +38,9 @@ export class Subtree
36
38
 
37
39
  // SubtreeNode
38
40
  readonly nodeType = 'subtree';
39
-
41
+ readonly appearances = null;
40
42
  readonly currentState: MaterializedChildren<CurrentState<SubtreeStateSpec>, GeneralChildNode>;
43
+ readonly validationState: AncestorNodeValidationState;
41
44
 
42
45
  constructor(parent: GeneralParentNode, definition: SubtreeDefinition) {
43
46
  super(parent, definition);
@@ -46,10 +49,17 @@ export class Subtree
46
49
 
47
50
  this.childrenState = childrenState;
48
51
 
52
+ const sharedStateOptions = {
53
+ clientStateFactory: this.engineConfig.stateFactory,
54
+ };
55
+
49
56
  const state = createSharedNodeState(
50
57
  this.scope,
51
58
  {
52
- ...this.buildSharedStateSpec(parent, definition),
59
+ reference: this.contextReference,
60
+ readonly: this.isReadonly,
61
+ relevant: this.isRelevant,
62
+ required: this.isRequired,
53
63
 
54
64
  label: null,
55
65
  hint: null,
@@ -57,20 +67,19 @@ export class Subtree
57
67
  valueOptions: null,
58
68
  value: null,
59
69
  },
60
- {
61
- clientStateFactory: this.engineConfig.stateFactory,
62
- }
70
+ sharedStateOptions
63
71
  );
64
72
 
65
73
  this.state = state;
66
74
  this.engineState = state.engineState;
67
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
75
+ this.currentState = materializeCurrentStateChildren(
76
+ this.scope,
77
+ state.currentState,
78
+ childrenState
79
+ );
68
80
 
69
81
  childrenState.setChildren(buildChildren(this));
70
- }
71
-
72
- protected computeReference(parent: GeneralParentNode): string {
73
- return this.computeChildStepReference(parent);
82
+ this.validationState = createAggregatedViolations(this, sharedStateOptions);
74
83
  }
75
84
 
76
85
  getChildren(): readonly GeneralChildNode[] {
@@ -1,19 +1,17 @@
1
1
  import type { XFormsXPathEvaluator } from '@getodk/xpath';
2
2
  import type { Accessor } from 'solid-js';
3
- import { createMemo } from 'solid-js';
4
3
  import type { BaseNode } from '../../client/BaseNode.ts';
5
4
  import { createComputedExpression } from '../../lib/reactivity/createComputedExpression.ts';
6
5
  import type { ReactiveScope } from '../../lib/reactivity/scope.ts';
7
6
  import type { AnyDescendantNodeDefinition } from '../../model/DescendentNodeDefinition.ts';
7
+ import type { LeafNodeDefinition } from '../../model/LeafNodeDefinition.ts';
8
8
  import type { AnyNodeDefinition } from '../../model/NodeDefinition.ts';
9
9
  import type { RepeatInstanceDefinition } from '../../model/RepeatInstanceDefinition.ts';
10
- import type { ValueNodeDefinition } from '../../model/ValueNodeDefinition.ts';
11
- import type { RepeatInstance } from '../RepeatInstance.ts';
12
- import type { RepeatRange } from '../RepeatRange.ts';
13
- import type { Root } from '../Root.ts';
14
- import type { AnyChildNode, GeneralParentNode } from '../hierarchy.ts';
10
+ import type { AnyChildNode, GeneralParentNode, RepeatRange } from '../hierarchy.ts';
15
11
  import type { EvaluationContext } from '../internal-api/EvaluationContext.ts';
16
12
  import type { SubscribableDependency } from '../internal-api/SubscribableDependency.ts';
13
+ import type { RepeatInstance } from '../repeat/RepeatInstance.ts';
14
+ import type { Root } from '../Root.ts';
17
15
  import type { InstanceNodeStateSpec } from './InstanceNode.ts';
18
16
  import { InstanceNode } from './InstanceNode.ts';
19
17
 
@@ -38,7 +36,7 @@ export type DescendantNodeDefinition = Extract<
38
36
 
39
37
  // prettier-ignore
40
38
  export type DescendantNodeParent<Definition extends DescendantNodeDefinition> =
41
- Definition extends ValueNodeDefinition
39
+ Definition extends LeafNodeDefinition
42
40
  ? GeneralParentNode
43
41
  : Definition extends RepeatInstanceDefinition
44
42
  ? RepeatRange
@@ -52,6 +50,10 @@ export type AnyDescendantNode = DescendantNode<
52
50
  any
53
51
  >;
54
52
 
53
+ interface DescendantNodeOptions {
54
+ readonly computeReference?: Accessor<string>;
55
+ }
56
+
55
57
  export abstract class DescendantNode<
56
58
  Definition extends DescendantNodeDefinition,
57
59
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -61,63 +63,62 @@ export abstract class DescendantNode<
61
63
  extends InstanceNode<Definition, Spec, Child>
62
64
  implements BaseNode, EvaluationContext, SubscribableDependency
63
65
  {
66
+ readonly hasReadonlyAncestor: Accessor<boolean> = () => {
67
+ const { parent } = this;
68
+
69
+ return parent.hasReadonlyAncestor() || parent.isReadonly();
70
+ };
71
+
72
+ readonly isSelfReadonly: Accessor<boolean>;
73
+
74
+ readonly isReadonly: Accessor<boolean> = () => {
75
+ if (this.hasReadonlyAncestor()) {
76
+ return true;
77
+ }
78
+
79
+ return this.isSelfReadonly();
80
+ };
81
+
82
+ readonly hasNonRelevantAncestor: Accessor<boolean> = () => {
83
+ const { parent } = this;
84
+
85
+ return parent.hasNonRelevantAncestor() || !parent.isRelevant();
86
+ };
87
+
88
+ readonly isSelfRelevant: Accessor<boolean>;
89
+
90
+ readonly isRelevant: Accessor<boolean> = () => {
91
+ if (this.hasNonRelevantAncestor()) {
92
+ return false;
93
+ }
94
+
95
+ return this.isSelfRelevant();
96
+ };
97
+
98
+ readonly isRequired: Accessor<boolean>;
99
+
64
100
  readonly root: Root;
65
101
  readonly evaluator: XFormsXPathEvaluator;
66
102
  readonly contextNode: Element;
67
103
 
68
104
  constructor(
69
105
  override readonly parent: DescendantNodeParent<Definition>,
70
- override readonly definition: Definition
106
+ override readonly definition: Definition,
107
+ options?: DescendantNodeOptions
71
108
  ) {
72
- super(parent.engineConfig, parent, definition);
109
+ super(parent.engineConfig, parent, definition, options);
73
110
 
74
111
  const { evaluator, root } = parent;
75
112
 
76
113
  this.root = root;
77
114
  this.evaluator = evaluator;
78
115
  this.contextNode = this.initializeContextNode(parent.contextNode, definition.nodeName);
79
- }
80
116
 
81
- protected computeChildStepReference(parent: DescendantNodeParent<Definition>): string {
82
- return `${parent.contextReference}/${this.definition.nodeName}`;
83
- }
117
+ const { readonly, relevant, required } = definition.bind;
84
118
 
85
- protected abstract override computeReference(
86
- parent: DescendantNodeParent<Definition>,
87
- definition: Definition
88
- ): string;
89
-
90
- protected buildSharedStateSpec(
91
- parent: DescendantNodeParent<Definition>,
92
- definition: Definition
93
- ): DescendantNodeSharedStateSpec {
94
- return this.scope.runTask(() => {
95
- const reference = createMemo(() => this.contextReference);
96
- const { bind } = definition;
97
-
98
- // TODO: we can likely short-circuit `readonly` computation when a node
99
- // is non-relevant.
100
- const selfReadonly = createComputedExpression(this, bind.readonly);
101
- const readonly = createMemo(() => {
102
- return parent.isReadonly || selfReadonly();
103
- });
104
-
105
- const selfRelevant = createComputedExpression(this, bind.relevant);
106
- const relevant = createMemo(() => {
107
- return parent.isRelevant && selfRelevant();
108
- });
109
-
110
- // TODO: we can likely short-circuit `required` computation when a node
111
- // is non-relevant.
112
- const required = createComputedExpression(this, bind.required);
113
-
114
- return {
115
- reference,
116
- readonly,
117
- relevant,
118
- required,
119
- };
120
- });
119
+ this.isSelfReadonly = createComputedExpression(this, readonly);
120
+ this.isSelfRelevant = createComputedExpression(this, relevant);
121
+ this.isRequired = createComputedExpression(this, required);
121
122
  }
122
123
 
123
124
  protected createContextNode(parentContextNode: Element, nodeName: string): Element {