Skip to content

Commit 4caeb10

Browse files
authored
feat: support scene descriptions and rect layout elements (#8)
* feat: support scene descriptions and rect layout elements * chore: bump model package version to 1.1.0
1 parent 745dc6c commit 4caeb10

File tree

4 files changed

+138
-13
lines changed

4 files changed

+138
-13
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@routevn/creator-model",
3-
"version": "1.0.5",
3+
"version": "1.1.0",
44
"private": false,
55
"type": "module",
66
"license": "MIT",

src/model.js

Lines changed: 108 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ const LAYOUT_ELEMENT_TEXT_STYLE_ALIGN_KEYS = ["left", "center", "right"];
112112
const LAYOUT_ELEMENT_BASE_TYPES = [
113113
"folder",
114114
"container",
115+
"rect",
115116
"sprite",
116117
"text",
117118
"text-revealing",
@@ -387,8 +388,8 @@ const validateSceneItems = ({ items, path, errorFactory }) => {
387388
value: item,
388389
allowedKeys:
389390
item?.type === "scene"
390-
? ["id", "type", "name", "position", "sections"]
391-
: ["id", "type", "name", "position"],
391+
? ["id", "type", "name", "description", "position", "sections"]
392+
: ["id", "type", "name", "description", "position"],
392393
path: itemPath,
393394
errorFactory,
394395
});
@@ -425,6 +426,13 @@ const validateSceneItems = ({ items, path, errorFactory }) => {
425426
);
426427
}
427428

429+
if (item.description !== undefined && !isString(item.description)) {
430+
return invalidFromErrorFactory(
431+
errorFactory,
432+
`${itemPath}.description must be a string when provided`,
433+
);
434+
}
435+
428436
{
429437
const result = validateOptionalPosition({
430438
value: item.position,
@@ -2121,6 +2129,47 @@ const validateLayoutElementStyle = ({ style, path, errorFactory }) => {
21212129
}
21222130
};
21232131

2132+
const validateLayoutElementBorder = ({ border, path, errorFactory }) => {
2133+
{
2134+
const result = validateAllowedKeys({
2135+
value: border,
2136+
allowedKeys: ["color", "alpha", "width"],
2137+
path,
2138+
errorFactory,
2139+
});
2140+
if (result?.valid === false) {
2141+
return result;
2142+
}
2143+
}
2144+
2145+
if (border.color !== undefined && !isString(border.color)) {
2146+
return invalidFromErrorFactory(
2147+
errorFactory,
2148+
`${path}.color must be a string when provided`,
2149+
);
2150+
}
2151+
2152+
if (
2153+
border.alpha !== undefined &&
2154+
(!isFiniteNumber(border.alpha) || border.alpha < 0 || border.alpha > 1)
2155+
) {
2156+
return invalidFromErrorFactory(
2157+
errorFactory,
2158+
`${path}.alpha must be a finite number between 0 and 1 when provided`,
2159+
);
2160+
}
2161+
2162+
if (
2163+
border.width !== undefined &&
2164+
(!isFiniteNumber(border.width) || border.width < 0)
2165+
) {
2166+
return invalidFromErrorFactory(
2167+
errorFactory,
2168+
`${path}.width must be a finite number greater than or equal to 0 when provided`,
2169+
);
2170+
}
2171+
};
2172+
21242173
const validateLayoutElementData = ({
21252174
data,
21262175
path,
@@ -2144,6 +2193,8 @@ const validateLayoutElementData = ({
21442193
"scaleY",
21452194
"rotation",
21462195
"opacity",
2196+
"fill",
2197+
"border",
21472198
"text",
21482199
"style",
21492200
"displaySpeed",
@@ -2273,6 +2324,13 @@ const validateLayoutElementData = ({
22732324
}
22742325
}
22752326

2327+
if (data.fill !== undefined && !isString(data.fill)) {
2328+
return invalidFromErrorFactory(
2329+
errorFactory,
2330+
`${path}.fill must be a string when provided`,
2331+
);
2332+
}
2333+
22762334
if (
22772335
data.direction !== undefined &&
22782336
data.direction !== "horizontal" &&
@@ -2314,6 +2372,26 @@ const validateLayoutElementData = ({
23142372
}
23152373
}
23162374

2375+
if (data.border !== undefined) {
2376+
if (!isPlainObject(data.border)) {
2377+
return invalidFromErrorFactory(
2378+
errorFactory,
2379+
`${path}.border must be an object when provided`,
2380+
);
2381+
}
2382+
2383+
{
2384+
const result = validateLayoutElementBorder({
2385+
border: data.border,
2386+
path: `${path}.border`,
2387+
errorFactory,
2388+
});
2389+
if (result?.valid === false) {
2390+
return result;
2391+
}
2392+
}
2393+
}
2394+
23172395
if (data.click !== undefined && !isPlainObject(data.click)) {
23182396
return invalidFromErrorFactory(
23192397
errorFactory,
@@ -2357,6 +2435,8 @@ const validateLayoutElementItems = ({ items, path, errorFactory }) => {
23572435
"scaleY",
23582436
"rotation",
23592437
"opacity",
2438+
"fill",
2439+
"border",
23602440
"text",
23612441
"style",
23622442
"displaySpeed",
@@ -2630,7 +2710,6 @@ const validateLayoutItems = ({ items, path, errorFactory }) => {
26302710
return result;
26312711
}
26322712
}
2633-
26342713
}
26352714
}
26362715
};
@@ -4085,7 +4164,7 @@ const validateSceneCreateData = ({ data, errorFactory }) => {
40854164
{
40864165
const result = validateAllowedKeys({
40874166
value: data,
4088-
allowedKeys: ["name", "type", "position"],
4167+
allowedKeys: ["name", "description", "type", "position"],
40894168
path: "payload.data",
40904169
errorFactory,
40914170
});
@@ -4101,6 +4180,13 @@ const validateSceneCreateData = ({ data, errorFactory }) => {
41014180
);
41024181
}
41034182

4183+
if (data.description !== undefined && !isString(data.description)) {
4184+
return invalidFromErrorFactory(
4185+
errorFactory,
4186+
"payload.data.description must be a string when provided",
4187+
);
4188+
}
4189+
41044190
if (
41054191
data.type !== undefined &&
41064192
data.type !== "scene" &&
@@ -4128,7 +4214,7 @@ const validateSceneUpdateData = ({ data, errorFactory }) => {
41284214
{
41294215
const result = validateAllowedKeys({
41304216
value: data,
4131-
allowedKeys: ["name", "position"],
4217+
allowedKeys: ["name", "description", "position"],
41324218
path: "payload.data",
41334219
errorFactory,
41344220
});
@@ -4138,9 +4224,10 @@ const validateSceneUpdateData = ({ data, errorFactory }) => {
41384224
}
41394225

41404226
const hasName = data.name !== undefined;
4227+
const hasDescription = data.description !== undefined;
41414228
const hasPosition = data.position !== undefined;
41424229

4143-
if (!hasName && !hasPosition) {
4230+
if (!hasName && !hasDescription && !hasPosition) {
41444231
return invalidFromErrorFactory(
41454232
errorFactory,
41464233
"payload.data must include at least one updatable field",
@@ -4154,6 +4241,13 @@ const validateSceneUpdateData = ({ data, errorFactory }) => {
41544241
);
41554242
}
41564243

4244+
if (hasDescription && !isString(data.description)) {
4245+
return invalidFromErrorFactory(
4246+
errorFactory,
4247+
"payload.data.description must be a string when provided",
4248+
);
4249+
}
4250+
41574251
if (hasPosition) {
41584252
{
41594253
const result = validateOptionalPosition({
@@ -6055,7 +6149,6 @@ const validateLayoutCreateData = ({ data, errorFactory }) => {
60556149
return result;
60566150
}
60576151
}
6058-
60596152
}
60606153
};
60616154

@@ -6095,7 +6188,6 @@ const validateLayoutUpdateData = ({ data, errorFactory }) => {
60956188
"payload.data.layoutType must be 'normal', 'dialogue', 'nvl', or 'choice' when provided",
60966189
);
60976190
}
6098-
60996191
};
61006192

61016193
const validateControlCreateData = ({ data, errorFactory }) => {
@@ -7326,6 +7418,10 @@ const COMMAND_DEFINITIONS = [
73267418
name: payload.data.name,
73277419
};
73287420

7421+
if (payload.data.description !== undefined) {
7422+
nextScene.description = payload.data.description;
7423+
}
7424+
73297425
if (nextScene.type === "scene") {
73307426
nextScene.sections = createEmptyNestedCollection();
73317427
}
@@ -7395,6 +7491,10 @@ const COMMAND_DEFINITIONS = [
73957491
nextScene.name = payload.data.name;
73967492
}
73977493

7494+
if (payload.data.description !== undefined) {
7495+
nextScene.description = payload.data.description;
7496+
}
7497+
73987498
if (payload.data.position !== undefined) {
73997499
nextScene.position = {
74007500
...(isPlainObject(nextScene.position) ? nextScene.position : {}),

tests/command-sequences.test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ test("applies a story and scenes command sequence with intermediate state snapsh
153153
parentId: "folder-prologue",
154154
data: {
155155
name: "Flashback",
156+
description: "First playable memory",
156157
position: {
157158
x: 120,
158159
y: 80,
@@ -166,6 +167,7 @@ test("applies a story and scenes command sequence with intermediate state snapsh
166167
sceneId: "scene-flashback",
167168
data: {
168169
name: "Train Station",
170+
description: "Arrival platform scene",
169171
position: {
170172
x: 320,
171173
},
@@ -196,6 +198,7 @@ test("applies a story and scenes command sequence with intermediate state snapsh
196198
id: "scene-flashback",
197199
type: "scene",
198200
name: "Flashback",
201+
description: "First playable memory",
199202
position: {
200203
x: 120,
201204
y: 80,
@@ -230,6 +233,7 @@ test("applies a story and scenes command sequence with intermediate state snapsh
230233
id: "scene-flashback",
231234
type: "scene",
232235
name: "Train Station",
236+
description: "Arrival platform scene",
233237
position: {
234238
x: 320,
235239
y: 80,
@@ -267,6 +271,7 @@ test("applies a story and scenes command sequence with intermediate state snapsh
267271
id: "scene-flashback",
268272
type: "scene",
269273
name: "Train Station",
274+
description: "Arrival platform scene",
270275
position: {
271276
x: 320,
272277
y: 80,

tests/story-and-scenes.spec.yaml

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ in:
3333
name: Wake Up
3434
position:
3535
x: 100
36-
'y': 200
36+
"y": 200
3737
sections:
3838
items: {}
3939
tree: []
@@ -109,7 +109,7 @@ out:
109109
name: Wake Up
110110
position:
111111
x: 100
112-
'y': 200
112+
"y": 200
113113
sections:
114114
items: {}
115115
tree: []
@@ -325,9 +325,10 @@ in:
325325
id: scene-a
326326
type: scene
327327
name: Intro
328+
description: Opening scene
328329
position:
329330
x: 120
330-
'y': 80
331+
"y": 80
331332
sections:
332333
items: {}
333334
tree: []
@@ -376,6 +377,7 @@ in:
376377
sceneId: scene-a
377378
data:
378379
name: Train Station
380+
description: Station platform intro
379381
position:
380382
x: 320
381383
out:
@@ -389,9 +391,10 @@ out:
389391
id: scene-a
390392
type: scene
391393
name: Train Station
394+
description: Station platform intro
392395
position:
393396
x: 320
394-
'y': 80
397+
"y": 80
395398
sections:
396399
items: {}
397400
tree: []
@@ -467,6 +470,23 @@ in:
467470
type: folder
468471
throws: payload.data.type is not allowed
469472
---
473+
case: scene.update accepts description-only patches
474+
in:
475+
- type: scene.update
476+
payload:
477+
sceneId: scene-a
478+
data:
479+
description: Updated scene summary
480+
---
481+
case: scene.create accepts description
482+
in:
483+
- type: scene.create
484+
payload:
485+
sceneId: scene-x
486+
data:
487+
name: Intro
488+
description: Opening scene
489+
---
470490
suite: validateAgainstState
471491
exportName: validateAgainstState
472492
---

0 commit comments

Comments
 (0)