@getodk/xforms-engine 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/BaseItem.d.ts +6 -0
- package/dist/client/GroupNode.d.ts +4 -4
- package/dist/client/MarkdownNode.d.ts +33 -0
- package/dist/client/RankNode.d.ts +2 -4
- package/dist/client/SelectNode.d.ts +2 -5
- package/dist/client/TextRange.d.ts +2 -11
- package/dist/client/hierarchy.d.ts +1 -2
- package/dist/client/index.d.ts +1 -1
- package/dist/client/node-types.d.ts +1 -1
- package/dist/index.js +10543 -332
- package/dist/index.js.map +1 -1
- package/dist/instance/RankControl.d.ts +3 -2
- package/dist/instance/SelectControl.d.ts +3 -2
- package/dist/instance/hierarchy.d.ts +5 -6
- package/dist/instance/markdown/MarkdownNode.d.ts +75 -0
- package/dist/instance/text/TextChunk.d.ts +0 -1
- package/dist/instance/text/TextRange.d.ts +2 -1
- package/dist/instance/text/markdownFormat.d.ts +3 -0
- package/dist/lib/reactivity/createItemCollection.d.ts +5 -7
- package/dist/parse/body/BodyDefinition.d.ts +2 -8
- package/dist/parse/body/GroupElementDefinition.d.ts +22 -0
- package/dist/parse/body/control/ItemsetDefinition.d.ts +4 -1
- package/dist/parse/expression/BindComputationExpression.d.ts +1 -1
- package/dist/parse/expression/ItemPropertyExpression.d.ts +6 -0
- package/dist/parse/expression/ItemsetNodesetExpression.d.ts +1 -2
- package/dist/parse/expression/TextChunkExpression.d.ts +9 -7
- package/dist/parse/expression/abstract/DependentExpression.d.ts +1 -15
- package/dist/parse/model/{SubtreeDefinition.d.ts → GroupDefinition.d.ts} +4 -3
- package/dist/parse/model/ModelDefinition.d.ts +6 -1
- package/dist/parse/model/NodeDefinition.d.ts +7 -8
- package/dist/parse/model/RepeatDefinition.d.ts +1 -1
- package/dist/parse/model/RootDefinition.d.ts +1 -1
- package/dist/parse/model/generateItextChunks.d.ts +5 -0
- package/dist/parse/text/LabelDefinition.d.ts +4 -5
- package/dist/parse/xpath/semantic-analysis.d.ts +1 -0
- package/dist/solid.js +10543 -332
- package/dist/solid.js.map +1 -1
- package/package.json +3 -2
- package/src/client/BaseItem.ts +7 -0
- package/src/client/GroupNode.ts +4 -10
- package/src/client/MarkdownNode.ts +53 -0
- package/src/client/RankNode.ts +2 -5
- package/src/client/SelectNode.ts +2 -6
- package/src/client/TextRange.ts +2 -11
- package/src/client/hierarchy.ts +0 -2
- package/src/client/index.ts +1 -1
- package/src/client/node-types.ts +0 -1
- package/src/instance/Group.ts +1 -1
- package/src/instance/RankControl.ts +8 -2
- package/src/instance/SelectControl.ts +8 -2
- package/src/instance/children/buildChildren.ts +1 -17
- package/src/instance/children/normalizeChildInitOptions.ts +44 -59
- package/src/instance/hierarchy.ts +0 -6
- package/src/instance/markdown/MarkdownNode.ts +115 -0
- package/src/instance/text/TextChunk.ts +0 -5
- package/src/instance/text/TextRange.ts +5 -3
- package/src/instance/text/markdownFormat.ts +214 -0
- package/src/integration/xpath/adapter/names.ts +0 -1
- package/src/lib/reactivity/createItemCollection.ts +25 -9
- package/src/lib/reactivity/text/createTextRange.ts +30 -30
- package/src/parse/body/BodyDefinition.ts +7 -34
- package/src/parse/body/GroupElementDefinition.ts +47 -0
- package/src/parse/body/control/ItemsetDefinition.ts +9 -2
- package/src/parse/expression/BindComputationExpression.ts +2 -7
- package/src/parse/expression/ItemPropertyExpression.ts +12 -0
- package/src/parse/expression/ItemsetNodesetExpression.ts +2 -3
- package/src/parse/expression/ItemsetValueExpression.ts +1 -1
- package/src/parse/expression/RepeatCountControlExpression.ts +4 -4
- package/src/parse/expression/TextChunkExpression.ts +25 -35
- package/src/parse/expression/abstract/DependentExpression.ts +2 -38
- package/src/parse/model/{SubtreeDefinition.ts → GroupDefinition.ts} +6 -8
- package/src/parse/model/ModelDefinition.ts +13 -0
- package/src/parse/model/NodeDefinition.ts +8 -9
- package/src/parse/model/RepeatDefinition.ts +2 -2
- package/src/parse/model/RootDefinition.ts +2 -2
- package/src/parse/model/generateItextChunks.ts +61 -0
- package/src/parse/text/ItemsetLabelDefinition.ts +4 -4
- package/src/parse/text/LabelDefinition.ts +4 -9
- package/src/parse/text/MessageDefinition.ts +4 -4
- package/src/parse/text/abstract/TextElementDefinition.ts +6 -7
- package/src/parse/xpath/semantic-analysis.ts +37 -8
- package/dist/client/SubtreeNode.d.ts +0 -56
- package/dist/instance/Subtree.d.ts +0 -38
- package/dist/instance/text/FormattedTextStub.d.ts +0 -1
- package/dist/parse/body/group/BaseGroupDefinition.d.ts +0 -40
- package/dist/parse/body/group/LogicalGroupDefinition.d.ts +0 -6
- package/dist/parse/body/group/PresentationGroupDefinition.d.ts +0 -11
- package/dist/parse/body/group/StructuralGroupDefinition.d.ts +0 -6
- package/src/client/SubtreeNode.ts +0 -61
- package/src/instance/Subtree.ts +0 -102
- package/src/instance/text/FormattedTextStub.ts +0 -8
- package/src/parse/body/group/BaseGroupDefinition.ts +0 -89
- package/src/parse/body/group/LogicalGroupDefinition.ts +0 -11
- package/src/parse/body/group/PresentationGroupDefinition.ts +0 -28
- package/src/parse/body/group/StructuralGroupDefinition.ts +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getodk/xforms-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "XForms engine for ODK Web Forms",
|
|
6
6
|
"type": "module",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"bin-packer": "1.7.0",
|
|
58
|
+
"mdast-util-from-markdown": "^2.0.2",
|
|
58
59
|
"papaparse": "^5.5.3",
|
|
59
60
|
"solid-js": "^1.9.7",
|
|
60
61
|
"temporal-polyfill": "^0.3.0"
|
|
@@ -62,7 +63,7 @@
|
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@babel/core": "^7.28.0",
|
|
64
65
|
"@getodk/tree-sitter-xpath": "0.2.0",
|
|
65
|
-
"@getodk/xpath": "0.
|
|
66
|
+
"@getodk/xpath": "0.8.1",
|
|
66
67
|
"@playwright/test": "^1.53.2",
|
|
67
68
|
"@types/papaparse": "^5.3.16",
|
|
68
69
|
"@vitest/browser": "^3.2.4",
|
package/src/client/GroupNode.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { GroupElementDefinition } from '../parse/body/GroupElementDefinition.ts';
|
|
2
|
+
import type { GroupDefinition as GroupNodeDefinition } from '../parse/model/GroupDefinition.ts';
|
|
3
3
|
import type { BaseNode, BaseNodeState } from './BaseNode.ts';
|
|
4
4
|
import type { NodeAppearances } from './NodeAppearances.ts';
|
|
5
5
|
import type { RootNode } from './RootNode.ts';
|
|
@@ -13,10 +13,8 @@ export interface GroupNodeState extends BaseNodeState {
|
|
|
13
13
|
get value(): null;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export interface GroupDefinition extends SubtreeDefinition {
|
|
19
|
-
readonly bodyElement: AnyGroupElementDefinition;
|
|
16
|
+
export interface GroupDefinition extends GroupNodeDefinition {
|
|
17
|
+
readonly bodyElement: GroupElementDefinition;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
export type GroupNodeAppearances = NodeAppearances<GroupDefinition>;
|
|
@@ -24,10 +22,6 @@ export type GroupNodeAppearances = NodeAppearances<GroupDefinition>;
|
|
|
24
22
|
/**
|
|
25
23
|
* A node corresponding to an XForms `<group>`.
|
|
26
24
|
*/
|
|
27
|
-
// TODO: test (fix?) case where a `<group>` is implicitly connected to a
|
|
28
|
-
// subtree, but doesn't reference it directly. See
|
|
29
|
-
// https://github.com/getodk/web-forms/blob/6cfff8b4c5a2cf6a23a71ef6d4308343bccd2436/packages/odk-web-forms/src/lib/xform/model/ModelDefinition.test.ts#L480-L540
|
|
30
|
-
// for context.
|
|
31
25
|
export interface GroupNode extends BaseNode {
|
|
32
26
|
readonly nodeType: 'group';
|
|
33
27
|
readonly appearances: GroupNodeAppearances;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type Heading = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
2
|
+
|
|
3
|
+
export type ElementName =
|
|
4
|
+
| Heading
|
|
5
|
+
| 'a'
|
|
6
|
+
| 'div'
|
|
7
|
+
| 'em'
|
|
8
|
+
| 'li'
|
|
9
|
+
| 'ol'
|
|
10
|
+
| 'p'
|
|
11
|
+
| 'span'
|
|
12
|
+
| 'strong'
|
|
13
|
+
| 'u'
|
|
14
|
+
| 'ul';
|
|
15
|
+
|
|
16
|
+
export type MarkdownNode = ChildMarkdownNode | HtmlMarkdownNode | ParentMarkdownNode;
|
|
17
|
+
|
|
18
|
+
export interface ParentMarkdownNode {
|
|
19
|
+
readonly role: 'parent';
|
|
20
|
+
readonly elementName: string;
|
|
21
|
+
readonly children: MarkdownNode[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ChildMarkdownNode {
|
|
25
|
+
readonly role: 'child';
|
|
26
|
+
readonly value: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface HtmlMarkdownNode {
|
|
30
|
+
readonly role: 'html';
|
|
31
|
+
readonly unsafeHtml: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AnchorMarkdownNode extends ParentMarkdownNode {
|
|
35
|
+
readonly elementName: 'a';
|
|
36
|
+
readonly url: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface StyledMarkdownNode extends ParentMarkdownNode {
|
|
40
|
+
readonly elementName: 'div' | 'p' | 'span';
|
|
41
|
+
readonly properties: MarkdownProperty | undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface MarkdownProperty {
|
|
45
|
+
readonly style: StyleProperty;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface StyleProperty {
|
|
49
|
+
readonly color: string | undefined;
|
|
50
|
+
readonly 'font-family': string | undefined;
|
|
51
|
+
readonly 'text-align': 'center' | 'left' | 'right' | undefined;
|
|
52
|
+
readonly 'font-size': string | undefined;
|
|
53
|
+
}
|
package/src/client/RankNode.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RankControlDefinition } from '../parse/body/control/RankControlDefinition.ts';
|
|
2
2
|
import type { LeafNodeDefinition } from '../parse/model/LeafNodeDefinition.ts';
|
|
3
3
|
import type { BaseValueNode, BaseValueNodeState } from './BaseValueNode.ts';
|
|
4
|
+
import type { BaseItem } from './BaseItem.ts';
|
|
4
5
|
import type { RootNode } from './RootNode.ts';
|
|
5
6
|
import type { TextRange } from './TextRange.ts';
|
|
6
7
|
import type { GeneralParentNode } from './hierarchy.ts';
|
|
@@ -8,11 +9,7 @@ import type { LeafNodeValidationState } from './validation.ts';
|
|
|
8
9
|
import type { UnknownAppearanceDefinition } from '../parse/body/appearance/unknownAppearanceParser.ts';
|
|
9
10
|
import type { ValueType } from './ValueType.ts';
|
|
10
11
|
|
|
11
|
-
export
|
|
12
|
-
get label(): TextRange<'item-label'>;
|
|
13
|
-
get value(): string;
|
|
14
|
-
}
|
|
15
|
-
|
|
12
|
+
export type RankItem = BaseItem;
|
|
16
13
|
export type RankValueOptions = readonly RankItem[];
|
|
17
14
|
|
|
18
15
|
export interface RankNodeState extends BaseValueNodeState<readonly string[]> {
|
package/src/client/SelectNode.ts
CHANGED
|
@@ -4,18 +4,14 @@ import type {
|
|
|
4
4
|
} from '../parse/body/control/SelectControlDefinition.ts';
|
|
5
5
|
import type { LeafNodeDefinition } from '../parse/model/LeafNodeDefinition.ts';
|
|
6
6
|
import type { BaseValueNode, BaseValueNodeState } from './BaseValueNode.ts';
|
|
7
|
+
import type { BaseItem } from './BaseItem.ts';
|
|
7
8
|
import type { NodeAppearances } from './NodeAppearances.ts';
|
|
8
9
|
import type { RootNode } from './RootNode.ts';
|
|
9
|
-
import type { TextRange } from './TextRange.ts';
|
|
10
10
|
import type { ValueType } from './ValueType.ts';
|
|
11
11
|
import type { GeneralParentNode } from './hierarchy.ts';
|
|
12
12
|
import type { LeafNodeValidationState } from './validation.ts';
|
|
13
13
|
|
|
14
|
-
export
|
|
15
|
-
get label(): TextRange<'item-label'>;
|
|
16
|
-
get value(): string;
|
|
17
|
-
}
|
|
18
|
-
|
|
14
|
+
export type SelectItem = BaseItem;
|
|
19
15
|
export type SelectValueOptions = readonly SelectItem[];
|
|
20
16
|
|
|
21
17
|
export interface SelectNodeState extends BaseValueNodeState<readonly string[]> {
|
package/src/client/TextRange.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { JRResourceURL } from '@getodk/common/jr-resources/JRResourceURL.ts';
|
|
2
2
|
import type { ActiveLanguage } from './FormLanguage.ts';
|
|
3
|
+
import type { MarkdownNode } from './MarkdownNode.ts';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* **COMMENTARY**
|
|
@@ -48,15 +49,6 @@ import type { ActiveLanguage } from './FormLanguage.ts';
|
|
|
48
49
|
* - `h:body//hint/@ref[not(is-translation-expr())]`
|
|
49
50
|
*
|
|
50
51
|
* (See notes above for clarification of `is-translation-expr()`.)
|
|
51
|
-
*
|
|
52
|
-
* @todo It's unclear whether this will all become simpler or more compelex when
|
|
53
|
-
* we add support for outputs in translations. In theory, the actual translation
|
|
54
|
-
* `<text>` nodes map quite well to the `TextRange` concept (i.e. they are a
|
|
55
|
-
* range of static and output chunks, just like labels and hints). The potential
|
|
56
|
-
* for complications arise from XPath implementation details being largely
|
|
57
|
-
* opaque (as in, the `jr:itext` implementation is encapsulated in the `xpath`
|
|
58
|
-
* package, and the engine doesn't really deal with itext translations at the
|
|
59
|
-
* node level at all).
|
|
60
52
|
*/
|
|
61
53
|
// prettier-ignore
|
|
62
54
|
export type TextChunkSource =
|
|
@@ -82,7 +74,6 @@ export interface TextChunk {
|
|
|
82
74
|
get language(): ActiveLanguage;
|
|
83
75
|
|
|
84
76
|
get asString(): string;
|
|
85
|
-
get formatted(): unknown;
|
|
86
77
|
}
|
|
87
78
|
|
|
88
79
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
@@ -155,7 +146,7 @@ export interface TextRange<Role extends TextRole, Origin extends TextOrigin = Te
|
|
|
155
146
|
[Symbol.iterator](): Iterable<TextChunk>;
|
|
156
147
|
|
|
157
148
|
get asString(): string;
|
|
158
|
-
get formatted():
|
|
149
|
+
get formatted(): MarkdownNode[];
|
|
159
150
|
|
|
160
151
|
get imageSource(): JRResourceURL | undefined;
|
|
161
152
|
get audioSource(): JRResourceURL | undefined;
|
package/src/client/hierarchy.ts
CHANGED
|
@@ -10,7 +10,6 @@ import type { RepeatRangeControlledNode } from './repeat/RepeatRangeControlledNo
|
|
|
10
10
|
import type { RepeatRangeUncontrolledNode } from './repeat/RepeatRangeUncontrolledNode.ts';
|
|
11
11
|
import type { RootNode } from './RootNode.ts';
|
|
12
12
|
import type { SelectNode } from './SelectNode.ts';
|
|
13
|
-
import type { SubtreeNode } from './SubtreeNode.ts';
|
|
14
13
|
import type { TriggerNode } from './TriggerNode.ts';
|
|
15
14
|
import type { UploadNode } from './UploadNode.ts';
|
|
16
15
|
|
|
@@ -40,7 +39,6 @@ export type RepeatRangeNode =
|
|
|
40
39
|
*/
|
|
41
40
|
export type GeneralParentNode =
|
|
42
41
|
| RootNode // eslint-disable-line @typescript-eslint/sort-type-constituents
|
|
43
|
-
| SubtreeNode
|
|
44
42
|
| GroupNode
|
|
45
43
|
| RepeatInstanceNode;
|
|
46
44
|
|
package/src/client/index.ts
CHANGED
|
@@ -24,6 +24,7 @@ export type {
|
|
|
24
24
|
} from './hierarchy.ts';
|
|
25
25
|
export type * from './identity.ts';
|
|
26
26
|
export type * from './InputNode.ts';
|
|
27
|
+
export type * from './MarkdownNode.ts';
|
|
27
28
|
export type * from './ModelValueNode.ts';
|
|
28
29
|
export type * from './NoteNode.ts';
|
|
29
30
|
export type * from './OpaqueReactiveObjectFactory.ts';
|
|
@@ -41,7 +42,6 @@ export type * from './serialization/InstancePayload.ts';
|
|
|
41
42
|
export type * from './serialization/InstancePayloadOptions.ts';
|
|
42
43
|
export type * from './serialization/InstanceState.ts';
|
|
43
44
|
export type * from './submission/SubmissionMeta.ts';
|
|
44
|
-
export type * from './SubtreeNode.ts';
|
|
45
45
|
export type * from './TextRange.ts';
|
|
46
46
|
export type * from './TriggerNode.ts';
|
|
47
47
|
export type * from './UploadNode.ts';
|
package/src/client/node-types.ts
CHANGED
package/src/instance/Group.ts
CHANGED
|
@@ -65,7 +65,7 @@ export class Group
|
|
|
65
65
|
) {
|
|
66
66
|
super(parent, instanceNode, definition);
|
|
67
67
|
|
|
68
|
-
this.appearances = definition.bodyElement
|
|
68
|
+
this.appearances = definition.bodyElement?.appearances ?? null;
|
|
69
69
|
|
|
70
70
|
const childrenState = createChildrenState<Group, GeneralChildNode>(this);
|
|
71
71
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { XPathNodeKindKey } from '@getodk/xpath';
|
|
1
|
+
import { type XPathChoiceNode, XPathNodeKindKey } from '@getodk/xpath';
|
|
2
2
|
import type { Accessor } from 'solid-js';
|
|
3
3
|
import { createMemo } from 'solid-js';
|
|
4
4
|
import type { RankDefinition, RankItem, RankNode, RankValueOptions } from '../client/RankNode.ts';
|
|
@@ -67,7 +67,8 @@ export class RankControl
|
|
|
67
67
|
XFormsXPathElement,
|
|
68
68
|
EvaluationContext,
|
|
69
69
|
ValidationContext,
|
|
70
|
-
ClientReactiveSerializableValueNode
|
|
70
|
+
ClientReactiveSerializableValueNode,
|
|
71
|
+
XPathChoiceNode
|
|
71
72
|
{
|
|
72
73
|
static from(
|
|
73
74
|
parent: GeneralParentNode,
|
|
@@ -205,4 +206,9 @@ export class RankControl
|
|
|
205
206
|
this.setValueState(valuesInOrder);
|
|
206
207
|
return this.root;
|
|
207
208
|
}
|
|
209
|
+
|
|
210
|
+
getChoiceName(value: string): string | null {
|
|
211
|
+
const option = this.mapOptionsByValue().get(value);
|
|
212
|
+
return option?.label?.asString ?? null;
|
|
213
|
+
}
|
|
208
214
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { XPathNodeKindKey } from '@getodk/xpath';
|
|
1
|
+
import { type XPathChoiceNode, XPathNodeKindKey } from '@getodk/xpath';
|
|
2
2
|
import type { Accessor } from 'solid-js';
|
|
3
3
|
import { createMemo } from 'solid-js';
|
|
4
4
|
import type {
|
|
@@ -61,7 +61,8 @@ export class SelectControl
|
|
|
61
61
|
XFormsXPathElement,
|
|
62
62
|
EvaluationContext,
|
|
63
63
|
ValidationContext,
|
|
64
|
-
ClientReactiveSerializableValueNode
|
|
64
|
+
ClientReactiveSerializableValueNode,
|
|
65
|
+
XPathChoiceNode
|
|
65
66
|
{
|
|
66
67
|
static from(
|
|
67
68
|
parent: GeneralParentNode,
|
|
@@ -231,4 +232,9 @@ export class SelectControl
|
|
|
231
232
|
|
|
232
233
|
return this.root;
|
|
233
234
|
}
|
|
235
|
+
|
|
236
|
+
getChoiceName(value: string): string | null {
|
|
237
|
+
const option = this.mapOptionsByValue().get(value);
|
|
238
|
+
return option?.label?.asString ?? null;
|
|
239
|
+
}
|
|
234
240
|
}
|
|
@@ -4,7 +4,6 @@ import type { InputDefinition } from '../../client/InputNode.ts';
|
|
|
4
4
|
import type { ModelValueDefinition } from '../../client/ModelValueNode.ts';
|
|
5
5
|
import type { RankDefinition } from '../../client/RankNode.ts';
|
|
6
6
|
import type { SelectDefinition } from '../../client/SelectNode.ts';
|
|
7
|
-
import type { SubtreeDefinition } from '../../client/SubtreeNode.ts';
|
|
8
7
|
import type { TriggerNodeDefinition } from '../../client/TriggerNode.ts';
|
|
9
8
|
import type { UploadDefinition } from '../../client/UploadNode.ts';
|
|
10
9
|
import { ErrorProductionDesignPendingError } from '../../error/ErrorProductionDesignPendingError.ts';
|
|
@@ -15,7 +14,6 @@ import type {
|
|
|
15
14
|
RangeLeafNodeDefinition,
|
|
16
15
|
} from '../../parse/model/RangeNodeDefinition.ts';
|
|
17
16
|
import { RangeNodeDefinition } from '../../parse/model/RangeNodeDefinition.ts';
|
|
18
|
-
import type { SubtreeDefinition as ModelSubtreeDefinition } from '../../parse/model/SubtreeDefinition.ts';
|
|
19
17
|
import { Group } from '../Group.ts';
|
|
20
18
|
import type { GeneralChildNode, GeneralParentNode } from '../hierarchy.ts';
|
|
21
19
|
import { InputControl } from '../InputControl.ts';
|
|
@@ -26,17 +24,10 @@ import { RankControl } from '../RankControl.ts';
|
|
|
26
24
|
import { RepeatRangeControlled } from '../repeat/RepeatRangeControlled.ts';
|
|
27
25
|
import { RepeatRangeUncontrolled } from '../repeat/RepeatRangeUncontrolled.ts';
|
|
28
26
|
import { SelectControl } from '../SelectControl.ts';
|
|
29
|
-
import { Subtree } from '../Subtree.ts';
|
|
30
27
|
import { TriggerControl } from '../TriggerControl.ts';
|
|
31
28
|
import { UploadControl } from '../UploadControl.ts';
|
|
32
29
|
import { childrenInitOptions } from './childrenInitOptions.ts';
|
|
33
30
|
|
|
34
|
-
const isSubtreeDefinition = (
|
|
35
|
-
definition: ModelSubtreeDefinition
|
|
36
|
-
): definition is SubtreeDefinition => {
|
|
37
|
-
return definition.bodyElement == null;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
31
|
// prettier-ignore
|
|
41
32
|
type ControlNodeDefinition =
|
|
42
33
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
@@ -131,14 +122,7 @@ export const buildChildren = (parent: GeneralParentNode): GeneralChildNode[] =>
|
|
|
131
122
|
const [instanceNode = null] = instanceNodes;
|
|
132
123
|
|
|
133
124
|
switch (definition.type) {
|
|
134
|
-
case '
|
|
135
|
-
if (isSubtreeDefinition(definition)) {
|
|
136
|
-
return new Subtree(parent, instanceNode, definition);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// TODO: it'd be good to be able to do without this type assertion. The
|
|
140
|
-
// only distinction between the types is whether `bodyElement` is
|
|
141
|
-
// `null`, but for some reason that's insufficient to narrow the union.
|
|
125
|
+
case 'group': {
|
|
142
126
|
return new Group(parent, instanceNode, definition as GroupDefinition);
|
|
143
127
|
}
|
|
144
128
|
|
|
@@ -3,21 +3,19 @@ import {
|
|
|
3
3
|
XFORMS_NAMESPACE_URI,
|
|
4
4
|
} from '@getodk/common/constants/xmlns.ts';
|
|
5
5
|
import type { GroupDefinition } from '../../client/GroupNode.ts';
|
|
6
|
-
import type { SubtreeDefinition } from '../../client/SubtreeNode.ts';
|
|
7
6
|
import { ErrorProductionDesignPendingError } from '../../error/ErrorProductionDesignPendingError.ts';
|
|
8
7
|
import { StaticDocument } from '../../integration/xpath/static-dom/StaticDocument.ts';
|
|
9
8
|
import type { StaticLeafElement } from '../../integration/xpath/static-dom/StaticElement.ts';
|
|
10
9
|
import { StaticElement } from '../../integration/xpath/static-dom/StaticElement.ts';
|
|
11
10
|
import type { NamespaceURL } from '../../lib/names/NamespaceURL.ts';
|
|
12
11
|
import type { QualifiedName } from '../../lib/names/QualifiedName.ts';
|
|
12
|
+
import type { GroupDefinition as ModelGroupDefinition } from '../../parse/model/GroupDefinition.ts';
|
|
13
13
|
import { LeafNodeDefinition } from '../../parse/model/LeafNodeDefinition.ts';
|
|
14
|
-
import type { SubtreeDefinition as ModelSubtreeDefinition } from '../../parse/model/SubtreeDefinition.ts';
|
|
15
14
|
import type { XFormDOM } from '../../parse/XFormDOM.ts';
|
|
16
15
|
import type { Group } from '../Group.ts';
|
|
17
16
|
import type { GeneralParentNode } from '../hierarchy.ts';
|
|
18
17
|
import type { PrimaryInstance } from '../PrimaryInstance.ts';
|
|
19
18
|
import type { Root } from '../Root.ts';
|
|
20
|
-
import type { Subtree } from '../Subtree.ts';
|
|
21
19
|
import type { ChildrenInitOptions } from './childrenInitOptions.ts';
|
|
22
20
|
import type { DescendantNodeInitOptions } from './DescendantNodeInitOptions.ts';
|
|
23
21
|
|
|
@@ -65,21 +63,12 @@ interface BaseMetaDefinition {
|
|
|
65
63
|
readonly qualifiedName: MetaName;
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
const isBaseMetaDefinition = <T extends
|
|
66
|
+
const isBaseMetaDefinition = <T extends ModelGroupDefinition>(
|
|
69
67
|
definition: T
|
|
70
68
|
): definition is BaseMetaDefinition & T => {
|
|
71
69
|
return isMetaName(definition.qualifiedName);
|
|
72
70
|
};
|
|
73
71
|
|
|
74
|
-
interface MetaSubtreeDefinition extends SubtreeDefinition {
|
|
75
|
-
readonly qualifiedName: MetaName;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
interface MetaSubtree extends Subtree {
|
|
79
|
-
readonly parent: Root;
|
|
80
|
-
readonly definition: MetaSubtreeDefinition;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
72
|
interface MetaGroupDefinition extends GroupDefinition {
|
|
84
73
|
readonly qualifiedName: MetaName;
|
|
85
74
|
}
|
|
@@ -89,17 +78,15 @@ interface MetaGroup extends Group {
|
|
|
89
78
|
readonly definition: MetaGroupDefinition;
|
|
90
79
|
}
|
|
91
80
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
interface MetaSubrootInitOptions extends ChildrenInitOptions {
|
|
95
|
-
readonly parent: MetaGroup | MetaSubtree;
|
|
81
|
+
interface MetaGroupInitOptions extends ChildrenInitOptions {
|
|
82
|
+
readonly parent: MetaGroup;
|
|
96
83
|
}
|
|
97
84
|
|
|
98
|
-
const
|
|
85
|
+
const isMetaGroup = (options: ChildrenInitOptions): options is MetaGroupInitOptions => {
|
|
99
86
|
const { nodeType } = options.parent;
|
|
100
87
|
|
|
101
88
|
return (
|
|
102
|
-
|
|
89
|
+
nodeType === 'group' &&
|
|
103
90
|
isDirectRootDescendant(options.parent) &&
|
|
104
91
|
isBaseMetaDefinition(options.parent.definition)
|
|
105
92
|
);
|
|
@@ -119,16 +106,16 @@ interface EditModeInstanceDescendant {
|
|
|
119
106
|
readonly rootDocument: EditModeInstance;
|
|
120
107
|
}
|
|
121
108
|
|
|
122
|
-
type
|
|
109
|
+
type EditModeMetaGroup = EditModeInstanceDescendant & MetaGroup;
|
|
123
110
|
|
|
124
|
-
interface
|
|
125
|
-
readonly parent:
|
|
111
|
+
interface EditModeMetaGroupInitOptions extends MetaGroupInitOptions {
|
|
112
|
+
readonly parent: EditModeMetaGroup;
|
|
126
113
|
}
|
|
127
114
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
):
|
|
131
|
-
return isEditModeInstance(
|
|
115
|
+
const isEditModeMetaGroup = (
|
|
116
|
+
group: MetaGroupInitOptions
|
|
117
|
+
): group is EditModeMetaGroupInitOptions => {
|
|
118
|
+
return isEditModeInstance(group.parent.rootDocument);
|
|
132
119
|
};
|
|
133
120
|
|
|
134
121
|
interface LeafNodeInitOptions extends DescendantNodeInitOptions {
|
|
@@ -147,13 +134,13 @@ const isLeafNodeInitOptions = (
|
|
|
147
134
|
type MetaLeafChildEntry = readonly [index: number, child: LeafNodeInitOptions];
|
|
148
135
|
|
|
149
136
|
const findMetaLeafChild = (
|
|
150
|
-
|
|
137
|
+
group: MetaGroupInitOptions,
|
|
151
138
|
localName: string
|
|
152
139
|
): MetaLeafChildEntry | null => {
|
|
153
|
-
const metaName =
|
|
140
|
+
const metaName = group.parent.definition.qualifiedName satisfies MetaName;
|
|
154
141
|
const namespaceURI = metaName.namespaceURI.href satisfies MetaNamespaceURIValue;
|
|
155
142
|
|
|
156
|
-
const result = Array.from(
|
|
143
|
+
const result = Array.from(group.children.entries()).find(
|
|
157
144
|
(entry): entry is [number, LeafNodeInitOptions] => {
|
|
158
145
|
const [, child] = entry;
|
|
159
146
|
|
|
@@ -173,8 +160,8 @@ const findMetaLeafChild = (
|
|
|
173
160
|
return result ?? null;
|
|
174
161
|
};
|
|
175
162
|
|
|
176
|
-
const getInstanceIDValue = (
|
|
177
|
-
const [, child = null] = findMetaLeafChild(
|
|
163
|
+
const getInstanceIDValue = (group: MetaGroupInitOptions): string | null => {
|
|
164
|
+
const [, child = null] = findMetaLeafChild(group, INSTANCE_ID_LOCAL_NAME) ?? [];
|
|
178
165
|
|
|
179
166
|
if (child == null) {
|
|
180
167
|
return null;
|
|
@@ -200,11 +187,11 @@ const assertStaticLeafElement: AssertStaticLeafElement = (element) => {
|
|
|
200
187
|
* on {@link populateDeprecatedID}.
|
|
201
188
|
*/
|
|
202
189
|
const buildMetaValueElement = (
|
|
203
|
-
|
|
190
|
+
group: MetaGroupInitOptions,
|
|
204
191
|
localName: string,
|
|
205
192
|
value: string
|
|
206
193
|
): StaticLeafElement => {
|
|
207
|
-
const { qualifiedName, nodeset } =
|
|
194
|
+
const { qualifiedName, nodeset } = group.parent.definition;
|
|
208
195
|
const { namespaceURI, prefix } = qualifiedName;
|
|
209
196
|
const { root } = new StaticDocument({
|
|
210
197
|
documentRoot: {
|
|
@@ -224,21 +211,21 @@ const buildMetaValueElement = (
|
|
|
224
211
|
};
|
|
225
212
|
|
|
226
213
|
const buildDeprecatedIDDefinition = (
|
|
227
|
-
|
|
214
|
+
group: EditModeMetaGroupInitOptions,
|
|
228
215
|
instanceNode: StaticLeafElement
|
|
229
216
|
): LeafNodeDefinition => {
|
|
230
217
|
const nodeset = instanceNode.nodeset;
|
|
231
|
-
const bind =
|
|
218
|
+
const bind = group.model.binds.getOrCreateBindDefinition(nodeset);
|
|
232
219
|
|
|
233
|
-
return new LeafNodeDefinition(
|
|
220
|
+
return new LeafNodeDefinition(group.parent.definition, bind, null, instanceNode);
|
|
234
221
|
};
|
|
235
222
|
|
|
236
223
|
const buildDeprecatedID = (
|
|
237
|
-
|
|
224
|
+
group: EditModeMetaGroupInitOptions,
|
|
238
225
|
value: string
|
|
239
226
|
): LeafNodeInitOptions => {
|
|
240
|
-
const instanceNode = buildMetaValueElement(
|
|
241
|
-
const definition = buildDeprecatedIDDefinition(
|
|
227
|
+
const instanceNode = buildMetaValueElement(group, DEPRECATED_ID_LOCAL_NAME, value);
|
|
228
|
+
const definition = buildDeprecatedIDDefinition(group, instanceNode);
|
|
242
229
|
|
|
243
230
|
return {
|
|
244
231
|
childNodeset: instanceNode.nodeset,
|
|
@@ -248,11 +235,11 @@ const buildDeprecatedID = (
|
|
|
248
235
|
};
|
|
249
236
|
|
|
250
237
|
const updateDeprecatedID = (
|
|
251
|
-
|
|
238
|
+
group: EditModeMetaGroupInitOptions,
|
|
252
239
|
child: LeafNodeInitOptions,
|
|
253
240
|
value: string
|
|
254
241
|
): LeafNodeInitOptions => {
|
|
255
|
-
const instanceNode = buildMetaValueElement(
|
|
242
|
+
const instanceNode = buildMetaValueElement(group, DEPRECATED_ID_LOCAL_NAME, value);
|
|
256
243
|
|
|
257
244
|
return {
|
|
258
245
|
childNodeset: child.childNodeset,
|
|
@@ -288,44 +275,42 @@ const replaceOrConcat = <T>(values: readonly T[], index: number | null, value: T
|
|
|
288
275
|
* hacky normalization for `instanceID`.
|
|
289
276
|
*/
|
|
290
277
|
const populateDeprecatedID = (
|
|
291
|
-
|
|
292
|
-
):
|
|
293
|
-
const value = getInstanceIDValue(
|
|
278
|
+
group: EditModeMetaGroupInitOptions
|
|
279
|
+
): EditModeMetaGroupInitOptions => {
|
|
280
|
+
const value = getInstanceIDValue(group);
|
|
294
281
|
|
|
295
282
|
if (value == null) {
|
|
296
|
-
return
|
|
283
|
+
return group;
|
|
297
284
|
}
|
|
298
285
|
|
|
299
|
-
const [index, currentDeprecatedID] = findMetaLeafChild(
|
|
300
|
-
null,
|
|
301
|
-
];
|
|
286
|
+
const [index, currentDeprecatedID] = findMetaLeafChild(group, DEPRECATED_ID_LOCAL_NAME) ?? [null];
|
|
302
287
|
|
|
303
288
|
let deprecatedID: LeafNodeInitOptions;
|
|
304
289
|
|
|
305
290
|
if (currentDeprecatedID == null) {
|
|
306
|
-
deprecatedID = buildDeprecatedID(
|
|
291
|
+
deprecatedID = buildDeprecatedID(group, value);
|
|
307
292
|
} else {
|
|
308
|
-
deprecatedID = updateDeprecatedID(
|
|
293
|
+
deprecatedID = updateDeprecatedID(group, currentDeprecatedID, value);
|
|
309
294
|
}
|
|
310
295
|
|
|
311
296
|
return {
|
|
312
|
-
model:
|
|
313
|
-
parent:
|
|
314
|
-
children: replaceOrConcat(
|
|
297
|
+
model: group.model,
|
|
298
|
+
parent: group.parent,
|
|
299
|
+
children: replaceOrConcat(group.children, index, deprecatedID),
|
|
315
300
|
};
|
|
316
301
|
};
|
|
317
302
|
|
|
318
|
-
const
|
|
319
|
-
if (
|
|
320
|
-
return populateDeprecatedID(
|
|
303
|
+
const normalizeMetaGroup = (group: MetaGroupInitOptions): MetaGroupInitOptions => {
|
|
304
|
+
if (isEditModeMetaGroup(group)) {
|
|
305
|
+
return populateDeprecatedID(group);
|
|
321
306
|
}
|
|
322
307
|
|
|
323
|
-
return
|
|
308
|
+
return group;
|
|
324
309
|
};
|
|
325
310
|
|
|
326
311
|
export const normalizeChildInitOptions = (options: ChildrenInitOptions): ChildrenInitOptions => {
|
|
327
|
-
if (
|
|
328
|
-
return
|
|
312
|
+
if (isMetaGroup(options)) {
|
|
313
|
+
return normalizeMetaGroup(options);
|
|
329
314
|
}
|
|
330
315
|
|
|
331
316
|
return options;
|
|
@@ -10,7 +10,6 @@ import type { RepeatRangeControlled } from './repeat/RepeatRangeControlled.ts';
|
|
|
10
10
|
import type { RepeatRangeUncontrolled } from './repeat/RepeatRangeUncontrolled.ts';
|
|
11
11
|
import type { Root } from './Root.ts';
|
|
12
12
|
import type { SelectControl } from './SelectControl.ts';
|
|
13
|
-
import type { Subtree } from './Subtree.ts';
|
|
14
13
|
import type { TriggerControl } from './TriggerControl.ts';
|
|
15
14
|
import type { UploadControl } from './UploadControl.ts';
|
|
16
15
|
|
|
@@ -22,7 +21,6 @@ export type AnyNode =
|
|
|
22
21
|
| PrimaryInstance
|
|
23
22
|
| Root
|
|
24
23
|
| Group
|
|
25
|
-
| Subtree
|
|
26
24
|
| RepeatRange
|
|
27
25
|
| RepeatInstance
|
|
28
26
|
| AnyNote
|
|
@@ -40,7 +38,6 @@ export type AnyParentNode =
|
|
|
40
38
|
| PrimaryInstance
|
|
41
39
|
| Root
|
|
42
40
|
| Group
|
|
43
|
-
| Subtree
|
|
44
41
|
| RepeatRange
|
|
45
42
|
| RepeatInstance;
|
|
46
43
|
|
|
@@ -49,7 +46,6 @@ export type GeneralParentNode =
|
|
|
49
46
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
50
47
|
| Root
|
|
51
48
|
| Group
|
|
52
|
-
| Subtree
|
|
53
49
|
| RepeatInstance;
|
|
54
50
|
|
|
55
51
|
// prettier-ignore
|
|
@@ -57,7 +53,6 @@ export type AnyChildNode =
|
|
|
57
53
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
58
54
|
| Root
|
|
59
55
|
| Group
|
|
60
|
-
| Subtree
|
|
61
56
|
| RepeatRange
|
|
62
57
|
| RepeatInstance
|
|
63
58
|
| AnyModelValue
|
|
@@ -73,7 +68,6 @@ export type AnyChildNode =
|
|
|
73
68
|
export type GeneralChildNode =
|
|
74
69
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
75
70
|
| Group
|
|
76
|
-
| Subtree
|
|
77
71
|
| RepeatRange
|
|
78
72
|
| AnyModelValue
|
|
79
73
|
| AnyNote
|