@getodk/xforms-engine 0.1.0 → 0.2.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/.vite/manifest.json +1 -0
- package/dist/XFormDOM.d.ts +1 -0
- package/dist/XFormDataType.d.ts +2 -1
- package/dist/XFormDefinition.d.ts +1 -0
- package/dist/body/BodyDefinition.d.ts +27 -9
- package/dist/body/BodyElementDefinition.d.ts +5 -4
- package/dist/body/RepeatElementDefinition.d.ts +19 -0
- package/dist/body/UnsupportedBodyElementDefinition.d.ts +3 -2
- package/dist/body/appearance/inputAppearanceParser.d.ts +4 -0
- package/dist/body/appearance/selectAppearanceParser.d.ts +4 -0
- package/dist/body/appearance/structureElementAppearanceParser.d.ts +4 -0
- package/dist/body/control/ControlDefinition.d.ts +5 -2
- package/dist/body/control/InputDefinition.d.ts +6 -0
- package/dist/body/control/select/ItemDefinition.d.ts +4 -3
- package/dist/body/control/select/ItemsetDefinition.d.ts +4 -3
- package/dist/body/control/select/ItemsetNodesetContext.d.ts +3 -2
- package/dist/body/control/select/ItemsetNodesetExpression.d.ts +2 -1
- package/dist/body/control/select/ItemsetValueExpression.d.ts +2 -1
- package/dist/body/control/select/SelectDefinition.d.ts +16 -5
- package/dist/body/group/BaseGroupDefinition.d.ts +6 -10
- package/dist/body/group/LogicalGroupDefinition.d.ts +1 -0
- package/dist/body/group/PresentationGroupDefinition.d.ts +3 -2
- package/dist/body/group/StructuralGroupDefinition.d.ts +1 -0
- package/dist/body/text/HintDefinition.d.ts +4 -4
- package/dist/body/text/LabelDefinition.d.ts +9 -7
- package/dist/body/text/TextElementDefinition.d.ts +9 -8
- package/dist/body/text/TextElementOutputPart.d.ts +2 -1
- package/dist/body/text/TextElementPart.d.ts +5 -4
- package/dist/body/text/TextElementReferencePart.d.ts +2 -1
- package/dist/body/text/TextElementStaticPart.d.ts +2 -1
- package/dist/client/BaseNode.d.ts +9 -3
- package/dist/client/EngineConfig.d.ts +2 -1
- package/dist/client/GroupNode.d.ts +10 -6
- package/dist/client/NodeAppearances.d.ts +15 -0
- package/dist/client/RepeatInstanceNode.d.ts +10 -6
- package/dist/client/RepeatRangeNode.d.ts +11 -7
- package/dist/client/RootNode.d.ts +24 -4
- package/dist/client/SelectNode.d.ts +10 -6
- package/dist/client/StringNode.d.ts +9 -5
- package/dist/client/SubtreeNode.d.ts +6 -4
- package/dist/client/TextRange.d.ts +2 -1
- package/dist/client/hierarchy.d.ts +9 -8
- package/dist/client/index.d.ts +3 -2
- package/dist/expression/DependencyContext.d.ts +2 -1
- package/dist/expression/DependentExpression.d.ts +3 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1882 -1757
- package/dist/index.js.map +1 -1
- package/dist/instance/Group.d.ts +15 -15
- package/dist/instance/RepeatInstance.d.ts +39 -15
- package/dist/instance/RepeatRange.d.ts +98 -20
- package/dist/instance/Root.d.ts +25 -39
- package/dist/instance/SelectField.d.ts +17 -17
- package/dist/instance/StringField.d.ts +17 -17
- package/dist/instance/Subtree.d.ts +13 -13
- package/dist/instance/abstract/DescendantNode.d.ts +26 -18
- package/dist/instance/abstract/InstanceNode.d.ts +44 -46
- package/dist/instance/children.d.ts +2 -1
- package/dist/instance/hierarchy.d.ts +8 -7
- package/dist/instance/index.d.ts +4 -3
- package/dist/instance/internal-api/EvaluationContext.d.ts +10 -8
- package/dist/instance/internal-api/InstanceConfig.d.ts +3 -2
- package/dist/instance/internal-api/SubscribableDependency.d.ts +2 -1
- package/dist/instance/internal-api/TranslationContext.d.ts +2 -1
- package/dist/instance/internal-api/ValueContext.d.ts +6 -5
- package/dist/instance/resource.d.ts +3 -2
- package/dist/instance/text/TextChunk.d.ts +4 -3
- package/dist/instance/text/TextRange.d.ts +2 -1
- package/dist/lib/TokenListParser.d.ts +84 -0
- package/dist/lib/dom/query.d.ts +8 -2
- package/dist/lib/reactivity/createChildrenState.d.ts +4 -3
- package/dist/lib/reactivity/createComputedExpression.d.ts +4 -3
- package/dist/lib/reactivity/createSelectItems.d.ts +4 -3
- package/dist/lib/reactivity/createValueState.d.ts +3 -2
- package/dist/lib/reactivity/materializeCurrentStateChildren.d.ts +6 -4
- package/dist/lib/reactivity/node-state/createClientState.d.ts +7 -6
- package/dist/lib/reactivity/node-state/createCurrentState.d.ts +5 -4
- package/dist/lib/reactivity/node-state/createEngineState.d.ts +4 -3
- package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +7 -6
- package/dist/lib/reactivity/node-state/createSpecifiedPropertyDescriptor.d.ts +2 -1
- package/dist/lib/reactivity/node-state/createSpecifiedState.d.ts +3 -2
- package/dist/lib/reactivity/node-state/representations.d.ts +2 -1
- package/dist/lib/reactivity/scope.d.ts +2 -1
- package/dist/lib/reactivity/text/createFieldHint.d.ts +4 -3
- package/dist/lib/reactivity/text/createNodeLabel.d.ts +4 -3
- package/dist/lib/reactivity/text/createTextRange.d.ts +6 -5
- package/dist/lib/reactivity/types.d.ts +2 -1
- package/dist/lib/xpath/analysis.d.ts +2 -1
- package/dist/model/BindComputation.d.ts +2 -1
- package/dist/model/BindDefinition.d.ts +6 -5
- package/dist/model/DescendentNodeDefinition.d.ts +6 -6
- package/dist/model/ModelBindMap.d.ts +4 -3
- package/dist/model/ModelDefinition.d.ts +2 -1
- package/dist/model/NodeDefinition.d.ts +20 -19
- package/dist/model/RepeatInstanceDefinition.d.ts +7 -7
- package/dist/model/{RepeatSequenceDefinition.d.ts → RepeatRangeDefinition.d.ts} +7 -6
- package/dist/model/RepeatTemplateDefinition.d.ts +8 -8
- package/dist/model/RootDefinition.d.ts +8 -5
- package/dist/model/SubtreeDefinition.d.ts +5 -4
- package/dist/model/ValueNodeDefinition.d.ts +5 -5
- package/dist/solid.js +1873 -1751
- package/dist/solid.js.map +1 -1
- package/package.json +14 -18
- package/src/XFormDOM.ts +81 -8
- package/src/body/BodyDefinition.ts +38 -23
- package/src/body/RepeatElementDefinition.ts +70 -0
- package/src/body/appearance/inputAppearanceParser.ts +39 -0
- package/src/body/appearance/selectAppearanceParser.ts +38 -0
- package/src/body/appearance/structureElementAppearanceParser.ts +7 -0
- package/src/body/control/ControlDefinition.ts +4 -0
- package/src/body/control/InputDefinition.ts +13 -0
- package/src/body/control/select/SelectDefinition.ts +14 -5
- package/src/body/group/BaseGroupDefinition.ts +11 -49
- package/src/body/text/LabelDefinition.ts +15 -1
- package/src/body/text/TextElementDefinition.ts +5 -5
- package/src/client/BaseNode.ts +9 -1
- package/src/client/GroupNode.ts +6 -2
- package/src/client/NodeAppearances.ts +22 -0
- package/src/client/RepeatInstanceNode.ts +4 -0
- package/src/client/RepeatRangeNode.ts +6 -2
- package/src/client/RootNode.ts +22 -0
- package/src/client/SelectNode.ts +4 -0
- package/src/client/StringNode.ts +4 -0
- package/src/client/SubtreeNode.ts +1 -0
- package/src/instance/Group.ts +14 -9
- package/src/instance/RepeatInstance.ts +59 -15
- package/src/instance/RepeatRange.ts +133 -15
- package/src/instance/Root.ts +20 -64
- package/src/instance/SelectField.ts +7 -7
- package/src/instance/StringField.ts +8 -7
- package/src/instance/Subtree.ts +10 -7
- package/src/instance/abstract/DescendantNode.ts +45 -43
- package/src/instance/abstract/InstanceNode.ts +69 -86
- package/src/instance/children.ts +17 -7
- package/src/instance/index.ts +1 -1
- package/src/instance/internal-api/EvaluationContext.ts +5 -6
- package/src/instance/internal-api/ValueContext.ts +2 -2
- package/src/lib/TokenListParser.ts +156 -0
- package/src/lib/dom/query.ts +13 -0
- package/src/lib/reactivity/createChildrenState.ts +51 -6
- package/src/lib/reactivity/createComputedExpression.ts +1 -1
- package/src/lib/reactivity/createSelectItems.ts +4 -6
- package/src/lib/reactivity/createValueState.ts +6 -6
- package/src/lib/reactivity/materializeCurrentStateChildren.ts +3 -1
- package/src/model/DescendentNodeDefinition.ts +1 -2
- package/src/model/ModelDefinition.ts +1 -1
- package/src/model/NodeDefinition.ts +12 -12
- package/src/model/RepeatInstanceDefinition.ts +8 -13
- package/src/model/{RepeatSequenceDefinition.ts → RepeatRangeDefinition.ts} +6 -6
- package/src/model/RepeatTemplateDefinition.ts +10 -15
- package/src/model/RootDefinition.ts +6 -12
- package/src/model/SubtreeDefinition.ts +3 -3
- package/src/model/ValueNodeDefinition.ts +2 -3
- package/dist/body/RepeatDefinition.d.ts +0 -15
- package/dist/body/group/RepeatGroupDefinition.d.ts +0 -12
- package/src/body/RepeatDefinition.ts +0 -54
- package/src/body/group/RepeatGroupDefinition.ts +0 -91
package/src/instance/children.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
|
|
2
|
-
import { SelectDefinition } from '../body/control/select/SelectDefinition.ts';
|
|
3
2
|
import type { GroupDefinition } from '../client/GroupNode.ts';
|
|
4
3
|
import type { SubtreeDefinition } from '../client/SubtreeNode.ts';
|
|
5
4
|
import type { SubtreeDefinition as ModelSubtreeDefinition } from '../model/SubtreeDefinition.ts';
|
|
6
5
|
import { Group } from './Group.ts';
|
|
7
6
|
import { RepeatRange } from './RepeatRange.ts';
|
|
8
|
-
import {
|
|
7
|
+
import type { SelectFieldDefinition } from './SelectField.ts';
|
|
8
|
+
import { SelectField } from './SelectField.ts';
|
|
9
|
+
import type { StringFieldDefinition } from './StringField.ts';
|
|
9
10
|
import { StringField } from './StringField.ts';
|
|
10
11
|
import { Subtree } from './Subtree.ts';
|
|
11
12
|
import type { GeneralChildNode, GeneralParentNode } from './hierarchy.ts';
|
|
@@ -32,16 +33,25 @@ export const buildChildren = (parent: GeneralParentNode): GeneralChildNode[] =>
|
|
|
32
33
|
return new Group(parent, child as GroupDefinition);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
case 'repeat-
|
|
36
|
+
case 'repeat-range': {
|
|
36
37
|
return new RepeatRange(parent, child);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
case 'value-node': {
|
|
40
|
-
if
|
|
41
|
-
|
|
41
|
+
// TODO: this sort of awkwardness might go away if we embrace a
|
|
42
|
+
// proliferation of node types throughout.
|
|
43
|
+
switch (child.bodyElement?.type) {
|
|
44
|
+
case 'select':
|
|
45
|
+
case 'select1':
|
|
46
|
+
return new SelectField(parent, child as SelectFieldDefinition);
|
|
47
|
+
|
|
48
|
+
case 'input':
|
|
49
|
+
case undefined:
|
|
50
|
+
return new StringField(parent, child as StringFieldDefinition);
|
|
51
|
+
|
|
52
|
+
default:
|
|
53
|
+
throw new UnreachableError(child.bodyElement);
|
|
42
54
|
}
|
|
43
|
-
|
|
44
|
-
return new StringField(parent, child);
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
default: {
|
package/src/instance/index.ts
CHANGED
|
@@ -31,7 +31,7 @@ export const initializeForm = async (
|
|
|
31
31
|
const sourceXML = await retrieveSourceXMLResource(input, engineConfig);
|
|
32
32
|
const form = new XFormDefinition(sourceXML);
|
|
33
33
|
|
|
34
|
-
return Root
|
|
34
|
+
return new Root(form.xformDOM, form.model.root, engineConfig);
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
initializeForm satisfies InitializeForm;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { XFormsXPathEvaluator } from '@getodk/xpath';
|
|
2
|
+
import type { Accessor } from 'solid-js';
|
|
2
3
|
import type { DependentExpression } from '../../expression/DependentExpression.ts';
|
|
3
4
|
import type { ReactiveScope } from '../../lib/reactivity/scope.ts';
|
|
4
5
|
import type { SubscribableDependency } from './SubscribableDependency.ts';
|
|
@@ -27,15 +28,13 @@ export interface EvaluationContext {
|
|
|
27
28
|
* Produces the current absolute reference to the {@link contextNode}, where
|
|
28
29
|
* the absolute `/` resolves to the active form state's primary instance root.
|
|
29
30
|
*/
|
|
30
|
-
|
|
31
|
+
readonly contextReference: Accessor<string>;
|
|
31
32
|
|
|
32
33
|
readonly contextNode: Node;
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
|
-
* Resolves
|
|
36
|
-
* {@link EvaluationContext.contextNode}.
|
|
36
|
+
* Resolves nodes corresponding to the provided node-set reference, possibly
|
|
37
|
+
* relative to the {@link EvaluationContext.contextNode}.
|
|
37
38
|
*/
|
|
38
|
-
|
|
39
|
-
reference: string
|
|
40
|
-
) => SubscribableDependency | null;
|
|
39
|
+
getSubscribableDependenciesByReference(reference: string): readonly SubscribableDependency[];
|
|
41
40
|
}
|
|
@@ -19,8 +19,8 @@ export interface ValueContext<RuntimeValue> extends EvaluationContext {
|
|
|
19
19
|
readonly definition: ValueContextDefinition;
|
|
20
20
|
readonly contextNode: Element;
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
isReadonly(): boolean;
|
|
23
|
+
isRelevant(): boolean;
|
|
24
24
|
|
|
25
25
|
readonly encodeValue: (this: unknown, runtimeValue: RuntimeValue) => InstanceValue;
|
|
26
26
|
readonly decodeValue: (this: unknown, instanceValue: InstanceValue) => RuntimeValue;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { xmlXPathWhitespaceSeparatedList } from '@getodk/common/lib/string/whitespace.ts';
|
|
2
|
+
import type { PartiallyKnownString } from '@getodk/common/types/string/PartiallyKnownString.ts';
|
|
3
|
+
|
|
4
|
+
type SymbolIterator = typeof Symbol.iterator;
|
|
5
|
+
|
|
6
|
+
type TokenListKey<CanonicalToken extends string> =
|
|
7
|
+
| PartiallyKnownString<CanonicalToken>
|
|
8
|
+
| SymbolIterator;
|
|
9
|
+
|
|
10
|
+
type TokenListIterator<CanonicalToken extends string> = IterableIterator<
|
|
11
|
+
PartiallyKnownString<CanonicalToken>
|
|
12
|
+
>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @see {@link TokenListParser}
|
|
16
|
+
*/
|
|
17
|
+
// prettier-ignore
|
|
18
|
+
export type TokenList<CanonicalToken extends string = string> = {
|
|
19
|
+
readonly [Key in TokenListKey<CanonicalToken>]:
|
|
20
|
+
Key extends SymbolIterator
|
|
21
|
+
? () => TokenListIterator<CanonicalToken>
|
|
22
|
+
: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
interface TokenListAlias<CanonicalToken extends string> {
|
|
26
|
+
readonly fromAlias: string;
|
|
27
|
+
readonly toCanonical: CanonicalToken;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type TokenAliases<CanonicalToken extends string> = ReadonlyArray<TokenListAlias<CanonicalToken>>;
|
|
31
|
+
|
|
32
|
+
interface TokenListParserOptions<CanonicalToken extends string> {
|
|
33
|
+
readonly aliases?: TokenAliases<CanonicalToken>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type TokenListAttributeName = PartiallyKnownString<'appearance' | 'class'>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Intended primarily for use in parsing these features:
|
|
40
|
+
*
|
|
41
|
+
* - {@link https://getodk.github.io/xforms-spec/#appearances | appearances} ({@link https://xlsform.org/en/#appearance | additional documentation})
|
|
42
|
+
*
|
|
43
|
+
* - {@link https://getodk.github.io/xforms-spec/#body-attributes | body `class` attribute}
|
|
44
|
+
*
|
|
45
|
+
* This class is named as a reference to {@link DOMTokenList}
|
|
46
|
+
* ({@link https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList | MDN}),
|
|
47
|
+
* with these similarities:
|
|
48
|
+
*
|
|
49
|
+
* - Represents a value whose serialization is a space-separated list.
|
|
50
|
+
* - Each member ("token") is a string (without whitespace).
|
|
51
|
+
* - Provides set-like semantics to determine presence of members.
|
|
52
|
+
* - Provides ordering semantics as determined by the serialization.
|
|
53
|
+
*
|
|
54
|
+
* This class differs from that prior art in that:
|
|
55
|
+
*
|
|
56
|
+
* - It provides a notion of "canonical" members. This is _mostly_ (but not
|
|
57
|
+
* entirely) intended as a convenience to client developers, automatically
|
|
58
|
+
* populating canonical/known tokens for a given parser type in
|
|
59
|
+
* editor-provided autocomplete, etc. **Importantly**, non-canonical tokens
|
|
60
|
+
* _are not ignored_ in either the {@link TokenList}'s types or runtime
|
|
61
|
+
* values.
|
|
62
|
+
*
|
|
63
|
+
* - Provided "canonical" members may also be used to specify
|
|
64
|
+
* {@link TokenListParserOptions.aliases | optional aliases}. (Example: when
|
|
65
|
+
* parsing "appearances", an alias might map an older deprecated appearance to
|
|
66
|
+
* a newer canonical equivalent.) **Importantly**, when a token matches an
|
|
67
|
+
* alias, both that alias _and the token as-specified_ will be present in the
|
|
68
|
+
* produced {@link TokenList}.
|
|
69
|
+
*
|
|
70
|
+
* - As a parser, it is intended to be read-only. The serialized format which it
|
|
71
|
+
* parses is _generally_ the source of truth (excepting e.g. aliases).
|
|
72
|
+
* Notably, and as mentioned above, ordering is determined by:
|
|
73
|
+
*
|
|
74
|
+
* - Iterating each member, as provided by the serialized representation
|
|
75
|
+
*
|
|
76
|
+
* - If that member corresponds to an alias, that alias is yielded first
|
|
77
|
+
*
|
|
78
|
+
* - Regardless of whether the member corresponds to an alias, the member is
|
|
79
|
+
* yielded as-specified
|
|
80
|
+
*
|
|
81
|
+
* - A parsed {@link TokenList} is intended to maximize convenience of read-only
|
|
82
|
+
* access. Despite many _conceptual similarities_, most of the
|
|
83
|
+
* {@link DOMTokenList} **interface** is eschewed in favor of two (mutually
|
|
84
|
+
* equivalent) access mechanisms:
|
|
85
|
+
*
|
|
86
|
+
* - `Iterable<Token>`, with the ordering semantics described above
|
|
87
|
+
* - `Record<Token, boolean>`
|
|
88
|
+
*
|
|
89
|
+
* \* This may change, as we refine requirements. In the future, we may
|
|
90
|
+
* introduce a notion of mutually exclusive tokens (e.g. "appearances" which
|
|
91
|
+
* cannot be used together), which may in turn utilize instance-defined ordering
|
|
92
|
+
* as part of that mechanism.
|
|
93
|
+
*/
|
|
94
|
+
export class TokenListParser<
|
|
95
|
+
CanonicalToken extends string,
|
|
96
|
+
// Note: this is a separate type parameter so that specifying an alias does
|
|
97
|
+
// not cause it to be mistakenly inferred as a `CanonicalToken` which was
|
|
98
|
+
// not otherwise specified.
|
|
99
|
+
TokenAlias extends CanonicalToken = CanonicalToken,
|
|
100
|
+
> {
|
|
101
|
+
private readonly aliases: ReadonlyMap<string, CanonicalToken>;
|
|
102
|
+
|
|
103
|
+
constructor(
|
|
104
|
+
readonly canonicalTokens: readonly CanonicalToken[],
|
|
105
|
+
options?: TokenListParserOptions<TokenAlias>
|
|
106
|
+
) {
|
|
107
|
+
this.aliases = new Map(
|
|
108
|
+
(options?.aliases ?? []).map(({ fromAlias, toCanonical }) => {
|
|
109
|
+
return [fromAlias, toCanonical];
|
|
110
|
+
})
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
parseFrom(element: Element, attributeName: TokenListAttributeName): TokenList<CanonicalToken> {
|
|
115
|
+
const serialized = element.getAttribute(attributeName) ?? '';
|
|
116
|
+
const specified = xmlXPathWhitespaceSeparatedList(serialized, {
|
|
117
|
+
ignoreEmpty: true,
|
|
118
|
+
});
|
|
119
|
+
const { aliases } = this;
|
|
120
|
+
const resolved = specified.flatMap((token) => {
|
|
121
|
+
const alias = aliases.get(token);
|
|
122
|
+
|
|
123
|
+
if (alias == null) {
|
|
124
|
+
return token;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return [alias, token];
|
|
128
|
+
});
|
|
129
|
+
const tokens = new Set(resolved);
|
|
130
|
+
|
|
131
|
+
return new Proxy(
|
|
132
|
+
{
|
|
133
|
+
[Symbol.iterator]() {
|
|
134
|
+
return resolved.values();
|
|
135
|
+
},
|
|
136
|
+
} satisfies TokenList<string> as TokenList<CanonicalToken>,
|
|
137
|
+
{
|
|
138
|
+
get(target, property, receiver) {
|
|
139
|
+
if (typeof property === 'symbol') {
|
|
140
|
+
return Reflect.get(target, property, receiver);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return tokens.has(property);
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
set() {
|
|
147
|
+
return false;
|
|
148
|
+
},
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
155
|
+
export type ParsedTokenList<Parser extends TokenListParser<any>> =
|
|
156
|
+
Parser extends TokenListParser<infer CanonicalToken> ? TokenList<CanonicalToken> : never;
|
package/src/lib/dom/query.ts
CHANGED
|
@@ -9,6 +9,10 @@ const hintLookup = new ScopedElementLookup(':scope > hint', 'hint');
|
|
|
9
9
|
const itemLookup = new ScopedElementLookup(':scope > item', 'item');
|
|
10
10
|
const itemsetLookup = new ScopedElementLookup(':scope > itemset[nodeset]', 'itemset[nodeset]');
|
|
11
11
|
const labelLookup = new ScopedElementLookup(':scope > label', 'label');
|
|
12
|
+
const repeatGroupLabelLookup = new ScopedElementLookup(
|
|
13
|
+
':scope > label[form-definition-source="repeat-group"]',
|
|
14
|
+
'label[form-definition-source="repeat-group"]'
|
|
15
|
+
);
|
|
12
16
|
const repeatLookup = new ScopedElementLookup(':scope > repeat[nodeset]', 'repeat[nodeset]');
|
|
13
17
|
const valueLookup = new ScopedElementLookup(':scope > value', 'value');
|
|
14
18
|
|
|
@@ -20,6 +24,11 @@ export interface ItemsetElement extends KnownAttributeLocalNamedElement<'itemset
|
|
|
20
24
|
|
|
21
25
|
export interface LabelElement extends LocalNamedElement<'label'> {}
|
|
22
26
|
|
|
27
|
+
export interface RepeatGroupLabelElement extends LabelElement {
|
|
28
|
+
getAttribute(name: 'form-definition-source'): 'repeat-group';
|
|
29
|
+
getAttribute(name: string): string;
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
export interface RepeatElement extends KnownAttributeLocalNamedElement<'repeat', 'nodeset'> {}
|
|
24
33
|
|
|
25
34
|
export interface ValueElement extends LocalNamedElement<'value'> {}
|
|
@@ -40,6 +49,10 @@ export const getLabelElement = (parent: Element): LabelElement | null => {
|
|
|
40
49
|
return labelLookup.getElement<LabelElement>(parent);
|
|
41
50
|
};
|
|
42
51
|
|
|
52
|
+
export const getRepeatGroupLabelElement = (parent: Element): RepeatGroupLabelElement | null => {
|
|
53
|
+
return repeatGroupLabelLookup.getElement<RepeatGroupLabelElement>(parent);
|
|
54
|
+
};
|
|
55
|
+
|
|
43
56
|
export const getRepeatElement = (parent: Element): RepeatElement | null => {
|
|
44
57
|
return repeatLookup.getElement<RepeatElement>(parent);
|
|
45
58
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Accessor, Setter, Signal } from 'solid-js';
|
|
2
|
+
import { createSignal } from 'solid-js';
|
|
2
3
|
import type { OpaqueReactiveObjectFactory } from '../../index.ts';
|
|
3
4
|
import type { AnyChildNode, AnyParentNode } from '../../instance/hierarchy.ts';
|
|
4
5
|
import type { NodeID } from '../../instance/identity.ts';
|
|
@@ -44,11 +45,55 @@ export const createChildrenState = <Parent extends AnyParentNode, Child extends
|
|
|
44
45
|
parent: Parent
|
|
45
46
|
): ChildrenState<Child> => {
|
|
46
47
|
return parent.scope.runTask(() => {
|
|
47
|
-
const
|
|
48
|
-
const [getChildren,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
const baseState = createSignal<readonly Child[]>([]);
|
|
49
|
+
const [getChildren, baseSetChildren] = baseState;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Note: this is clearly derived state. It would be obvious to use
|
|
53
|
+
* `createMemo`, which is exactly what a previous iteration did. This caused
|
|
54
|
+
* mysterious issues when clients:
|
|
55
|
+
*
|
|
56
|
+
* - Also used Solid-based reactivity
|
|
57
|
+
* - Accessed node children state within their own `createMemo` calls
|
|
58
|
+
*
|
|
59
|
+
* It's quite likely that there's a more robust and general solution, which
|
|
60
|
+
* may be applicable if we also generalize this approach to
|
|
61
|
+
* encode/materialize shared structured state (e.g. it may be applicable for
|
|
62
|
+
* select values, form language, probably much more in coming features).
|
|
63
|
+
*
|
|
64
|
+
* In the meantime, while this approach is marginally more complex, it is
|
|
65
|
+
* likely also slightly more efficient. We can revisit the tradeoff if/when
|
|
66
|
+
* those hypothetical generalizations become a priority.
|
|
67
|
+
*/
|
|
68
|
+
const ids = createSignal<readonly NodeID[]>([]);
|
|
69
|
+
const [childIds, setChildIds] = ids;
|
|
70
|
+
|
|
71
|
+
type ChildrenSetterCallback = (prev: readonly Child[]) => readonly Child[];
|
|
72
|
+
type ChildrenOrSetterCallback = ChildrenSetterCallback | readonly Child[];
|
|
73
|
+
|
|
74
|
+
const setChildren: Setter<readonly Child[]> = (
|
|
75
|
+
valueOrSetterCallback: ChildrenOrSetterCallback
|
|
76
|
+
) => {
|
|
77
|
+
let setterCallback: ChildrenSetterCallback;
|
|
78
|
+
|
|
79
|
+
if (typeof valueOrSetterCallback === 'function') {
|
|
80
|
+
setterCallback = valueOrSetterCallback;
|
|
81
|
+
} else {
|
|
82
|
+
setterCallback = (_) => valueOrSetterCallback;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return baseSetChildren((prev) => {
|
|
86
|
+
const result = setterCallback(prev);
|
|
87
|
+
|
|
88
|
+
setChildIds(() => {
|
|
89
|
+
return result.map((child) => child.nodeId);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return result;
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const children: Signal<readonly Child[]> = [getChildren, setChildren];
|
|
52
97
|
|
|
53
98
|
return {
|
|
54
99
|
children,
|
|
@@ -87,7 +87,7 @@ export const createComputedExpression = <Type extends DependentExpressionResultT
|
|
|
87
87
|
|
|
88
88
|
const getReferencedDependencies = createMemo(() => {
|
|
89
89
|
return dependencyReferences.flatMap((reference) => {
|
|
90
|
-
return context.
|
|
90
|
+
return context.getSubscribableDependenciesByReference(reference) ?? [];
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
|
|
@@ -52,10 +52,7 @@ class ItemsetItemEvaluationContext implements EvaluationContext {
|
|
|
52
52
|
readonly scope: ReactiveScope;
|
|
53
53
|
readonly evaluator: XFormsXPathEvaluator;
|
|
54
54
|
readonly root: EvaluationContextRoot;
|
|
55
|
-
|
|
56
|
-
get contextReference(): string {
|
|
57
|
-
return this.selectField.contextReference;
|
|
58
|
-
}
|
|
55
|
+
readonly contextReference: Accessor<string>;
|
|
59
56
|
|
|
60
57
|
constructor(
|
|
61
58
|
private readonly selectField: SelectField,
|
|
@@ -64,10 +61,11 @@ class ItemsetItemEvaluationContext implements EvaluationContext {
|
|
|
64
61
|
this.scope = selectField.scope;
|
|
65
62
|
this.evaluator = selectField.evaluator;
|
|
66
63
|
this.root = selectField.root;
|
|
64
|
+
this.contextReference = selectField.contextReference;
|
|
67
65
|
}
|
|
68
66
|
|
|
69
|
-
|
|
70
|
-
return this.selectField.
|
|
67
|
+
getSubscribableDependenciesByReference(reference: string): readonly SubscribableDependency[] {
|
|
68
|
+
return this.selectField.getSubscribableDependenciesByReference(reference);
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
|
|
@@ -72,7 +72,7 @@ const createPrimaryInstanceValueState = <RuntimeValue>(
|
|
|
72
72
|
|
|
73
73
|
const persistedValueState = createSignal<PersistedValueState>(
|
|
74
74
|
{
|
|
75
|
-
isRelevant: context.isRelevant,
|
|
75
|
+
isRelevant: context.isRelevant(),
|
|
76
76
|
value: initialValue,
|
|
77
77
|
},
|
|
78
78
|
{
|
|
@@ -91,7 +91,7 @@ const createPrimaryInstanceValueState = <RuntimeValue>(
|
|
|
91
91
|
const [persistedValue, setValueForPersistence] = persistedValueState;
|
|
92
92
|
|
|
93
93
|
createComputed(() => {
|
|
94
|
-
const isRelevant = context.isRelevant;
|
|
94
|
+
const isRelevant = context.isRelevant();
|
|
95
95
|
|
|
96
96
|
setValueForPersistence((persisted) => {
|
|
97
97
|
return {
|
|
@@ -122,7 +122,7 @@ const createPrimaryInstanceValueState = <RuntimeValue>(
|
|
|
122
122
|
const setPrimaryInstanceValue: SimpleAtomicStateSetter<string> = (value) => {
|
|
123
123
|
// TODO: Check (error?) for non-relevant value change?
|
|
124
124
|
const persisted = setValueForPersistence({
|
|
125
|
-
isRelevant: context.isRelevant,
|
|
125
|
+
isRelevant: context.isRelevant(),
|
|
126
126
|
value,
|
|
127
127
|
});
|
|
128
128
|
|
|
@@ -189,8 +189,8 @@ const guardDownstreamReadonlyWrites = <RuntimeValue>(
|
|
|
189
189
|
const [getValue, baseSetValue] = baseState;
|
|
190
190
|
|
|
191
191
|
const setValue: SimpleAtomicStateSetter<RuntimeValue> = (value) => {
|
|
192
|
-
if (context.isReadonly) {
|
|
193
|
-
const reference = untrack(() => context.contextReference);
|
|
192
|
+
if (context.isReadonly()) {
|
|
193
|
+
const reference = untrack(() => context.contextReference());
|
|
194
194
|
|
|
195
195
|
throw new Error(`Cannot write to readonly field: ${reference}`);
|
|
196
196
|
}
|
|
@@ -215,7 +215,7 @@ const createCalculation = <RuntimeValue>(
|
|
|
215
215
|
const calculate = createComputedExpression(context, calculateDefinition);
|
|
216
216
|
|
|
217
217
|
createComputed(() => {
|
|
218
|
-
if (context.isRelevant) {
|
|
218
|
+
if (context.isRelevant()) {
|
|
219
219
|
const calculated = calculate();
|
|
220
220
|
const value = context.decodeValue(calculated);
|
|
221
221
|
|
|
@@ -3,6 +3,7 @@ import type { NodeID } from '../../instance/identity.ts';
|
|
|
3
3
|
import type { ChildrenState, createChildrenState } from './createChildrenState.ts';
|
|
4
4
|
import type { ClientState } from './node-state/createClientState.ts';
|
|
5
5
|
import type { CurrentState } from './node-state/createCurrentState.ts';
|
|
6
|
+
import type { ReactiveScope } from './scope.ts';
|
|
6
7
|
|
|
7
8
|
interface InconsistentChildrenStateDetails {
|
|
8
9
|
readonly missingIds: readonly NodeID[];
|
|
@@ -96,6 +97,7 @@ export const materializeCurrentStateChildren = <
|
|
|
96
97
|
Child extends AnyChildNode,
|
|
97
98
|
ParentState extends EncodedParentState,
|
|
98
99
|
>(
|
|
100
|
+
scope: ReactiveScope,
|
|
99
101
|
currentState: ParentState,
|
|
100
102
|
childrenState: ChildrenState<Child>
|
|
101
103
|
): MaterializedChildren<ParentState, Child> => {
|
|
@@ -105,7 +107,7 @@ export const materializeCurrentStateChildren = <
|
|
|
105
107
|
return new Proxy(proxyTarget, {
|
|
106
108
|
get(_, key) {
|
|
107
109
|
if (key === 'children') {
|
|
108
|
-
const expectedChildIDs = currentState.children;
|
|
110
|
+
const expectedChildIDs = scope.runTask(() => currentState.children);
|
|
109
111
|
const children = childrenState.getChildren();
|
|
110
112
|
|
|
111
113
|
if (import.meta.env.DEV) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
|
|
2
|
-
import type { RepeatDefinition } from '../body/RepeatDefinition.ts';
|
|
3
2
|
import type { BindDefinition } from './BindDefinition.ts';
|
|
4
3
|
import type {
|
|
5
4
|
ModelNode,
|
|
@@ -14,7 +13,7 @@ import type { RootDefinition } from './RootDefinition.ts';
|
|
|
14
13
|
|
|
15
14
|
export type DescendentNodeType = Exclude<NodeDefinitionType, 'root'>;
|
|
16
15
|
|
|
17
|
-
type DescendentNodeBodyElement = AnyBodyElementDefinition
|
|
16
|
+
type DescendentNodeBodyElement = AnyBodyElementDefinition;
|
|
18
17
|
|
|
19
18
|
export abstract class DescendentNodeDefinition<
|
|
20
19
|
Type extends DescendentNodeType,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
|
|
2
|
-
import type {
|
|
2
|
+
import type { RepeatElementDefinition } from '../body/RepeatElementDefinition.ts';
|
|
3
3
|
import type { BindDefinition } from './BindDefinition.ts';
|
|
4
4
|
import type { RepeatInstanceDefinition } from './RepeatInstanceDefinition.ts';
|
|
5
|
-
import type {
|
|
5
|
+
import type { RepeatRangeDefinition } from './RepeatRangeDefinition.ts';
|
|
6
6
|
import type { RepeatTemplateDefinition } from './RepeatTemplateDefinition.ts';
|
|
7
7
|
import type { RootDefinition } from './RootDefinition.ts';
|
|
8
8
|
import type { SubtreeDefinition } from './SubtreeDefinition.ts';
|
|
@@ -17,13 +17,13 @@ import type { ValueNodeDefinition } from './ValueNodeDefinition.ts';
|
|
|
17
17
|
export type RootNodeType = 'root';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Corresponds to a sequence of model/entry DOM subtrees which in turn
|
|
20
|
+
* Corresponds to a range/sequence of model/entry DOM subtrees which in turn
|
|
21
21
|
* corresponds to a <repeat> in the form body definition.
|
|
22
22
|
*/
|
|
23
|
-
export type
|
|
23
|
+
export type RepeatRangeType = 'repeat-range';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Corresponds to a template definition for a repeat
|
|
26
|
+
* Corresponds to a template definition for a repeat range, which either has
|
|
27
27
|
* an explicit `jr:template=""` attribute in the form definition or is inferred
|
|
28
28
|
* as a template from the form's first element matched by a <repeat nodeset>.
|
|
29
29
|
*/
|
|
@@ -32,7 +32,7 @@ export type RepeatTemplateType = 'repeat-template';
|
|
|
32
32
|
/**
|
|
33
33
|
* Corresponds to a single instance of a model/entry DOM subtree which
|
|
34
34
|
* in turn corresponds to a <repeat> in the form body definition, and a
|
|
35
|
-
* 'repeat-
|
|
35
|
+
* 'repeat-range' definition.
|
|
36
36
|
*/
|
|
37
37
|
export type RepeatInstanceType = 'repeat-instance';
|
|
38
38
|
|
|
@@ -55,7 +55,7 @@ export type ValueNodeType = 'value-node';
|
|
|
55
55
|
export type NodeDefinitionType =
|
|
56
56
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
57
57
|
| RootNodeType
|
|
58
|
-
|
|
|
58
|
+
| RepeatRangeType
|
|
59
59
|
| RepeatTemplateType
|
|
60
60
|
| RepeatInstanceType
|
|
61
61
|
| SubtreeNodeType
|
|
@@ -71,7 +71,7 @@ export type ParentNodeDefinition =
|
|
|
71
71
|
|
|
72
72
|
// prettier-ignore
|
|
73
73
|
export type ChildNodeDefinition =
|
|
74
|
-
|
|
|
74
|
+
| RepeatRangeDefinition
|
|
75
75
|
| SubtreeDefinition
|
|
76
76
|
| ValueNodeDefinition;
|
|
77
77
|
|
|
@@ -91,7 +91,7 @@ export type NodeChildren<Type extends NodeDefinitionType> =
|
|
|
91
91
|
|
|
92
92
|
// prettier-ignore
|
|
93
93
|
export type NodeInstances<Type extends NodeDefinitionType> =
|
|
94
|
-
Type extends 'repeat-
|
|
94
|
+
Type extends 'repeat-range'
|
|
95
95
|
? readonly RepeatInstanceDefinition[]
|
|
96
96
|
: null;
|
|
97
97
|
|
|
@@ -104,7 +104,7 @@ export type NodeParent<Type extends NodeDefinitionType> =
|
|
|
104
104
|
// TODO: value-node may be Attr
|
|
105
105
|
// prettier-ignore
|
|
106
106
|
export type ModelNode<Type extends NodeDefinitionType> =
|
|
107
|
-
Type extends 'repeat-
|
|
107
|
+
Type extends 'repeat-range'
|
|
108
108
|
? null
|
|
109
109
|
: Element;
|
|
110
110
|
|
|
@@ -120,7 +120,7 @@ export interface NodeDefinition<Type extends NodeDefinitionType> {
|
|
|
120
120
|
readonly bind: BindDefinition;
|
|
121
121
|
readonly nodeset: string;
|
|
122
122
|
readonly nodeName: string;
|
|
123
|
-
readonly bodyElement: AnyBodyElementDefinition |
|
|
123
|
+
readonly bodyElement: AnyBodyElementDefinition | RepeatElementDefinition | null;
|
|
124
124
|
|
|
125
125
|
readonly isTranslated: boolean;
|
|
126
126
|
readonly dependencyExpressions: ReadonlySet<string>;
|
|
@@ -137,7 +137,7 @@ export interface NodeDefinition<Type extends NodeDefinitionType> {
|
|
|
137
137
|
export type AnyNodeDefinition =
|
|
138
138
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
139
139
|
| RootDefinition
|
|
140
|
-
|
|
|
140
|
+
| RepeatRangeDefinition
|
|
141
141
|
| RepeatTemplateDefinition
|
|
142
142
|
| RepeatInstanceDefinition
|
|
143
143
|
| SubtreeDefinition
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RepeatElementDefinition } from '../body/RepeatElementDefinition.ts';
|
|
2
2
|
import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
|
|
3
3
|
import type { ChildNodeDefinition, NodeDefinition } from './NodeDefinition.ts';
|
|
4
|
-
import type {
|
|
4
|
+
import type { RepeatRangeDefinition } from './RepeatRangeDefinition.ts';
|
|
5
5
|
|
|
6
6
|
export class RepeatInstanceDefinition
|
|
7
|
-
extends DescendentNodeDefinition<'repeat-instance',
|
|
7
|
+
extends DescendentNodeDefinition<'repeat-instance', RepeatElementDefinition>
|
|
8
8
|
implements NodeDefinition<'repeat-instance'>
|
|
9
9
|
{
|
|
10
10
|
readonly type = 'repeat-instance';
|
|
@@ -15,24 +15,19 @@ export class RepeatInstanceDefinition
|
|
|
15
15
|
readonly defaultValue = null;
|
|
16
16
|
|
|
17
17
|
constructor(
|
|
18
|
-
|
|
18
|
+
range: RepeatRangeDefinition,
|
|
19
19
|
readonly node: Element
|
|
20
20
|
) {
|
|
21
|
-
const {
|
|
22
|
-
bind,
|
|
23
|
-
bodyElement: repeatGroupBodyElement,
|
|
24
|
-
parent: repeatSequenceParent,
|
|
25
|
-
root,
|
|
26
|
-
} = sequence;
|
|
21
|
+
const { bind, bodyElement, parent, root } = range;
|
|
27
22
|
|
|
28
|
-
super(
|
|
23
|
+
super(parent, bind, bodyElement);
|
|
29
24
|
|
|
30
|
-
this.nodeName =
|
|
25
|
+
this.nodeName = range.nodeName;
|
|
31
26
|
this.children = root.buildSubtree(this);
|
|
32
27
|
}
|
|
33
28
|
|
|
34
29
|
toJSON() {
|
|
35
|
-
const { bind, bodyElement, parent, root,
|
|
30
|
+
const { bind, bodyElement, parent, root, ...rest } = this;
|
|
36
31
|
|
|
37
32
|
return rest;
|
|
38
33
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { RepeatElementDefinition } from '../body/RepeatElementDefinition.ts';
|
|
2
2
|
import type { BindDefinition } from './BindDefinition.ts';
|
|
3
3
|
import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
|
|
4
4
|
import type { NodeDefinition, ParentNodeDefinition } from './NodeDefinition.ts';
|
|
5
5
|
import { RepeatInstanceDefinition } from './RepeatInstanceDefinition.ts';
|
|
6
6
|
import { RepeatTemplateDefinition } from './RepeatTemplateDefinition.ts';
|
|
7
7
|
|
|
8
|
-
export class
|
|
9
|
-
extends DescendentNodeDefinition<'repeat-
|
|
10
|
-
implements NodeDefinition<'repeat-
|
|
8
|
+
export class RepeatRangeDefinition
|
|
9
|
+
extends DescendentNodeDefinition<'repeat-range', RepeatElementDefinition>
|
|
10
|
+
implements NodeDefinition<'repeat-range'>
|
|
11
11
|
{
|
|
12
12
|
// TODO: if an implicit template is derived from an instance in a form
|
|
13
13
|
// definition, should its default values (if any) be cleared? Probably!
|
|
@@ -19,7 +19,7 @@ export class RepeatSequenceDefinition
|
|
|
19
19
|
return templateElement.cloneNode(true) as Element;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
readonly type = 'repeat-
|
|
22
|
+
readonly type = 'repeat-range';
|
|
23
23
|
|
|
24
24
|
readonly template: RepeatTemplateDefinition;
|
|
25
25
|
readonly children = null;
|
|
@@ -32,7 +32,7 @@ export class RepeatSequenceDefinition
|
|
|
32
32
|
constructor(
|
|
33
33
|
parent: ParentNodeDefinition,
|
|
34
34
|
bind: BindDefinition,
|
|
35
|
-
bodyElement:
|
|
35
|
+
bodyElement: RepeatElementDefinition,
|
|
36
36
|
modelNodes: readonly [Element, ...Element[]]
|
|
37
37
|
) {
|
|
38
38
|
super(parent, bind, bodyElement);
|