@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
@@ -0,0 +1,13 @@
1
+ import { XFormsSpecViolationError } from './XFormsSpecViolationError.ts';
2
+ import type { RankDefinition } from '../client/RankNode.ts';
3
+ import type { UnsupportedBaseItemValueType } from '../lib/codecs/items/BaseItemCodec.ts';
4
+
5
+ export class RankValueTypeError extends XFormsSpecViolationError {
6
+ constructor(definition: RankDefinition<UnsupportedBaseItemValueType>) {
7
+ const { valueType } = definition;
8
+
9
+ super(
10
+ `Ranks of type ${valueType} are not currently supported. If this functionality would be useful for your form, your feedback is welcome!`
11
+ );
12
+ }
13
+ }
@@ -0,0 +1,22 @@
1
+ import type { SelectDefinition } from '../client/SelectNode.ts';
2
+ import type { UnsupportedBaseItemValueType } from '../lib/codecs/items/BaseItemCodec.ts';
3
+ import { XFormsSpecViolationError } from './XFormsSpecViolationError.ts';
4
+
5
+ /**
6
+ * @todo It would be good to have a standardized way to use specific types of
7
+ * errors as a prompt for feedback. There is currently a feedback link presented
8
+ * by `@getodk/web-forms`, which is conditionally displayed (condition is
9
+ * evidently controlled by a
10
+ * {@link https://vuejs.org/api/sfc-script-setup#defineoptions | Vue component option}).
11
+ *
12
+ * @see {@link https://github.com/getodk/web-forms/issues/276}
13
+ */
14
+ export class SelectValueTypeError extends XFormsSpecViolationError {
15
+ constructor(definition: SelectDefinition<UnsupportedBaseItemValueType>) {
16
+ const { valueType } = definition;
17
+
18
+ super(
19
+ `Selects of type ${valueType} are not currently supported. If this functionality would be useful for your form, your feedback is welcome!`
20
+ );
21
+ }
22
+ }
@@ -0,0 +1 @@
1
+ export abstract class XFormsSpecViolationError extends Error {}
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ export type * from './client/InputNode.ts';
24
24
  export type * from './client/ModelValueNode.ts';
25
25
  export type * from './client/NoteNode.ts';
26
26
  export type * from './client/OpaqueReactiveObjectFactory.ts';
27
+ export type * from './client/RangeNode.ts';
27
28
  export type * from './client/repeat/RepeatInstanceNode.ts';
28
29
  export type * from './client/repeat/RepeatRangeControlledNode.ts';
29
30
  export type * from './client/repeat/RepeatRangeUncontrolledNode.ts';
@@ -39,18 +40,7 @@ export type * from './client/submission/SubmissionState.ts';
39
40
  export type * from './client/SubtreeNode.ts';
40
41
  export type * from './client/TextRange.ts';
41
42
  export type * from './client/TriggerNode.ts';
42
- export type * from './client/unsupported/RangeNode.ts';
43
- export type * from './client/unsupported/RankNode.ts';
43
+ export type * from './client/RankNode.ts';
44
44
  export type * from './client/unsupported/UploadNode.ts';
45
45
  export type * from './client/validation.ts';
46
46
  export type * from './client/ValueType.ts';
47
-
48
- // TODO: notwithstanding potential conflicts with parallel work on `web-forms`
49
- // (former `ui-vue`), these are the last remaining references **outside of
50
- // `xforms-engine`** to anything besides /client/* and the `initializeForm`
51
- // entrypoint implementation. We'll refine the various `definition` types in due
52
- // time.
53
- export type {
54
- AnySelectDefinition,
55
- SelectDefinition,
56
- } from './parse/body/control/select/SelectDefinition.ts';
@@ -52,6 +52,7 @@ export class Group
52
52
  // GroupNode
53
53
  readonly nodeType = 'group';
54
54
  readonly appearances: GroupNodeAppearances;
55
+ readonly nodeOptions = null;
55
56
  readonly currentState: MaterializedChildren<CurrentState<GroupStateSpec>, GeneralChildNode>;
56
57
  readonly validationState: AncestorNodeValidationState;
57
58
  readonly submissionState: SubmissionState;
@@ -5,6 +5,7 @@ import type {
5
5
  InputNode,
6
6
  InputNodeAppearances,
7
7
  InputNodeInputValue,
8
+ InputNodeOptions,
8
9
  } from '../client/InputNode.ts';
9
10
  import type { TextRange } from '../client/TextRange.ts';
10
11
  import type { ValueType } from '../client/ValueType.ts';
@@ -17,20 +18,57 @@ import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedN
17
18
  import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
18
19
  import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
19
20
  import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
20
- import type { Root } from './Root.ts';
21
+ import type { InputControlDefinition } from '../parse/body/control/InputControlDefinition.ts';
21
22
  import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
22
23
  import type { GeneralParentNode } from './hierarchy.ts';
23
24
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
24
- import type { ValidationContext } from './internal-api/ValidationContext.ts';
25
25
  import type { ClientReactiveSubmittableValueNode } from './internal-api/submission/ClientReactiveSubmittableValueNode.ts';
26
+ import type { ValidationContext } from './internal-api/ValidationContext.ts';
27
+ import type { Root } from './Root.ts';
26
28
 
27
29
  export type AnyInputDefinition = {
28
30
  [V in ValueType]: InputDefinition<V>;
29
31
  }[ValueType];
30
32
 
33
+ const stringInputNodeOptions = (control: InputControlDefinition): InputNodeOptions<'string'> => ({
34
+ rows: control.rows,
35
+ });
36
+
37
+ const geoInputNodeOptions = (
38
+ control: InputControlDefinition
39
+ ): InputNodeOptions<'geopoint' | 'geoshape' | 'geotrace'> => ({
40
+ accuracyThreshold: control.accuracyThreshold,
41
+ unacceptableAccuracyThreshold: control.unacceptableAccuracyThreshold,
42
+ });
43
+
44
+ type NodeOptionsFactory<V extends ValueType> = (
45
+ controlDefinition: InputControlDefinition
46
+ ) => InputNodeOptions<V>;
47
+
48
+ type NodeOptionsFactoryByType = {
49
+ [V in ValueType]: NodeOptionsFactory<V>;
50
+ };
51
+
52
+ const nodeOptionsFactoryByType: NodeOptionsFactoryByType = {
53
+ string: stringInputNodeOptions,
54
+ int: () => null,
55
+ boolean: () => null,
56
+ decimal: () => null,
57
+ date: () => null,
58
+ time: () => null,
59
+ dateTime: () => null,
60
+ geopoint: geoInputNodeOptions,
61
+ geotrace: geoInputNodeOptions,
62
+ geoshape: geoInputNodeOptions,
63
+ binary: () => null,
64
+ barcode: () => null,
65
+ intent: () => null,
66
+ };
67
+
31
68
  interface InputControlStateSpec<V extends ValueType> extends ValueNodeStateSpec<RuntimeValue<V>> {
32
69
  readonly label: Accessor<TextRange<'label'> | null>;
33
70
  readonly hint: Accessor<TextRange<'hint'> | null>;
71
+ readonly valueOptions: null;
34
72
  }
35
73
 
36
74
  export class InputControl<V extends ValueType = ValueType>
@@ -60,6 +98,7 @@ export class InputControl<V extends ValueType = ValueType>
60
98
  // InputNode
61
99
  readonly nodeType = 'input';
62
100
  readonly appearances: InputNodeAppearances;
101
+ readonly nodeOptions: InputNodeOptions<V>;
63
102
  readonly currentState: CurrentState<InputControlStateSpec<V>>;
64
103
 
65
104
  constructor(parent: GeneralParentNode, definition: InputDefinition<V>) {
@@ -68,6 +107,7 @@ export class InputControl<V extends ValueType = ValueType>
68
107
  super(parent, definition, codec);
69
108
 
70
109
  this.appearances = definition.bodyElement.appearances;
110
+ this.nodeOptions = nodeOptionsFactoryByType[definition.valueType](definition.bodyElement);
71
111
 
72
112
  const sharedStateOptions = {
73
113
  clientStateFactory: this.engineConfig.stateFactory,
@@ -17,6 +17,7 @@ import type { ClientReactiveSubmittableValueNode } from './internal-api/submissi
17
17
  interface ModelValueStateSpec<V extends ValueType> extends ValueNodeStateSpec<RuntimeValue<V>> {
18
18
  readonly label: null;
19
19
  readonly hint: null;
20
+ readonly valueOptions: null;
20
21
  }
21
22
 
22
23
  export class ModelValue<V extends ValueType = ValueType>
@@ -46,6 +47,7 @@ export class ModelValue<V extends ValueType = ValueType>
46
47
  // ModelValueNode
47
48
  readonly nodeType = 'model-value';
48
49
  readonly appearances = null;
50
+ readonly nodeOptions = null;
49
51
  readonly currentState: CurrentState<ModelValueStateSpec<V>>;
50
52
 
51
53
  constructor(parent: GeneralParentNode, definition: ModelValueDefinition<V>) {
@@ -1,15 +1,13 @@
1
1
  import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
2
- import { identity } from '@getodk/common/lib/identity.ts';
3
2
  import { XPathNodeKindKey } from '@getodk/xpath';
4
3
  import type { Accessor } from 'solid-js';
5
- import type { NoteNode, NoteNodeAppearances } from '../client/NoteNode.ts';
6
- import type { SubmissionState } from '../client/submission/SubmissionState.ts';
4
+ import type { NoteNode, NoteNodeAppearances, NoteValue } from '../client/NoteNode.ts';
7
5
  import type { TextRange } from '../client/TextRange.ts';
8
- import type { AnyViolation, LeafNodeValidationState } from '../client/validation.ts';
6
+ import type { ValueType } from '../client/ValueType.ts';
9
7
  import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPathNode.ts';
10
- import { createLeafNodeSubmissionState } from '../lib/client-reactivity/submission/createLeafNodeSubmissionState.ts';
8
+ import { getNoteCodec } from '../lib/codecs/getNoteCodec.ts';
9
+ import type { NoteInputValue, NoteRuntimeValue } from '../lib/codecs/NoteCodec.ts';
11
10
  import { createNoteReadonlyThunk } from '../lib/reactivity/createNoteReadonlyThunk.ts';
12
- import { createValueState } from '../lib/reactivity/createValueState.ts';
13
11
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
14
12
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
15
13
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
@@ -17,65 +15,47 @@ import { createSharedNodeState } from '../lib/reactivity/node-state/createShared
17
15
  import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
18
16
  import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
19
17
  import { createNoteText, type ComputedNoteText } from '../lib/reactivity/text/createNoteText.ts';
20
- import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
21
- import type { SharedValidationState } from '../lib/reactivity/validation/createValidation.ts';
22
- import { createValidationState } from '../lib/reactivity/validation/createValidation.ts';
23
18
  import type { NoteNodeDefinition } from '../parse/model/NoteNodeDefinition.ts';
24
- import type { DescendantNodeStateSpec } from './abstract/DescendantNode.ts';
25
- import { DescendantNode } from './abstract/DescendantNode.ts';
19
+ import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
26
20
  import type { GeneralParentNode } from './hierarchy.ts';
27
21
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
28
- import type { ClientReactiveSubmittableLeafNode } from './internal-api/submission/ClientReactiveSubmittableLeafNode.ts';
22
+ import type { ClientReactiveSubmittableValueNode } from './internal-api/submission/ClientReactiveSubmittableValueNode.ts';
29
23
  import type { ValidationContext } from './internal-api/ValidationContext.ts';
30
- import type { ValueContext } from './internal-api/ValueContext.ts';
31
24
 
32
- interface NoteStateSpec extends DescendantNodeStateSpec<string> {
25
+ interface NoteStateSpec<V extends ValueType> extends ValueNodeStateSpec<NoteValue<V>> {
33
26
  readonly readonly: Accessor<true>;
34
27
  readonly noteText: ComputedNoteText;
35
28
  readonly label: Accessor<TextRange<'label', 'form'> | null>;
36
29
  readonly hint: Accessor<TextRange<'hint', 'form'> | null>;
37
- readonly children: null;
38
- readonly value: SimpleAtomicState<string>;
39
30
  readonly valueOptions: null;
40
31
  }
41
32
 
42
- export class Note
43
- extends DescendantNode<NoteNodeDefinition, NoteStateSpec, GeneralParentNode, null>
33
+ export class Note<V extends ValueType = ValueType>
34
+ extends ValueNode<V, NoteNodeDefinition<V>, NoteRuntimeValue<V>, NoteInputValue<V>>
44
35
  implements
45
36
  NoteNode,
46
37
  XFormsXPathElement,
47
38
  EvaluationContext,
48
39
  ValidationContext,
49
- ValueContext<string>,
50
- ClientReactiveSubmittableLeafNode<string>
40
+ ClientReactiveSubmittableValueNode
51
41
  {
52
- private readonly validation: SharedValidationState;
53
-
54
42
  // XFormsXPathElement
55
43
  override readonly [XPathNodeKindKey] = 'element';
56
44
 
57
45
  // InstanceNode
58
- protected readonly state: SharedNodeState<NoteStateSpec>;
59
- protected readonly engineState: EngineState<NoteStateSpec>;
46
+ protected readonly state: SharedNodeState<NoteStateSpec<V>>;
47
+ protected readonly engineState: EngineState<NoteStateSpec<V>>;
60
48
 
61
49
  // NoteNode
62
50
  readonly nodeType = 'note';
63
51
  readonly appearances: NoteNodeAppearances;
64
- readonly currentState: CurrentState<NoteStateSpec>;
65
-
66
- get validationState(): LeafNodeValidationState {
67
- return this.validation.currentState;
68
- }
69
-
70
- readonly submissionState: SubmissionState;
52
+ readonly nodeOptions = null;
53
+ readonly currentState: CurrentState<NoteStateSpec<V>>;
71
54
 
72
- // ValueContext
73
- override readonly contextNode = this;
74
- readonly encodeValue = identity<string>;
75
- readonly decodeValue = identity<string>;
55
+ constructor(parent: GeneralParentNode, definition: NoteNodeDefinition<V>) {
56
+ const codec = getNoteCodec(definition.valueType);
76
57
 
77
- constructor(parent: GeneralParentNode, definition: NoteNodeDefinition) {
78
- super(parent, definition);
58
+ super(parent, definition, codec);
79
59
 
80
60
  this.appearances = definition.bodyElement.appearances;
81
61
 
@@ -125,7 +105,8 @@ export class Note
125
105
 
126
106
  children: null,
127
107
  valueOptions: null,
128
- value: createValueState(this),
108
+ value: this.valueState,
109
+ instanceValue: this.getInstanceValue,
129
110
  },
130
111
  sharedStateOptions
131
112
  );
@@ -133,26 +114,20 @@ export class Note
133
114
  this.state = state;
134
115
  this.engineState = state.engineState;
135
116
  this.currentState = state.currentState;
136
- this.validation = createValidationState(this, sharedStateOptions);
137
- this.submissionState = createLeafNodeSubmissionState(this);
138
- }
139
-
140
- // XFormsXPathElement
141
- override getXPathValue(): string {
142
- return this.engineState.value;
143
- }
144
-
145
- // ValidationContext
146
- getViolation(): AnyViolation | null {
147
- return this.validation.engineState.violation;
148
- }
149
-
150
- isBlank(): boolean {
151
- return this.engineState.value === '';
152
- }
153
-
154
- // InstanceNode
155
- getChildren(): readonly [] {
156
- return [];
157
117
  }
158
118
  }
119
+
120
+ export type AnyNote =
121
+ | Note<'barcode'>
122
+ | Note<'binary'>
123
+ | Note<'boolean'>
124
+ | Note<'date'>
125
+ | Note<'dateTime'>
126
+ | Note<'decimal'>
127
+ | Note<'geopoint'>
128
+ | Note<'geoshape'>
129
+ | Note<'geotrace'>
130
+ | Note<'int'>
131
+ | Note<'intent'>
132
+ | Note<'string'>
133
+ | Note<'time'>;
@@ -102,6 +102,7 @@ export class PrimaryInstance
102
102
  // PrimaryInstanceDocument, ClientReactiveSubmittableInstance
103
103
  readonly nodeType = 'primary-instance';
104
104
  readonly appearances = null;
105
+ readonly nodeOptions = null;
105
106
  readonly classes: BodyClassList;
106
107
  readonly root: Root;
107
108
  readonly currentState: MaterializedChildren<CurrentState<PrimaryInstanceStateSpec>, Root>;
@@ -0,0 +1,113 @@
1
+ import { XPathNodeKindKey } from '@getodk/xpath';
2
+ import type { Accessor } from 'solid-js';
3
+ import type {
4
+ RangeInputValue,
5
+ RangeNode,
6
+ RangeNodeAppearances,
7
+ RangeValue,
8
+ } from '../client/RangeNode.ts';
9
+ import type { TextRange } from '../client/TextRange.ts';
10
+ import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPathNode.ts';
11
+ import { RangeCodec } from '../lib/codecs/RangeCodec.ts';
12
+ import { getSharedValueCodec } from '../lib/codecs/getSharedValueCodec.ts';
13
+ import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
14
+ import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
15
+ import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
16
+ import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
17
+ import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
18
+ import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
19
+ import type {
20
+ AnyRangeNodeDefinition,
21
+ RangeNodeDefinition,
22
+ RangeValueType,
23
+ } from '../parse/model/RangeNodeDefinition.ts';
24
+ import type { Root } from './Root.ts';
25
+ import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
26
+ import type { GeneralParentNode } from './hierarchy.ts';
27
+ import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
28
+ import type { ValidationContext } from './internal-api/ValidationContext.ts';
29
+ import type { ClientReactiveSubmittableValueNode } from './internal-api/submission/ClientReactiveSubmittableValueNode.ts';
30
+
31
+ interface RangeControlStateSpec<V extends RangeValueType>
32
+ extends ValueNodeStateSpec<RangeValue<V>> {
33
+ readonly label: Accessor<TextRange<'label'> | null>;
34
+ readonly hint: Accessor<TextRange<'hint'> | null>;
35
+ readonly valueOptions: null;
36
+ }
37
+
38
+ export class RangeControl<V extends RangeValueType = RangeValueType>
39
+ extends ValueNode<V, RangeNodeDefinition<V>, RangeValue<V>, RangeInputValue<V>>
40
+ implements
41
+ RangeNode<V>,
42
+ XFormsXPathElement,
43
+ EvaluationContext,
44
+ ValidationContext,
45
+ ClientReactiveSubmittableValueNode
46
+ {
47
+ static from(parent: GeneralParentNode, definition: AnyRangeNodeDefinition): AnyRangeControl;
48
+ static from<V extends RangeValueType>(
49
+ parent: GeneralParentNode,
50
+ definition: RangeNodeDefinition<V>
51
+ ): RangeControl<V> {
52
+ return new this<V>(parent, definition);
53
+ }
54
+
55
+ // XFormsXPathElement
56
+ override readonly [XPathNodeKindKey] = 'element';
57
+
58
+ // InstanceNode
59
+ protected readonly state: SharedNodeState<RangeControlStateSpec<V>>;
60
+ protected readonly engineState: EngineState<RangeControlStateSpec<V>>;
61
+
62
+ // RangeNode
63
+ readonly nodeType = 'range';
64
+ readonly appearances: RangeNodeAppearances;
65
+ readonly nodeOptions = null;
66
+ readonly currentState: CurrentState<RangeControlStateSpec<V>>;
67
+
68
+ constructor(parent: GeneralParentNode, definition: RangeNodeDefinition<V>) {
69
+ const baseCodec = getSharedValueCodec(definition.valueType);
70
+ const codec = new RangeCodec(baseCodec, definition);
71
+
72
+ super(parent, definition, codec);
73
+
74
+ this.appearances = definition.bodyElement.appearances;
75
+
76
+ const sharedStateOptions = {
77
+ clientStateFactory: this.engineConfig.stateFactory,
78
+ };
79
+
80
+ const state = createSharedNodeState(
81
+ this.scope,
82
+ {
83
+ reference: this.contextReference,
84
+ readonly: this.isReadonly,
85
+ relevant: this.isRelevant,
86
+ required: this.isRequired,
87
+
88
+ label: createNodeLabel(this, definition),
89
+ hint: createFieldHint(this, definition),
90
+ children: null,
91
+ valueOptions: null,
92
+ value: this.valueState,
93
+ instanceValue: this.getInstanceValue,
94
+ },
95
+ sharedStateOptions
96
+ );
97
+
98
+ this.state = state;
99
+ this.engineState = state.engineState;
100
+ this.currentState = state.currentState;
101
+ }
102
+
103
+ setValue(value: RangeInputValue<V>): Root {
104
+ this.setValueState(value);
105
+
106
+ return this.root;
107
+ }
108
+ }
109
+
110
+ // prettier-ignore
111
+ export type AnyRangeControl =
112
+ | RangeControl<'decimal'>
113
+ | RangeControl<'int'>;
@@ -0,0 +1,199 @@
1
+ import { XPathNodeKindKey } from '@getodk/xpath';
2
+ import type { Accessor } from 'solid-js';
3
+ import { createMemo } from 'solid-js';
4
+ import type { RankDefinition, RankItem, RankNode, RankValueOptions } from '../client/RankNode.ts';
5
+ import type { TextRange } from '../client/TextRange.ts';
6
+ import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPathNode.ts';
7
+ import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
8
+ import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
9
+ import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
10
+ import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
11
+ import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
12
+ import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
13
+ import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
14
+ import type { Root } from './Root.ts';
15
+ import type { ValueNodeStateSpec } from './abstract/ValueNode.ts';
16
+ import { ValueNode } from './abstract/ValueNode.ts';
17
+ import type { GeneralParentNode } from './hierarchy.ts';
18
+ import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
19
+ import type { ValidationContext } from './internal-api/ValidationContext.ts';
20
+ import type { ClientReactiveSubmittableValueNode } from './internal-api/submission/ClientReactiveSubmittableValueNode.ts';
21
+ import { MultipleValueItemCodec } from '../lib/codecs/items/MultipleValueItemCodec.ts';
22
+ import { sharedValueCodecs } from '../lib/codecs/getSharedValueCodec.ts';
23
+ import { createItemCollection } from '../lib/reactivity/createItemCollection.ts';
24
+ import type { UnknownAppearanceDefinition } from '../parse/body/appearance/unknownAppearanceParser.ts';
25
+ import type { ValueType } from '../client/ValueType.ts';
26
+ import { RankMissingValueError } from '../error/RankMissingValueError.ts';
27
+ import { RankValueTypeError } from '../error/RankValueTypeError.ts';
28
+
29
+ export type AnyRankDefinition = {
30
+ [V in ValueType]: RankDefinition<V>;
31
+ }[ValueType];
32
+
33
+ type AssertRankNodeDefinition = (
34
+ definition: AnyRankDefinition
35
+ ) => asserts definition is RankDefinition<'string'>;
36
+
37
+ const assertRankNodeDefinition: AssertRankNodeDefinition = (definition) => {
38
+ if (definition.valueType !== 'string') {
39
+ throw new RankValueTypeError(definition);
40
+ }
41
+ };
42
+
43
+ type RankItemMap = ReadonlyMap<string, RankItem>;
44
+
45
+ interface RankControlStateSpec extends ValueNodeStateSpec<readonly string[]> {
46
+ readonly label: Accessor<TextRange<'label'> | null>;
47
+ readonly hint: Accessor<TextRange<'hint'> | null>;
48
+ readonly valueOptions: Accessor<RankValueOptions>;
49
+ }
50
+
51
+ /**
52
+ * @ToDo We currently represent a blank value for `<odk:rank>` as an empty array.
53
+ * This is ambiguous, and likely to change.
54
+ *
55
+ * @see {@link https://github.com/getodk/web-forms/issues/295}
56
+ */
57
+ type TempBlankValueState = readonly [];
58
+ const isBlankValueState = (values: readonly string[]): values is TempBlankValueState => {
59
+ return values.length === 0;
60
+ };
61
+
62
+ export class RankControl
63
+ extends ValueNode<'string', RankDefinition<'string'>, readonly string[], readonly string[]>
64
+ implements
65
+ RankNode,
66
+ XFormsXPathElement,
67
+ EvaluationContext,
68
+ ValidationContext,
69
+ ClientReactiveSubmittableValueNode
70
+ {
71
+ static from(parent: GeneralParentNode, definition: RankDefinition): RankControl;
72
+ static from(parent: GeneralParentNode, definition: AnyRankDefinition): RankControl {
73
+ assertRankNodeDefinition(definition);
74
+ return new this(parent, definition);
75
+ }
76
+
77
+ private readonly mapOptionsByValue: Accessor<RankItemMap>;
78
+
79
+ protected override readonly getInstanceValue: Accessor<string>;
80
+
81
+ // XFormsXPathElement
82
+ override readonly [XPathNodeKindKey] = 'element';
83
+
84
+ // InstanceNode
85
+ protected readonly state: SharedNodeState<RankControlStateSpec>;
86
+ protected readonly engineState: EngineState<RankControlStateSpec>;
87
+
88
+ // RankNode
89
+ readonly nodeType = 'rank';
90
+ readonly appearances: UnknownAppearanceDefinition;
91
+ readonly nodeOptions = null;
92
+ readonly currentState: CurrentState<RankControlStateSpec>;
93
+
94
+ private constructor(parent: GeneralParentNode, definition: RankDefinition<'string'>) {
95
+ const codec = new MultipleValueItemCodec(sharedValueCodecs.string);
96
+ super(parent, definition, codec);
97
+
98
+ this.appearances = definition.bodyElement.appearances;
99
+
100
+ const valueOptions = createItemCollection(this);
101
+ const mapOptionsByValue: Accessor<RankItemMap> = this.scope.runTask(() => {
102
+ return createMemo(() => {
103
+ return new Map(valueOptions().map((item) => [item.value, item]));
104
+ });
105
+ });
106
+
107
+ this.mapOptionsByValue = mapOptionsByValue;
108
+
109
+ const baseValueState = this.valueState;
110
+ const [baseGetValue, setValue] = baseValueState;
111
+
112
+ /**
113
+ * @ToDo As new value options become available, they're not yet in the
114
+ * `currentValues` state. This appends them. We intend to change this
115
+ * behavior, likely clearing the previous state instead.
116
+ *
117
+ * However, there's an open question about what we should do when a filter
118
+ * change **only removes values**.
119
+ */
120
+ const getValue = this.scope.runTask(() => {
121
+ return createMemo(() => {
122
+ const options = valueOptions();
123
+ const values = baseGetValue();
124
+
125
+ if (isBlankValueState(values)) {
126
+ return values;
127
+ }
128
+
129
+ const optionValues = new Set(options.map((option) => option.value));
130
+
131
+ /**
132
+ * @see {@link getValue} ToDo paragraph 2.
133
+ */
134
+ const currentValues = values.filter((value) => optionValues.has(value));
135
+
136
+ return Array.from(
137
+ new Set([
138
+ ...currentValues,
139
+ /**
140
+ * @see {@link getValue} ToDo paragraph 1.
141
+ */
142
+ ...optionValues,
143
+ ])
144
+ );
145
+ });
146
+ });
147
+ const valueState: SimpleAtomicState<readonly string[]> = [getValue, setValue];
148
+
149
+ this.getInstanceValue = this.scope.runTask(() => {
150
+ return createMemo(() => codec.encodeValue(getValue()));
151
+ });
152
+
153
+ const sharedStateOptions = {
154
+ clientStateFactory: this.engineConfig.stateFactory,
155
+ };
156
+
157
+ const state = createSharedNodeState(
158
+ this.scope,
159
+ {
160
+ reference: this.contextReference,
161
+ readonly: this.isReadonly,
162
+ relevant: this.isRelevant,
163
+ required: this.isRequired,
164
+ label: createNodeLabel(this, definition),
165
+ hint: createFieldHint(this, definition),
166
+ children: null,
167
+ valueOptions,
168
+ value: valueState,
169
+ instanceValue: this.getInstanceValue,
170
+ },
171
+ sharedStateOptions
172
+ );
173
+
174
+ this.state = state;
175
+ this.engineState = state.engineState;
176
+ this.currentState = state.currentState;
177
+ }
178
+
179
+ getValueLabel(value: string): TextRange<'item-label'> | null {
180
+ const valueOption = this.currentState.valueOptions.find((item) => item.value === value);
181
+ return valueOption?.label ?? null;
182
+ }
183
+
184
+ setValues(valuesInOrder: readonly string[]): Root {
185
+ if (isBlankValueState(valuesInOrder)) {
186
+ this.setValueState(valuesInOrder);
187
+ return this.root;
188
+ }
189
+
190
+ const sourceValues: string[] = Array.from(this.mapOptionsByValue().keys());
191
+ const hasAllValues = sourceValues.every((sourceValue) => valuesInOrder.includes(sourceValue));
192
+ if (!hasAllValues) {
193
+ throw new RankMissingValueError('There are missing options. Rank should have all options.');
194
+ }
195
+
196
+ this.setValueState(valuesInOrder);
197
+ return this.root;
198
+ }
199
+ }