@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.
@@ -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 Order is preserved", async () => {
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
- expect((await render(element)).jsonValue).toStrictEqual({
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("s1mple", async () => {
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 = React.createContext<TemplateContextType>({
22
- depth: 0,
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
- let prefix = false;
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>,
@@ -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;