@player-tools/dsl 0.5.0--canary.62.1176 → 0.5.0-next.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/cjs/index.cjs +973 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/index.legacy-esm.js +910 -0
- package/dist/index.mjs +910 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +27 -33
- package/src/__tests__/asset-api.test.tsx +354 -0
- package/src/__tests__/edge-cases.test.tsx +81 -0
- package/src/__tests__/helpers/asset-library.tsx +371 -0
- package/src/__tests__/helpers/mock-data-refs.ts +21 -0
- package/src/__tests__/json.test.ts +12 -0
- package/src/__tests__/jsx.test.tsx +138 -0
- package/src/__tests__/schema.test.tsx +378 -0
- package/src/__tests__/switch.test.tsx +251 -0
- package/src/__tests__/template.test.tsx +294 -0
- package/src/__tests__/view-api.test.tsx +46 -0
- package/src/auto-id.tsx +10 -10
- package/src/compiler/__tests__/compiler.test.tsx +264 -0
- package/src/compiler/__tests__/schema.test.ts +127 -0
- package/src/compiler/compiler.ts +50 -46
- package/src/compiler/index.ts +3 -3
- package/src/compiler/schema.ts +29 -25
- package/src/compiler/types.ts +15 -12
- package/src/compiler/utils.ts +7 -7
- package/src/components.tsx +17 -12
- package/src/index.ts +11 -11
- package/src/string-templates/__tests__/binding.test.ts +87 -0
- package/src/string-templates/__tests__/edge-cases.test.ts +6 -0
- package/src/string-templates/__tests__/expression.test.ts +9 -0
- package/src/string-templates/__tests__/react.test.tsx +75 -0
- package/src/string-templates/index.ts +40 -22
- package/src/switch.tsx +12 -12
- package/src/template.tsx +38 -34
- package/src/types.ts +5 -5
- package/src/utils.tsx +8 -8
- package/types/auto-id.d.ts +33 -0
- package/types/compiler/compiler.d.ts +27 -0
- package/types/compiler/index.d.ts +4 -0
- package/types/compiler/schema.d.ts +61 -0
- package/types/compiler/types.d.ts +55 -0
- package/types/compiler/utils.d.ts +4 -0
- package/types/components.d.ts +90 -0
- package/types/index.d.ts +12 -0
- package/types/string-templates/index.d.ts +50 -0
- package/types/switch.d.ts +19 -0
- package/types/template.d.ts +20 -0
- package/types/types.d.ts +42 -0
- package/types/utils.d.ts +33 -0
- package/README.md +0 -9
- package/dist/index.cjs.js +0 -990
- package/dist/index.d.ts +0 -404
- package/dist/index.esm.js +0 -923
package/src/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
10
|
-
export * from
|
|
11
|
-
export * from
|
|
1
|
+
export * from "./components";
|
|
2
|
+
export * from "./auto-id";
|
|
3
|
+
export * from "./types";
|
|
4
|
+
export * from "./string-templates";
|
|
5
|
+
export * from "./utils";
|
|
6
|
+
export * from "./switch";
|
|
7
|
+
export * from "./template";
|
|
8
|
+
export * from "react-json-reconciler";
|
|
9
|
+
export * from "./compiler/schema";
|
|
10
|
+
export * from "./compiler";
|
|
11
|
+
export * from "./compiler/types";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { test, expect, describe } from "vitest";
|
|
2
|
+
import { binding as b, expression as e } from "..";
|
|
3
|
+
|
|
4
|
+
describe("string template binding", () => {
|
|
5
|
+
test("returns string versions", () => {
|
|
6
|
+
expect(b`foo.bar.baz`.toValue()).toBe("foo.bar.baz");
|
|
7
|
+
expect(b`foo.bar.${b`foo.bar`}`.toValue()).toBe("foo.bar.{{foo.bar}}");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("returns string ref versions", () => {
|
|
11
|
+
expect(b`foo.bar.baz`.toRefString()).toBe("{{foo.bar.baz}}");
|
|
12
|
+
expect(b`foo.bar.${b`foo.bar`}`.toRefString()).toBe(
|
|
13
|
+
"{{foo.bar.{{foo.bar}}}}"
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("works with nested expressions", () => {
|
|
18
|
+
expect(b`foo.bar.${e`test()`}`.toValue()).toBe("foo.bar.`test()`");
|
|
19
|
+
expect(b`foo.bar.${e`test()`}`.toRefString()).toBe("{{foo.bar.`test()`}}");
|
|
20
|
+
|
|
21
|
+
const expr = e`test() == "foo"`;
|
|
22
|
+
expect(b`${expr}.${expr}`.toValue()).toBe(
|
|
23
|
+
'`test() == "foo"`.`test() == "foo"`'
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("works when in a string", () => {
|
|
28
|
+
expect(`This is a ${b`foo.bar`} reference.`).toBe(
|
|
29
|
+
"This is a {{foo.bar}} reference."
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("works when template is just a string", () => {
|
|
34
|
+
const segments = ["foo", "bar", "_index_", "baz"];
|
|
35
|
+
expect(b`${segments.join(".")}`.toValue()).toBe("foo.bar._index_.baz");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("should provide unique identifiers for each _index_ provided for the binding in the template", () => {
|
|
39
|
+
const segments = ["some", "_index_", "data", "_index_", "foo", "_index_"];
|
|
40
|
+
expect(b`${segments.join(".")}`.toValue()).toBe(
|
|
41
|
+
"some._index_.data._index1_.foo._index2_"
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
expect(b`${segments.join(".")}`.toRefString()).toBe(
|
|
45
|
+
"{{some._index_.data._index1_.foo._index2_}}"
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("should not place index identifiers if an expression literal is called", () => {
|
|
50
|
+
const expressionWithBindings =
|
|
51
|
+
"conditional({{foo.bar._index_.field1._index_.innerField}} == true, {{foo.bar._index_.field1}} = true)";
|
|
52
|
+
|
|
53
|
+
expect(e`${expressionWithBindings}`.toValue()).toBe(expressionWithBindings);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("should place index identifiers in binding templates nested in an expression", () => {
|
|
57
|
+
const segments = ["some", "_index_", "data", "_index_", "foo", "_index_"];
|
|
58
|
+
|
|
59
|
+
const expressionNestedBindings = e`test(${b`${segments.join(
|
|
60
|
+
"."
|
|
61
|
+
)}`})`.toValue();
|
|
62
|
+
|
|
63
|
+
expect(expressionNestedBindings).toBe(
|
|
64
|
+
"test({{some._index_.data._index1_.foo._index2_}})"
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("should reset index identifiers in multiple binding templates nested in an expression", () => {
|
|
69
|
+
const segments = [
|
|
70
|
+
"some",
|
|
71
|
+
"_index_",
|
|
72
|
+
"data",
|
|
73
|
+
"_index_",
|
|
74
|
+
"foo",
|
|
75
|
+
"_index_",
|
|
76
|
+
"bar",
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const expressionNestedBindings = e`test(${b`${segments.join(
|
|
80
|
+
"."
|
|
81
|
+
)}`} == true, ${b`some._index_.data`} = ${b`some._index_.other._index_.data`})`.toValue();
|
|
82
|
+
|
|
83
|
+
expect(expressionNestedBindings).toBe(
|
|
84
|
+
"test({{some._index_.data._index1_.foo._index2_.bar}} == true, {{some._index_.data}} = {{some._index_.other._index1_.data}})"
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { test, expect } from "vitest";
|
|
2
|
+
import { expression as e } from "..";
|
|
3
|
+
|
|
4
|
+
test("works with nested expressions", () => {
|
|
5
|
+
const exp1 = e`foo() == bar()`;
|
|
6
|
+
const exp2 = e`conditional(${exp1})`;
|
|
7
|
+
|
|
8
|
+
expect(exp2.toString()).toBe(`@[conditional(foo() == bar())]@`);
|
|
9
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { test, expect } from "vitest";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { render } from "react-json-reconciler";
|
|
4
|
+
import { binding as b, expression as e } from "..";
|
|
5
|
+
import { Switch } from "../../switch";
|
|
6
|
+
import { Collection } from "../../__tests__/helpers/asset-library";
|
|
7
|
+
|
|
8
|
+
test("can be used as a react child element", async () => {
|
|
9
|
+
const content = (
|
|
10
|
+
await render(
|
|
11
|
+
<object>
|
|
12
|
+
<property name="expression">{e`test()`}</property>
|
|
13
|
+
<property name="binding">{b`foo.bar`}</property>
|
|
14
|
+
</object>
|
|
15
|
+
)
|
|
16
|
+
).jsonValue;
|
|
17
|
+
|
|
18
|
+
expect(content).toStrictEqual({
|
|
19
|
+
expression: "@[test()]@",
|
|
20
|
+
binding: "{{foo.bar}}",
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("Works when used as a child asset", async () => {
|
|
25
|
+
const content = (
|
|
26
|
+
await render(
|
|
27
|
+
<Collection>
|
|
28
|
+
<Collection.Label>{b`foo.bar`}</Collection.Label>
|
|
29
|
+
</Collection>
|
|
30
|
+
)
|
|
31
|
+
).jsonValue;
|
|
32
|
+
|
|
33
|
+
expect(content).toStrictEqual({
|
|
34
|
+
id: "root",
|
|
35
|
+
type: "collection",
|
|
36
|
+
label: {
|
|
37
|
+
asset: {
|
|
38
|
+
id: "label",
|
|
39
|
+
type: "text",
|
|
40
|
+
value: "{{foo.bar}}",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("Works as a switch child", async () => {
|
|
47
|
+
const content = (
|
|
48
|
+
await render(
|
|
49
|
+
<Collection>
|
|
50
|
+
<Collection.Label>
|
|
51
|
+
<Switch>
|
|
52
|
+
<Switch.Case>Testing 123 {b`foo.bar`}</Switch.Case>
|
|
53
|
+
</Switch>
|
|
54
|
+
</Collection.Label>
|
|
55
|
+
</Collection>
|
|
56
|
+
)
|
|
57
|
+
).jsonValue;
|
|
58
|
+
|
|
59
|
+
expect(content).toStrictEqual({
|
|
60
|
+
id: "root",
|
|
61
|
+
type: "collection",
|
|
62
|
+
label: {
|
|
63
|
+
staticSwitch: [
|
|
64
|
+
{
|
|
65
|
+
case: true,
|
|
66
|
+
asset: {
|
|
67
|
+
id: "label-staticSwitch-0",
|
|
68
|
+
type: "text",
|
|
69
|
+
value: "Testing 123 {{foo.bar}}",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import { parseExpression } from
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { parseExpression } from "@player-ui/player";
|
|
3
3
|
|
|
4
|
-
export type TemplateInstanceRefStringContext =
|
|
4
|
+
export type TemplateInstanceRefStringContext = "binding" | "expression";
|
|
5
5
|
export interface TemplateRefStringOptions {
|
|
6
6
|
/** If this template string is inside of another binding or expression */
|
|
7
7
|
nestedContext?: TemplateInstanceRefStringContext;
|
|
8
8
|
}
|
|
9
9
|
export interface TemplateInstanceRefStringOptions {
|
|
10
10
|
/** The array of strings for the template */
|
|
11
|
-
strings:
|
|
11
|
+
strings: ReadonlyArray<string>;
|
|
12
12
|
/** the other data that's present in the template */
|
|
13
13
|
other: Array<string | TemplateStringType>;
|
|
14
14
|
|
|
@@ -22,7 +22,7 @@ export interface TemplateInstanceRefStringOptions {
|
|
|
22
22
|
) => string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const OpaqueIdentifier = Symbol(
|
|
25
|
+
const OpaqueIdentifier = Symbol("TemplateStringType");
|
|
26
26
|
|
|
27
27
|
export type TemplateStringType = React.ReactElement & {
|
|
28
28
|
/** An identifier to show that this is a template type */
|
|
@@ -37,12 +37,12 @@ export type TemplateStringType = React.ReactElement & {
|
|
|
37
37
|
|
|
38
38
|
export type BindingTemplateInstance = TemplateStringType & {
|
|
39
39
|
/** An identifier for a binding instance */
|
|
40
|
-
__type:
|
|
40
|
+
__type: "binding";
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
export type ExpressionTemplateInstance = TemplateStringType & {
|
|
44
44
|
/** The identifier for an expression instance */
|
|
45
|
-
__type:
|
|
45
|
+
__type: "expression";
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
/** A react component for rendering a template string type */
|
|
@@ -51,7 +51,7 @@ export const TemplateStringComponent = (props: {
|
|
|
51
51
|
value: string;
|
|
52
52
|
}) => {
|
|
53
53
|
return React.createElement(
|
|
54
|
-
|
|
54
|
+
"value",
|
|
55
55
|
{
|
|
56
56
|
value: props.value,
|
|
57
57
|
},
|
|
@@ -65,15 +65,15 @@ const createTemplateInstance = (
|
|
|
65
65
|
): TemplateStringType => {
|
|
66
66
|
const value = options.strings.reduce((sum, next, i) => {
|
|
67
67
|
const element = options.other[i];
|
|
68
|
-
if (typeof element ===
|
|
68
|
+
if (typeof element === "string") {
|
|
69
69
|
return sum + next + element;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
return sum + next + (element?.toRefString(options) ??
|
|
73
|
-
},
|
|
72
|
+
return sum + next + (element?.toRefString(options) ?? "");
|
|
73
|
+
}, "");
|
|
74
74
|
|
|
75
75
|
/** Try to parse the expression as valid */
|
|
76
|
-
if (options.nestedContext ===
|
|
76
|
+
if (options.nestedContext === "expression") {
|
|
77
77
|
const parsedExpression = parseExpression(value, { strict: false });
|
|
78
78
|
if (parsedExpression.error) {
|
|
79
79
|
throw parsedExpression.error;
|
|
@@ -110,39 +110,57 @@ const createTemplateInstance = (
|
|
|
110
110
|
};
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
+
/** Helper for Iterating the binding to add a dynamic numeric value to each index found */
|
|
114
|
+
const addBindingIndexes = (binding: string): string => {
|
|
115
|
+
let currentIndex = 0;
|
|
116
|
+
|
|
117
|
+
return binding.replace(/_index_/g, () => {
|
|
118
|
+
const result = `_index${currentIndex > 0 ? currentIndex : ""}_`;
|
|
119
|
+
currentIndex += 1;
|
|
120
|
+
|
|
121
|
+
return result;
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
|
|
113
125
|
/** Creating an instance of a handler for bindings */
|
|
114
126
|
const createBindingTemplateInstance = (
|
|
115
|
-
options: Omit<TemplateInstanceRefStringOptions,
|
|
127
|
+
options: Omit<TemplateInstanceRefStringOptions, "toRefString">
|
|
116
128
|
): BindingTemplateInstance => {
|
|
117
129
|
const templateInstance = createTemplateInstance({
|
|
118
130
|
...options,
|
|
131
|
+
strings: options.strings.map((element: string) =>
|
|
132
|
+
addBindingIndexes(element)
|
|
133
|
+
),
|
|
134
|
+
other: options.other.map((element) =>
|
|
135
|
+
typeof element === "string" ? addBindingIndexes(element) : element
|
|
136
|
+
),
|
|
119
137
|
toRefString: (context, value) => {
|
|
120
138
|
return `{{${value}}}`;
|
|
121
139
|
},
|
|
122
140
|
}) as BindingTemplateInstance;
|
|
123
141
|
|
|
124
|
-
templateInstance.__type =
|
|
142
|
+
templateInstance.__type = "binding";
|
|
125
143
|
|
|
126
144
|
return templateInstance;
|
|
127
145
|
};
|
|
128
146
|
|
|
129
147
|
/** Creating an instance of a handler for bindings */
|
|
130
148
|
const createExpressionTemplateInstance = (
|
|
131
|
-
options: Omit<TemplateInstanceRefStringOptions,
|
|
149
|
+
options: Omit<TemplateInstanceRefStringOptions, "toRefString">
|
|
132
150
|
) => {
|
|
133
151
|
const templateInstance = createTemplateInstance({
|
|
134
152
|
...options,
|
|
135
153
|
toRefString: (contextOptions, value) => {
|
|
136
|
-
if (contextOptions?.nestedContext ===
|
|
154
|
+
if (contextOptions?.nestedContext === "expression") {
|
|
137
155
|
return value;
|
|
138
156
|
}
|
|
139
157
|
|
|
140
|
-
const inBinding = contextOptions?.nestedContext ===
|
|
141
|
-
return `${inBinding ?
|
|
158
|
+
const inBinding = contextOptions?.nestedContext === "binding";
|
|
159
|
+
return `${inBinding ? "`" : "@["}${value}${inBinding ? "`" : "]@"}`;
|
|
142
160
|
},
|
|
143
161
|
}) as ExpressionTemplateInstance;
|
|
144
162
|
|
|
145
|
-
templateInstance.__type =
|
|
163
|
+
templateInstance.__type = "expression";
|
|
146
164
|
|
|
147
165
|
return templateInstance;
|
|
148
166
|
};
|
|
@@ -155,7 +173,7 @@ export const binding = (
|
|
|
155
173
|
return createBindingTemplateInstance({
|
|
156
174
|
strings,
|
|
157
175
|
other: nested,
|
|
158
|
-
nestedContext:
|
|
176
|
+
nestedContext: "binding",
|
|
159
177
|
});
|
|
160
178
|
};
|
|
161
179
|
|
|
@@ -169,7 +187,7 @@ export const expression = (
|
|
|
169
187
|
return createExpressionTemplateInstance({
|
|
170
188
|
strings,
|
|
171
189
|
other: nested,
|
|
172
|
-
nestedContext:
|
|
190
|
+
nestedContext: "expression",
|
|
173
191
|
});
|
|
174
192
|
};
|
|
175
193
|
|
|
@@ -179,7 +197,7 @@ export const isTemplateStringInstance = (
|
|
|
179
197
|
): val is ExpressionTemplateInstance | BindingTemplateInstance => {
|
|
180
198
|
return (
|
|
181
199
|
val !== null &&
|
|
182
|
-
typeof val ===
|
|
200
|
+
typeof val === "object" &&
|
|
183
201
|
(val as any)[OpaqueIdentifier] === true
|
|
184
202
|
);
|
|
185
203
|
};
|
package/src/switch.tsx
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type { PropsWithChildren } from
|
|
2
|
-
import React from
|
|
3
|
-
import type { ArrayNode, JsonNode, ObjectNode } from
|
|
4
|
-
import { flattenNodes, PropertyNode } from
|
|
5
|
-
import { SlotContext } from
|
|
6
|
-
import { IDSuffixProvider, OptionalIDSuffixProvider } from
|
|
1
|
+
import type { PropsWithChildren } from "react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import type { ArrayNode, JsonNode, ObjectNode } from "react-json-reconciler";
|
|
4
|
+
import { flattenNodes, PropertyNode } from "react-json-reconciler";
|
|
5
|
+
import { SlotContext } from ".";
|
|
6
|
+
import { IDSuffixProvider, OptionalIDSuffixProvider } from "./auto-id";
|
|
7
7
|
import type {
|
|
8
8
|
BindingTemplateInstance,
|
|
9
9
|
ExpressionTemplateInstance,
|
|
10
|
-
} from
|
|
11
|
-
import { isTemplateStringInstance } from
|
|
12
|
-
import { normalizeToCollection, toJsonProperties } from
|
|
10
|
+
} from "./string-templates";
|
|
11
|
+
import { isTemplateStringInstance } from "./string-templates";
|
|
12
|
+
import { normalizeToCollection, toJsonProperties } from "./utils";
|
|
13
13
|
|
|
14
14
|
export interface SwitchProps {
|
|
15
15
|
/** defaults to a staticSwitch */
|
|
@@ -43,7 +43,7 @@ export const Switch = (props: PropsWithChildren<SwitchProps>) => {
|
|
|
43
43
|
}}
|
|
44
44
|
>
|
|
45
45
|
<OptionalIDSuffixProvider wrapperRef={propertyNode}>
|
|
46
|
-
<property name={props.isDynamic ?
|
|
46
|
+
<property name={props.isDynamic ? "dynamicSwitch" : "staticSwitch"}>
|
|
47
47
|
<SlotContext.Provider value={undefined}>
|
|
48
48
|
<array>{props.children}</array>
|
|
49
49
|
</SlotContext.Provider>
|
|
@@ -63,7 +63,7 @@ export interface CaseProps {
|
|
|
63
63
|
|
|
64
64
|
/** Find the first parent array */
|
|
65
65
|
const findParentArray = (node: JsonNode): ArrayNode => {
|
|
66
|
-
if (node.type ===
|
|
66
|
+
if (node.type === "array") {
|
|
67
67
|
return node;
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -112,7 +112,7 @@ const Case = (props: PropsWithChildren<CaseProps>) => {
|
|
|
112
112
|
</property>
|
|
113
113
|
<IDSuffixProvider
|
|
114
114
|
suffix={`${
|
|
115
|
-
switchContext.isDynamic ?
|
|
115
|
+
switchContext.isDynamic ? "dynamicSwitch" : "staticSwitch"
|
|
116
116
|
}-${caseIndex}`}
|
|
117
117
|
>
|
|
118
118
|
<SlotContext.Provider
|
package/src/template.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import type { ObjectNode, JsonNode } from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { ObjectNode, JsonNode } from "react-json-reconciler";
|
|
3
3
|
import {
|
|
4
4
|
ArrayNode,
|
|
5
5
|
PropertyNode,
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
createPortal,
|
|
8
8
|
ProxyNode,
|
|
9
9
|
toJSON,
|
|
10
|
-
} from
|
|
11
|
-
import { OptionalIDSuffixProvider } from
|
|
12
|
-
import type { BindingTemplateInstance } from
|
|
13
|
-
import type { WithChildren } from
|
|
14
|
-
import { toJsonElement } from
|
|
10
|
+
} from "react-json-reconciler";
|
|
11
|
+
import { OptionalIDSuffixProvider } from "./auto-id";
|
|
12
|
+
import type { BindingTemplateInstance } from "./string-templates";
|
|
13
|
+
import type { WithChildren } from "./types";
|
|
14
|
+
import { toJsonElement } from "./utils";
|
|
15
15
|
|
|
16
16
|
export interface TemplateContextType {
|
|
17
17
|
/** The number of nested templates */
|
|
@@ -46,11 +46,11 @@ function addTemplateToObject(
|
|
|
46
46
|
// add one if none exists
|
|
47
47
|
|
|
48
48
|
let templateProp = obj.properties.find(
|
|
49
|
-
(p) => p.keyNode.value ===
|
|
49
|
+
(p) => p.keyNode.value === "template" && p.valueNode?.type === "array"
|
|
50
50
|
);
|
|
51
51
|
|
|
52
52
|
if (!templateProp) {
|
|
53
|
-
templateProp = new PropertyNode(new ValueNode(
|
|
53
|
+
templateProp = new PropertyNode(new ValueNode("template"), new ArrayNode());
|
|
54
54
|
templateProp.parent = obj;
|
|
55
55
|
obj.properties.push(templateProp);
|
|
56
56
|
}
|
|
@@ -64,7 +64,7 @@ function addTemplateToObject(
|
|
|
64
64
|
const templateParentProp = obj.properties.find(
|
|
65
65
|
(p) =>
|
|
66
66
|
p.keyNode.value === templateParentNodeType &&
|
|
67
|
-
p.valueNode?.type ===
|
|
67
|
+
p.valueNode?.type === "array"
|
|
68
68
|
);
|
|
69
69
|
|
|
70
70
|
if (templateParentProp) {
|
|
@@ -108,7 +108,7 @@ const TemplateProvider = (props: WithChildren) => {
|
|
|
108
108
|
|
|
109
109
|
/** Find the first object node in the tree */
|
|
110
110
|
const getParentObject = (node: JsonNode): ObjectNode | undefined => {
|
|
111
|
-
if (node.type ===
|
|
111
|
+
if (node.type === "object") {
|
|
112
112
|
return node;
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -119,7 +119,7 @@ const getParentObject = (node: JsonNode): ObjectNode | undefined => {
|
|
|
119
119
|
|
|
120
120
|
/** Find the property of the node on the parent */
|
|
121
121
|
const getParentProperty = (node: JsonNode): PropertyNode | undefined => {
|
|
122
|
-
if (node.type ===
|
|
122
|
+
if (node.type === "property") {
|
|
123
123
|
return node;
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -154,7 +154,7 @@ export const Template = (props: TemplateProps) => {
|
|
|
154
154
|
const parentObject = getParentObject(proxyRef.current);
|
|
155
155
|
|
|
156
156
|
if (!parentObject) {
|
|
157
|
-
throw new Error(
|
|
157
|
+
throw new Error("Unable to find parent to add template to");
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
if (!outputProp) {
|
|
@@ -168,27 +168,31 @@ export const Template = (props: TemplateProps) => {
|
|
|
168
168
|
|
|
169
169
|
return (
|
|
170
170
|
<proxy ref={proxyRef}>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
<
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
171
|
+
<>
|
|
172
|
+
{createPortal(
|
|
173
|
+
<OptionalIDSuffixProvider
|
|
174
|
+
wrapperRef={valueRef}
|
|
175
|
+
templateIndex={`_index${
|
|
176
|
+
baseContext.depth === 0 ? "" : baseContext.depth
|
|
177
|
+
}_`}
|
|
178
|
+
>
|
|
179
|
+
<TemplateProvider>
|
|
180
|
+
<object>
|
|
181
|
+
<property name="data">{props.data.toValue()}</property>
|
|
182
|
+
<property name="output">{outputProp}</property>
|
|
183
|
+
<property name="value">{props.children}</property>
|
|
184
|
+
{dynamicProp && (
|
|
185
|
+
<property name="dynamic">
|
|
186
|
+
{toJsonElement(dynamicProp)}
|
|
187
|
+
</property>
|
|
188
|
+
)}
|
|
189
|
+
</object>
|
|
190
|
+
</TemplateProvider>
|
|
191
|
+
</OptionalIDSuffixProvider>,
|
|
192
|
+
outputElement
|
|
193
|
+
)}
|
|
194
|
+
<value ref={valueRef} value={undefined} />
|
|
195
|
+
</>
|
|
192
196
|
</proxy>
|
|
193
197
|
);
|
|
194
198
|
};
|
package/src/types.ts
CHANGED
|
@@ -2,11 +2,11 @@ import type {
|
|
|
2
2
|
Asset,
|
|
3
3
|
Expression,
|
|
4
4
|
Navigation as PlayerNav,
|
|
5
|
-
} from
|
|
5
|
+
} from "@player-ui/types";
|
|
6
6
|
import type {
|
|
7
7
|
BindingTemplateInstance,
|
|
8
8
|
ExpressionTemplateInstance,
|
|
9
|
-
} from
|
|
9
|
+
} from "./string-templates";
|
|
10
10
|
|
|
11
11
|
export type WithChildren<T = Record<string, unknown>> = T & {
|
|
12
12
|
/** child nodes */
|
|
@@ -40,7 +40,7 @@ export interface PlayerApplicability {
|
|
|
40
40
|
|
|
41
41
|
export type AssetPropsWithChildren<T extends Asset> = WithChildren<
|
|
42
42
|
WithTemplateTypes<
|
|
43
|
-
OmitProp<RemoveUnknownIndex<T>,
|
|
43
|
+
OmitProp<RemoveUnknownIndex<T>, "id" | "type"> & Partial<Pick<Asset, "id">>
|
|
44
44
|
> &
|
|
45
45
|
PlayerApplicability
|
|
46
46
|
>;
|
|
@@ -55,7 +55,7 @@ export type WithTemplateTypes<T> = T extends Record<any, any>
|
|
|
55
55
|
}
|
|
56
56
|
: T | BindingTemplateInstance | ExpressionTemplateInstance;
|
|
57
57
|
|
|
58
|
-
type ValidKeys =
|
|
58
|
+
type ValidKeys = "exp" | "onStart" | "onEnd";
|
|
59
59
|
|
|
60
60
|
type DeepReplace<T, Old, New> = {
|
|
61
61
|
[P in keyof T]: T[P] extends Old
|
|
@@ -83,7 +83,7 @@ export type Navigation = DeepReplace<
|
|
|
83
83
|
export interface toJsonOptions {
|
|
84
84
|
/**
|
|
85
85
|
* List of string keys that should not be parsed in a special way
|
|
86
|
-
* default is
|
|
86
|
+
* default is "applicability"
|
|
87
87
|
*/
|
|
88
88
|
propertiesToSkip?: string[];
|
|
89
89
|
}
|
package/src/utils.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import * as React from
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
import {
|
|
3
3
|
isTemplateStringInstance,
|
|
4
4
|
TemplateStringComponent,
|
|
5
|
-
} from
|
|
6
|
-
import type { toJsonOptions } from
|
|
5
|
+
} from "./string-templates";
|
|
6
|
+
import type { toJsonOptions } from "./types";
|
|
7
7
|
|
|
8
8
|
/** Get an array version of the value */
|
|
9
9
|
export function toArray<T>(val: T | Array<T>): Array<T> {
|
|
@@ -16,7 +16,7 @@ export function toJsonElement(
|
|
|
16
16
|
indexOrKey?: number | string,
|
|
17
17
|
options?: toJsonOptions
|
|
18
18
|
): React.ReactElement {
|
|
19
|
-
const indexProp = typeof indexOrKey ===
|
|
19
|
+
const indexProp = typeof indexOrKey === "number" ? { key: indexOrKey } : null;
|
|
20
20
|
|
|
21
21
|
if (Array.isArray(value)) {
|
|
22
22
|
return (
|
|
@@ -29,7 +29,7 @@ export function toJsonElement(
|
|
|
29
29
|
/** Allow users to pass in BindingTemplateInstance and ExpressionTemplateInstance directly without turning them into strings first */
|
|
30
30
|
if (isTemplateStringInstance(value)) {
|
|
31
31
|
if (
|
|
32
|
-
typeof indexOrKey ===
|
|
32
|
+
typeof indexOrKey === "string" &&
|
|
33
33
|
options?.propertiesToSkip?.includes(indexOrKey)
|
|
34
34
|
) {
|
|
35
35
|
return <value {...indexProp}>{value.toValue()}</value>;
|
|
@@ -38,7 +38,7 @@ export function toJsonElement(
|
|
|
38
38
|
return <value {...indexProp}>{value.toRefString()}</value>;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
if (typeof value ===
|
|
41
|
+
if (typeof value === "object" && value !== null) {
|
|
42
42
|
return (
|
|
43
43
|
<obj {...indexProp}>
|
|
44
44
|
{Object.keys(value).map((key) => (
|
|
@@ -56,7 +56,7 @@ export function toJsonElement(
|
|
|
56
56
|
/** Create a fragment for the properties */
|
|
57
57
|
export function toJsonProperties(
|
|
58
58
|
value: Record<string, any>,
|
|
59
|
-
options: toJsonOptions = { propertiesToSkip: [
|
|
59
|
+
options: toJsonOptions = { propertiesToSkip: ["applicability"] }
|
|
60
60
|
) {
|
|
61
61
|
return Object.keys(value).map((key) => {
|
|
62
62
|
return (
|
|
@@ -111,7 +111,7 @@ export function normalizeToCollection(options: {
|
|
|
111
111
|
|
|
112
112
|
if (
|
|
113
113
|
React.Children.count(node) > 1 &&
|
|
114
|
-
React.Children.toArray(node).every((n) => typeof n !==
|
|
114
|
+
React.Children.toArray(node).every((n) => typeof n !== "string")
|
|
115
115
|
) {
|
|
116
116
|
if (!CollectionComp) {
|
|
117
117
|
throw new Error(
|