@yaoxiu/marketing-dsl 1.0.0 → 1.2.0
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 +16 -9
- package/dist/index.cjs +130 -22
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +130 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ import { createRuntime } from '@yaoxiu/marketing-dsl';
|
|
|
47
47
|
|
|
48
48
|
const runtime = createRuntime(dsl, {
|
|
49
49
|
user: { shopName: '麦爆了旗舰店', version: '专业版' },
|
|
50
|
-
sources: { listRenewTiers:
|
|
50
|
+
sources: { listRenewTiers: params => api.getTiers(params) },
|
|
51
51
|
handlers: { setNeverRemind: () => localStorage.setItem('never', '1') },
|
|
52
52
|
emit: (event, payload) => console.log(event, payload),
|
|
53
53
|
});
|
|
@@ -56,11 +56,18 @@ const unsubscribe = runtime.subscribe(() => {
|
|
|
56
56
|
render(runtime.getTree()); // 每次通知后重新取树画一遍
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
+
// 用户信息异步到了、或者要切换编辑态,用 update 就地更新。
|
|
60
|
+
// 不要为此重建 runtime——重建会重新拉数据源、重放曝光埋点,
|
|
61
|
+
// 还会把用户已经切到的 tab 打回默认值。
|
|
62
|
+
runtime.update({ user: { shopName: '麦爆了旗舰店' } });
|
|
63
|
+
|
|
59
64
|
// 卸载时必须调,否则倒计时定时器不会停
|
|
60
65
|
runtime.destroy();
|
|
61
66
|
unsubscribe();
|
|
62
67
|
```
|
|
63
68
|
|
|
69
|
+
`update` 能改 `user` / `handlers` / `editMode`。换 `dsl` 本身要重建 runtime。
|
|
70
|
+
|
|
64
71
|
## 渲染树
|
|
65
72
|
|
|
66
73
|
`getTree()` 返回的是纯数据,所有解释工作都已经做完:插值算完了、条件判断过了、循环展开了、样式转成 CSS 了、坐标换算好了、动作绑好了。
|
|
@@ -97,14 +104,14 @@ unsubscribe();
|
|
|
97
104
|
|
|
98
105
|
这几条是设计红线,不会因为「就差一点点」而放开:
|
|
99
106
|
|
|
100
|
-
| 边界
|
|
101
|
-
|
|
|
102
|
-
| 不执行任意代码
|
|
103
|
-
| 不能沿原型链逃逸
|
|
104
|
-
| 不能通过样式作恶
|
|
105
|
-
| 不能执行 `javascript:` | 链接只允许 `http` / `https` / `mailto` / `tel` 和相对路径
|
|
106
|
-
| 不能打任意接口
|
|
107
|
-
| 不能调任意方法
|
|
107
|
+
| 边界 | 做法 |
|
|
108
|
+
| ---------------------- | -------------------------------------------------------------------- |
|
|
109
|
+
| 不执行任意代码 | 手写解析器,不用 `eval` / `new Function`;不支持函数调用和赋值 |
|
|
110
|
+
| 不能沿原型链逃逸 | `constructor` / `__proto__` / `prototype` 一律读不到,函数值也读不到 |
|
|
111
|
+
| 不能通过样式作恶 | 只有白名单里的属性会输出,`position: fixed` 这类会被丢弃 |
|
|
112
|
+
| 不能执行 `javascript:` | 链接只允许 `http` / `https` / `mailto` / `tel` 和相对路径 |
|
|
113
|
+
| 不能打任意接口 | 数据源用注册名引用,配置里写不了 URL |
|
|
114
|
+
| 不能调任意方法 | `call` 只能调宿主注册在 `handlers` 里的方法 |
|
|
108
115
|
|
|
109
116
|
## 完整 DSL 语法
|
|
110
117
|
|
package/dist/index.cjs
CHANGED
|
@@ -326,9 +326,11 @@ function safeImageUrl(input) {
|
|
|
326
326
|
|
|
327
327
|
// src/actions.ts
|
|
328
328
|
function createDispatcher(options) {
|
|
329
|
-
const { setState, emit, openView, closeTop, closeAll
|
|
329
|
+
const { setState, emit, openView, closeTop, closeAll } = options;
|
|
330
330
|
function dispatch(action, context) {
|
|
331
331
|
if (!action || !action.type) return;
|
|
332
|
+
const handlers = options.handlers || {};
|
|
333
|
+
const editMode = !!options.editMode;
|
|
332
334
|
switch (action.type) {
|
|
333
335
|
case "sequence":
|
|
334
336
|
(action.actions || []).forEach((item) => dispatch(item, context));
|
|
@@ -608,7 +610,13 @@ function resolveTree(input) {
|
|
|
608
610
|
);
|
|
609
611
|
const countdowns = { endTimes: [], precision: "s" };
|
|
610
612
|
const renderLayers = ready ? layers.map(
|
|
611
|
-
(item, index) => resolveLayer(
|
|
613
|
+
(item, index) => resolveLayer(
|
|
614
|
+
item.name,
|
|
615
|
+
item.view,
|
|
616
|
+
index === layers.length - 1,
|
|
617
|
+
input,
|
|
618
|
+
countdowns
|
|
619
|
+
)
|
|
612
620
|
) : [];
|
|
613
621
|
return {
|
|
614
622
|
ready: ready && layers.length > 0,
|
|
@@ -627,7 +635,9 @@ function resolveLayer(name, view, isTop, input, countdowns) {
|
|
|
627
635
|
const rootLayout = stage.layout === "flow" ? "flow" : "absolute";
|
|
628
636
|
const maskClosable = !!stage.maskClosable;
|
|
629
637
|
const onMaskClick = maskClosable ? () => closeTop("mask") : void 0;
|
|
630
|
-
const nodes = (view.nodes || []).map(
|
|
638
|
+
const nodes = (view.nodes || []).map(
|
|
639
|
+
(node, index) => resolveNode(node, `${name}-${index}`, rootLayout, context, input, countdowns)
|
|
640
|
+
).filter((el) => !!el);
|
|
631
641
|
return {
|
|
632
642
|
name,
|
|
633
643
|
type: view.type,
|
|
@@ -690,8 +700,12 @@ function resolveStageStyle(stage, isPopup, context) {
|
|
|
690
700
|
base,
|
|
691
701
|
{
|
|
692
702
|
// width / height 支持数字(px)、'100%' / 'auto' / calc(...) 等相对单位,也支持 {{ }}
|
|
693
|
-
width: toLength(
|
|
694
|
-
|
|
703
|
+
width: toLength(
|
|
704
|
+
interpolate(stage.width === void 0 ? 320 : stage.width, context)
|
|
705
|
+
),
|
|
706
|
+
height: toLength(
|
|
707
|
+
interpolate(stage.height === void 0 ? 400 : stage.height, context)
|
|
708
|
+
)
|
|
695
709
|
},
|
|
696
710
|
// stage.style 也走一遍插值,这样切 tab 时弹窗本身的背景能跟着变
|
|
697
711
|
toCssStyle(interpolateDeep(stage.style, context))
|
|
@@ -737,14 +751,21 @@ function resolveCloseButton(config, layerName, closeTop) {
|
|
|
737
751
|
return {
|
|
738
752
|
key: `${layerName}-close`,
|
|
739
753
|
tag: "div",
|
|
754
|
+
className: "dsl-close",
|
|
740
755
|
style,
|
|
741
756
|
onClick,
|
|
742
757
|
children: [
|
|
743
758
|
{
|
|
744
759
|
key: `${layerName}-close-img`,
|
|
745
760
|
tag: "img",
|
|
761
|
+
className: "dsl-close-img",
|
|
746
762
|
src: image,
|
|
747
|
-
style: {
|
|
763
|
+
style: {
|
|
764
|
+
width: "100%",
|
|
765
|
+
height: "100%",
|
|
766
|
+
objectFit: "contain",
|
|
767
|
+
display: "block"
|
|
768
|
+
}
|
|
748
769
|
}
|
|
749
770
|
]
|
|
750
771
|
};
|
|
@@ -752,12 +773,27 @@ function resolveCloseButton(config, layerName, closeTop) {
|
|
|
752
773
|
return {
|
|
753
774
|
key: `${layerName}-close`,
|
|
754
775
|
tag: "div",
|
|
776
|
+
className: "dsl-close",
|
|
755
777
|
style,
|
|
756
778
|
onClick,
|
|
757
779
|
text: config.icon || "\xD7"
|
|
758
780
|
};
|
|
759
781
|
}
|
|
760
782
|
var BASE_STYLE = { boxSizing: "border-box" };
|
|
783
|
+
var NODE_CLASS = {
|
|
784
|
+
box: "dsl-box",
|
|
785
|
+
flex: "dsl-flex",
|
|
786
|
+
repeat: "dsl-flex",
|
|
787
|
+
tabs: "dsl-tabs",
|
|
788
|
+
image: "dsl-image",
|
|
789
|
+
text: "dsl-text",
|
|
790
|
+
button: "dsl-button",
|
|
791
|
+
countdown: "dsl-flex"
|
|
792
|
+
};
|
|
793
|
+
function nodeClass(type, clickable) {
|
|
794
|
+
const base = `dsl-node ${NODE_CLASS[type] || ""}`.trim();
|
|
795
|
+
return clickable ? `${base} is-clickable` : base;
|
|
796
|
+
}
|
|
761
797
|
function resolveNode(node, key, layout, context, input, countdowns) {
|
|
762
798
|
if (!node || typeof node !== "object") return void 0;
|
|
763
799
|
if (node.visibleWhen && !evaluate(node.visibleWhen, context)) return void 0;
|
|
@@ -776,14 +812,23 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
776
812
|
return {
|
|
777
813
|
key,
|
|
778
814
|
tag: "div",
|
|
815
|
+
className: nodeClass("box", !!onClick),
|
|
779
816
|
style: Object.assign({ position: "relative" }, style),
|
|
780
817
|
onClick,
|
|
781
|
-
children: resolveChildren(
|
|
818
|
+
children: resolveChildren(
|
|
819
|
+
node.children,
|
|
820
|
+
key,
|
|
821
|
+
"absolute",
|
|
822
|
+
context,
|
|
823
|
+
input,
|
|
824
|
+
countdowns
|
|
825
|
+
)
|
|
782
826
|
};
|
|
783
827
|
case "flex":
|
|
784
828
|
return {
|
|
785
829
|
key,
|
|
786
830
|
tag: "div",
|
|
831
|
+
className: nodeClass("flex", !!onClick),
|
|
787
832
|
style: Object.assign({ display: "flex" }, style),
|
|
788
833
|
onClick,
|
|
789
834
|
children: resolveChildren(node.children, key, "flow", context, input, countdowns)
|
|
@@ -792,6 +837,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
792
837
|
return {
|
|
793
838
|
key,
|
|
794
839
|
tag: "div",
|
|
840
|
+
className: nodeClass("repeat", !!onClick),
|
|
795
841
|
style: Object.assign({ display: "flex" }, style),
|
|
796
842
|
onClick,
|
|
797
843
|
children: resolveRepeat(node, key, context, input, countdowns)
|
|
@@ -800,6 +846,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
800
846
|
return {
|
|
801
847
|
key,
|
|
802
848
|
tag: "div",
|
|
849
|
+
className: nodeClass("tabs", false),
|
|
803
850
|
style: Object.assign({ display: "flex" }, style),
|
|
804
851
|
children: resolveTabs(node, key, context, input)
|
|
805
852
|
};
|
|
@@ -807,6 +854,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
807
854
|
return {
|
|
808
855
|
key,
|
|
809
856
|
tag: "img",
|
|
857
|
+
className: nodeClass("image", !!onClick),
|
|
810
858
|
src: safeImageUrl(interpolate(node.src, context)),
|
|
811
859
|
style: Object.assign({ display: "block", objectFit: "cover" }, style),
|
|
812
860
|
onClick
|
|
@@ -815,6 +863,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
815
863
|
return {
|
|
816
864
|
key,
|
|
817
865
|
tag: "div",
|
|
866
|
+
className: nodeClass("text", !!onClick),
|
|
818
867
|
style: Object.assign({ wordBreak: "break-word" }, style),
|
|
819
868
|
text: toText(interpolate(node.content, context)),
|
|
820
869
|
onClick
|
|
@@ -823,6 +872,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
823
872
|
return {
|
|
824
873
|
key,
|
|
825
874
|
tag: "div",
|
|
875
|
+
className: nodeClass("button", true),
|
|
826
876
|
style: Object.assign(
|
|
827
877
|
{
|
|
828
878
|
display: "flex",
|
|
@@ -857,7 +907,14 @@ function resolveRepeat(node, key, context, input, countdowns) {
|
|
|
857
907
|
[node.itemName || "item"]: item,
|
|
858
908
|
[node.indexName || "index"]: index
|
|
859
909
|
});
|
|
860
|
-
return resolveNode(
|
|
910
|
+
return resolveNode(
|
|
911
|
+
node.template,
|
|
912
|
+
`${key}-${index}`,
|
|
913
|
+
"flow",
|
|
914
|
+
itemContext,
|
|
915
|
+
input,
|
|
916
|
+
countdowns
|
|
917
|
+
);
|
|
861
918
|
}).filter((el) => !!el);
|
|
862
919
|
}
|
|
863
920
|
function resolveTabs(node, key, context, input) {
|
|
@@ -873,6 +930,7 @@ function resolveTabs(node, key, context, input) {
|
|
|
873
930
|
return {
|
|
874
931
|
key: `${key}-${index}`,
|
|
875
932
|
tag: "div",
|
|
933
|
+
className: index === active ? "dsl-tab-item is-active" : "dsl-tab-item",
|
|
876
934
|
style: Object.assign(
|
|
877
935
|
{
|
|
878
936
|
display: "flex",
|
|
@@ -909,6 +967,7 @@ function resolveCountdown(node, key, style, context, input, countdowns) {
|
|
|
909
967
|
return {
|
|
910
968
|
key,
|
|
911
969
|
tag: "div",
|
|
970
|
+
className: "dsl-node dsl-text dsl-countdown",
|
|
912
971
|
style: Object.assign({ wordBreak: "break-word" }, style),
|
|
913
972
|
text: node.endText || "\u5DF2\u7ED3\u675F"
|
|
914
973
|
};
|
|
@@ -917,6 +976,7 @@ function resolveCountdown(node, key, style, context, input, countdowns) {
|
|
|
917
976
|
return {
|
|
918
977
|
key,
|
|
919
978
|
tag: "div",
|
|
979
|
+
className: "dsl-node dsl-text dsl-countdown",
|
|
920
980
|
style: Object.assign({ wordBreak: "break-word" }, style),
|
|
921
981
|
text: formatParts(parts, node.format)
|
|
922
982
|
};
|
|
@@ -925,8 +985,16 @@ function resolveCountdown(node, key, style, context, input, countdowns) {
|
|
|
925
985
|
return {
|
|
926
986
|
key,
|
|
927
987
|
tag: "div",
|
|
988
|
+
className: "dsl-node dsl-flex dsl-countdown",
|
|
928
989
|
style: Object.assign({ display: "flex" }, style),
|
|
929
|
-
children: resolveChildren(
|
|
990
|
+
children: resolveChildren(
|
|
991
|
+
node.children,
|
|
992
|
+
key,
|
|
993
|
+
"flow",
|
|
994
|
+
childContext,
|
|
995
|
+
input,
|
|
996
|
+
countdowns
|
|
997
|
+
)
|
|
930
998
|
};
|
|
931
999
|
}
|
|
932
1000
|
function toText(value) {
|
|
@@ -937,7 +1005,12 @@ function toText(value) {
|
|
|
937
1005
|
var noopEmit = () => {
|
|
938
1006
|
};
|
|
939
1007
|
function createRuntime(dsl, options = {}) {
|
|
940
|
-
const {
|
|
1008
|
+
const { sources = {}, emit = noopEmit } = options;
|
|
1009
|
+
const live = {
|
|
1010
|
+
user: options.user || {},
|
|
1011
|
+
handlers: options.handlers || {},
|
|
1012
|
+
editMode: !!options.editMode
|
|
1013
|
+
};
|
|
941
1014
|
const normalized = normalizeViews(dsl);
|
|
942
1015
|
let state = Object.assign({}, dsl.state || {});
|
|
943
1016
|
let resolvedData = {};
|
|
@@ -953,7 +1026,10 @@ function createRuntime(dsl, options = {}) {
|
|
|
953
1026
|
};
|
|
954
1027
|
const ticker = createTicker({ onTick: notify });
|
|
955
1028
|
function buildContext() {
|
|
956
|
-
const context = Object.assign({}, resolvedData, {
|
|
1029
|
+
const context = Object.assign({}, resolvedData, {
|
|
1030
|
+
state,
|
|
1031
|
+
user: live.user
|
|
1032
|
+
});
|
|
957
1033
|
const derived = dsl.derived || {};
|
|
958
1034
|
Object.keys(derived).forEach((name) => {
|
|
959
1035
|
const config = derived[name] || {};
|
|
@@ -1014,8 +1090,12 @@ function createRuntime(dsl, options = {}) {
|
|
|
1014
1090
|
// 配置里 close 可以自带 reason(如 'never-remind'),要透传给埋点,不能吞掉
|
|
1015
1091
|
closeTop: (reason) => closeTop(reason),
|
|
1016
1092
|
closeAll: (reason) => closeAll(reason),
|
|
1017
|
-
handlers
|
|
1018
|
-
|
|
1093
|
+
get handlers() {
|
|
1094
|
+
return live.handlers;
|
|
1095
|
+
},
|
|
1096
|
+
get editMode() {
|
|
1097
|
+
return live.editMode;
|
|
1098
|
+
}
|
|
1019
1099
|
});
|
|
1020
1100
|
function fireLifecycle(viewName, hook, extra) {
|
|
1021
1101
|
const view = normalized.views[viewName];
|
|
@@ -1042,7 +1122,7 @@ function createRuntime(dsl, options = {}) {
|
|
|
1042
1122
|
return;
|
|
1043
1123
|
}
|
|
1044
1124
|
pending.push(
|
|
1045
|
-
Promise.resolve(source(ref.params || {}, user)).then((result) => {
|
|
1125
|
+
Promise.resolve(source(ref.params || {}, live.user)).then((result) => {
|
|
1046
1126
|
resolved[key] = result;
|
|
1047
1127
|
})
|
|
1048
1128
|
);
|
|
@@ -1080,6 +1160,12 @@ function createRuntime(dsl, options = {}) {
|
|
|
1080
1160
|
}
|
|
1081
1161
|
return {
|
|
1082
1162
|
getTree,
|
|
1163
|
+
update(patch) {
|
|
1164
|
+
if (patch.user !== void 0) live.user = patch.user;
|
|
1165
|
+
if (patch.handlers !== void 0) live.handlers = patch.handlers;
|
|
1166
|
+
if (patch.editMode !== void 0) live.editMode = patch.editMode;
|
|
1167
|
+
notify();
|
|
1168
|
+
},
|
|
1083
1169
|
subscribe(listener) {
|
|
1084
1170
|
listeners.add(listener);
|
|
1085
1171
|
return () => listeners.delete(listener);
|
|
@@ -1158,7 +1244,10 @@ function validate(dsl) {
|
|
|
1158
1244
|
}
|
|
1159
1245
|
const doc = dsl;
|
|
1160
1246
|
if (doc.version !== DSL_VERSION) {
|
|
1161
|
-
add(
|
|
1247
|
+
add(
|
|
1248
|
+
"version",
|
|
1249
|
+
`version \u5FC5\u987B\u662F ${DSL_VERSION}\uFF0C\u5F53\u524D\u4E3A ${JSON.stringify(doc.version)}`
|
|
1250
|
+
);
|
|
1162
1251
|
}
|
|
1163
1252
|
const dataKeys = validateData(doc.data, add);
|
|
1164
1253
|
const stateKeys = validateState(doc.state, add);
|
|
@@ -1258,7 +1347,10 @@ function validateStage(stage, add, warn, ctx, prefix) {
|
|
|
1258
1347
|
if (stage.onShow !== void 0) validateAction(stage.onShow, at("onShow"), ctx);
|
|
1259
1348
|
if (stage.onClose !== void 0) validateAction(stage.onClose, at("onClose"), ctx);
|
|
1260
1349
|
if (stage.height === "auto" && stage.layout !== "flow") {
|
|
1261
|
-
add(
|
|
1350
|
+
add(
|
|
1351
|
+
"stage.height",
|
|
1352
|
+
"height: 'auto' \u9700\u8981\u540C\u65F6\u8BBE\u7F6E stage.layout: 'flow'\uFF0C\u5426\u5219\u9AD8\u5EA6\u4F1A\u584C\u6210 0"
|
|
1353
|
+
);
|
|
1262
1354
|
}
|
|
1263
1355
|
}
|
|
1264
1356
|
function validateCloseButton(config, add, warn) {
|
|
@@ -1272,7 +1364,10 @@ function validateCloseButton(config, add, warn) {
|
|
|
1272
1364
|
}
|
|
1273
1365
|
if (config.offset !== void 0) {
|
|
1274
1366
|
if (!Array.isArray(config.offset) || config.offset.length !== 2) {
|
|
1275
|
-
add(
|
|
1367
|
+
add(
|
|
1368
|
+
"stage.closeButton.offset",
|
|
1369
|
+
"offset \u5FC5\u987B\u662F [x, y] \u4E24\u9879\u6570\u7EC4\uFF0C\u8D1F\u503C\u4F1A\u628A\u6309\u94AE\u79FB\u5230\u5F39\u7A97\u5916\u9762"
|
|
1370
|
+
);
|
|
1276
1371
|
} else {
|
|
1277
1372
|
config.offset.forEach((value, index) => {
|
|
1278
1373
|
if (!checkLength(value)) {
|
|
@@ -1293,7 +1388,10 @@ function validateCloseButton(config, add, warn) {
|
|
|
1293
1388
|
if (config.style) {
|
|
1294
1389
|
Object.keys(config.style).forEach((key) => {
|
|
1295
1390
|
if (ALLOWED_STYLE_KEYS.indexOf(key) === -1) {
|
|
1296
|
-
warn(
|
|
1391
|
+
warn(
|
|
1392
|
+
`stage.closeButton.style.${key}`,
|
|
1393
|
+
`\u6837\u5F0F\u5C5E\u6027 "${key}" \u4E0D\u5728\u767D\u540D\u5355\u5185\uFF0C\u6E32\u67D3\u65F6\u4F1A\u88AB\u5FFD\u7565`
|
|
1394
|
+
);
|
|
1297
1395
|
}
|
|
1298
1396
|
});
|
|
1299
1397
|
}
|
|
@@ -1364,7 +1462,10 @@ function validateNode(node, path, layout, ctx) {
|
|
|
1364
1462
|
} else {
|
|
1365
1463
|
node.rect.forEach((value, index) => {
|
|
1366
1464
|
if (value !== void 0 && value !== null && !checkLength(value)) {
|
|
1367
|
-
add(
|
|
1465
|
+
add(
|
|
1466
|
+
`${path}.rect[${index}]`,
|
|
1467
|
+
`"${value}" \u4E0D\u662F\u5408\u6CD5\u957F\u5EA6\uFF0C\u652F\u6301\u6570\u5B57(px) / '100%' / 'auto' \u7B49`
|
|
1468
|
+
);
|
|
1368
1469
|
}
|
|
1369
1470
|
});
|
|
1370
1471
|
}
|
|
@@ -1382,7 +1483,10 @@ function validateNode(node, path, layout, ctx) {
|
|
|
1382
1483
|
);
|
|
1383
1484
|
}
|
|
1384
1485
|
if (key === "aspectRatio" && !checkAspectRatio(node.style[key])) {
|
|
1385
|
-
add(
|
|
1486
|
+
add(
|
|
1487
|
+
`${path}.style.aspectRatio`,
|
|
1488
|
+
"aspectRatio \u8981\u5199\u6210 '750 / 200' \u6216 1.5 \u8FD9\u6837\u7684\u5BBD\u9AD8\u6BD4"
|
|
1489
|
+
);
|
|
1386
1490
|
}
|
|
1387
1491
|
});
|
|
1388
1492
|
}
|
|
@@ -1412,7 +1516,8 @@ function validateNodeByType(node, path, ctx) {
|
|
|
1412
1516
|
if (typeof node.bind !== "string") {
|
|
1413
1517
|
add(`${path}.bind`, "tabs \u5FC5\u987B\u63D0\u4F9B bind\uFF08data \u4E2D\u7684\u6570\u7EC4\u5B57\u6BB5\u540D\uFF09");
|
|
1414
1518
|
}
|
|
1415
|
-
if (typeof node.stateKey !== "string")
|
|
1519
|
+
if (typeof node.stateKey !== "string")
|
|
1520
|
+
add(`${path}.stateKey`, "tabs \u5FC5\u987B\u63D0\u4F9B stateKey");
|
|
1416
1521
|
if (node.labelField !== void 0 && typeof node.labelField !== "string") {
|
|
1417
1522
|
add(`${path}.labelField`, "labelField \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
|
|
1418
1523
|
}
|
|
@@ -1478,7 +1583,10 @@ function validateAction(action, path, ctx) {
|
|
|
1478
1583
|
add(`${path}.view`, `views \u91CC\u4E0D\u5B58\u5728\u89C6\u56FE "${action.view}"`);
|
|
1479
1584
|
}
|
|
1480
1585
|
if (action.mode !== void 0 && ["stack", "replace"].indexOf(action.mode) === -1) {
|
|
1481
|
-
add(
|
|
1586
|
+
add(
|
|
1587
|
+
`${path}.mode`,
|
|
1588
|
+
"mode \u53EA\u80FD\u662F 'stack'\uFF08\u9ED8\u8BA4\uFF0C\u53E0\u4E00\u5C42\uFF09\u6216 'replace'\uFF08\u6362\u6389\u5F53\u524D\u5C42\uFF09"
|
|
1589
|
+
);
|
|
1482
1590
|
}
|
|
1483
1591
|
}
|
|
1484
1592
|
if (action.type === "sequence") {
|
package/dist/index.d.cts
CHANGED
|
@@ -153,6 +153,12 @@ interface RenderElement {
|
|
|
153
153
|
key: string;
|
|
154
154
|
/** 画成什么。div 带 text 就是文本节点,带 children 就是容器 */
|
|
155
155
|
tag: 'div' | 'img';
|
|
156
|
+
/**
|
|
157
|
+
* 类名,如 "dsl-node dsl-text is-clickable"。
|
|
158
|
+
* 不参与样式(样式全在 style 里),纯粹是留给调试和宿主微调的钩子。
|
|
159
|
+
* 由 core 统一生成,不让各框架壳各拼一套,否则 DOM 结构会慢慢跑偏。
|
|
160
|
+
*/
|
|
161
|
+
className: string;
|
|
156
162
|
style: CssStyle;
|
|
157
163
|
text?: string;
|
|
158
164
|
src?: string;
|
|
@@ -287,6 +293,16 @@ interface RuntimeOptions {
|
|
|
287
293
|
interface DslRuntime {
|
|
288
294
|
/** 当前渲染树。每次 subscribe 回调后重新取 */
|
|
289
295
|
getTree: () => RenderTree;
|
|
296
|
+
/**
|
|
297
|
+
* 更新宿主传进来的东西。
|
|
298
|
+
*
|
|
299
|
+
* user 常常是异步到的(弹窗先挂载、用户信息后到),
|
|
300
|
+
* 不给更新入口的话文案就永远停在初始值。
|
|
301
|
+
* editMode / handlers 同理,宿主可能中途换。
|
|
302
|
+
*
|
|
303
|
+
* 不会重新拉数据源,也不会重放 onShow 曝光埋点——那些是一次性的。
|
|
304
|
+
*/
|
|
305
|
+
update: (patch: Pick<RuntimeOptions, 'user' | 'handlers' | 'editMode'>) => void;
|
|
290
306
|
/** 状态变化时回调,返回取消订阅的函数 */
|
|
291
307
|
subscribe: (listener: () => void) => () => void;
|
|
292
308
|
/** 供调试台等外部读取,正常渲染不需要 */
|
package/dist/index.d.ts
CHANGED
|
@@ -153,6 +153,12 @@ interface RenderElement {
|
|
|
153
153
|
key: string;
|
|
154
154
|
/** 画成什么。div 带 text 就是文本节点,带 children 就是容器 */
|
|
155
155
|
tag: 'div' | 'img';
|
|
156
|
+
/**
|
|
157
|
+
* 类名,如 "dsl-node dsl-text is-clickable"。
|
|
158
|
+
* 不参与样式(样式全在 style 里),纯粹是留给调试和宿主微调的钩子。
|
|
159
|
+
* 由 core 统一生成,不让各框架壳各拼一套,否则 DOM 结构会慢慢跑偏。
|
|
160
|
+
*/
|
|
161
|
+
className: string;
|
|
156
162
|
style: CssStyle;
|
|
157
163
|
text?: string;
|
|
158
164
|
src?: string;
|
|
@@ -287,6 +293,16 @@ interface RuntimeOptions {
|
|
|
287
293
|
interface DslRuntime {
|
|
288
294
|
/** 当前渲染树。每次 subscribe 回调后重新取 */
|
|
289
295
|
getTree: () => RenderTree;
|
|
296
|
+
/**
|
|
297
|
+
* 更新宿主传进来的东西。
|
|
298
|
+
*
|
|
299
|
+
* user 常常是异步到的(弹窗先挂载、用户信息后到),
|
|
300
|
+
* 不给更新入口的话文案就永远停在初始值。
|
|
301
|
+
* editMode / handlers 同理,宿主可能中途换。
|
|
302
|
+
*
|
|
303
|
+
* 不会重新拉数据源,也不会重放 onShow 曝光埋点——那些是一次性的。
|
|
304
|
+
*/
|
|
305
|
+
update: (patch: Pick<RuntimeOptions, 'user' | 'handlers' | 'editMode'>) => void;
|
|
290
306
|
/** 状态变化时回调,返回取消订阅的函数 */
|
|
291
307
|
subscribe: (listener: () => void) => () => void;
|
|
292
308
|
/** 供调试台等外部读取,正常渲染不需要 */
|
package/dist/index.js
CHANGED
|
@@ -324,9 +324,11 @@ function safeImageUrl(input) {
|
|
|
324
324
|
|
|
325
325
|
// src/actions.ts
|
|
326
326
|
function createDispatcher(options) {
|
|
327
|
-
const { setState, emit, openView, closeTop, closeAll
|
|
327
|
+
const { setState, emit, openView, closeTop, closeAll } = options;
|
|
328
328
|
function dispatch(action, context) {
|
|
329
329
|
if (!action || !action.type) return;
|
|
330
|
+
const handlers = options.handlers || {};
|
|
331
|
+
const editMode = !!options.editMode;
|
|
330
332
|
switch (action.type) {
|
|
331
333
|
case "sequence":
|
|
332
334
|
(action.actions || []).forEach((item) => dispatch(item, context));
|
|
@@ -606,7 +608,13 @@ function resolveTree(input) {
|
|
|
606
608
|
);
|
|
607
609
|
const countdowns = { endTimes: [], precision: "s" };
|
|
608
610
|
const renderLayers = ready ? layers.map(
|
|
609
|
-
(item, index) => resolveLayer(
|
|
611
|
+
(item, index) => resolveLayer(
|
|
612
|
+
item.name,
|
|
613
|
+
item.view,
|
|
614
|
+
index === layers.length - 1,
|
|
615
|
+
input,
|
|
616
|
+
countdowns
|
|
617
|
+
)
|
|
610
618
|
) : [];
|
|
611
619
|
return {
|
|
612
620
|
ready: ready && layers.length > 0,
|
|
@@ -625,7 +633,9 @@ function resolveLayer(name, view, isTop, input, countdowns) {
|
|
|
625
633
|
const rootLayout = stage.layout === "flow" ? "flow" : "absolute";
|
|
626
634
|
const maskClosable = !!stage.maskClosable;
|
|
627
635
|
const onMaskClick = maskClosable ? () => closeTop("mask") : void 0;
|
|
628
|
-
const nodes = (view.nodes || []).map(
|
|
636
|
+
const nodes = (view.nodes || []).map(
|
|
637
|
+
(node, index) => resolveNode(node, `${name}-${index}`, rootLayout, context, input, countdowns)
|
|
638
|
+
).filter((el) => !!el);
|
|
629
639
|
return {
|
|
630
640
|
name,
|
|
631
641
|
type: view.type,
|
|
@@ -688,8 +698,12 @@ function resolveStageStyle(stage, isPopup, context) {
|
|
|
688
698
|
base,
|
|
689
699
|
{
|
|
690
700
|
// width / height 支持数字(px)、'100%' / 'auto' / calc(...) 等相对单位,也支持 {{ }}
|
|
691
|
-
width: toLength(
|
|
692
|
-
|
|
701
|
+
width: toLength(
|
|
702
|
+
interpolate(stage.width === void 0 ? 320 : stage.width, context)
|
|
703
|
+
),
|
|
704
|
+
height: toLength(
|
|
705
|
+
interpolate(stage.height === void 0 ? 400 : stage.height, context)
|
|
706
|
+
)
|
|
693
707
|
},
|
|
694
708
|
// stage.style 也走一遍插值,这样切 tab 时弹窗本身的背景能跟着变
|
|
695
709
|
toCssStyle(interpolateDeep(stage.style, context))
|
|
@@ -735,14 +749,21 @@ function resolveCloseButton(config, layerName, closeTop) {
|
|
|
735
749
|
return {
|
|
736
750
|
key: `${layerName}-close`,
|
|
737
751
|
tag: "div",
|
|
752
|
+
className: "dsl-close",
|
|
738
753
|
style,
|
|
739
754
|
onClick,
|
|
740
755
|
children: [
|
|
741
756
|
{
|
|
742
757
|
key: `${layerName}-close-img`,
|
|
743
758
|
tag: "img",
|
|
759
|
+
className: "dsl-close-img",
|
|
744
760
|
src: image,
|
|
745
|
-
style: {
|
|
761
|
+
style: {
|
|
762
|
+
width: "100%",
|
|
763
|
+
height: "100%",
|
|
764
|
+
objectFit: "contain",
|
|
765
|
+
display: "block"
|
|
766
|
+
}
|
|
746
767
|
}
|
|
747
768
|
]
|
|
748
769
|
};
|
|
@@ -750,12 +771,27 @@ function resolveCloseButton(config, layerName, closeTop) {
|
|
|
750
771
|
return {
|
|
751
772
|
key: `${layerName}-close`,
|
|
752
773
|
tag: "div",
|
|
774
|
+
className: "dsl-close",
|
|
753
775
|
style,
|
|
754
776
|
onClick,
|
|
755
777
|
text: config.icon || "\xD7"
|
|
756
778
|
};
|
|
757
779
|
}
|
|
758
780
|
var BASE_STYLE = { boxSizing: "border-box" };
|
|
781
|
+
var NODE_CLASS = {
|
|
782
|
+
box: "dsl-box",
|
|
783
|
+
flex: "dsl-flex",
|
|
784
|
+
repeat: "dsl-flex",
|
|
785
|
+
tabs: "dsl-tabs",
|
|
786
|
+
image: "dsl-image",
|
|
787
|
+
text: "dsl-text",
|
|
788
|
+
button: "dsl-button",
|
|
789
|
+
countdown: "dsl-flex"
|
|
790
|
+
};
|
|
791
|
+
function nodeClass(type, clickable) {
|
|
792
|
+
const base = `dsl-node ${NODE_CLASS[type] || ""}`.trim();
|
|
793
|
+
return clickable ? `${base} is-clickable` : base;
|
|
794
|
+
}
|
|
759
795
|
function resolveNode(node, key, layout, context, input, countdowns) {
|
|
760
796
|
if (!node || typeof node !== "object") return void 0;
|
|
761
797
|
if (node.visibleWhen && !evaluate(node.visibleWhen, context)) return void 0;
|
|
@@ -774,14 +810,23 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
774
810
|
return {
|
|
775
811
|
key,
|
|
776
812
|
tag: "div",
|
|
813
|
+
className: nodeClass("box", !!onClick),
|
|
777
814
|
style: Object.assign({ position: "relative" }, style),
|
|
778
815
|
onClick,
|
|
779
|
-
children: resolveChildren(
|
|
816
|
+
children: resolveChildren(
|
|
817
|
+
node.children,
|
|
818
|
+
key,
|
|
819
|
+
"absolute",
|
|
820
|
+
context,
|
|
821
|
+
input,
|
|
822
|
+
countdowns
|
|
823
|
+
)
|
|
780
824
|
};
|
|
781
825
|
case "flex":
|
|
782
826
|
return {
|
|
783
827
|
key,
|
|
784
828
|
tag: "div",
|
|
829
|
+
className: nodeClass("flex", !!onClick),
|
|
785
830
|
style: Object.assign({ display: "flex" }, style),
|
|
786
831
|
onClick,
|
|
787
832
|
children: resolveChildren(node.children, key, "flow", context, input, countdowns)
|
|
@@ -790,6 +835,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
790
835
|
return {
|
|
791
836
|
key,
|
|
792
837
|
tag: "div",
|
|
838
|
+
className: nodeClass("repeat", !!onClick),
|
|
793
839
|
style: Object.assign({ display: "flex" }, style),
|
|
794
840
|
onClick,
|
|
795
841
|
children: resolveRepeat(node, key, context, input, countdowns)
|
|
@@ -798,6 +844,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
798
844
|
return {
|
|
799
845
|
key,
|
|
800
846
|
tag: "div",
|
|
847
|
+
className: nodeClass("tabs", false),
|
|
801
848
|
style: Object.assign({ display: "flex" }, style),
|
|
802
849
|
children: resolveTabs(node, key, context, input)
|
|
803
850
|
};
|
|
@@ -805,6 +852,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
805
852
|
return {
|
|
806
853
|
key,
|
|
807
854
|
tag: "img",
|
|
855
|
+
className: nodeClass("image", !!onClick),
|
|
808
856
|
src: safeImageUrl(interpolate(node.src, context)),
|
|
809
857
|
style: Object.assign({ display: "block", objectFit: "cover" }, style),
|
|
810
858
|
onClick
|
|
@@ -813,6 +861,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
813
861
|
return {
|
|
814
862
|
key,
|
|
815
863
|
tag: "div",
|
|
864
|
+
className: nodeClass("text", !!onClick),
|
|
816
865
|
style: Object.assign({ wordBreak: "break-word" }, style),
|
|
817
866
|
text: toText(interpolate(node.content, context)),
|
|
818
867
|
onClick
|
|
@@ -821,6 +870,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
821
870
|
return {
|
|
822
871
|
key,
|
|
823
872
|
tag: "div",
|
|
873
|
+
className: nodeClass("button", true),
|
|
824
874
|
style: Object.assign(
|
|
825
875
|
{
|
|
826
876
|
display: "flex",
|
|
@@ -855,7 +905,14 @@ function resolveRepeat(node, key, context, input, countdowns) {
|
|
|
855
905
|
[node.itemName || "item"]: item,
|
|
856
906
|
[node.indexName || "index"]: index
|
|
857
907
|
});
|
|
858
|
-
return resolveNode(
|
|
908
|
+
return resolveNode(
|
|
909
|
+
node.template,
|
|
910
|
+
`${key}-${index}`,
|
|
911
|
+
"flow",
|
|
912
|
+
itemContext,
|
|
913
|
+
input,
|
|
914
|
+
countdowns
|
|
915
|
+
);
|
|
859
916
|
}).filter((el) => !!el);
|
|
860
917
|
}
|
|
861
918
|
function resolveTabs(node, key, context, input) {
|
|
@@ -871,6 +928,7 @@ function resolveTabs(node, key, context, input) {
|
|
|
871
928
|
return {
|
|
872
929
|
key: `${key}-${index}`,
|
|
873
930
|
tag: "div",
|
|
931
|
+
className: index === active ? "dsl-tab-item is-active" : "dsl-tab-item",
|
|
874
932
|
style: Object.assign(
|
|
875
933
|
{
|
|
876
934
|
display: "flex",
|
|
@@ -907,6 +965,7 @@ function resolveCountdown(node, key, style, context, input, countdowns) {
|
|
|
907
965
|
return {
|
|
908
966
|
key,
|
|
909
967
|
tag: "div",
|
|
968
|
+
className: "dsl-node dsl-text dsl-countdown",
|
|
910
969
|
style: Object.assign({ wordBreak: "break-word" }, style),
|
|
911
970
|
text: node.endText || "\u5DF2\u7ED3\u675F"
|
|
912
971
|
};
|
|
@@ -915,6 +974,7 @@ function resolveCountdown(node, key, style, context, input, countdowns) {
|
|
|
915
974
|
return {
|
|
916
975
|
key,
|
|
917
976
|
tag: "div",
|
|
977
|
+
className: "dsl-node dsl-text dsl-countdown",
|
|
918
978
|
style: Object.assign({ wordBreak: "break-word" }, style),
|
|
919
979
|
text: formatParts(parts, node.format)
|
|
920
980
|
};
|
|
@@ -923,8 +983,16 @@ function resolveCountdown(node, key, style, context, input, countdowns) {
|
|
|
923
983
|
return {
|
|
924
984
|
key,
|
|
925
985
|
tag: "div",
|
|
986
|
+
className: "dsl-node dsl-flex dsl-countdown",
|
|
926
987
|
style: Object.assign({ display: "flex" }, style),
|
|
927
|
-
children: resolveChildren(
|
|
988
|
+
children: resolveChildren(
|
|
989
|
+
node.children,
|
|
990
|
+
key,
|
|
991
|
+
"flow",
|
|
992
|
+
childContext,
|
|
993
|
+
input,
|
|
994
|
+
countdowns
|
|
995
|
+
)
|
|
928
996
|
};
|
|
929
997
|
}
|
|
930
998
|
function toText(value) {
|
|
@@ -935,7 +1003,12 @@ function toText(value) {
|
|
|
935
1003
|
var noopEmit = () => {
|
|
936
1004
|
};
|
|
937
1005
|
function createRuntime(dsl, options = {}) {
|
|
938
|
-
const {
|
|
1006
|
+
const { sources = {}, emit = noopEmit } = options;
|
|
1007
|
+
const live = {
|
|
1008
|
+
user: options.user || {},
|
|
1009
|
+
handlers: options.handlers || {},
|
|
1010
|
+
editMode: !!options.editMode
|
|
1011
|
+
};
|
|
939
1012
|
const normalized = normalizeViews(dsl);
|
|
940
1013
|
let state = Object.assign({}, dsl.state || {});
|
|
941
1014
|
let resolvedData = {};
|
|
@@ -951,7 +1024,10 @@ function createRuntime(dsl, options = {}) {
|
|
|
951
1024
|
};
|
|
952
1025
|
const ticker = createTicker({ onTick: notify });
|
|
953
1026
|
function buildContext() {
|
|
954
|
-
const context = Object.assign({}, resolvedData, {
|
|
1027
|
+
const context = Object.assign({}, resolvedData, {
|
|
1028
|
+
state,
|
|
1029
|
+
user: live.user
|
|
1030
|
+
});
|
|
955
1031
|
const derived = dsl.derived || {};
|
|
956
1032
|
Object.keys(derived).forEach((name) => {
|
|
957
1033
|
const config = derived[name] || {};
|
|
@@ -1012,8 +1088,12 @@ function createRuntime(dsl, options = {}) {
|
|
|
1012
1088
|
// 配置里 close 可以自带 reason(如 'never-remind'),要透传给埋点,不能吞掉
|
|
1013
1089
|
closeTop: (reason) => closeTop(reason),
|
|
1014
1090
|
closeAll: (reason) => closeAll(reason),
|
|
1015
|
-
handlers
|
|
1016
|
-
|
|
1091
|
+
get handlers() {
|
|
1092
|
+
return live.handlers;
|
|
1093
|
+
},
|
|
1094
|
+
get editMode() {
|
|
1095
|
+
return live.editMode;
|
|
1096
|
+
}
|
|
1017
1097
|
});
|
|
1018
1098
|
function fireLifecycle(viewName, hook, extra) {
|
|
1019
1099
|
const view = normalized.views[viewName];
|
|
@@ -1040,7 +1120,7 @@ function createRuntime(dsl, options = {}) {
|
|
|
1040
1120
|
return;
|
|
1041
1121
|
}
|
|
1042
1122
|
pending.push(
|
|
1043
|
-
Promise.resolve(source(ref.params || {}, user)).then((result) => {
|
|
1123
|
+
Promise.resolve(source(ref.params || {}, live.user)).then((result) => {
|
|
1044
1124
|
resolved[key] = result;
|
|
1045
1125
|
})
|
|
1046
1126
|
);
|
|
@@ -1078,6 +1158,12 @@ function createRuntime(dsl, options = {}) {
|
|
|
1078
1158
|
}
|
|
1079
1159
|
return {
|
|
1080
1160
|
getTree,
|
|
1161
|
+
update(patch) {
|
|
1162
|
+
if (patch.user !== void 0) live.user = patch.user;
|
|
1163
|
+
if (patch.handlers !== void 0) live.handlers = patch.handlers;
|
|
1164
|
+
if (patch.editMode !== void 0) live.editMode = patch.editMode;
|
|
1165
|
+
notify();
|
|
1166
|
+
},
|
|
1081
1167
|
subscribe(listener) {
|
|
1082
1168
|
listeners.add(listener);
|
|
1083
1169
|
return () => listeners.delete(listener);
|
|
@@ -1156,7 +1242,10 @@ function validate(dsl) {
|
|
|
1156
1242
|
}
|
|
1157
1243
|
const doc = dsl;
|
|
1158
1244
|
if (doc.version !== DSL_VERSION) {
|
|
1159
|
-
add(
|
|
1245
|
+
add(
|
|
1246
|
+
"version",
|
|
1247
|
+
`version \u5FC5\u987B\u662F ${DSL_VERSION}\uFF0C\u5F53\u524D\u4E3A ${JSON.stringify(doc.version)}`
|
|
1248
|
+
);
|
|
1160
1249
|
}
|
|
1161
1250
|
const dataKeys = validateData(doc.data, add);
|
|
1162
1251
|
const stateKeys = validateState(doc.state, add);
|
|
@@ -1256,7 +1345,10 @@ function validateStage(stage, add, warn, ctx, prefix) {
|
|
|
1256
1345
|
if (stage.onShow !== void 0) validateAction(stage.onShow, at("onShow"), ctx);
|
|
1257
1346
|
if (stage.onClose !== void 0) validateAction(stage.onClose, at("onClose"), ctx);
|
|
1258
1347
|
if (stage.height === "auto" && stage.layout !== "flow") {
|
|
1259
|
-
add(
|
|
1348
|
+
add(
|
|
1349
|
+
"stage.height",
|
|
1350
|
+
"height: 'auto' \u9700\u8981\u540C\u65F6\u8BBE\u7F6E stage.layout: 'flow'\uFF0C\u5426\u5219\u9AD8\u5EA6\u4F1A\u584C\u6210 0"
|
|
1351
|
+
);
|
|
1260
1352
|
}
|
|
1261
1353
|
}
|
|
1262
1354
|
function validateCloseButton(config, add, warn) {
|
|
@@ -1270,7 +1362,10 @@ function validateCloseButton(config, add, warn) {
|
|
|
1270
1362
|
}
|
|
1271
1363
|
if (config.offset !== void 0) {
|
|
1272
1364
|
if (!Array.isArray(config.offset) || config.offset.length !== 2) {
|
|
1273
|
-
add(
|
|
1365
|
+
add(
|
|
1366
|
+
"stage.closeButton.offset",
|
|
1367
|
+
"offset \u5FC5\u987B\u662F [x, y] \u4E24\u9879\u6570\u7EC4\uFF0C\u8D1F\u503C\u4F1A\u628A\u6309\u94AE\u79FB\u5230\u5F39\u7A97\u5916\u9762"
|
|
1368
|
+
);
|
|
1274
1369
|
} else {
|
|
1275
1370
|
config.offset.forEach((value, index) => {
|
|
1276
1371
|
if (!checkLength(value)) {
|
|
@@ -1291,7 +1386,10 @@ function validateCloseButton(config, add, warn) {
|
|
|
1291
1386
|
if (config.style) {
|
|
1292
1387
|
Object.keys(config.style).forEach((key) => {
|
|
1293
1388
|
if (ALLOWED_STYLE_KEYS.indexOf(key) === -1) {
|
|
1294
|
-
warn(
|
|
1389
|
+
warn(
|
|
1390
|
+
`stage.closeButton.style.${key}`,
|
|
1391
|
+
`\u6837\u5F0F\u5C5E\u6027 "${key}" \u4E0D\u5728\u767D\u540D\u5355\u5185\uFF0C\u6E32\u67D3\u65F6\u4F1A\u88AB\u5FFD\u7565`
|
|
1392
|
+
);
|
|
1295
1393
|
}
|
|
1296
1394
|
});
|
|
1297
1395
|
}
|
|
@@ -1362,7 +1460,10 @@ function validateNode(node, path, layout, ctx) {
|
|
|
1362
1460
|
} else {
|
|
1363
1461
|
node.rect.forEach((value, index) => {
|
|
1364
1462
|
if (value !== void 0 && value !== null && !checkLength(value)) {
|
|
1365
|
-
add(
|
|
1463
|
+
add(
|
|
1464
|
+
`${path}.rect[${index}]`,
|
|
1465
|
+
`"${value}" \u4E0D\u662F\u5408\u6CD5\u957F\u5EA6\uFF0C\u652F\u6301\u6570\u5B57(px) / '100%' / 'auto' \u7B49`
|
|
1466
|
+
);
|
|
1366
1467
|
}
|
|
1367
1468
|
});
|
|
1368
1469
|
}
|
|
@@ -1380,7 +1481,10 @@ function validateNode(node, path, layout, ctx) {
|
|
|
1380
1481
|
);
|
|
1381
1482
|
}
|
|
1382
1483
|
if (key === "aspectRatio" && !checkAspectRatio(node.style[key])) {
|
|
1383
|
-
add(
|
|
1484
|
+
add(
|
|
1485
|
+
`${path}.style.aspectRatio`,
|
|
1486
|
+
"aspectRatio \u8981\u5199\u6210 '750 / 200' \u6216 1.5 \u8FD9\u6837\u7684\u5BBD\u9AD8\u6BD4"
|
|
1487
|
+
);
|
|
1384
1488
|
}
|
|
1385
1489
|
});
|
|
1386
1490
|
}
|
|
@@ -1410,7 +1514,8 @@ function validateNodeByType(node, path, ctx) {
|
|
|
1410
1514
|
if (typeof node.bind !== "string") {
|
|
1411
1515
|
add(`${path}.bind`, "tabs \u5FC5\u987B\u63D0\u4F9B bind\uFF08data \u4E2D\u7684\u6570\u7EC4\u5B57\u6BB5\u540D\uFF09");
|
|
1412
1516
|
}
|
|
1413
|
-
if (typeof node.stateKey !== "string")
|
|
1517
|
+
if (typeof node.stateKey !== "string")
|
|
1518
|
+
add(`${path}.stateKey`, "tabs \u5FC5\u987B\u63D0\u4F9B stateKey");
|
|
1414
1519
|
if (node.labelField !== void 0 && typeof node.labelField !== "string") {
|
|
1415
1520
|
add(`${path}.labelField`, "labelField \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
|
|
1416
1521
|
}
|
|
@@ -1476,7 +1581,10 @@ function validateAction(action, path, ctx) {
|
|
|
1476
1581
|
add(`${path}.view`, `views \u91CC\u4E0D\u5B58\u5728\u89C6\u56FE "${action.view}"`);
|
|
1477
1582
|
}
|
|
1478
1583
|
if (action.mode !== void 0 && ["stack", "replace"].indexOf(action.mode) === -1) {
|
|
1479
|
-
add(
|
|
1584
|
+
add(
|
|
1585
|
+
`${path}.mode`,
|
|
1586
|
+
"mode \u53EA\u80FD\u662F 'stack'\uFF08\u9ED8\u8BA4\uFF0C\u53E0\u4E00\u5C42\uFF09\u6216 'replace'\uFF08\u6362\u6389\u5F53\u524D\u5C42\uFF09"
|
|
1587
|
+
);
|
|
1480
1588
|
}
|
|
1481
1589
|
}
|
|
1482
1590
|
if (action.type === "sequence") {
|