@routevn/creator-model 1.10.2 → 1.10.4
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/README.md +29 -0
- package/package.json +1 -1
- package/src/model.js +217 -25
package/README.md
CHANGED
|
@@ -106,6 +106,35 @@ Design rules:
|
|
|
106
106
|
- random ids across RouteVN should use `nanoid` with the RouteVN base58 variant;
|
|
107
107
|
deterministic derived tokens such as partition hashes are a separate case
|
|
108
108
|
|
|
109
|
+
## Font Weight Metadata
|
|
110
|
+
|
|
111
|
+
Font resources may store extracted weight capabilities in three flat fields:
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
{
|
|
115
|
+
minWeight: 100,
|
|
116
|
+
defaultWeight: 400,
|
|
117
|
+
maxWeight: 900,
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The fields are optional for compatibility with existing font resources. When
|
|
122
|
+
one is present, all three are required and must satisfy
|
|
123
|
+
`1 <= minWeight <= defaultWeight <= maxWeight <= 1000`. Static fonts store the
|
|
124
|
+
same value in all three fields; variable fonts store their `fvar` `wght` axis
|
|
125
|
+
range and default.
|
|
126
|
+
|
|
127
|
+
## Text Style Font References
|
|
128
|
+
|
|
129
|
+
`textStyle.fontId` accepts either one font ID or a non-empty array of unique
|
|
130
|
+
font IDs. Both forms are persisted as supplied, and every ID must reference an
|
|
131
|
+
existing non-folder font:
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
{ fontId: "font-primary" }
|
|
135
|
+
{ fontId: ["font-primary", "font-fallback"] }
|
|
136
|
+
```
|
|
137
|
+
|
|
109
138
|
## File Structure
|
|
110
139
|
|
|
111
140
|
```text
|
package/package.json
CHANGED
package/src/model.js
CHANGED
|
@@ -42,6 +42,7 @@ const LINE_UPDATE_ACTIONS_PRESERVE_PATHS_SET = new Set(
|
|
|
42
42
|
LINE_UPDATE_ACTIONS_PRESERVE_PATHS,
|
|
43
43
|
);
|
|
44
44
|
const CURRENT_LAYOUT_SCHEMA_VERSION = 2;
|
|
45
|
+
const FONT_WEIGHT_KEYS = ["minWeight", "defaultWeight", "maxWeight"];
|
|
45
46
|
const isPositiveFiniteNumber = (value) => isFiniteNumber(value) && value > 0;
|
|
46
47
|
const normalizeLayoutSchemaVersion = (value) =>
|
|
47
48
|
Number.isInteger(value) && value >= 1 ? value : 1;
|
|
@@ -395,6 +396,13 @@ const LAYOUT_ELEMENT_BASE_TYPES = [
|
|
|
395
396
|
"container-ref-confirm-dialog-ok",
|
|
396
397
|
"container-ref-confirm-dialog-cancel",
|
|
397
398
|
];
|
|
399
|
+
const SAVE_LOAD_DATE_FORMATS = new Set([
|
|
400
|
+
"DD/MM/YYYY",
|
|
401
|
+
"MM/DD/YYYY",
|
|
402
|
+
"YYYY-MM-DD",
|
|
403
|
+
"DD MMM YYYY",
|
|
404
|
+
"YYYY年MM月DD日",
|
|
405
|
+
]);
|
|
398
406
|
export const SCHEMA_VERSION = 10;
|
|
399
407
|
const LAYOUT_CONTAINER_ELEMENT_TYPES = [
|
|
400
408
|
"folder",
|
|
@@ -2381,6 +2389,41 @@ const validateAnimationItems = ({ items, path, errorFactory }) => {
|
|
|
2381
2389
|
}
|
|
2382
2390
|
};
|
|
2383
2391
|
|
|
2392
|
+
const validateFontWeightFields = ({ value, path, errorFactory }) => {
|
|
2393
|
+
const presentKeys = FONT_WEIGHT_KEYS.filter((key) =>
|
|
2394
|
+
Object.hasOwn(value, key),
|
|
2395
|
+
);
|
|
2396
|
+
if (presentKeys.length === 0) {
|
|
2397
|
+
return;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
if (presentKeys.length !== FONT_WEIGHT_KEYS.length) {
|
|
2401
|
+
return invalidFromErrorFactory(
|
|
2402
|
+
errorFactory,
|
|
2403
|
+
`${path} must include minWeight, defaultWeight, and maxWeight together`,
|
|
2404
|
+
);
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
for (const key of FONT_WEIGHT_KEYS) {
|
|
2408
|
+
if (!isFiniteNumber(value[key]) || value[key] < 1 || value[key] > 1000) {
|
|
2409
|
+
return invalidFromErrorFactory(
|
|
2410
|
+
errorFactory,
|
|
2411
|
+
`${path}.${key} must be a finite number between 1 and 1000`,
|
|
2412
|
+
);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
if (
|
|
2417
|
+
value.minWeight > value.defaultWeight ||
|
|
2418
|
+
value.defaultWeight > value.maxWeight
|
|
2419
|
+
) {
|
|
2420
|
+
return invalidFromErrorFactory(
|
|
2421
|
+
errorFactory,
|
|
2422
|
+
`${path} must satisfy minWeight <= defaultWeight <= maxWeight`,
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
|
|
2384
2427
|
const validateFontItems = ({ items, path, errorFactory }) => {
|
|
2385
2428
|
for (const [itemId, item] of Object.entries(items)) {
|
|
2386
2429
|
const itemPath = `${path}.${itemId}`;
|
|
@@ -2406,6 +2449,7 @@ const validateFontItems = ({ items, path, errorFactory }) => {
|
|
|
2406
2449
|
"tagIds",
|
|
2407
2450
|
"fileId",
|
|
2408
2451
|
"fontFamily",
|
|
2452
|
+
...FONT_WEIGHT_KEYS,
|
|
2409
2453
|
],
|
|
2410
2454
|
path: itemPath,
|
|
2411
2455
|
errorFactory,
|
|
@@ -2469,6 +2513,17 @@ const validateFontItems = ({ items, path, errorFactory }) => {
|
|
|
2469
2513
|
`${itemPath}.fontFamily must be a non-empty string`,
|
|
2470
2514
|
);
|
|
2471
2515
|
}
|
|
2516
|
+
|
|
2517
|
+
{
|
|
2518
|
+
const result = validateFontWeightFields({
|
|
2519
|
+
value: item,
|
|
2520
|
+
path: itemPath,
|
|
2521
|
+
errorFactory,
|
|
2522
|
+
});
|
|
2523
|
+
if (result?.valid === false) {
|
|
2524
|
+
return result;
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2472
2527
|
}
|
|
2473
2528
|
}
|
|
2474
2529
|
};
|
|
@@ -3209,11 +3264,15 @@ const validateTextStyleItems = ({ items, path, errorFactory }) => {
|
|
|
3209
3264
|
}
|
|
3210
3265
|
}
|
|
3211
3266
|
|
|
3212
|
-
|
|
3213
|
-
|
|
3267
|
+
{
|
|
3268
|
+
const result = validateRequiredStringOrUniqueIdArray({
|
|
3269
|
+
value: item.fontId,
|
|
3270
|
+
path: `${itemPath}.fontId`,
|
|
3214
3271
|
errorFactory,
|
|
3215
|
-
|
|
3216
|
-
)
|
|
3272
|
+
});
|
|
3273
|
+
if (result?.valid === false) {
|
|
3274
|
+
return result;
|
|
3275
|
+
}
|
|
3217
3276
|
}
|
|
3218
3277
|
|
|
3219
3278
|
if (!isNonEmptyString(item.colorId)) {
|
|
@@ -3874,6 +3933,7 @@ const validateLayoutElementData = ({
|
|
|
3874
3933
|
"border",
|
|
3875
3934
|
"text",
|
|
3876
3935
|
"content",
|
|
3936
|
+
"dateFormat",
|
|
3877
3937
|
"textStyle",
|
|
3878
3938
|
"displaySpeed",
|
|
3879
3939
|
"revealEffect",
|
|
@@ -3966,6 +4026,27 @@ const validateLayoutElementData = ({
|
|
|
3966
4026
|
}
|
|
3967
4027
|
}
|
|
3968
4028
|
|
|
4029
|
+
if (
|
|
4030
|
+
data.dateFormat !== undefined &&
|
|
4031
|
+
!SAVE_LOAD_DATE_FORMATS.has(data.dateFormat)
|
|
4032
|
+
) {
|
|
4033
|
+
return invalidFromErrorFactory(
|
|
4034
|
+
errorFactory,
|
|
4035
|
+
`${path}.dateFormat must be one of ${Array.from(SAVE_LOAD_DATE_FORMATS).join(", ")} when provided`,
|
|
4036
|
+
);
|
|
4037
|
+
}
|
|
4038
|
+
|
|
4039
|
+
if (
|
|
4040
|
+
data.dateFormat !== undefined &&
|
|
4041
|
+
data.type !== undefined &&
|
|
4042
|
+
data.type !== "text-ref-save-load-slot-date"
|
|
4043
|
+
) {
|
|
4044
|
+
return invalidFromErrorFactory(
|
|
4045
|
+
errorFactory,
|
|
4046
|
+
`${path}.dateFormat can only be provided for save/load date elements`,
|
|
4047
|
+
);
|
|
4048
|
+
}
|
|
4049
|
+
|
|
3969
4050
|
if (data.hidden !== undefined && typeof data.hidden !== "boolean") {
|
|
3970
4051
|
return invalidFromErrorFactory(
|
|
3971
4052
|
errorFactory,
|
|
@@ -4642,6 +4723,7 @@ const validateLayoutElementItems = ({ items, path, errorFactory }) => {
|
|
|
4642
4723
|
"border",
|
|
4643
4724
|
"text",
|
|
4644
4725
|
"content",
|
|
4726
|
+
"dateFormat",
|
|
4645
4727
|
"textStyle",
|
|
4646
4728
|
"displaySpeed",
|
|
4647
4729
|
"revealEffect",
|
|
@@ -7162,15 +7244,17 @@ export const assertInvariants = ({ state }) => {
|
|
|
7162
7244
|
continue;
|
|
7163
7245
|
}
|
|
7164
7246
|
|
|
7165
|
-
const
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7247
|
+
for (const fontId of toIdArray(textStyle.fontId)) {
|
|
7248
|
+
const font = state.fonts.items[fontId];
|
|
7249
|
+
if (!isPlainObject(font) || font.type === "folder") {
|
|
7250
|
+
return invalidInvariant(
|
|
7251
|
+
"textStyle.fontId must reference an existing non-folder font",
|
|
7252
|
+
{
|
|
7253
|
+
textStyleId,
|
|
7254
|
+
fontId,
|
|
7255
|
+
},
|
|
7256
|
+
);
|
|
7257
|
+
}
|
|
7174
7258
|
}
|
|
7175
7259
|
|
|
7176
7260
|
const color = state.colors.items[textStyle.colorId];
|
|
@@ -8659,6 +8743,27 @@ const validateRequiredUniqueIdArray = ({ value, path, errorFactory }) => {
|
|
|
8659
8743
|
}
|
|
8660
8744
|
};
|
|
8661
8745
|
|
|
8746
|
+
const validateRequiredStringOrUniqueIdArray = ({
|
|
8747
|
+
value,
|
|
8748
|
+
path,
|
|
8749
|
+
errorFactory,
|
|
8750
|
+
}) => {
|
|
8751
|
+
if (isNonEmptyString(value)) {
|
|
8752
|
+
return VALID_RESULT;
|
|
8753
|
+
}
|
|
8754
|
+
|
|
8755
|
+
if (!Array.isArray(value)) {
|
|
8756
|
+
return invalidFromErrorFactory(
|
|
8757
|
+
errorFactory,
|
|
8758
|
+
`${path} must be a non-empty string or a non-empty array of strings`,
|
|
8759
|
+
);
|
|
8760
|
+
}
|
|
8761
|
+
|
|
8762
|
+
return validateRequiredUniqueIdArray({ value, path, errorFactory });
|
|
8763
|
+
};
|
|
8764
|
+
|
|
8765
|
+
const toIdArray = (value) => (Array.isArray(value) ? value : [value]);
|
|
8766
|
+
|
|
8662
8767
|
const validateOptionalUniqueIdArray = ({
|
|
8663
8768
|
value,
|
|
8664
8769
|
path,
|
|
@@ -9948,7 +10053,15 @@ const validateFontCreateData = ({ data, errorFactory }) => {
|
|
|
9948
10053
|
allowedKeys:
|
|
9949
10054
|
data.type === "folder"
|
|
9950
10055
|
? ["type", "name", "description"]
|
|
9951
|
-
: [
|
|
10056
|
+
: [
|
|
10057
|
+
"type",
|
|
10058
|
+
"name",
|
|
10059
|
+
"description",
|
|
10060
|
+
"tagIds",
|
|
10061
|
+
"fileId",
|
|
10062
|
+
"fontFamily",
|
|
10063
|
+
...FONT_WEIGHT_KEYS,
|
|
10064
|
+
],
|
|
9952
10065
|
path: "payload.data",
|
|
9953
10066
|
errorFactory,
|
|
9954
10067
|
});
|
|
@@ -9996,6 +10109,17 @@ const validateFontCreateData = ({ data, errorFactory }) => {
|
|
|
9996
10109
|
"payload.data.fontFamily must be a non-empty string",
|
|
9997
10110
|
);
|
|
9998
10111
|
}
|
|
10112
|
+
|
|
10113
|
+
{
|
|
10114
|
+
const result = validateFontWeightFields({
|
|
10115
|
+
value: data,
|
|
10116
|
+
path: "payload.data",
|
|
10117
|
+
errorFactory,
|
|
10118
|
+
});
|
|
10119
|
+
if (result?.valid === false) {
|
|
10120
|
+
return result;
|
|
10121
|
+
}
|
|
10122
|
+
}
|
|
9999
10123
|
}
|
|
10000
10124
|
};
|
|
10001
10125
|
|
|
@@ -10003,7 +10127,14 @@ const validateFontUpdateData = ({ data, errorFactory }) => {
|
|
|
10003
10127
|
{
|
|
10004
10128
|
const result = validateAllowedKeys({
|
|
10005
10129
|
value: data,
|
|
10006
|
-
allowedKeys: [
|
|
10130
|
+
allowedKeys: [
|
|
10131
|
+
"name",
|
|
10132
|
+
"description",
|
|
10133
|
+
"tagIds",
|
|
10134
|
+
"fileId",
|
|
10135
|
+
"fontFamily",
|
|
10136
|
+
...FONT_WEIGHT_KEYS,
|
|
10137
|
+
],
|
|
10007
10138
|
path: "payload.data",
|
|
10008
10139
|
errorFactory,
|
|
10009
10140
|
});
|
|
@@ -10057,6 +10188,17 @@ const validateFontUpdateData = ({ data, errorFactory }) => {
|
|
|
10057
10188
|
"payload.data.fontFamily must be a non-empty string when provided",
|
|
10058
10189
|
);
|
|
10059
10190
|
}
|
|
10191
|
+
|
|
10192
|
+
{
|
|
10193
|
+
const result = validateFontWeightFields({
|
|
10194
|
+
value: data,
|
|
10195
|
+
path: "payload.data",
|
|
10196
|
+
errorFactory,
|
|
10197
|
+
});
|
|
10198
|
+
if (result?.valid === false) {
|
|
10199
|
+
return result;
|
|
10200
|
+
}
|
|
10201
|
+
}
|
|
10060
10202
|
};
|
|
10061
10203
|
|
|
10062
10204
|
const validateFileCreateData = ({ data, errorFactory }) => {
|
|
@@ -11253,7 +11395,18 @@ const validateTextStyleUpdateData = ({ data, errorFactory }) => {
|
|
|
11253
11395
|
}
|
|
11254
11396
|
}
|
|
11255
11397
|
|
|
11256
|
-
|
|
11398
|
+
if (data.fontId !== undefined) {
|
|
11399
|
+
const result = validateRequiredStringOrUniqueIdArray({
|
|
11400
|
+
value: data.fontId,
|
|
11401
|
+
path: "payload.data.fontId",
|
|
11402
|
+
errorFactory,
|
|
11403
|
+
});
|
|
11404
|
+
if (result?.valid === false) {
|
|
11405
|
+
return result;
|
|
11406
|
+
}
|
|
11407
|
+
}
|
|
11408
|
+
|
|
11409
|
+
for (const key of ["colorId", "strokeColorId"]) {
|
|
11257
11410
|
if (data[key] !== undefined && !isNonEmptyString(data[key])) {
|
|
11258
11411
|
return invalidFromErrorFactory(
|
|
11259
11412
|
errorFactory,
|
|
@@ -12218,6 +12371,21 @@ const validateVisualElementReferenceTargets = ({
|
|
|
12218
12371
|
state,
|
|
12219
12372
|
errorFactory,
|
|
12220
12373
|
}) => {
|
|
12374
|
+
if (
|
|
12375
|
+
data.dateFormat !== undefined &&
|
|
12376
|
+
data.type !== "text-ref-save-load-slot-date"
|
|
12377
|
+
) {
|
|
12378
|
+
return invalidFromErrorFactory(
|
|
12379
|
+
errorFactory,
|
|
12380
|
+
`${ownerLabel} element dateFormat can only be provided for save/load date elements`,
|
|
12381
|
+
{
|
|
12382
|
+
[ownerIdField]: ownerId,
|
|
12383
|
+
elementId,
|
|
12384
|
+
field: "dateFormat",
|
|
12385
|
+
},
|
|
12386
|
+
);
|
|
12387
|
+
}
|
|
12388
|
+
|
|
12221
12389
|
if (data.blur !== undefined && data.type !== "sprite") {
|
|
12222
12390
|
return invalidFromErrorFactory(
|
|
12223
12391
|
errorFactory,
|
|
@@ -17376,6 +17544,11 @@ const COMMAND_DEFINITIONS = [
|
|
|
17376
17544
|
});
|
|
17377
17545
|
nextFont.fileId = payload.data.fileId;
|
|
17378
17546
|
nextFont.fontFamily = payload.data.fontFamily;
|
|
17547
|
+
for (const key of FONT_WEIGHT_KEYS) {
|
|
17548
|
+
if (payload.data[key] !== undefined) {
|
|
17549
|
+
nextFont[key] = payload.data[key];
|
|
17550
|
+
}
|
|
17551
|
+
}
|
|
17379
17552
|
}
|
|
17380
17553
|
|
|
17381
17554
|
state.fonts.items[payload.fontId] = nextFont;
|
|
@@ -17436,7 +17609,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
17436
17609
|
currentFont.type === "folder" &&
|
|
17437
17610
|
(payload.data.tagIds !== undefined ||
|
|
17438
17611
|
payload.data.fileId !== undefined ||
|
|
17439
|
-
payload.data.fontFamily !== undefined
|
|
17612
|
+
payload.data.fontFamily !== undefined ||
|
|
17613
|
+
FONT_WEIGHT_KEYS.some((key) => payload.data[key] !== undefined))
|
|
17440
17614
|
) {
|
|
17441
17615
|
return invalidPrecondition(
|
|
17442
17616
|
"folder font items cannot update font fields",
|
|
@@ -18486,16 +18660,24 @@ const COMMAND_DEFINITIONS = [
|
|
|
18486
18660
|
}
|
|
18487
18661
|
}
|
|
18488
18662
|
|
|
18489
|
-
for (const
|
|
18663
|
+
for (const fontId of toIdArray(data.fontId)) {
|
|
18664
|
+
const item = state.fonts.items[fontId];
|
|
18665
|
+
if (!isPlainObject(item) || item.type === "folder") {
|
|
18666
|
+
return invalidPrecondition(
|
|
18667
|
+
"payload.data.fontId must reference an existing non-folder font",
|
|
18668
|
+
);
|
|
18669
|
+
}
|
|
18670
|
+
}
|
|
18671
|
+
|
|
18672
|
+
for (const field of ["colorId", "strokeColorId"]) {
|
|
18490
18673
|
if (data[field] === undefined) {
|
|
18491
18674
|
continue;
|
|
18492
18675
|
}
|
|
18493
18676
|
|
|
18494
|
-
const
|
|
18495
|
-
const item = state[collectionKey].items[data[field]];
|
|
18677
|
+
const item = state.colors.items[data[field]];
|
|
18496
18678
|
if (!isPlainObject(item) || item.type === "folder") {
|
|
18497
18679
|
return invalidPrecondition(
|
|
18498
|
-
`payload.data.${field} must reference an existing non-folder
|
|
18680
|
+
`payload.data.${field} must reference an existing non-folder color`,
|
|
18499
18681
|
);
|
|
18500
18682
|
}
|
|
18501
18683
|
}
|
|
@@ -18538,16 +18720,26 @@ const COMMAND_DEFINITIONS = [
|
|
|
18538
18720
|
}
|
|
18539
18721
|
}
|
|
18540
18722
|
|
|
18541
|
-
|
|
18723
|
+
if (payload.data.fontId !== undefined) {
|
|
18724
|
+
for (const fontId of toIdArray(payload.data.fontId)) {
|
|
18725
|
+
const item = state.fonts.items[fontId];
|
|
18726
|
+
if (!isPlainObject(item) || item.type === "folder") {
|
|
18727
|
+
return invalidPrecondition(
|
|
18728
|
+
"payload.data.fontId must reference an existing non-folder font",
|
|
18729
|
+
);
|
|
18730
|
+
}
|
|
18731
|
+
}
|
|
18732
|
+
}
|
|
18733
|
+
|
|
18734
|
+
for (const field of ["colorId", "strokeColorId"]) {
|
|
18542
18735
|
if (payload.data[field] === undefined) {
|
|
18543
18736
|
continue;
|
|
18544
18737
|
}
|
|
18545
18738
|
|
|
18546
|
-
const
|
|
18547
|
-
const item = state[collectionKey].items[payload.data[field]];
|
|
18739
|
+
const item = state.colors.items[payload.data[field]];
|
|
18548
18740
|
if (!isPlainObject(item) || item.type === "folder") {
|
|
18549
18741
|
return invalidPrecondition(
|
|
18550
|
-
`payload.data.${field} must reference an existing non-folder
|
|
18742
|
+
`payload.data.${field} must reference an existing non-folder color`,
|
|
18551
18743
|
);
|
|
18552
18744
|
}
|
|
18553
18745
|
}
|