@routevn/creator-model 1.1.4 → 1.1.5
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/package.json +1 -1
- package/src/model.js +605 -25
- package/src/systemVariables.js +9 -0
package/package.json
CHANGED
package/src/model.js
CHANGED
|
@@ -38,6 +38,7 @@ const createEmptyCollectionState = () => ({
|
|
|
38
38
|
items: {},
|
|
39
39
|
tree: [],
|
|
40
40
|
});
|
|
41
|
+
|
|
41
42
|
const normalizeStateCollections = (state) => {
|
|
42
43
|
if (!isPlainObject(state)) {
|
|
43
44
|
return state;
|
|
@@ -108,7 +109,14 @@ const ANIMATION_EASING_KEYS = [
|
|
|
108
109
|
];
|
|
109
110
|
const VARIABLE_SCOPE_KEYS = ["context", "global-device", "global-account"];
|
|
110
111
|
const VARIABLE_TYPE_KEYS = ["string", "number", "boolean"];
|
|
111
|
-
const LAYOUT_TYPE_KEYS = [
|
|
112
|
+
const LAYOUT_TYPE_KEYS = [
|
|
113
|
+
"normal",
|
|
114
|
+
"save-load",
|
|
115
|
+
"confirmDialog",
|
|
116
|
+
"dialogue",
|
|
117
|
+
"nvl",
|
|
118
|
+
"choice",
|
|
119
|
+
];
|
|
112
120
|
const LAYOUT_ELEMENT_TEXT_STYLE_ALIGN_KEYS = ["left", "center", "right"];
|
|
113
121
|
const LAYOUT_TEXT_REVEAL_EFFECT_KEYS = ["typewriter", "softWipe", "none"];
|
|
114
122
|
const LAYOUT_ELEMENT_BASE_TYPES = [
|
|
@@ -122,17 +130,26 @@ const LAYOUT_ELEMENT_BASE_TYPES = [
|
|
|
122
130
|
"text-ref-character-name",
|
|
123
131
|
"text-revealing-ref-dialogue-content",
|
|
124
132
|
"text-ref-choice-item-content",
|
|
133
|
+
"text-ref-save-load-slot-date",
|
|
125
134
|
"text-ref-dialogue-line-character-name",
|
|
126
135
|
"text-ref-dialogue-line-content",
|
|
136
|
+
"sprite-ref-save-load-slot-image",
|
|
137
|
+
"fragment-ref",
|
|
127
138
|
"container-ref-choice-item",
|
|
139
|
+
"container-ref-save-load-slot",
|
|
128
140
|
"container-ref-dialogue-line",
|
|
141
|
+
"container-ref-confirm-dialog-ok",
|
|
142
|
+
"container-ref-confirm-dialog-cancel",
|
|
129
143
|
];
|
|
130
144
|
export const SCHEMA_VERSION = 2;
|
|
131
145
|
const LAYOUT_CONTAINER_ELEMENT_TYPES = [
|
|
132
146
|
"folder",
|
|
133
147
|
"container",
|
|
134
148
|
"container-ref-choice-item",
|
|
149
|
+
"container-ref-save-load-slot",
|
|
135
150
|
"container-ref-dialogue-line",
|
|
151
|
+
"container-ref-confirm-dialog-ok",
|
|
152
|
+
"container-ref-confirm-dialog-cancel",
|
|
136
153
|
];
|
|
137
154
|
const DOMAIN_ERROR_KIND_BY_NAME = {
|
|
138
155
|
PayloadValidationError: "payload",
|
|
@@ -230,6 +247,81 @@ const isVariableReferenceTarget = (state, variableId) => {
|
|
|
230
247
|
return isPlainObject(variable) && variable.type !== "folder";
|
|
231
248
|
};
|
|
232
249
|
|
|
250
|
+
const LAYOUT_CONDITION_TARGET_SET = new Set([
|
|
251
|
+
"item.savedAt",
|
|
252
|
+
"isLineCompleted",
|
|
253
|
+
"autoMode",
|
|
254
|
+
"skipMode",
|
|
255
|
+
]);
|
|
256
|
+
const VARIABLE_TARGET_DOT_PATTERN = /^variables\.([A-Za-z_$][A-Za-z0-9_$]*)$/;
|
|
257
|
+
const VARIABLE_TARGET_BRACKET_PATTERN = /^variables\[(.+)\]$/;
|
|
258
|
+
|
|
259
|
+
const parseLayoutConditionTarget = (target) => {
|
|
260
|
+
if (!isNonEmptyString(target)) {
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (LAYOUT_CONDITION_TARGET_SET.has(target)) {
|
|
265
|
+
return {
|
|
266
|
+
kind: "runtime",
|
|
267
|
+
target,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const dotMatch = target.match(VARIABLE_TARGET_DOT_PATTERN);
|
|
272
|
+
if (dotMatch) {
|
|
273
|
+
return {
|
|
274
|
+
kind: "variable",
|
|
275
|
+
target,
|
|
276
|
+
variableId: dotMatch[1],
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const bracketMatch = target.match(VARIABLE_TARGET_BRACKET_PATTERN);
|
|
281
|
+
if (!bracketMatch) {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const rawValue = bracketMatch[1].trim();
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
const variableId = JSON.parse(rawValue);
|
|
289
|
+
if (isNonEmptyString(variableId)) {
|
|
290
|
+
return {
|
|
291
|
+
kind: "variable",
|
|
292
|
+
target,
|
|
293
|
+
variableId,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
} catch {}
|
|
297
|
+
|
|
298
|
+
if (rawValue.startsWith("'") && rawValue.endsWith("'")) {
|
|
299
|
+
const variableId = rawValue.slice(1, -1);
|
|
300
|
+
if (isNonEmptyString(variableId)) {
|
|
301
|
+
return {
|
|
302
|
+
kind: "variable",
|
|
303
|
+
target,
|
|
304
|
+
variableId,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return undefined;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const isLayoutConditionTarget = (state, target) => {
|
|
313
|
+
const parsedTarget = parseLayoutConditionTarget(target);
|
|
314
|
+
if (!parsedTarget) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (parsedTarget.kind === "runtime") {
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return isVariableReferenceTarget(state, parsedTarget.variableId);
|
|
323
|
+
};
|
|
324
|
+
|
|
233
325
|
const toDomainErrorDetails = (publicError) => {
|
|
234
326
|
const details = isPlainObject(publicError?.details)
|
|
235
327
|
? { ...publicError.details }
|
|
@@ -2181,6 +2273,29 @@ const validateLayoutElementBorder = ({ border, path, errorFactory }) => {
|
|
|
2181
2273
|
}
|
|
2182
2274
|
};
|
|
2183
2275
|
|
|
2276
|
+
const validateLayoutElementInteraction = ({
|
|
2277
|
+
interaction,
|
|
2278
|
+
path,
|
|
2279
|
+
errorFactory,
|
|
2280
|
+
}) => {
|
|
2281
|
+
if (!isPlainObject(interaction)) {
|
|
2282
|
+
return invalidFromErrorFactory(
|
|
2283
|
+
errorFactory,
|
|
2284
|
+
`${path} must be an object when provided`,
|
|
2285
|
+
);
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
if (
|
|
2289
|
+
interaction.inheritToChildren !== undefined &&
|
|
2290
|
+
typeof interaction.inheritToChildren !== "boolean"
|
|
2291
|
+
) {
|
|
2292
|
+
return invalidFromErrorFactory(
|
|
2293
|
+
errorFactory,
|
|
2294
|
+
`${path}.inheritToChildren must be a boolean when provided`,
|
|
2295
|
+
);
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
|
|
2184
2299
|
const validateLayoutElementData = ({
|
|
2185
2300
|
data,
|
|
2186
2301
|
path,
|
|
@@ -2216,10 +2331,14 @@ const validateLayoutElementData = ({
|
|
|
2216
2331
|
"textStyleId",
|
|
2217
2332
|
"hoverTextStyleId",
|
|
2218
2333
|
"clickTextStyleId",
|
|
2334
|
+
"conditionalOverrides",
|
|
2219
2335
|
"direction",
|
|
2220
2336
|
"gap",
|
|
2221
2337
|
"containerType",
|
|
2222
2338
|
"scroll",
|
|
2339
|
+
"hover",
|
|
2340
|
+
"click",
|
|
2341
|
+
"rightClick",
|
|
2223
2342
|
"anchorToBottom",
|
|
2224
2343
|
"thumbImageId",
|
|
2225
2344
|
"barImageId",
|
|
@@ -2230,9 +2349,11 @@ const validateLayoutElementData = ({
|
|
|
2230
2349
|
"step",
|
|
2231
2350
|
"initialValue",
|
|
2232
2351
|
"variableId",
|
|
2352
|
+
"fragmentLayoutId",
|
|
2353
|
+
"paginationMode",
|
|
2354
|
+
"paginationVariableId",
|
|
2355
|
+
"paginationSize",
|
|
2233
2356
|
"$when",
|
|
2234
|
-
"click",
|
|
2235
|
-
"rightClick",
|
|
2236
2357
|
"change",
|
|
2237
2358
|
];
|
|
2238
2359
|
|
|
@@ -2281,6 +2402,7 @@ const validateLayoutElementData = ({
|
|
|
2281
2402
|
"min",
|
|
2282
2403
|
"max",
|
|
2283
2404
|
"step",
|
|
2405
|
+
"paginationSize",
|
|
2284
2406
|
"opacity",
|
|
2285
2407
|
]) {
|
|
2286
2408
|
if (data[key] !== undefined && !isFiniteNumber(data[key])) {
|
|
@@ -2322,6 +2444,9 @@ const validateLayoutElementData = ({
|
|
|
2322
2444
|
"clickTextStyleId",
|
|
2323
2445
|
"containerType",
|
|
2324
2446
|
"variableId",
|
|
2447
|
+
"fragmentLayoutId",
|
|
2448
|
+
"paginationMode",
|
|
2449
|
+
"paginationVariableId",
|
|
2325
2450
|
"revealEffect",
|
|
2326
2451
|
"thumbImageId",
|
|
2327
2452
|
"barImageId",
|
|
@@ -2337,6 +2462,191 @@ const validateLayoutElementData = ({
|
|
|
2337
2462
|
}
|
|
2338
2463
|
}
|
|
2339
2464
|
|
|
2465
|
+
if (data.conditionalOverrides !== undefined) {
|
|
2466
|
+
if (!Array.isArray(data.conditionalOverrides)) {
|
|
2467
|
+
return invalidFromErrorFactory(
|
|
2468
|
+
errorFactory,
|
|
2469
|
+
`${path}.conditionalOverrides must be an array when provided`,
|
|
2470
|
+
);
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
for (let index = 0; index < data.conditionalOverrides.length; index += 1) {
|
|
2474
|
+
const rule = data.conditionalOverrides[index];
|
|
2475
|
+
const rulePath = `${path}.conditionalOverrides.${index}`;
|
|
2476
|
+
|
|
2477
|
+
if (!isPlainObject(rule)) {
|
|
2478
|
+
return invalidFromErrorFactory(
|
|
2479
|
+
errorFactory,
|
|
2480
|
+
`${rulePath} must be an object`,
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
{
|
|
2485
|
+
const result = validateAllowedKeys({
|
|
2486
|
+
value: rule,
|
|
2487
|
+
allowedKeys: ["when", "set"],
|
|
2488
|
+
path: rulePath,
|
|
2489
|
+
errorFactory,
|
|
2490
|
+
});
|
|
2491
|
+
if (result?.valid === false) {
|
|
2492
|
+
return result;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
if (!isPlainObject(rule.when)) {
|
|
2497
|
+
return invalidFromErrorFactory(
|
|
2498
|
+
errorFactory,
|
|
2499
|
+
`${rulePath}.when must be an object`,
|
|
2500
|
+
);
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
{
|
|
2504
|
+
const result = validateAllowedKeys({
|
|
2505
|
+
value: rule.when,
|
|
2506
|
+
allowedKeys: ["target", "op", "value"],
|
|
2507
|
+
path: `${rulePath}.when`,
|
|
2508
|
+
errorFactory,
|
|
2509
|
+
});
|
|
2510
|
+
if (result?.valid === false) {
|
|
2511
|
+
return result;
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
if (!isNonEmptyString(rule.when.target)) {
|
|
2516
|
+
return invalidFromErrorFactory(
|
|
2517
|
+
errorFactory,
|
|
2518
|
+
`${rulePath}.when.target must be a non-empty string`,
|
|
2519
|
+
);
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
if (!parseLayoutConditionTarget(rule.when.target)) {
|
|
2523
|
+
return invalidFromErrorFactory(
|
|
2524
|
+
errorFactory,
|
|
2525
|
+
`${rulePath}.when.target must be a supported layout condition target`,
|
|
2526
|
+
);
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
if (
|
|
2530
|
+
!isString(rule.when.value) &&
|
|
2531
|
+
typeof rule.when.value !== "boolean" &&
|
|
2532
|
+
!isFiniteNumber(rule.when.value)
|
|
2533
|
+
) {
|
|
2534
|
+
return invalidFromErrorFactory(
|
|
2535
|
+
errorFactory,
|
|
2536
|
+
`${rulePath}.when.value must be a string, boolean, or finite number`,
|
|
2537
|
+
);
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
if (rule.when.op !== "eq") {
|
|
2541
|
+
return invalidFromErrorFactory(
|
|
2542
|
+
errorFactory,
|
|
2543
|
+
`${rulePath}.when.op must be "eq"`,
|
|
2544
|
+
);
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
if (!isPlainObject(rule.set)) {
|
|
2548
|
+
return invalidFromErrorFactory(
|
|
2549
|
+
errorFactory,
|
|
2550
|
+
`${rulePath}.set must be an object`,
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
{
|
|
2555
|
+
const result = validateAllowedKeys({
|
|
2556
|
+
value: rule.set,
|
|
2557
|
+
allowedKeys: [
|
|
2558
|
+
"textStyleId",
|
|
2559
|
+
"hoverTextStyleId",
|
|
2560
|
+
"clickTextStyleId",
|
|
2561
|
+
"imageId",
|
|
2562
|
+
"hoverImageId",
|
|
2563
|
+
"clickImageId",
|
|
2564
|
+
"opacity",
|
|
2565
|
+
"anchorX",
|
|
2566
|
+
"anchorY",
|
|
2567
|
+
"visible",
|
|
2568
|
+
"textStyle",
|
|
2569
|
+
],
|
|
2570
|
+
path: `${rulePath}.set`,
|
|
2571
|
+
errorFactory,
|
|
2572
|
+
});
|
|
2573
|
+
if (result?.valid === false) {
|
|
2574
|
+
return result;
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
for (const field of [
|
|
2579
|
+
"textStyleId",
|
|
2580
|
+
"hoverTextStyleId",
|
|
2581
|
+
"clickTextStyleId",
|
|
2582
|
+
"imageId",
|
|
2583
|
+
"hoverImageId",
|
|
2584
|
+
"clickImageId",
|
|
2585
|
+
]) {
|
|
2586
|
+
if (
|
|
2587
|
+
rule.set[field] !== undefined &&
|
|
2588
|
+
!isNonEmptyString(rule.set[field])
|
|
2589
|
+
) {
|
|
2590
|
+
return invalidFromErrorFactory(
|
|
2591
|
+
errorFactory,
|
|
2592
|
+
`${rulePath}.set.${field} must be a non-empty string when provided`,
|
|
2593
|
+
);
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
if (
|
|
2598
|
+
rule.set.opacity !== undefined &&
|
|
2599
|
+
(!isFiniteNumber(rule.set.opacity) ||
|
|
2600
|
+
rule.set.opacity < 0 ||
|
|
2601
|
+
rule.set.opacity > 1)
|
|
2602
|
+
) {
|
|
2603
|
+
return invalidFromErrorFactory(
|
|
2604
|
+
errorFactory,
|
|
2605
|
+
`${rulePath}.set.opacity must be a finite number between 0 and 1 when provided`,
|
|
2606
|
+
);
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
for (const field of ["anchorX", "anchorY"]) {
|
|
2610
|
+
if (rule.set[field] !== undefined && !isFiniteNumber(rule.set[field])) {
|
|
2611
|
+
return invalidFromErrorFactory(
|
|
2612
|
+
errorFactory,
|
|
2613
|
+
`${rulePath}.set.${field} must be a finite number when provided`,
|
|
2614
|
+
);
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
if (
|
|
2619
|
+
rule.set.visible !== undefined &&
|
|
2620
|
+
typeof rule.set.visible !== "boolean"
|
|
2621
|
+
) {
|
|
2622
|
+
return invalidFromErrorFactory(
|
|
2623
|
+
errorFactory,
|
|
2624
|
+
`${rulePath}.set.visible must be a boolean when provided`,
|
|
2625
|
+
);
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
if (rule.set.textStyle !== undefined) {
|
|
2629
|
+
if (!isPlainObject(rule.set.textStyle)) {
|
|
2630
|
+
return invalidFromErrorFactory(
|
|
2631
|
+
errorFactory,
|
|
2632
|
+
`${rulePath}.set.textStyle must be an object when provided`,
|
|
2633
|
+
);
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
{
|
|
2637
|
+
const result = validateLayoutElementTextStyle({
|
|
2638
|
+
textStyle: rule.set.textStyle,
|
|
2639
|
+
path: `${rulePath}.set.textStyle`,
|
|
2640
|
+
errorFactory,
|
|
2641
|
+
});
|
|
2642
|
+
if (result?.valid === false) {
|
|
2643
|
+
return result;
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2340
2650
|
if (data.fill !== undefined && !isString(data.fill)) {
|
|
2341
2651
|
return invalidFromErrorFactory(
|
|
2342
2652
|
errorFactory,
|
|
@@ -2365,6 +2675,17 @@ const validateLayoutElementData = ({
|
|
|
2365
2675
|
);
|
|
2366
2676
|
}
|
|
2367
2677
|
|
|
2678
|
+
if (
|
|
2679
|
+
data.paginationMode !== undefined &&
|
|
2680
|
+
data.paginationMode !== "continuous" &&
|
|
2681
|
+
data.paginationMode !== "paginated"
|
|
2682
|
+
) {
|
|
2683
|
+
return invalidFromErrorFactory(
|
|
2684
|
+
errorFactory,
|
|
2685
|
+
`${path}.paginationMode must be 'continuous' or 'paginated' when provided`,
|
|
2686
|
+
);
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2368
2689
|
if (data.scroll !== undefined && typeof data.scroll !== "boolean") {
|
|
2369
2690
|
return invalidFromErrorFactory(
|
|
2370
2691
|
errorFactory,
|
|
@@ -2372,6 +2693,45 @@ const validateLayoutElementData = ({
|
|
|
2372
2693
|
);
|
|
2373
2694
|
}
|
|
2374
2695
|
|
|
2696
|
+
if (data.hover !== undefined) {
|
|
2697
|
+
{
|
|
2698
|
+
const result = validateLayoutElementInteraction({
|
|
2699
|
+
interaction: data.hover,
|
|
2700
|
+
path: `${path}.hover`,
|
|
2701
|
+
errorFactory,
|
|
2702
|
+
});
|
|
2703
|
+
if (result?.valid === false) {
|
|
2704
|
+
return result;
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
if (data.click !== undefined) {
|
|
2710
|
+
{
|
|
2711
|
+
const result = validateLayoutElementInteraction({
|
|
2712
|
+
interaction: data.click,
|
|
2713
|
+
path: `${path}.click`,
|
|
2714
|
+
errorFactory,
|
|
2715
|
+
});
|
|
2716
|
+
if (result?.valid === false) {
|
|
2717
|
+
return result;
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
if (data.rightClick !== undefined) {
|
|
2723
|
+
{
|
|
2724
|
+
const result = validateLayoutElementInteraction({
|
|
2725
|
+
interaction: data.rightClick,
|
|
2726
|
+
path: `${path}.rightClick`,
|
|
2727
|
+
errorFactory,
|
|
2728
|
+
});
|
|
2729
|
+
if (result?.valid === false) {
|
|
2730
|
+
return result;
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2375
2735
|
if (
|
|
2376
2736
|
data.anchorToBottom !== undefined &&
|
|
2377
2737
|
typeof data.anchorToBottom !== "boolean"
|
|
@@ -2415,20 +2775,6 @@ const validateLayoutElementData = ({
|
|
|
2415
2775
|
}
|
|
2416
2776
|
}
|
|
2417
2777
|
|
|
2418
|
-
if (data.click !== undefined && !isPlainObject(data.click)) {
|
|
2419
|
-
return invalidFromErrorFactory(
|
|
2420
|
-
errorFactory,
|
|
2421
|
-
`${path}.click must be an object when provided`,
|
|
2422
|
-
);
|
|
2423
|
-
}
|
|
2424
|
-
|
|
2425
|
-
if (data.rightClick !== undefined && !isPlainObject(data.rightClick)) {
|
|
2426
|
-
return invalidFromErrorFactory(
|
|
2427
|
-
errorFactory,
|
|
2428
|
-
`${path}.rightClick must be an object when provided`,
|
|
2429
|
-
);
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
2778
|
if (data.change !== undefined && !isPlainObject(data.change)) {
|
|
2433
2779
|
return invalidFromErrorFactory(
|
|
2434
2780
|
errorFactory,
|
|
@@ -2470,10 +2816,14 @@ const validateLayoutElementItems = ({ items, path, errorFactory }) => {
|
|
|
2470
2816
|
"textStyleId",
|
|
2471
2817
|
"hoverTextStyleId",
|
|
2472
2818
|
"clickTextStyleId",
|
|
2819
|
+
"conditionalOverrides",
|
|
2473
2820
|
"direction",
|
|
2474
2821
|
"gap",
|
|
2475
2822
|
"containerType",
|
|
2476
2823
|
"scroll",
|
|
2824
|
+
"hover",
|
|
2825
|
+
"click",
|
|
2826
|
+
"rightClick",
|
|
2477
2827
|
"anchorToBottom",
|
|
2478
2828
|
"thumbImageId",
|
|
2479
2829
|
"barImageId",
|
|
@@ -2484,9 +2834,11 @@ const validateLayoutElementItems = ({ items, path, errorFactory }) => {
|
|
|
2484
2834
|
"step",
|
|
2485
2835
|
"initialValue",
|
|
2486
2836
|
"variableId",
|
|
2837
|
+
"fragmentLayoutId",
|
|
2838
|
+
"paginationMode",
|
|
2839
|
+
"paginationVariableId",
|
|
2840
|
+
"paginationSize",
|
|
2487
2841
|
"$when",
|
|
2488
|
-
"click",
|
|
2489
|
-
"rightClick",
|
|
2490
2842
|
"change",
|
|
2491
2843
|
],
|
|
2492
2844
|
path: itemPath,
|
|
@@ -2684,7 +3036,15 @@ const validateLayoutItems = ({ items, path, errorFactory }) => {
|
|
|
2684
3036
|
allowedKeys:
|
|
2685
3037
|
item.type === "folder"
|
|
2686
3038
|
? ["id", "type", "name"]
|
|
2687
|
-
: [
|
|
3039
|
+
: [
|
|
3040
|
+
"id",
|
|
3041
|
+
"type",
|
|
3042
|
+
"name",
|
|
3043
|
+
"description",
|
|
3044
|
+
"layoutType",
|
|
3045
|
+
"isFragment",
|
|
3046
|
+
"elements",
|
|
3047
|
+
],
|
|
2688
3048
|
path: itemPath,
|
|
2689
3049
|
errorFactory,
|
|
2690
3050
|
});
|
|
@@ -2714,11 +3074,28 @@ const validateLayoutItems = ({ items, path, errorFactory }) => {
|
|
|
2714
3074
|
);
|
|
2715
3075
|
}
|
|
2716
3076
|
|
|
3077
|
+
if (item.description !== undefined && !isString(item.description)) {
|
|
3078
|
+
return invalidFromErrorFactory(
|
|
3079
|
+
errorFactory,
|
|
3080
|
+
`${itemPath}.description must be a string when provided`,
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
|
|
2717
3084
|
if (item.type === "layout") {
|
|
2718
3085
|
if (!LAYOUT_TYPE_KEYS.includes(item.layoutType)) {
|
|
2719
3086
|
return invalidFromErrorFactory(
|
|
2720
3087
|
errorFactory,
|
|
2721
|
-
`${itemPath}.layoutType must be 'normal', 'dialogue', 'nvl', or 'choice'`,
|
|
3088
|
+
`${itemPath}.layoutType must be 'normal', 'save-load', 'confirmDialog', 'dialogue', 'nvl', or 'choice'`,
|
|
3089
|
+
);
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
if (
|
|
3093
|
+
item.isFragment !== undefined &&
|
|
3094
|
+
typeof item.isFragment !== "boolean"
|
|
3095
|
+
) {
|
|
3096
|
+
return invalidFromErrorFactory(
|
|
3097
|
+
errorFactory,
|
|
3098
|
+
`${itemPath}.isFragment must be a boolean when provided`,
|
|
2722
3099
|
);
|
|
2723
3100
|
}
|
|
2724
3101
|
|
|
@@ -3923,6 +4300,32 @@ export const assertInvariants = ({ state }) => {
|
|
|
3923
4300
|
return VALID_RESULT;
|
|
3924
4301
|
};
|
|
3925
4302
|
|
|
4303
|
+
const assertFragmentLayoutReference = ({
|
|
4304
|
+
ownerIdField,
|
|
4305
|
+
ownerId,
|
|
4306
|
+
ownerLabel,
|
|
4307
|
+
elementId,
|
|
4308
|
+
targetId,
|
|
4309
|
+
}) => {
|
|
4310
|
+
const layout = state.layouts.items[targetId];
|
|
4311
|
+
if (
|
|
4312
|
+
!isPlainObject(layout) ||
|
|
4313
|
+
layout.type !== "layout" ||
|
|
4314
|
+
layout.isFragment !== true
|
|
4315
|
+
) {
|
|
4316
|
+
return invalidInvariant(
|
|
4317
|
+
`${ownerLabel} element fragmentLayoutId must reference an existing fragment layout`,
|
|
4318
|
+
{
|
|
4319
|
+
[ownerIdField]: ownerId,
|
|
4320
|
+
elementId,
|
|
4321
|
+
fragmentLayoutId: targetId,
|
|
4322
|
+
},
|
|
4323
|
+
);
|
|
4324
|
+
}
|
|
4325
|
+
|
|
4326
|
+
return VALID_RESULT;
|
|
4327
|
+
};
|
|
4328
|
+
|
|
3926
4329
|
const assertElementReferencesForCollection = ({
|
|
3927
4330
|
items,
|
|
3928
4331
|
ownerIdField,
|
|
@@ -3979,6 +4382,51 @@ export const assertInvariants = ({ state }) => {
|
|
|
3979
4382
|
}
|
|
3980
4383
|
}
|
|
3981
4384
|
|
|
4385
|
+
if (Array.isArray(element.conditionalOverrides)) {
|
|
4386
|
+
for (
|
|
4387
|
+
let index = 0;
|
|
4388
|
+
index < element.conditionalOverrides.length;
|
|
4389
|
+
index += 1
|
|
4390
|
+
) {
|
|
4391
|
+
const rule = element.conditionalOverrides[index];
|
|
4392
|
+
|
|
4393
|
+
for (const field of [
|
|
4394
|
+
"textStyleId",
|
|
4395
|
+
"hoverTextStyleId",
|
|
4396
|
+
"clickTextStyleId",
|
|
4397
|
+
]) {
|
|
4398
|
+
if (rule?.set?.[field] !== undefined) {
|
|
4399
|
+
const result = assertTextStyleReference({
|
|
4400
|
+
ownerIdField,
|
|
4401
|
+
ownerId,
|
|
4402
|
+
ownerLabel,
|
|
4403
|
+
elementId,
|
|
4404
|
+
field: `conditionalOverrides.${index}.set.${field}`,
|
|
4405
|
+
targetId: rule.set[field],
|
|
4406
|
+
});
|
|
4407
|
+
if (!result.valid) {
|
|
4408
|
+
return result;
|
|
4409
|
+
}
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
|
|
4413
|
+
if (
|
|
4414
|
+
rule?.when?.target !== undefined &&
|
|
4415
|
+
!isLayoutConditionTarget(state, rule.when.target)
|
|
4416
|
+
) {
|
|
4417
|
+
return invalidInvariant(
|
|
4418
|
+
`${ownerLabel} element conditionalOverrides when target must reference an existing variable or supported runtime condition`,
|
|
4419
|
+
{
|
|
4420
|
+
[ownerIdField]: ownerId,
|
|
4421
|
+
elementId,
|
|
4422
|
+
field: `conditionalOverrides.${index}.when.target`,
|
|
4423
|
+
targetId: rule.when.target,
|
|
4424
|
+
},
|
|
4425
|
+
);
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
|
|
3982
4430
|
if (element.variableId !== undefined) {
|
|
3983
4431
|
const result = assertVariableReference({
|
|
3984
4432
|
ownerIdField,
|
|
@@ -3991,6 +4439,19 @@ export const assertInvariants = ({ state }) => {
|
|
|
3991
4439
|
return result;
|
|
3992
4440
|
}
|
|
3993
4441
|
}
|
|
4442
|
+
|
|
4443
|
+
if (element.fragmentLayoutId !== undefined) {
|
|
4444
|
+
const result = assertFragmentLayoutReference({
|
|
4445
|
+
ownerIdField,
|
|
4446
|
+
ownerId,
|
|
4447
|
+
ownerLabel,
|
|
4448
|
+
elementId,
|
|
4449
|
+
targetId: element.fragmentLayoutId,
|
|
4450
|
+
});
|
|
4451
|
+
if (!result.valid) {
|
|
4452
|
+
return result;
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
3994
4455
|
}
|
|
3995
4456
|
}
|
|
3996
4457
|
|
|
@@ -6136,7 +6597,14 @@ const validateLayoutCreateData = ({ data, errorFactory }) => {
|
|
|
6136
6597
|
allowedKeys:
|
|
6137
6598
|
data.type === "folder"
|
|
6138
6599
|
? ["type", "name"]
|
|
6139
|
-
: [
|
|
6600
|
+
: [
|
|
6601
|
+
"type",
|
|
6602
|
+
"name",
|
|
6603
|
+
"description",
|
|
6604
|
+
"layoutType",
|
|
6605
|
+
"isFragment",
|
|
6606
|
+
"elements",
|
|
6607
|
+
],
|
|
6140
6608
|
path: "payload.data",
|
|
6141
6609
|
errorFactory,
|
|
6142
6610
|
});
|
|
@@ -6152,11 +6620,25 @@ const validateLayoutCreateData = ({ data, errorFactory }) => {
|
|
|
6152
6620
|
);
|
|
6153
6621
|
}
|
|
6154
6622
|
|
|
6623
|
+
if (data.description !== undefined && !isString(data.description)) {
|
|
6624
|
+
return invalidFromErrorFactory(
|
|
6625
|
+
errorFactory,
|
|
6626
|
+
"payload.data.description must be a string when provided",
|
|
6627
|
+
);
|
|
6628
|
+
}
|
|
6629
|
+
|
|
6155
6630
|
if (data.type === "layout") {
|
|
6156
6631
|
if (!LAYOUT_TYPE_KEYS.includes(data.layoutType)) {
|
|
6157
6632
|
return invalidFromErrorFactory(
|
|
6158
6633
|
errorFactory,
|
|
6159
|
-
"payload.data.layoutType must be 'normal', 'dialogue', 'nvl', or 'choice'",
|
|
6634
|
+
"payload.data.layoutType must be 'normal', 'save-load', 'confirmDialog', 'dialogue', 'nvl', or 'choice'",
|
|
6635
|
+
);
|
|
6636
|
+
}
|
|
6637
|
+
|
|
6638
|
+
if (data.isFragment !== undefined && typeof data.isFragment !== "boolean") {
|
|
6639
|
+
return invalidFromErrorFactory(
|
|
6640
|
+
errorFactory,
|
|
6641
|
+
"payload.data.isFragment must be a boolean when provided",
|
|
6160
6642
|
);
|
|
6161
6643
|
}
|
|
6162
6644
|
|
|
@@ -6179,7 +6661,7 @@ const validateLayoutUpdateData = ({ data, errorFactory }) => {
|
|
|
6179
6661
|
{
|
|
6180
6662
|
const result = validateAllowedKeys({
|
|
6181
6663
|
value: data,
|
|
6182
|
-
allowedKeys: ["name", "layoutType"],
|
|
6664
|
+
allowedKeys: ["name", "description", "layoutType", "isFragment"],
|
|
6183
6665
|
path: "payload.data",
|
|
6184
6666
|
errorFactory,
|
|
6185
6667
|
});
|
|
@@ -6202,13 +6684,27 @@ const validateLayoutUpdateData = ({ data, errorFactory }) => {
|
|
|
6202
6684
|
);
|
|
6203
6685
|
}
|
|
6204
6686
|
|
|
6687
|
+
if (data.description !== undefined && !isString(data.description)) {
|
|
6688
|
+
return invalidFromErrorFactory(
|
|
6689
|
+
errorFactory,
|
|
6690
|
+
"payload.data.description must be a string when provided",
|
|
6691
|
+
);
|
|
6692
|
+
}
|
|
6693
|
+
|
|
6205
6694
|
if (
|
|
6206
6695
|
data.layoutType !== undefined &&
|
|
6207
6696
|
!LAYOUT_TYPE_KEYS.includes(data.layoutType)
|
|
6208
6697
|
) {
|
|
6209
6698
|
return invalidFromErrorFactory(
|
|
6210
6699
|
errorFactory,
|
|
6211
|
-
"payload.data.layoutType must be 'normal', 'dialogue', 'nvl', or 'choice' when provided",
|
|
6700
|
+
"payload.data.layoutType must be 'normal', 'save-load', 'confirmDialog', 'dialogue', 'nvl', or 'choice' when provided",
|
|
6701
|
+
);
|
|
6702
|
+
}
|
|
6703
|
+
|
|
6704
|
+
if (data.isFragment !== undefined && typeof data.isFragment !== "boolean") {
|
|
6705
|
+
return invalidFromErrorFactory(
|
|
6706
|
+
errorFactory,
|
|
6707
|
+
"payload.data.isFragment must be a boolean when provided",
|
|
6212
6708
|
);
|
|
6213
6709
|
}
|
|
6214
6710
|
};
|
|
@@ -6519,6 +7015,69 @@ const validateVisualElementReferenceTargets = ({
|
|
|
6519
7015
|
}
|
|
6520
7016
|
}
|
|
6521
7017
|
|
|
7018
|
+
if (Array.isArray(data.conditionalOverrides)) {
|
|
7019
|
+
for (let index = 0; index < data.conditionalOverrides.length; index += 1) {
|
|
7020
|
+
const rule = data.conditionalOverrides[index];
|
|
7021
|
+
|
|
7022
|
+
for (const field of [
|
|
7023
|
+
"textStyleId",
|
|
7024
|
+
"hoverTextStyleId",
|
|
7025
|
+
"clickTextStyleId",
|
|
7026
|
+
]) {
|
|
7027
|
+
if (rule?.set?.[field] === undefined) {
|
|
7028
|
+
continue;
|
|
7029
|
+
}
|
|
7030
|
+
|
|
7031
|
+
const textStyle = state.textStyles.items[rule.set[field]];
|
|
7032
|
+
if (!isPlainObject(textStyle) || textStyle.type === "folder") {
|
|
7033
|
+
return invalidFromErrorFactory(
|
|
7034
|
+
errorFactory,
|
|
7035
|
+
`${ownerLabel} element conditionalOverrides.${index}.set.${field} must reference an existing non-folder text style`,
|
|
7036
|
+
{
|
|
7037
|
+
[ownerIdField]: ownerId,
|
|
7038
|
+
elementId,
|
|
7039
|
+
field: `conditionalOverrides.${index}.set.${field}`,
|
|
7040
|
+
targetId: rule.set[field],
|
|
7041
|
+
},
|
|
7042
|
+
);
|
|
7043
|
+
}
|
|
7044
|
+
}
|
|
7045
|
+
|
|
7046
|
+
for (const field of ["imageId", "hoverImageId", "clickImageId"]) {
|
|
7047
|
+
if (rule?.set?.[field] === undefined) {
|
|
7048
|
+
continue;
|
|
7049
|
+
}
|
|
7050
|
+
|
|
7051
|
+
const image = state.images.items[rule.set[field]];
|
|
7052
|
+
if (!isPlainObject(image) || image.type === "folder") {
|
|
7053
|
+
return invalidFromErrorFactory(
|
|
7054
|
+
errorFactory,
|
|
7055
|
+
`${ownerLabel} element conditionalOverrides.${index}.set.${field} must reference an existing non-folder image`,
|
|
7056
|
+
{
|
|
7057
|
+
[ownerIdField]: ownerId,
|
|
7058
|
+
elementId,
|
|
7059
|
+
field: `conditionalOverrides.${index}.set.${field}`,
|
|
7060
|
+
targetId: rule.set[field],
|
|
7061
|
+
},
|
|
7062
|
+
);
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
7065
|
+
|
|
7066
|
+
if (!isLayoutConditionTarget(state, rule?.when?.target)) {
|
|
7067
|
+
return invalidFromErrorFactory(
|
|
7068
|
+
errorFactory,
|
|
7069
|
+
`${ownerLabel} element conditionalOverrides.${index}.when.target must reference an existing variable or supported runtime condition`,
|
|
7070
|
+
{
|
|
7071
|
+
[ownerIdField]: ownerId,
|
|
7072
|
+
elementId,
|
|
7073
|
+
field: `conditionalOverrides.${index}.when.target`,
|
|
7074
|
+
targetId: rule?.when?.target,
|
|
7075
|
+
},
|
|
7076
|
+
);
|
|
7077
|
+
}
|
|
7078
|
+
}
|
|
7079
|
+
}
|
|
7080
|
+
|
|
6522
7081
|
if (data.variableId !== undefined) {
|
|
6523
7082
|
if (!isVariableReferenceTarget(state, data.variableId)) {
|
|
6524
7083
|
return invalidFromErrorFactory(
|
|
@@ -6532,6 +7091,25 @@ const validateVisualElementReferenceTargets = ({
|
|
|
6532
7091
|
);
|
|
6533
7092
|
}
|
|
6534
7093
|
}
|
|
7094
|
+
|
|
7095
|
+
if (data.fragmentLayoutId !== undefined) {
|
|
7096
|
+
const fragmentLayout = state.layouts.items[data.fragmentLayoutId];
|
|
7097
|
+
if (
|
|
7098
|
+
!isPlainObject(fragmentLayout) ||
|
|
7099
|
+
fragmentLayout.type !== "layout" ||
|
|
7100
|
+
fragmentLayout.isFragment !== true
|
|
7101
|
+
) {
|
|
7102
|
+
return invalidFromErrorFactory(
|
|
7103
|
+
errorFactory,
|
|
7104
|
+
`${ownerLabel} element fragmentLayoutId must reference an existing fragment layout`,
|
|
7105
|
+
{
|
|
7106
|
+
[ownerIdField]: ownerId,
|
|
7107
|
+
elementId,
|
|
7108
|
+
fragmentLayoutId: data.fragmentLayoutId,
|
|
7109
|
+
},
|
|
7110
|
+
);
|
|
7111
|
+
}
|
|
7112
|
+
}
|
|
6535
7113
|
};
|
|
6536
7114
|
|
|
6537
7115
|
const getTreeIdsInOrder = ({ nodes }) => {
|
|
@@ -11198,7 +11776,9 @@ const COMMAND_DEFINITIONS = [
|
|
|
11198
11776
|
name: payload.data.name,
|
|
11199
11777
|
...(payload.data.type === "layout"
|
|
11200
11778
|
? {
|
|
11779
|
+
description: payload.data.description,
|
|
11201
11780
|
layoutType: payload.data.layoutType,
|
|
11781
|
+
isFragment: payload.data.isFragment,
|
|
11202
11782
|
elements: structuredClone(payload.data.elements),
|
|
11203
11783
|
}
|
|
11204
11784
|
: {}),
|
package/src/systemVariables.js
CHANGED
|
@@ -21,6 +21,15 @@ export const SYSTEM_VARIABLE_GROUPS = Object.freeze([
|
|
|
21
21
|
description:
|
|
22
22
|
"Controls the default dialogue text speed stored for this device.",
|
|
23
23
|
},
|
|
24
|
+
{
|
|
25
|
+
id: "_currentSaveLoadPagination",
|
|
26
|
+
name: "Current Save/Load Pagination",
|
|
27
|
+
scope: "runtime-screen",
|
|
28
|
+
type: "number",
|
|
29
|
+
default: 0,
|
|
30
|
+
description:
|
|
31
|
+
"The current save/load pagination index. Resolves to 0, 1, 2, 3, and so on for the active save/load page.",
|
|
32
|
+
},
|
|
24
33
|
]),
|
|
25
34
|
},
|
|
26
35
|
]);
|