@player-tools/dsl 0.9.1-next.2 → 0.10.0--canary.196.4206
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 +39 -13
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.legacy-esm.js +39 -13
- package/dist/index.mjs +39 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/template.test.tsx +126 -30
- package/src/template.tsx +86 -25
- package/types/template.d.ts +2 -0
|
@@ -26,6 +26,7 @@ test("finds output property based on array context", async () => {
|
|
|
26
26
|
data: "foo.output",
|
|
27
27
|
value: "bar",
|
|
28
28
|
output: "foo",
|
|
29
|
+
placement: "append",
|
|
29
30
|
},
|
|
30
31
|
],
|
|
31
32
|
});
|
|
@@ -53,6 +54,7 @@ test("finds dynamic property in a template", async () => {
|
|
|
53
54
|
dynamic: true,
|
|
54
55
|
value: "bar",
|
|
55
56
|
output: "foo",
|
|
57
|
+
placement: "append",
|
|
56
58
|
},
|
|
57
59
|
],
|
|
58
60
|
});
|
|
@@ -75,7 +77,7 @@ test("works if already in a template array", async () => {
|
|
|
75
77
|
});
|
|
76
78
|
});
|
|
77
79
|
|
|
78
|
-
test("Template
|
|
80
|
+
test("Template order is preserved", async () => {
|
|
79
81
|
const element = (
|
|
80
82
|
<Collection id="view-1">
|
|
81
83
|
<Collection.Values>
|
|
@@ -105,7 +107,8 @@ test("Template Order is preserved", async () => {
|
|
|
105
107
|
</Collection.Values>
|
|
106
108
|
</Collection>
|
|
107
109
|
);
|
|
108
|
-
|
|
110
|
+
|
|
111
|
+
const expectedOutput = {
|
|
109
112
|
id: "view-1",
|
|
110
113
|
type: "collection",
|
|
111
114
|
values: [
|
|
@@ -131,6 +134,7 @@ test("Template Order is preserved", async () => {
|
|
|
131
134
|
value: "Dynamic Item _index_",
|
|
132
135
|
},
|
|
133
136
|
},
|
|
137
|
+
placement: "prepend",
|
|
134
138
|
},
|
|
135
139
|
],
|
|
136
140
|
values: [
|
|
@@ -175,11 +179,99 @@ test("Template Order is preserved", async () => {
|
|
|
175
179
|
value: "Dynamic Item _index_",
|
|
176
180
|
},
|
|
177
181
|
},
|
|
182
|
+
placement: "append",
|
|
178
183
|
},
|
|
179
184
|
],
|
|
180
185
|
},
|
|
181
186
|
},
|
|
182
187
|
],
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
expect(JSON.stringify((await render(element)).jsonValue)).toBe(
|
|
191
|
+
JSON.stringify(expectedOutput)
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("Template placement is inferred as prepend", async () => {
|
|
196
|
+
const element = (
|
|
197
|
+
<obj>
|
|
198
|
+
<property name="foo">
|
|
199
|
+
<array>
|
|
200
|
+
<Template dynamic data={b`foo.output`}>
|
|
201
|
+
<value>bar</value>
|
|
202
|
+
</Template>
|
|
203
|
+
<value>Foo</value>
|
|
204
|
+
</array>
|
|
205
|
+
</property>
|
|
206
|
+
</obj>
|
|
207
|
+
);
|
|
208
|
+
expect((await render(element)).jsonValue).toStrictEqual({
|
|
209
|
+
foo: ["Foo"],
|
|
210
|
+
template: [
|
|
211
|
+
{
|
|
212
|
+
data: "foo.output",
|
|
213
|
+
placement: "prepend",
|
|
214
|
+
dynamic: true,
|
|
215
|
+
value: "bar",
|
|
216
|
+
output: "foo",
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("Template placement is inferred as append", async () => {
|
|
223
|
+
const element = (
|
|
224
|
+
<obj>
|
|
225
|
+
<property name="foo">
|
|
226
|
+
<array>
|
|
227
|
+
<value>Foo</value>
|
|
228
|
+
<Template dynamic data={b`foo.output`}>
|
|
229
|
+
<value>bar</value>
|
|
230
|
+
</Template>
|
|
231
|
+
</array>
|
|
232
|
+
</property>
|
|
233
|
+
</obj>
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
expect((await render(element)).jsonValue).toStrictEqual({
|
|
237
|
+
foo: ["Foo"],
|
|
238
|
+
template: [
|
|
239
|
+
{
|
|
240
|
+
data: "foo.output",
|
|
241
|
+
placement: "append",
|
|
242
|
+
dynamic: true,
|
|
243
|
+
value: "bar",
|
|
244
|
+
output: "foo",
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("Template with explicit placement is respected", async () => {
|
|
251
|
+
const element = (
|
|
252
|
+
<obj>
|
|
253
|
+
<property name="foo">
|
|
254
|
+
<array>
|
|
255
|
+
<value>Foo</value>
|
|
256
|
+
<Template dynamic data={b`foo.output`} placement="prepend">
|
|
257
|
+
<value>bar</value>
|
|
258
|
+
</Template>
|
|
259
|
+
</array>
|
|
260
|
+
</property>
|
|
261
|
+
</obj>
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
expect((await render(element)).jsonValue).toStrictEqual({
|
|
265
|
+
foo: ["Foo"],
|
|
266
|
+
template: [
|
|
267
|
+
{
|
|
268
|
+
data: "foo.output",
|
|
269
|
+
placement: "prepend",
|
|
270
|
+
dynamic: true,
|
|
271
|
+
value: "bar",
|
|
272
|
+
output: "foo",
|
|
273
|
+
},
|
|
274
|
+
],
|
|
183
275
|
});
|
|
184
276
|
});
|
|
185
277
|
|
|
@@ -234,6 +326,7 @@ test("template will delete empty arrays related to the template only", async ()
|
|
|
234
326
|
{
|
|
235
327
|
data: "foo.bar",
|
|
236
328
|
output: "values",
|
|
329
|
+
placement: "prepend",
|
|
237
330
|
value: {
|
|
238
331
|
asset: {
|
|
239
332
|
id: "values-_index_",
|
|
@@ -269,10 +362,20 @@ test("template will delete empty arrays related to the template only", async ()
|
|
|
269
362
|
actions: [],
|
|
270
363
|
id: "values-2",
|
|
271
364
|
type: "collection",
|
|
365
|
+
values: [
|
|
366
|
+
{
|
|
367
|
+
asset: {
|
|
368
|
+
id: "values-2-values-0",
|
|
369
|
+
type: "text",
|
|
370
|
+
value: "This should not be deleted by template",
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
],
|
|
272
374
|
template: [
|
|
273
375
|
{
|
|
274
376
|
data: "foo.bar",
|
|
275
377
|
output: "values",
|
|
378
|
+
placement: "append",
|
|
276
379
|
value: {
|
|
277
380
|
asset: {
|
|
278
381
|
id: "values-2-values-_index_",
|
|
@@ -282,15 +385,6 @@ test("template will delete empty arrays related to the template only", async ()
|
|
|
282
385
|
},
|
|
283
386
|
},
|
|
284
387
|
],
|
|
285
|
-
values: [
|
|
286
|
-
{
|
|
287
|
-
asset: {
|
|
288
|
-
id: "values-2-values-0",
|
|
289
|
-
type: "text",
|
|
290
|
-
value: "This should not be deleted by template",
|
|
291
|
-
},
|
|
292
|
-
},
|
|
293
|
-
],
|
|
294
388
|
},
|
|
295
389
|
},
|
|
296
390
|
],
|
|
@@ -298,7 +392,7 @@ test("template will delete empty arrays related to the template only", async ()
|
|
|
298
392
|
});
|
|
299
393
|
|
|
300
394
|
describe("template auto id", () => {
|
|
301
|
-
test("
|
|
395
|
+
test("simple", async () => {
|
|
302
396
|
const element = (
|
|
303
397
|
<Collection>
|
|
304
398
|
<Collection.Values>
|
|
@@ -315,15 +409,6 @@ describe("template auto id", () => {
|
|
|
315
409
|
expect(actual).toStrictEqual({
|
|
316
410
|
id: "root",
|
|
317
411
|
type: "collection",
|
|
318
|
-
values: [
|
|
319
|
-
{
|
|
320
|
-
asset: {
|
|
321
|
-
id: "static",
|
|
322
|
-
type: "text",
|
|
323
|
-
value: "Value 1",
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
],
|
|
327
412
|
template: [
|
|
328
413
|
{
|
|
329
414
|
data: "foo.bar",
|
|
@@ -335,6 +420,16 @@ describe("template auto id", () => {
|
|
|
335
420
|
value: "Template Value",
|
|
336
421
|
},
|
|
337
422
|
},
|
|
423
|
+
placement: "prepend",
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
values: [
|
|
427
|
+
{
|
|
428
|
+
asset: {
|
|
429
|
+
id: "static",
|
|
430
|
+
type: "text",
|
|
431
|
+
value: "Value 1",
|
|
432
|
+
},
|
|
338
433
|
},
|
|
339
434
|
],
|
|
340
435
|
});
|
|
@@ -363,15 +458,6 @@ describe("template auto id", () => {
|
|
|
363
458
|
expect(actual).toStrictEqual({
|
|
364
459
|
id: "root",
|
|
365
460
|
type: "collection",
|
|
366
|
-
values: [
|
|
367
|
-
{
|
|
368
|
-
asset: {
|
|
369
|
-
id: "static",
|
|
370
|
-
type: "text",
|
|
371
|
-
value: "Value 1",
|
|
372
|
-
},
|
|
373
|
-
},
|
|
374
|
-
],
|
|
375
461
|
template: [
|
|
376
462
|
{
|
|
377
463
|
data: "foo.bar",
|
|
@@ -395,6 +481,16 @@ describe("template auto id", () => {
|
|
|
395
481
|
type: "collection",
|
|
396
482
|
},
|
|
397
483
|
},
|
|
484
|
+
placement: "prepend",
|
|
485
|
+
},
|
|
486
|
+
],
|
|
487
|
+
values: [
|
|
488
|
+
{
|
|
489
|
+
asset: {
|
|
490
|
+
id: "static",
|
|
491
|
+
type: "text",
|
|
492
|
+
value: "Value 1",
|
|
493
|
+
},
|
|
398
494
|
},
|
|
399
495
|
],
|
|
400
496
|
});
|
package/src/template.tsx
CHANGED
|
@@ -18,9 +18,10 @@ export interface TemplateContextType {
|
|
|
18
18
|
depth: number;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export const TemplateContext
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
export const TemplateContext: React.Context<TemplateContextType> =
|
|
22
|
+
React.createContext<TemplateContextType>({
|
|
23
|
+
depth: 0,
|
|
24
|
+
});
|
|
24
25
|
|
|
25
26
|
export interface TemplateProps {
|
|
26
27
|
/** The source binding */
|
|
@@ -34,6 +35,73 @@ export interface TemplateProps {
|
|
|
34
35
|
|
|
35
36
|
/** boolean that specifies whether template should recompute when data changes */
|
|
36
37
|
dynamic?: boolean;
|
|
38
|
+
|
|
39
|
+
/** Specifies the expanded template placement in relation to existing elements*/
|
|
40
|
+
placement?: "append" | "prepend";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Represents the position information for template insertion
|
|
45
|
+
*/
|
|
46
|
+
interface PositionInfo {
|
|
47
|
+
placement?: "append" | "prepend";
|
|
48
|
+
insertionIndex?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Determines placement based on its proxy node and uses that for AST parity
|
|
53
|
+
*/
|
|
54
|
+
function determinePositionInfo(proxyNode: ProxyNode | null): PositionInfo {
|
|
55
|
+
const result: PositionInfo = {
|
|
56
|
+
placement: undefined,
|
|
57
|
+
insertionIndex: undefined,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (!proxyNode || !proxyNode.parent) {
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (proxyNode.parent.type === "array") {
|
|
65
|
+
const parentArray = proxyNode.parent;
|
|
66
|
+
const proxyIndex = parentArray.items.indexOf(proxyNode);
|
|
67
|
+
|
|
68
|
+
// Check if there are any non-proxy items before this proxy
|
|
69
|
+
const hasNonProxyBefore = parentArray.items
|
|
70
|
+
.slice(0, proxyIndex)
|
|
71
|
+
.some((item) => item.type !== "proxy");
|
|
72
|
+
|
|
73
|
+
const hasNonProxyAfter = parentArray.items
|
|
74
|
+
.slice(proxyIndex + 1)
|
|
75
|
+
.some((item) => item.type !== "proxy");
|
|
76
|
+
|
|
77
|
+
if (hasNonProxyBefore) {
|
|
78
|
+
result.placement = "append";
|
|
79
|
+
} else if (hasNonProxyAfter) {
|
|
80
|
+
result.placement = "prepend";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const containingSlot = proxyNode.parent?.parent;
|
|
85
|
+
if (
|
|
86
|
+
containingSlot &&
|
|
87
|
+
containingSlot.type === "property" &&
|
|
88
|
+
containingSlot.keyNode.type === "value"
|
|
89
|
+
) {
|
|
90
|
+
const parentObject = getParentObject(proxyNode);
|
|
91
|
+
if (parentObject) {
|
|
92
|
+
const index = parentObject.properties.findIndex((properties) => {
|
|
93
|
+
return properties.keyNode.value === containingSlot.keyNode.value;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Assigns AST ordering based on placement
|
|
97
|
+
if (index !== -1) {
|
|
98
|
+
result.insertionIndex =
|
|
99
|
+
result.placement === "prepend" ? index : index + 1;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return result;
|
|
37
105
|
}
|
|
38
106
|
|
|
39
107
|
/** Add a template instance to the object */
|
|
@@ -134,7 +202,7 @@ const getParentProperty = (node: JsonNode): PropertyNode | undefined => {
|
|
|
134
202
|
};
|
|
135
203
|
|
|
136
204
|
/** A template allows users to dynamically map over an array of data */
|
|
137
|
-
export const Template = (props: TemplateProps) => {
|
|
205
|
+
export const Template = (props: TemplateProps): React.JSX.Element => {
|
|
138
206
|
const baseContext = React.useContext(TemplateContext);
|
|
139
207
|
const dynamicProp = props.dynamic ?? false;
|
|
140
208
|
const [outputProp, setOutputProp] = React.useState<string | undefined>(
|
|
@@ -166,27 +234,7 @@ export const Template = (props: TemplateProps) => {
|
|
|
166
234
|
return;
|
|
167
235
|
}
|
|
168
236
|
|
|
169
|
-
|
|
170
|
-
if (
|
|
171
|
-
proxyRef.current.parent &&
|
|
172
|
-
proxyRef.current.parent.type === "array" &&
|
|
173
|
-
proxyRef.current.parent.items[0].type === "proxy"
|
|
174
|
-
) {
|
|
175
|
-
prefix = true;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const containingSlot = proxyRef.current.parent?.parent;
|
|
179
|
-
let insertionIndex = undefined;
|
|
180
|
-
if (
|
|
181
|
-
containingSlot &&
|
|
182
|
-
containingSlot.type === "property" &&
|
|
183
|
-
containingSlot.keyNode.type === "value"
|
|
184
|
-
) {
|
|
185
|
-
insertionIndex = parentObject.properties.findIndex((properties) => {
|
|
186
|
-
return properties.keyNode.value === containingSlot.keyNode.value;
|
|
187
|
-
});
|
|
188
|
-
insertionIndex = prefix ? insertionIndex : insertionIndex + 1;
|
|
189
|
-
}
|
|
237
|
+
const { insertionIndex } = determinePositionInfo(proxyRef.current);
|
|
190
238
|
|
|
191
239
|
// remove the template when unmounted
|
|
192
240
|
return addTemplateToObject(
|
|
@@ -198,6 +246,14 @@ export const Template = (props: TemplateProps) => {
|
|
|
198
246
|
}
|
|
199
247
|
}, [proxyRef, outputProp, outputElement.items]);
|
|
200
248
|
|
|
249
|
+
// Get position information for the template
|
|
250
|
+
const { placement: inferredPlacement } = determinePositionInfo(
|
|
251
|
+
proxyRef.current
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
// Use the explicitly provided placement or the inferred one
|
|
255
|
+
const resolvedPlacement = props.placement || inferredPlacement;
|
|
256
|
+
|
|
201
257
|
return (
|
|
202
258
|
<proxy ref={proxyRef}>
|
|
203
259
|
<>
|
|
@@ -218,6 +274,11 @@ export const Template = (props: TemplateProps) => {
|
|
|
218
274
|
{toJsonElement(dynamicProp)}
|
|
219
275
|
</property>
|
|
220
276
|
)}
|
|
277
|
+
{resolvedPlacement && (
|
|
278
|
+
<property name="placement">
|
|
279
|
+
{toJsonElement(resolvedPlacement)}
|
|
280
|
+
</property>
|
|
281
|
+
)}
|
|
221
282
|
</object>
|
|
222
283
|
</TemplateProvider>
|
|
223
284
|
</OptionalIDSuffixProvider>,
|
package/types/template.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface TemplateProps {
|
|
|
14
14
|
children: React.ReactNode;
|
|
15
15
|
/** boolean that specifies whether template should recompute when data changes */
|
|
16
16
|
dynamic?: boolean;
|
|
17
|
+
/** Specifies the expanded template placement in relation to existing elements*/
|
|
18
|
+
placement?: "append" | "prepend";
|
|
17
19
|
}
|
|
18
20
|
/** A template allows users to dynamically map over an array of data */
|
|
19
21
|
export declare const Template: (props: TemplateProps) => React.JSX.Element;
|