@getodk/xforms-engine 0.5.0 → 0.6.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 (232) hide show
  1. package/README.md +1 -1
  2. package/dist/client/BaseNode.d.ts +4 -0
  3. package/dist/client/BaseValueNode.d.ts +1 -1
  4. package/dist/client/GroupNode.d.ts +1 -0
  5. package/dist/client/InputNode.d.ts +32 -3
  6. package/dist/client/ModelValueNode.d.ts +1 -0
  7. package/dist/client/NoteNode.d.ts +24 -7
  8. package/dist/client/RangeNode.d.ts +36 -0
  9. package/dist/client/RankNode.d.ts +46 -0
  10. package/dist/client/RootNode.d.ts +6 -3
  11. package/dist/client/SelectNode.d.ts +47 -25
  12. package/dist/client/SubtreeNode.d.ts +1 -0
  13. package/dist/client/TriggerNode.d.ts +10 -6
  14. package/dist/client/hierarchy.d.ts +5 -5
  15. package/dist/client/node-types.d.ts +2 -2
  16. package/dist/client/unsupported/UnsupportedControlNode.d.ts +1 -3
  17. package/dist/error/RankMissingValueError.d.ts +3 -0
  18. package/dist/error/RankValueTypeError.d.ts +6 -0
  19. package/dist/error/SelectValueTypeError.d.ts +15 -0
  20. package/dist/error/XFormsSpecViolationError.d.ts +2 -0
  21. package/dist/index.d.ts +2 -3
  22. package/dist/index.js +5604 -4666
  23. package/dist/index.js.map +1 -1
  24. package/dist/instance/Group.d.ts +1 -0
  25. package/dist/instance/InputControl.d.ts +5 -3
  26. package/dist/instance/ModelValue.d.ts +2 -0
  27. package/dist/instance/Note.d.ts +13 -25
  28. package/dist/instance/PrimaryInstance.d.ts +1 -0
  29. package/dist/instance/RangeControl.d.ts +34 -0
  30. package/dist/instance/RankControl.d.ts +40 -0
  31. package/dist/instance/Root.d.ts +1 -0
  32. package/dist/instance/SelectControl.d.ts +66 -0
  33. package/dist/instance/Subtree.d.ts +1 -0
  34. package/dist/instance/TriggerControl.d.ts +9 -22
  35. package/dist/instance/abstract/DescendantNode.d.ts +1 -2
  36. package/dist/instance/abstract/InstanceNode.d.ts +3 -1
  37. package/dist/instance/abstract/UnsupportedControl.d.ts +1 -0
  38. package/dist/instance/abstract/ValueNode.d.ts +0 -1
  39. package/dist/instance/hierarchy.d.ts +9 -9
  40. package/dist/instance/internal-api/InstanceValueContext.d.ts +2 -0
  41. package/dist/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.d.ts +2 -1
  42. package/dist/instance/internal-api/submission/ClientReactiveSubmittableParentNode.d.ts +2 -1
  43. package/dist/instance/internal-api/submission/ClientReactiveSubmittableValueNode.d.ts +2 -1
  44. package/dist/instance/repeat/BaseRepeatRange.d.ts +1 -0
  45. package/dist/instance/repeat/RepeatInstance.d.ts +1 -0
  46. package/dist/integration/xpath/adapter/names.d.ts +1 -11
  47. package/dist/integration/xpath/adapter/traversal.d.ts +10 -9
  48. package/dist/integration/xpath/static-dom/StaticAttribute.d.ts +10 -3
  49. package/dist/integration/xpath/static-dom/StaticDocument.d.ts +0 -1
  50. package/dist/integration/xpath/static-dom/StaticElement.d.ts +12 -4
  51. package/dist/lib/client-reactivity/submission/createRootSubmissionState.d.ts +3 -0
  52. package/dist/lib/codecs/Geopoint/Geopoint.d.ts +48 -0
  53. package/dist/lib/codecs/Geopoint/GeopointValueCodec.d.ts +5 -0
  54. package/dist/lib/codecs/NoteCodec.d.ts +8 -0
  55. package/dist/lib/codecs/RangeCodec.d.ts +8 -0
  56. package/dist/lib/codecs/TriggerCodec.d.ts +7 -0
  57. package/dist/lib/codecs/ValueArrayCodec.d.ts +11 -0
  58. package/dist/lib/codecs/ValueCodec.d.ts +2 -2
  59. package/dist/lib/codecs/getNoteCodec.d.ts +3 -0
  60. package/dist/lib/codecs/getSelectCodec.d.ts +5 -0
  61. package/dist/lib/codecs/getSharedValueCodec.d.ts +3 -2
  62. package/dist/lib/codecs/items/BaseItemCodec.d.ts +9 -0
  63. package/dist/lib/codecs/items/MultipleValueItemCodec.d.ts +14 -0
  64. package/dist/lib/codecs/items/SingleValueItemCodec.d.ts +24 -0
  65. package/dist/lib/dom/query.d.ts +1 -2
  66. package/dist/lib/names/NamespaceDeclaration.d.ts +45 -0
  67. package/dist/lib/names/NamespaceDeclarationMap.d.ts +137 -0
  68. package/dist/lib/names/NamespaceURL.d.ts +30 -0
  69. package/dist/lib/names/QualifiedName.d.ts +113 -0
  70. package/dist/lib/number-parsers.d.ts +2 -0
  71. package/dist/lib/reactivity/createItemCollection.d.ts +21 -0
  72. package/dist/lib/xml-serialization.d.ts +11 -2
  73. package/dist/parse/XFormDOM.d.ts +1 -1
  74. package/dist/parse/body/BodyDefinition.d.ts +2 -2
  75. package/dist/parse/body/appearance/rangeAppearanceParser.d.ts +3 -0
  76. package/dist/parse/body/control/InputControlDefinition.d.ts +3 -0
  77. package/dist/parse/body/control/ItemDefinition.d.ts +14 -0
  78. package/dist/parse/body/control/ItemsetDefinition.d.ts +18 -0
  79. package/dist/parse/body/control/RangeControlDefinition.d.ts +31 -2
  80. package/dist/parse/body/control/RankControlDefinition.d.ts +7 -3
  81. package/dist/parse/body/control/{select/SelectDefinition.d.ts → SelectControlDefinition.d.ts} +9 -9
  82. package/dist/parse/expression/ItemsetNodesetExpression.d.ts +1 -1
  83. package/dist/parse/expression/ItemsetValueExpression.d.ts +1 -1
  84. package/dist/parse/model/BindDefinition.d.ts +3 -1
  85. package/dist/parse/model/BindPreloadDefinition.d.ts +42 -0
  86. package/dist/parse/model/DescendentNodeDefinition.d.ts +4 -13
  87. package/dist/parse/model/ItextTranslation/ItextTranslationRootDefinition.d.ts +2 -1
  88. package/dist/parse/model/LeafNodeDefinition.d.ts +7 -4
  89. package/dist/parse/model/ModelBindMap.d.ts +1 -1
  90. package/dist/parse/model/NodeDefinition.d.ts +16 -19
  91. package/dist/parse/model/NoteNodeDefinition.d.ts +6 -5
  92. package/dist/parse/model/RangeNodeDefinition.d.ts +41 -0
  93. package/dist/parse/model/RepeatInstanceDefinition.d.ts +7 -4
  94. package/dist/parse/model/RepeatRangeDefinition.d.ts +7 -4
  95. package/dist/parse/model/RepeatTemplateDefinition.d.ts +7 -4
  96. package/dist/parse/model/RootAttributeDefinition.d.ts +24 -0
  97. package/dist/parse/model/RootAttributeMap.d.ts +23 -0
  98. package/dist/parse/model/RootDefinition.d.ts +9 -7
  99. package/dist/parse/model/SubtreeDefinition.d.ts +7 -4
  100. package/dist/parse/shared/parseStaticDocumentFromDOMSubtree.d.ts +2 -3
  101. package/dist/parse/text/ItemLabelDefinition.d.ts +1 -1
  102. package/dist/parse/text/ItemsetLabelDefinition.d.ts +2 -2
  103. package/dist/parse/text/abstract/TextElementDefinition.d.ts +1 -1
  104. package/dist/parse/xpath/semantic-analysis.d.ts +1 -3
  105. package/dist/solid.js +5603 -4665
  106. package/dist/solid.js.map +1 -1
  107. package/package.json +15 -12
  108. package/src/client/BaseNode.ts +5 -0
  109. package/src/client/BaseValueNode.ts +1 -1
  110. package/src/client/GroupNode.ts +1 -0
  111. package/src/client/InputNode.ts +38 -2
  112. package/src/client/ModelValueNode.ts +1 -0
  113. package/src/client/NoteNode.ts +43 -7
  114. package/src/client/RangeNode.ts +51 -0
  115. package/src/client/RankNode.ts +54 -0
  116. package/src/client/RootNode.ts +11 -5
  117. package/src/client/SelectNode.ts +53 -26
  118. package/src/client/SubtreeNode.ts +1 -0
  119. package/src/client/TriggerNode.ts +12 -6
  120. package/src/client/hierarchy.ts +7 -8
  121. package/src/client/node-types.ts +1 -1
  122. package/src/client/unsupported/UnsupportedControlNode.ts +2 -6
  123. package/src/error/RankMissingValueError.ts +5 -0
  124. package/src/error/RankValueTypeError.ts +13 -0
  125. package/src/error/SelectValueTypeError.ts +22 -0
  126. package/src/error/XFormsSpecViolationError.ts +1 -0
  127. package/src/index.ts +2 -12
  128. package/src/instance/Group.ts +1 -0
  129. package/src/instance/InputControl.ts +42 -2
  130. package/src/instance/ModelValue.ts +2 -0
  131. package/src/instance/Note.ts +34 -59
  132. package/src/instance/PrimaryInstance.ts +1 -0
  133. package/src/instance/RangeControl.ts +113 -0
  134. package/src/instance/RankControl.ts +199 -0
  135. package/src/instance/Root.ts +3 -2
  136. package/src/instance/SelectControl.ts +219 -0
  137. package/src/instance/Subtree.ts +1 -0
  138. package/src/instance/TriggerControl.ts +36 -75
  139. package/src/instance/abstract/DescendantNode.ts +1 -6
  140. package/src/instance/abstract/InstanceNode.ts +10 -2
  141. package/src/instance/abstract/UnsupportedControl.ts +1 -0
  142. package/src/instance/abstract/ValueNode.ts +3 -2
  143. package/src/instance/children.ts +71 -30
  144. package/src/instance/hierarchy.ts +21 -16
  145. package/src/instance/internal-api/InstanceValueContext.ts +2 -0
  146. package/src/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.ts +2 -1
  147. package/src/instance/internal-api/submission/ClientReactiveSubmittableParentNode.ts +2 -1
  148. package/src/instance/internal-api/submission/ClientReactiveSubmittableValueNode.ts +2 -1
  149. package/src/instance/repeat/BaseRepeatRange.ts +2 -0
  150. package/src/instance/repeat/RepeatInstance.ts +1 -0
  151. package/src/instance/resource.ts +4 -1
  152. package/src/integration/xpath/adapter/names.ts +66 -17
  153. package/src/integration/xpath/adapter/traversal.ts +10 -9
  154. package/src/integration/xpath/static-dom/StaticAttribute.ts +15 -7
  155. package/src/integration/xpath/static-dom/StaticDocument.ts +0 -2
  156. package/src/integration/xpath/static-dom/StaticElement.ts +21 -8
  157. package/src/lib/client-reactivity/submission/createLeafNodeSubmissionState.ts +1 -1
  158. package/src/lib/client-reactivity/submission/createParentNodeSubmissionState.ts +1 -1
  159. package/src/lib/client-reactivity/submission/createRootSubmissionState.ts +19 -0
  160. package/src/lib/client-reactivity/submission/createValueNodeSubmissionState.ts +2 -2
  161. package/src/lib/codecs/Geopoint/Geopoint.ts +150 -0
  162. package/src/lib/codecs/Geopoint/GeopointValueCodec.ts +20 -0
  163. package/src/lib/codecs/NoteCodec.ts +32 -0
  164. package/src/lib/codecs/RangeCodec.ts +65 -0
  165. package/src/lib/codecs/TriggerCodec.ts +64 -0
  166. package/src/lib/codecs/ValueArrayCodec.ts +42 -0
  167. package/src/lib/codecs/ValueCodec.ts +2 -2
  168. package/src/lib/codecs/getNoteCodec.ts +27 -0
  169. package/src/lib/codecs/getSelectCodec.ts +27 -0
  170. package/src/lib/codecs/getSharedValueCodec.ts +5 -3
  171. package/src/lib/codecs/items/BaseItemCodec.ts +20 -0
  172. package/src/lib/codecs/items/MultipleValueItemCodec.ts +28 -0
  173. package/src/lib/codecs/items/SingleValueItemCodec.ts +67 -0
  174. package/src/lib/dom/query.ts +1 -2
  175. package/src/lib/names/NamespaceDeclaration.ts +106 -0
  176. package/src/lib/names/NamespaceDeclarationMap.ts +228 -0
  177. package/src/lib/names/NamespaceURL.ts +44 -0
  178. package/src/lib/names/QualifiedName.ts +170 -0
  179. package/src/lib/number-parsers.ts +25 -0
  180. package/src/lib/reactivity/createInstanceValueState.ts +50 -0
  181. package/src/lib/reactivity/{createSelectItems.ts → createItemCollection.ts} +41 -36
  182. package/src/lib/xml-serialization.ts +76 -9
  183. package/src/parse/XFormDOM.ts +141 -21
  184. package/src/parse/XFormDefinition.ts +1 -4
  185. package/src/parse/body/BodyDefinition.ts +4 -4
  186. package/src/parse/body/appearance/rangeAppearanceParser.ts +11 -0
  187. package/src/parse/body/control/InputControlDefinition.ts +9 -0
  188. package/src/parse/body/control/{select/ItemDefinition.ts → ItemDefinition.ts} +8 -6
  189. package/src/parse/body/control/{select/ItemsetDefinition.ts → ItemsetDefinition.ts} +11 -9
  190. package/src/parse/body/control/RangeControlDefinition.ts +91 -6
  191. package/src/parse/body/control/RankControlDefinition.ts +25 -7
  192. package/src/parse/body/control/{select/SelectDefinition.ts → SelectControlDefinition.ts} +9 -9
  193. package/src/parse/expression/ItemsetNodesetExpression.ts +1 -1
  194. package/src/parse/expression/ItemsetValueExpression.ts +1 -1
  195. package/src/parse/model/BindDefinition.ts +4 -0
  196. package/src/parse/model/BindPreloadDefinition.ts +100 -0
  197. package/src/parse/model/DescendentNodeDefinition.ts +7 -25
  198. package/src/parse/model/ItextTranslation/ItextTranslationRootDefinition.ts +2 -1
  199. package/src/parse/model/LeafNodeDefinition.ts +11 -4
  200. package/src/parse/model/NodeDefinition.ts +24 -45
  201. package/src/parse/model/NoteNodeDefinition.ts +8 -7
  202. package/src/parse/model/RangeNodeDefinition.ts +118 -0
  203. package/src/parse/model/RepeatInstanceDefinition.ts +11 -7
  204. package/src/parse/model/RepeatRangeDefinition.ts +11 -7
  205. package/src/parse/model/RepeatTemplateDefinition.ts +11 -7
  206. package/src/parse/model/RootAttributeDefinition.ts +45 -0
  207. package/src/parse/model/RootAttributeMap.ts +44 -0
  208. package/src/parse/model/RootDefinition.ts +29 -28
  209. package/src/parse/model/SecondaryInstance/sources/GeoJSONExternalSecondaryInstance.ts +1 -0
  210. package/src/parse/model/SubtreeDefinition.ts +12 -12
  211. package/src/parse/shared/parseStaticDocumentFromDOMSubtree.ts +3 -3
  212. package/src/parse/text/ItemLabelDefinition.ts +1 -1
  213. package/src/parse/text/ItemsetLabelDefinition.ts +2 -2
  214. package/src/parse/text/abstract/TextElementDefinition.ts +1 -1
  215. package/src/parse/xpath/semantic-analysis.ts +4 -3
  216. package/dist/client/unsupported/RangeNode.d.ts +0 -9
  217. package/dist/client/unsupported/RankNode.d.ts +0 -9
  218. package/dist/instance/SelectField.d.ts +0 -58
  219. package/dist/instance/unsupported/RangeControl.d.ts +0 -6
  220. package/dist/instance/unsupported/RankControl.d.ts +0 -6
  221. package/dist/integration/xpath/static-dom/StaticNamedNode.d.ts +0 -17
  222. package/dist/lib/reactivity/createSelectItems.d.ts +0 -16
  223. package/dist/parse/body/control/select/ItemDefinition.d.ts +0 -13
  224. package/dist/parse/body/control/select/ItemsetDefinition.d.ts +0 -17
  225. package/dist/parse/body/control/select/ItemsetNodesetContext.d.ts +0 -9
  226. package/src/client/unsupported/RangeNode.ts +0 -14
  227. package/src/client/unsupported/RankNode.ts +0 -14
  228. package/src/instance/SelectField.ts +0 -263
  229. package/src/instance/unsupported/RangeControl.ts +0 -9
  230. package/src/instance/unsupported/RankControl.ts +0 -9
  231. package/src/integration/xpath/static-dom/StaticNamedNode.ts +0 -45
  232. package/src/parse/body/control/select/ItemsetNodesetContext.ts +0 -21
@@ -12,7 +12,7 @@ import type { SubmissionResult } from '../client/submission/SubmissionResult.ts'
12
12
  import type { SubmissionState } from '../client/submission/SubmissionState.ts';
13
13
  import type { AncestorNodeValidationState } from '../client/validation.ts';
14
14
  import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPathNode.ts';
15
- import { createParentNodeSubmissionState } from '../lib/client-reactivity/submission/createParentNodeSubmissionState.ts';
15
+ import { createRootSubmissionState } from '../lib/client-reactivity/submission/createRootSubmissionState.ts';
16
16
  import type { ChildrenState } from '../lib/reactivity/createChildrenState.ts';
17
17
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
18
18
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -76,6 +76,7 @@ export class Root
76
76
  // RootNode
77
77
  readonly nodeType = 'root';
78
78
  readonly appearances = null;
79
+ readonly nodeOptions = null;
79
80
  readonly classes: BodyClassList;
80
81
  readonly currentState: MaterializedChildren<CurrentState<RootStateSpec>, GeneralChildNode>;
81
82
  readonly validationState: AncestorNodeValidationState;
@@ -136,7 +137,7 @@ export class Root
136
137
 
137
138
  childrenState.setChildren(buildChildren(this));
138
139
  this.validationState = createAggregatedViolations(this, sharedStateOptions);
139
- this.submissionState = createParentNodeSubmissionState(this);
140
+ this.submissionState = createRootSubmissionState(this);
140
141
  }
141
142
 
142
143
  getChildren(): readonly GeneralChildNode[] {
@@ -0,0 +1,219 @@
1
+ import { XPathNodeKindKey } from '@getodk/xpath';
2
+ import type { Accessor } from 'solid-js';
3
+ import { createMemo } from 'solid-js';
4
+ import type {
5
+ SelectDefinition,
6
+ SelectItem,
7
+ SelectNode,
8
+ SelectNodeAppearances,
9
+ SelectValueOptions,
10
+ } from '../client/SelectNode.ts';
11
+ import type { TextRange } from '../client/TextRange.ts';
12
+ import type { ValueType } from '../client/ValueType.ts';
13
+ import { SelectValueTypeError } from '../error/SelectValueTypeError.ts';
14
+ import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPathNode.ts';
15
+ import { getSelectCodec } from '../lib/codecs/getSelectCodec.ts';
16
+ import { createItemCollection } from '../lib/reactivity/createItemCollection.ts';
17
+ import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
18
+ import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
19
+ import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
20
+ import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
21
+ import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
22
+ import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
23
+ import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
24
+ import type { SelectType } from '../parse/body/control/SelectControlDefinition.ts';
25
+ import type { Root } from './Root.ts';
26
+ import type { ValueNodeStateSpec } from './abstract/ValueNode.ts';
27
+ import { ValueNode } from './abstract/ValueNode.ts';
28
+ import type { GeneralParentNode } from './hierarchy.ts';
29
+ import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
30
+ import type { ValidationContext } from './internal-api/ValidationContext.ts';
31
+ import type { ClientReactiveSubmittableValueNode } from './internal-api/submission/ClientReactiveSubmittableValueNode.ts';
32
+
33
+ export type AnySelectDefinition = {
34
+ [V in ValueType]: SelectDefinition<V>;
35
+ }[ValueType];
36
+
37
+ type AssertSupportedSelectValueType = (
38
+ definition: AnySelectDefinition
39
+ ) => asserts definition is SelectDefinition<'string'>;
40
+
41
+ const assertSupportedSelectValueType: AssertSupportedSelectValueType = (definition) => {
42
+ if (definition.valueType !== 'string') {
43
+ throw new SelectValueTypeError(definition);
44
+ }
45
+ };
46
+
47
+ type SelectItemMap = ReadonlyMap<string, SelectItem>;
48
+
49
+ interface SelectControlStateSpec extends ValueNodeStateSpec<readonly string[]> {
50
+ readonly label: Accessor<TextRange<'label'> | null>;
51
+ readonly hint: Accessor<TextRange<'hint'> | null>;
52
+ readonly valueOptions: Accessor<SelectValueOptions>;
53
+ }
54
+
55
+ export class SelectControl
56
+ extends ValueNode<'string', SelectDefinition<'string'>, readonly string[], readonly string[]>
57
+ implements
58
+ SelectNode,
59
+ XFormsXPathElement,
60
+ EvaluationContext,
61
+ ValidationContext,
62
+ ClientReactiveSubmittableValueNode
63
+ {
64
+ static from(parent: GeneralParentNode, definition: SelectDefinition): SelectControl;
65
+ static from(parent: GeneralParentNode, definition: AnySelectDefinition): SelectControl {
66
+ assertSupportedSelectValueType(definition);
67
+
68
+ return new this(parent, definition);
69
+ }
70
+
71
+ private readonly mapOptionsByValue: Accessor<SelectItemMap>;
72
+
73
+ protected override readonly getInstanceValue: Accessor<string>;
74
+
75
+ // XFormsXPathElement
76
+ override readonly [XPathNodeKindKey] = 'element';
77
+
78
+ // InstanceNode
79
+ protected readonly state: SharedNodeState<SelectControlStateSpec>;
80
+ protected readonly engineState: EngineState<SelectControlStateSpec>;
81
+
82
+ // SelectNode
83
+ readonly nodeType = 'select';
84
+ readonly selectType: SelectType;
85
+ readonly appearances: SelectNodeAppearances;
86
+ readonly nodeOptions = null;
87
+ readonly currentState: CurrentState<SelectControlStateSpec>;
88
+
89
+ private constructor(parent: GeneralParentNode, definition: SelectDefinition<'string'>) {
90
+ const codec = getSelectCodec(definition);
91
+
92
+ super(parent, definition, codec);
93
+
94
+ this.appearances = definition.bodyElement.appearances;
95
+ this.selectType = definition.bodyElement.type;
96
+
97
+ const valueOptions = createItemCollection(this);
98
+
99
+ const mapOptionsByValue: Accessor<SelectItemMap> = this.scope.runTask(() => {
100
+ return createMemo(() => {
101
+ return new Map(valueOptions().map((item) => [item.value, item]));
102
+ });
103
+ });
104
+
105
+ this.mapOptionsByValue = mapOptionsByValue;
106
+
107
+ const baseValueState = this.valueState;
108
+ const [baseGetValue, setValue] = baseValueState;
109
+ const getValue = this.scope.runTask(() => {
110
+ return createMemo(() => {
111
+ const optionsByValue = mapOptionsByValue();
112
+
113
+ return baseGetValue().filter((value) => {
114
+ return optionsByValue.has(value);
115
+ });
116
+ });
117
+ });
118
+ const valueState: SimpleAtomicState<readonly string[]> = [getValue, setValue];
119
+
120
+ this.getInstanceValue = this.scope.runTask(() => {
121
+ return createMemo(() => {
122
+ return codec.encodeValue(getValue());
123
+ });
124
+ });
125
+
126
+ const sharedStateOptions = {
127
+ clientStateFactory: this.engineConfig.stateFactory,
128
+ };
129
+
130
+ const state = createSharedNodeState(
131
+ this.scope,
132
+ {
133
+ reference: this.contextReference,
134
+ readonly: this.isReadonly,
135
+ relevant: this.isRelevant,
136
+ required: this.isRequired,
137
+
138
+ label: createNodeLabel(this, definition),
139
+ hint: createFieldHint(this, definition),
140
+ children: null,
141
+ valueOptions,
142
+ value: valueState,
143
+ instanceValue: this.getInstanceValue,
144
+ },
145
+ sharedStateOptions
146
+ );
147
+
148
+ this.state = state;
149
+ this.engineState = state.engineState;
150
+ this.currentState = state.currentState;
151
+ }
152
+
153
+ /**
154
+ * Filters {@link values} to include only those values which are currently
155
+ * available in the mapping produced by {@link mapOptionsByValue}, i.e. within
156
+ * a potentially filtered itemset.
157
+ *
158
+ * Note: this method effectively produces an intersection of
159
+ * {@link sourceValues} and {@link values}. **Importantly**, ordering of the
160
+ * results is deterministic, preserving the order of values as yielded _by
161
+ * {@link sourceValues}_.
162
+ *
163
+ * At time of writing, there are several tests (in `@getodk/scenario`, ported
164
+ * from JavaRosa) which expect the values of a `<select>` to match the order
165
+ * they appear in the control's (potentially filtered) `<itemset>` (or list of
166
+ * `<item>`s, for forms defining those inline).
167
+ *
168
+ * @todo The `<odk:rank>` control, having semantics very similar to
169
+ * `<select>`, will likely perform similar filtering logic. However, one of
170
+ * the important distinctions between these controls is that `<odk:rank>`
171
+ * exists explicitly to control the order of values. It's quite likely that
172
+ * would be achieved by invoking the same logic with the parameter order
173
+ * reversed.
174
+ */
175
+ private filterValues(
176
+ sourceValues: Iterable<string>,
177
+ values: Iterable<string>
178
+ ): readonly string[] {
179
+ const selectedValues = new Set(values);
180
+
181
+ return Array.from(sourceValues).filter((sourceValue) => {
182
+ return selectedValues.has(sourceValue);
183
+ });
184
+ }
185
+
186
+ // SelectNode
187
+ getValueOption(value: string): SelectItem | null {
188
+ // Note: this method is a client-facing convenience API for reading state,
189
+ // so it **MUST** read from client-reactive state!
190
+ const valueOption = this.currentState.valueOptions.find((item) => {
191
+ return item.value === value;
192
+ });
193
+
194
+ return valueOption ?? null;
195
+ }
196
+
197
+ isSelected(value: string): boolean {
198
+ // Note: this method is a client-facing convenience API for reading state,
199
+ // so it **MUST** read from client-reactive state!
200
+ return this.currentState.value.includes(value);
201
+ }
202
+
203
+ selectValue(value: string | null): Root {
204
+ if (value == null) {
205
+ return this.selectValues([]);
206
+ }
207
+
208
+ return this.selectValues([value]);
209
+ }
210
+
211
+ selectValues(values: readonly string[]): Root {
212
+ const sourceValues = this.mapOptionsByValue().keys();
213
+ const effectiveValues = this.filterValues(sourceValues, values);
214
+
215
+ this.setValueState(effectiveValues);
216
+
217
+ return this.root;
218
+ }
219
+ }
@@ -49,6 +49,7 @@ export class Subtree
49
49
  // SubtreeNode
50
50
  readonly nodeType = 'subtree';
51
51
  readonly appearances = null;
52
+ readonly nodeOptions = null;
52
53
  readonly currentState: MaterializedChildren<CurrentState<SubtreeStateSpec>, GeneralChildNode>;
53
54
  readonly validationState: AncestorNodeValidationState;
54
55
  readonly submissionState: SubmissionState;
@@ -2,51 +2,61 @@ import { XPathNodeKindKey } from '@getodk/xpath';
2
2
  import type { Accessor } from 'solid-js';
3
3
  import type { TextRange } from '../client/TextRange.ts';
4
4
  import type { TriggerNode, TriggerNodeDefinition } from '../client/TriggerNode.ts';
5
- import type { SubmissionState } from '../client/submission/SubmissionState.ts';
6
- import type { AnyViolation, LeafNodeValidationState } from '../client/validation.ts';
5
+ import type { ValueType } from '../client/ValueType.ts';
6
+ import { ErrorProductionDesignPendingError } from '../error/ErrorProductionDesignPendingError.ts';
7
7
  import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPathNode.ts';
8
- import { createLeafNodeSubmissionState } from '../lib/client-reactivity/submission/createLeafNodeSubmissionState.ts';
9
- import { createValueState } from '../lib/reactivity/createValueState.ts';
8
+ import type { TriggerInputValue, TriggerRuntimeValue } from '../lib/codecs/TriggerCodec.ts';
9
+ import { TriggerCodec } from '../lib/codecs/TriggerCodec.ts';
10
10
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
11
11
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
12
12
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
13
13
  import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
14
14
  import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
15
15
  import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
16
- import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
17
- import type { SharedValidationState } from '../lib/reactivity/validation/createValidation.ts';
18
- import { createValidationState } from '../lib/reactivity/validation/createValidation.ts';
19
16
  import type { UnknownAppearanceDefinition } from '../parse/body/appearance/unknownAppearanceParser.ts';
20
17
  import type { Root } from './Root.ts';
21
- import type { DescendantNodeStateSpec } from './abstract/DescendantNode.ts';
22
- import { DescendantNode } from './abstract/DescendantNode.ts';
18
+ import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
23
19
  import type { GeneralParentNode } from './hierarchy.ts';
24
20
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
25
21
  import type { ValidationContext } from './internal-api/ValidationContext.ts';
26
- import type { ValueContext } from './internal-api/ValueContext.ts';
27
- import type { ClientReactiveSubmittableLeafNode } from './internal-api/submission/ClientReactiveSubmittableLeafNode.ts';
22
+ import type { ClientReactiveSubmittableValueNode } from './internal-api/submission/ClientReactiveSubmittableValueNode.ts';
28
23
 
29
- interface TriggerControlStateSpec extends DescendantNodeStateSpec<boolean> {
24
+ interface TriggerControlStateSpec extends ValueNodeStateSpec<TriggerRuntimeValue> {
30
25
  readonly label: Accessor<TextRange<'label'> | null>;
31
26
  readonly hint: Accessor<TextRange<'hint'> | null>;
32
- readonly children: null;
33
- readonly value: SimpleAtomicState<boolean>;
34
27
  readonly valueOptions: null;
35
28
  }
36
29
 
37
- const TRIGGER_ASSIGNED_VALUE = 'OK';
30
+ type AnyTriggerNodeDefinition = {
31
+ [V in ValueType]: TriggerNodeDefinition<V>;
32
+ }[ValueType];
33
+
34
+ const codec = new TriggerCodec();
38
35
 
39
36
  export class TriggerControl
40
- extends DescendantNode<TriggerNodeDefinition, TriggerControlStateSpec, GeneralParentNode, null>
37
+ extends ValueNode<
38
+ 'string',
39
+ TriggerNodeDefinition<'string'>,
40
+ TriggerRuntimeValue,
41
+ TriggerInputValue
42
+ >
41
43
  implements
42
44
  TriggerNode,
43
45
  XFormsXPathElement,
44
46
  EvaluationContext,
45
47
  ValidationContext,
46
- ValueContext<boolean>,
47
- ClientReactiveSubmittableLeafNode<boolean>
48
+ ClientReactiveSubmittableValueNode
48
49
  {
49
- private readonly validation: SharedValidationState;
50
+ static from(parent: GeneralParentNode, definition: TriggerNodeDefinition): TriggerControl;
51
+ static from(parent: GeneralParentNode, definition: AnyTriggerNodeDefinition): TriggerControl {
52
+ if (definition.valueType !== 'string') {
53
+ throw new ErrorProductionDesignPendingError(
54
+ `Unsupported trigger value type: ${definition.valueType}`
55
+ );
56
+ }
57
+
58
+ return new this(parent, definition);
59
+ }
50
60
 
51
61
  // XFormsXPathElement
52
62
  override readonly [XPathNodeKindKey] = 'element';
@@ -58,42 +68,13 @@ export class TriggerControl
58
68
  // TriggerNode
59
69
  readonly nodeType = 'trigger';
60
70
  readonly appearances: UnknownAppearanceDefinition;
71
+ readonly nodeOptions = null;
61
72
  readonly currentState: CurrentState<TriggerControlStateSpec>;
62
73
 
63
- get validationState(): LeafNodeValidationState {
64
- return this.validation.currentState;
65
- }
66
-
67
- readonly submissionState: SubmissionState;
68
-
69
- // ValueContext
70
- override readonly contextNode = this;
71
- readonly encodeValue: (runtimeValue: boolean) => string;
72
- readonly decodeValue: (instanceValue: string) => boolean;
73
-
74
- constructor(parent: GeneralParentNode, definition: TriggerNodeDefinition) {
75
- super(parent, definition);
74
+ private constructor(parent: GeneralParentNode, definition: TriggerNodeDefinition<'string'>) {
75
+ super(parent, definition, codec);
76
76
 
77
77
  this.appearances = definition.bodyElement.appearances;
78
- this.encodeValue = (runtimeValue) => {
79
- return runtimeValue ? TRIGGER_ASSIGNED_VALUE : '';
80
- };
81
- this.decodeValue = (instanceValue) => {
82
- const value = instanceValue.trim();
83
-
84
- switch (value) {
85
- case TRIGGER_ASSIGNED_VALUE:
86
- return true;
87
-
88
- case '':
89
- return false;
90
-
91
- // TODO (robustness principle): Case insensitivity? Handle
92
- // XPath-semantic booleans?? Other known/common equivalents?
93
- default:
94
- throw new Error(`Unexpected trigger value: ${value}`);
95
- }
96
- };
97
78
 
98
79
  const sharedStateOptions = {
99
80
  clientStateFactory: this.engineConfig.stateFactory,
@@ -111,7 +92,8 @@ export class TriggerControl
111
92
  hint: createFieldHint(this, definition),
112
93
  children: null,
113
94
  valueOptions: null,
114
- value: createValueState(this),
95
+ value: this.valueState,
96
+ instanceValue: this.getInstanceValue,
115
97
  },
116
98
  sharedStateOptions
117
99
  );
@@ -119,32 +101,11 @@ export class TriggerControl
119
101
  this.state = state;
120
102
  this.engineState = state.engineState;
121
103
  this.currentState = state.currentState;
122
- this.validation = createValidationState(this, sharedStateOptions);
123
- this.submissionState = createLeafNodeSubmissionState(this);
124
- }
125
-
126
- // XFormsXPathElement
127
- override getXPathValue(): string {
128
- return this.encodeValue(this.engineState.value);
129
- }
130
-
131
- // ValidationContext
132
- getViolation(): AnyViolation | null {
133
- return this.validation.engineState.violation;
134
- }
135
-
136
- isBlank(): boolean {
137
- return this.engineState.value == null;
138
- }
139
-
140
- // InstanceNode
141
- getChildren(): readonly [] {
142
- return [];
143
104
  }
144
105
 
145
106
  // TriggerNode
146
- setValue(value: boolean): Root {
147
- this.state.setProperty('value', value);
107
+ setValue(value: TriggerInputValue): Root {
108
+ this.setValueState(value);
148
109
 
149
110
  return this.root;
150
111
  }
@@ -13,7 +13,6 @@ import { XFORMS_XPATH_NODE_RANGE_KIND } from '../../integration/xpath/adapter/XF
13
13
  import type { EngineXPathEvaluator } from '../../integration/xpath/EngineXPathEvaluator.ts';
14
14
  import { createComputedExpression } from '../../lib/reactivity/createComputedExpression.ts';
15
15
  import type { ReactiveScope } from '../../lib/reactivity/scope.ts';
16
- import type { AnyDescendantNodeDefinition } from '../../parse/model/DescendentNodeDefinition.ts';
17
16
  import type { AnyNodeDefinition } from '../../parse/model/NodeDefinition.ts';
18
17
  import type { AnyChildNode, AnyParentNode, RepeatRange } from '../hierarchy.ts';
19
18
  import type { EvaluationContext } from '../internal-api/EvaluationContext.ts';
@@ -35,11 +34,7 @@ export type DescendantNodeStateSpec<Value = never> =
35
34
  & InstanceNodeStateSpec<Value>
36
35
  & DescendantNodeSharedStateSpec;
37
36
 
38
- // prettier-ignore
39
- export type DescendantNodeDefinition = Extract<
40
- AnyNodeDefinition,
41
- AnyDescendantNodeDefinition
42
- >;
37
+ export type DescendantNodeDefinition = AnyNodeDefinition;
43
38
 
44
39
  export type AnyDescendantNode = DescendantNode<
45
40
  DescendantNodeDefinition,
@@ -34,6 +34,12 @@ export interface BaseEngineNode extends Omit<BaseNode, 'nodeType'> {
34
34
  readonly nodeType: EngineInstanceNodeType;
35
35
  }
36
36
 
37
+ // prettier-ignore
38
+ export type InstanceNodeValueOptionsStateSpec =
39
+ | Accessor<null>
40
+ | Accessor<readonly unknown[]>
41
+ | null
42
+
37
43
  export interface InstanceNodeStateSpec<Value = never> {
38
44
  readonly reference: Accessor<string> | string;
39
45
  readonly readonly: Accessor<boolean> | boolean;
@@ -42,7 +48,7 @@ export interface InstanceNodeStateSpec<Value = never> {
42
48
  readonly label: Accessor<TextRange<'label'> | null> | null;
43
49
  readonly hint: Accessor<TextRange<'hint'> | null> | null;
44
50
  readonly children: Accessor<readonly FormNodeID[]> | null;
45
- readonly valueOptions: Accessor<null> | Accessor<readonly unknown[]> | null;
51
+ readonly valueOptions: InstanceNodeValueOptionsStateSpec;
46
52
  readonly value: Signal<Value> | SimpleAtomicState<Value> | null;
47
53
  }
48
54
 
@@ -136,6 +142,8 @@ export abstract class InstanceNode<
136
142
 
137
143
  abstract readonly appearances: NodeAppearances<Definition>;
138
144
 
145
+ abstract readonly nodeOptions: object | null;
146
+
139
147
  abstract readonly currentState: InstanceNodeCurrentState<Spec, Child>;
140
148
 
141
149
  abstract readonly validationState: NodeValidationState;
@@ -161,7 +169,7 @@ export abstract class InstanceNode<
161
169
  );
162
170
  }
163
171
 
164
- return `${parent.contextReference()}/${definition.nodeName}`;
172
+ return `${parent.contextReference()}/${definition.qualifiedName.getPrefixedName()}`;
165
173
  };
166
174
 
167
175
  // EvaluationContext: node-specific
@@ -92,6 +92,7 @@ export abstract class UnsupportedControl<Type extends UnsupportedControlNodeType
92
92
  abstract override readonly nodeType: Type;
93
93
 
94
94
  readonly appearances: UnknownAppearanceDefinition;
95
+ readonly nodeOptions = null;
95
96
  readonly currentState: CurrentState<UnsupportedControlStateSpec>;
96
97
 
97
98
  get validationState(): LeafNodeValidationState {
@@ -35,7 +35,6 @@ export type ValueNodeDefinition<V extends ValueType> = LeafNodeDefinition<V>;
35
35
 
36
36
  export interface ValueNodeStateSpec<RuntimeValue> extends DescendantNodeStateSpec<RuntimeValue> {
37
37
  readonly children: null;
38
- readonly valueOptions: null;
39
38
  readonly value: SimpleAtomicState<RuntimeValue>;
40
39
  readonly instanceValue: Accessor<string>;
41
40
  }
@@ -103,7 +102,9 @@ export abstract class ValueNode<
103
102
 
104
103
  this.getInstanceValue = getInstanceValue;
105
104
  this.setValueState = setValueState;
106
- this.getXPathValue = getInstanceValue;
105
+ this.getXPathValue = () => {
106
+ return this.getInstanceValue();
107
+ };
107
108
  this.valueState = valueState;
108
109
  this.validation = createValidationState(this, {
109
110
  clientStateFactory: this.engineConfig.stateFactory,
@@ -2,28 +2,32 @@ import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
2
2
  import type { GroupDefinition } from '../client/GroupNode.ts';
3
3
  import type { InputDefinition } from '../client/InputNode.ts';
4
4
  import type { ModelValueDefinition } from '../client/ModelValueNode.ts';
5
+ import type { SelectDefinition } from '../client/SelectNode.ts';
5
6
  import type { SubtreeDefinition } from '../client/SubtreeNode.ts';
6
7
  import type { TriggerNodeDefinition } from '../client/TriggerNode.ts';
7
- import type { RangeNodeDefinition } from '../client/unsupported/RangeNode.ts';
8
- import type { RankNodeDefinition } from '../client/unsupported/RankNode.ts';
9
- import type { UnsupportedControlDefinition } from '../client/unsupported/UnsupportedControlNode.ts';
8
+ import type { RankDefinition } from '../client/RankNode.ts';
10
9
  import type { UploadNodeDefinition } from '../client/unsupported/UploadNode.ts';
10
+ import { ErrorProductionDesignPendingError } from '../error/ErrorProductionDesignPendingError.ts';
11
11
  import type { LeafNodeDefinition } from '../parse/model/LeafNodeDefinition.ts';
12
12
  import { NoteNodeDefinition } from '../parse/model/NoteNodeDefinition.ts';
13
+ import type {
14
+ AnyRangeNodeDefinition,
15
+ RangeLeafNodeDefinition,
16
+ } from '../parse/model/RangeNodeDefinition.ts';
17
+ import { RangeNodeDefinition } from '../parse/model/RangeNodeDefinition.ts';
13
18
  import type { SubtreeDefinition as ModelSubtreeDefinition } from '../parse/model/SubtreeDefinition.ts';
14
19
  import { Group } from './Group.ts';
15
20
  import type { GeneralChildNode, GeneralParentNode } from './hierarchy.ts';
16
21
  import { InputControl } from './InputControl.ts';
17
22
  import { ModelValue } from './ModelValue.ts';
18
23
  import { Note } from './Note.ts';
24
+ import { RangeControl } from './RangeControl.ts';
19
25
  import { RepeatRangeControlled } from './repeat/RepeatRangeControlled.ts';
20
26
  import { RepeatRangeUncontrolled } from './repeat/RepeatRangeUncontrolled.ts';
21
- import type { SelectFieldDefinition } from './SelectField.ts';
22
- import { SelectField } from './SelectField.ts';
27
+ import { SelectControl } from './SelectControl.ts';
23
28
  import { Subtree } from './Subtree.ts';
24
29
  import { TriggerControl } from './TriggerControl.ts';
25
- import { RangeControl } from './unsupported/RangeControl.ts';
26
- import { RankControl } from './unsupported/RankControl.ts';
30
+ import { RankControl } from './RankControl.ts';
27
31
  import { UploadControl } from './unsupported/UploadControl.ts';
28
32
 
29
33
  const isSubtreeDefinition = (
@@ -32,16 +36,16 @@ const isSubtreeDefinition = (
32
36
  return definition.bodyElement == null;
33
37
  };
34
38
 
35
- type AnyUnsupportedControlDefinition =
36
- | RangeNodeDefinition
37
- | RankNodeDefinition
38
- | UploadNodeDefinition;
39
+ // prettier-ignore
40
+ type AnyUnsupportedControlDefinition = UploadNodeDefinition;
39
41
 
40
42
  // prettier-ignore
41
43
  type ControlNodeDefinition =
42
44
  // eslint-disable-next-line @typescript-eslint/sort-type-constituents
43
45
  | InputDefinition
44
- | SelectFieldDefinition
46
+ | RangeLeafNodeDefinition
47
+ | SelectDefinition
48
+ | RankDefinition
45
49
  | TriggerNodeDefinition
46
50
  | AnyUnsupportedControlDefinition;
47
51
 
@@ -57,22 +61,57 @@ const isInputDefinition = (definition: ControlNodeDefinition): definition is Inp
57
61
  return definition.bodyElement.type === 'input';
58
62
  };
59
63
 
60
- const isSelectFieldDefinition = (
61
- definition: ControlNodeDefinition
62
- ): definition is SelectFieldDefinition => {
64
+ const isSelectDefinition = (definition: ControlNodeDefinition): definition is SelectDefinition => {
63
65
  return definition.bodyElement.type === 'select' || definition.bodyElement.type === 'select1';
64
66
  };
65
67
 
66
- const isRangeNodeDefinition = (
67
- definition: UnsupportedControlDefinition
68
- ): definition is RangeNodeDefinition => {
68
+ const isRankDefinition = (definition: ControlNodeDefinition): definition is RankDefinition => {
69
+ return definition.bodyElement.type === 'rank';
70
+ };
71
+
72
+ const isRangeLeafNodeDefinition = (
73
+ definition: ControlNodeDefinition
74
+ ): definition is RangeLeafNodeDefinition => {
69
75
  return definition.bodyElement.type === 'range';
70
76
  };
71
77
 
72
- const isRankNodeDefinition = (
73
- definition: UnsupportedControlDefinition
74
- ): definition is RankNodeDefinition => {
75
- return definition.bodyElement.type === 'rank';
78
+ type AssertRangeNodeDefinition = (
79
+ definition: RangeLeafNodeDefinition
80
+ ) => asserts definition is AnyRangeNodeDefinition;
81
+
82
+ /**
83
+ * We need some way to narrow the base {@link RangeLeafNodeDefinition} type to
84
+ * {@link RankNodeDefinition} type.
85
+ *
86
+ * At time of writing, we know there is no code path to produce the broader
87
+ * type, but appeasing the type checker for it will help guard against
88
+ * introducing one. (And it would have caught exactly such a mistake in this
89
+ * phase of development, where a more optimistic type cast did not.)
90
+ *
91
+ * An `instanceof` check is appropriate because the narrower type refines all of
92
+ * the following:
93
+ *
94
+ * - `valueType` is a subset of the full range of
95
+ * {@link ValueType | specified value types} (only `int` or `decimal` are
96
+ * supported for range controls)
97
+ *
98
+ * - addition of range-specific properties: `min`, `max`, `step`
99
+ */
100
+ const assertRangeNodeDefinition: AssertRangeNodeDefinition = (definition) => {
101
+ if (definition instanceof RangeNodeDefinition) {
102
+ return;
103
+ }
104
+
105
+ /**
106
+ * At time of writing we know there is no code path producing this
107
+ * case, but appeasing the type checker for it now will guard against
108
+ * it happening mistakenly in any future refactoring. (Hint: it
109
+ * occurred during some refactoring that arrived here, which is why
110
+ * this isn't a type cast!)
111
+ */
112
+ throw new ErrorProductionDesignPendingError(
113
+ `Invalid <range> definition with value type: ${definition.valueType}`
114
+ );
76
115
  };
77
116
 
78
117
  const isTriggerNodeDefinition = (
@@ -127,20 +166,22 @@ export const buildChildren = (parent: GeneralParentNode): GeneralChildNode[] =>
127
166
  return InputControl.from(parent, leafChild);
128
167
  }
129
168
 
130
- if (isSelectFieldDefinition(leafChild)) {
131
- return new SelectField(parent, leafChild);
169
+ if (isSelectDefinition(leafChild)) {
170
+ return SelectControl.from(parent, leafChild);
132
171
  }
133
172
 
134
- if (isTriggerNodeDefinition(leafChild)) {
135
- return new TriggerControl(parent, leafChild);
173
+ if (isRankDefinition(leafChild)) {
174
+ return RankControl.from(parent, leafChild);
136
175
  }
137
176
 
138
- if (isRangeNodeDefinition(leafChild)) {
139
- return new RangeControl(parent, leafChild);
177
+ if (isTriggerNodeDefinition(leafChild)) {
178
+ return TriggerControl.from(parent, leafChild);
140
179
  }
141
180
 
142
- if (isRankNodeDefinition(leafChild)) {
143
- return new RankControl(parent, leafChild);
181
+ if (isRangeLeafNodeDefinition(leafChild)) {
182
+ assertRangeNodeDefinition(leafChild);
183
+
184
+ return RangeControl.from(parent, leafChild);
144
185
  }
145
186
 
146
187
  if (isUploadNodeDefinition(leafChild)) {