@routevn/creator-model 1.10.3 → 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 +172 -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;
|
|
@@ -2388,6 +2389,41 @@ const validateAnimationItems = ({ items, path, errorFactory }) => {
|
|
|
2388
2389
|
}
|
|
2389
2390
|
};
|
|
2390
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
|
+
|
|
2391
2427
|
const validateFontItems = ({ items, path, errorFactory }) => {
|
|
2392
2428
|
for (const [itemId, item] of Object.entries(items)) {
|
|
2393
2429
|
const itemPath = `${path}.${itemId}`;
|
|
@@ -2413,6 +2449,7 @@ const validateFontItems = ({ items, path, errorFactory }) => {
|
|
|
2413
2449
|
"tagIds",
|
|
2414
2450
|
"fileId",
|
|
2415
2451
|
"fontFamily",
|
|
2452
|
+
...FONT_WEIGHT_KEYS,
|
|
2416
2453
|
],
|
|
2417
2454
|
path: itemPath,
|
|
2418
2455
|
errorFactory,
|
|
@@ -2476,6 +2513,17 @@ const validateFontItems = ({ items, path, errorFactory }) => {
|
|
|
2476
2513
|
`${itemPath}.fontFamily must be a non-empty string`,
|
|
2477
2514
|
);
|
|
2478
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
|
+
}
|
|
2479
2527
|
}
|
|
2480
2528
|
}
|
|
2481
2529
|
};
|
|
@@ -3216,11 +3264,15 @@ const validateTextStyleItems = ({ items, path, errorFactory }) => {
|
|
|
3216
3264
|
}
|
|
3217
3265
|
}
|
|
3218
3266
|
|
|
3219
|
-
|
|
3220
|
-
|
|
3267
|
+
{
|
|
3268
|
+
const result = validateRequiredStringOrUniqueIdArray({
|
|
3269
|
+
value: item.fontId,
|
|
3270
|
+
path: `${itemPath}.fontId`,
|
|
3221
3271
|
errorFactory,
|
|
3222
|
-
|
|
3223
|
-
)
|
|
3272
|
+
});
|
|
3273
|
+
if (result?.valid === false) {
|
|
3274
|
+
return result;
|
|
3275
|
+
}
|
|
3224
3276
|
}
|
|
3225
3277
|
|
|
3226
3278
|
if (!isNonEmptyString(item.colorId)) {
|
|
@@ -7192,15 +7244,17 @@ export const assertInvariants = ({ state }) => {
|
|
|
7192
7244
|
continue;
|
|
7193
7245
|
}
|
|
7194
7246
|
|
|
7195
|
-
const
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
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
|
+
}
|
|
7204
7258
|
}
|
|
7205
7259
|
|
|
7206
7260
|
const color = state.colors.items[textStyle.colorId];
|
|
@@ -8689,6 +8743,27 @@ const validateRequiredUniqueIdArray = ({ value, path, errorFactory }) => {
|
|
|
8689
8743
|
}
|
|
8690
8744
|
};
|
|
8691
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
|
+
|
|
8692
8767
|
const validateOptionalUniqueIdArray = ({
|
|
8693
8768
|
value,
|
|
8694
8769
|
path,
|
|
@@ -9978,7 +10053,15 @@ const validateFontCreateData = ({ data, errorFactory }) => {
|
|
|
9978
10053
|
allowedKeys:
|
|
9979
10054
|
data.type === "folder"
|
|
9980
10055
|
? ["type", "name", "description"]
|
|
9981
|
-
: [
|
|
10056
|
+
: [
|
|
10057
|
+
"type",
|
|
10058
|
+
"name",
|
|
10059
|
+
"description",
|
|
10060
|
+
"tagIds",
|
|
10061
|
+
"fileId",
|
|
10062
|
+
"fontFamily",
|
|
10063
|
+
...FONT_WEIGHT_KEYS,
|
|
10064
|
+
],
|
|
9982
10065
|
path: "payload.data",
|
|
9983
10066
|
errorFactory,
|
|
9984
10067
|
});
|
|
@@ -10026,6 +10109,17 @@ const validateFontCreateData = ({ data, errorFactory }) => {
|
|
|
10026
10109
|
"payload.data.fontFamily must be a non-empty string",
|
|
10027
10110
|
);
|
|
10028
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
|
+
}
|
|
10029
10123
|
}
|
|
10030
10124
|
};
|
|
10031
10125
|
|
|
@@ -10033,7 +10127,14 @@ const validateFontUpdateData = ({ data, errorFactory }) => {
|
|
|
10033
10127
|
{
|
|
10034
10128
|
const result = validateAllowedKeys({
|
|
10035
10129
|
value: data,
|
|
10036
|
-
allowedKeys: [
|
|
10130
|
+
allowedKeys: [
|
|
10131
|
+
"name",
|
|
10132
|
+
"description",
|
|
10133
|
+
"tagIds",
|
|
10134
|
+
"fileId",
|
|
10135
|
+
"fontFamily",
|
|
10136
|
+
...FONT_WEIGHT_KEYS,
|
|
10137
|
+
],
|
|
10037
10138
|
path: "payload.data",
|
|
10038
10139
|
errorFactory,
|
|
10039
10140
|
});
|
|
@@ -10087,6 +10188,17 @@ const validateFontUpdateData = ({ data, errorFactory }) => {
|
|
|
10087
10188
|
"payload.data.fontFamily must be a non-empty string when provided",
|
|
10088
10189
|
);
|
|
10089
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
|
+
}
|
|
10090
10202
|
};
|
|
10091
10203
|
|
|
10092
10204
|
const validateFileCreateData = ({ data, errorFactory }) => {
|
|
@@ -11283,7 +11395,18 @@ const validateTextStyleUpdateData = ({ data, errorFactory }) => {
|
|
|
11283
11395
|
}
|
|
11284
11396
|
}
|
|
11285
11397
|
|
|
11286
|
-
|
|
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"]) {
|
|
11287
11410
|
if (data[key] !== undefined && !isNonEmptyString(data[key])) {
|
|
11288
11411
|
return invalidFromErrorFactory(
|
|
11289
11412
|
errorFactory,
|
|
@@ -17421,6 +17544,11 @@ const COMMAND_DEFINITIONS = [
|
|
|
17421
17544
|
});
|
|
17422
17545
|
nextFont.fileId = payload.data.fileId;
|
|
17423
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
|
+
}
|
|
17424
17552
|
}
|
|
17425
17553
|
|
|
17426
17554
|
state.fonts.items[payload.fontId] = nextFont;
|
|
@@ -17481,7 +17609,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
17481
17609
|
currentFont.type === "folder" &&
|
|
17482
17610
|
(payload.data.tagIds !== undefined ||
|
|
17483
17611
|
payload.data.fileId !== undefined ||
|
|
17484
|
-
payload.data.fontFamily !== undefined
|
|
17612
|
+
payload.data.fontFamily !== undefined ||
|
|
17613
|
+
FONT_WEIGHT_KEYS.some((key) => payload.data[key] !== undefined))
|
|
17485
17614
|
) {
|
|
17486
17615
|
return invalidPrecondition(
|
|
17487
17616
|
"folder font items cannot update font fields",
|
|
@@ -18531,16 +18660,24 @@ const COMMAND_DEFINITIONS = [
|
|
|
18531
18660
|
}
|
|
18532
18661
|
}
|
|
18533
18662
|
|
|
18534
|
-
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"]) {
|
|
18535
18673
|
if (data[field] === undefined) {
|
|
18536
18674
|
continue;
|
|
18537
18675
|
}
|
|
18538
18676
|
|
|
18539
|
-
const
|
|
18540
|
-
const item = state[collectionKey].items[data[field]];
|
|
18677
|
+
const item = state.colors.items[data[field]];
|
|
18541
18678
|
if (!isPlainObject(item) || item.type === "folder") {
|
|
18542
18679
|
return invalidPrecondition(
|
|
18543
|
-
`payload.data.${field} must reference an existing non-folder
|
|
18680
|
+
`payload.data.${field} must reference an existing non-folder color`,
|
|
18544
18681
|
);
|
|
18545
18682
|
}
|
|
18546
18683
|
}
|
|
@@ -18583,16 +18720,26 @@ const COMMAND_DEFINITIONS = [
|
|
|
18583
18720
|
}
|
|
18584
18721
|
}
|
|
18585
18722
|
|
|
18586
|
-
|
|
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"]) {
|
|
18587
18735
|
if (payload.data[field] === undefined) {
|
|
18588
18736
|
continue;
|
|
18589
18737
|
}
|
|
18590
18738
|
|
|
18591
|
-
const
|
|
18592
|
-
const item = state[collectionKey].items[payload.data[field]];
|
|
18739
|
+
const item = state.colors.items[payload.data[field]];
|
|
18593
18740
|
if (!isPlainObject(item) || item.type === "folder") {
|
|
18594
18741
|
return invalidPrecondition(
|
|
18595
|
-
`payload.data.${field} must reference an existing non-folder
|
|
18742
|
+
`payload.data.${field} must reference an existing non-folder color`,
|
|
18596
18743
|
);
|
|
18597
18744
|
}
|
|
18598
18745
|
}
|