@yaoxiu/marketing-dsl 2.3.0 → 2.5.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 +15 -0
- package/dist/{chunk-DTHKC5AL.js → chunk-DT3ZINDI.js} +381 -45
- package/dist/docs/index.cjs +1606 -104
- package/dist/docs/index.d.cts +4 -3
- package/dist/docs/index.d.ts +4 -3
- package/dist/docs/index.js +1574 -108
- package/dist/index.cjs +523 -87
- package/dist/index.d.cts +56 -4
- package/dist/index.d.ts +56 -4
- package/dist/index.js +140 -45
- package/dist/report/index.d.cts +1 -1
- package/dist/report/index.d.ts +1 -1
- package/dist/{types-Bj0xyWnx.d.cts → types-8FNxDXp0.d.cts} +116 -2
- package/dist/{types-Bj0xyWnx.d.ts → types-8FNxDXp0.d.ts} +116 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -141,11 +141,26 @@ unsubscribe();
|
|
|
141
141
|
}],
|
|
142
142
|
countdownEndTimes: [], // 有倒计时时运行时会自己起定时器
|
|
143
143
|
countdownPrecision: 's',
|
|
144
|
+
css: '', // 要注入的 CSS 文本(keyframes + hover 规则),没有动画时是空串
|
|
145
|
+
rootClassName: 'dsl-renderer dsl-r3', // 根容器类名,后半截是这份物料的作用域前缀
|
|
144
146
|
}
|
|
145
147
|
```
|
|
146
148
|
|
|
147
149
|
框架壳照着 layer 的结构套四层 div 即可,**不需要自带任何 CSS**,基础样式已经写进行内样式了。
|
|
148
150
|
|
|
151
|
+
### 动画与 hover:唯一需要一段真 CSS 的地方
|
|
152
|
+
|
|
153
|
+
`@keyframes` 和 `:hover` 行内表达不了,所以渲染树多带两个字段。框架壳的义务只有两条:
|
|
154
|
+
根容器的 class 用 `tree.rootClassName`(不要写死 `dsl-renderer`),`tree.css` 非空时把它原样塞进
|
|
155
|
+
一个 `<style>`、随变化更新、销毁时移除。**壳里不做任何 CSS 生成或判断**。
|
|
156
|
+
|
|
157
|
+
- `animate` 落的是**行内** `animation` 简写(节点、`stage`、`stage.maskAnimate`、`stage.closeButton` 都支持),
|
|
158
|
+
只有 keyframes 定义与 hover 规则进 `tree.css`
|
|
159
|
+
- keyframes 名带作用域前缀(`dsl-kf-{uid}-{name}`),同一页面上多份物料不会互相覆盖
|
|
160
|
+
- `tree.css` 末尾恒带 `prefers-reduced-motion` 兜底:系统开了「减少动态效果」就全部静止
|
|
161
|
+
- CSS 文本是字符串拼接,所以每个值都过 `sanitizeCssValue`——含 `{ } < > ; @`、`/*` 或反斜杠的
|
|
162
|
+
**整条声明**直接丢弃(行内那条路径不需要,框架自己会转义)
|
|
163
|
+
|
|
149
164
|
### 框架壳必须遵守的一条契约
|
|
150
165
|
|
|
151
166
|
节点有 `onClick` 时,挂事件要**阻止冒泡**。
|
|
@@ -440,6 +440,11 @@ function isLength(value) {
|
|
|
440
440
|
if (/^calc\([^;{}]*\)$/.test(trimmed)) return true;
|
|
441
441
|
return LENGTH_RE.test(trimmed);
|
|
442
442
|
}
|
|
443
|
+
function clampZIndex(value) {
|
|
444
|
+
const num = typeof value === "number" ? value : parseFloat(String(value));
|
|
445
|
+
if (!isFinite(num)) return 0;
|
|
446
|
+
return Math.min(10, Math.max(0, Math.round(num)));
|
|
447
|
+
}
|
|
443
448
|
var STYLE_MAP = {
|
|
444
449
|
// 文字
|
|
445
450
|
color: ["color", String],
|
|
@@ -492,6 +497,26 @@ var STYLE_MAP = {
|
|
|
492
497
|
minHeight: ["minHeight", px],
|
|
493
498
|
maxWidth: ["maxWidth", px],
|
|
494
499
|
maxHeight: ["maxHeight", px],
|
|
500
|
+
// 动效相关。transform / transition 是做位移、缩放、hover 过渡的最小必需集;
|
|
501
|
+
// 不放开 position / display / grid —— rect 已经提供绝对定位,栅格用 flex wrap + 百分比宽复刻,
|
|
502
|
+
// 放开 position 等于把「全屏覆盖」这类越权手段又交回给配置。
|
|
503
|
+
transform: ["transform", String],
|
|
504
|
+
transformOrigin: ["transformOrigin", String],
|
|
505
|
+
transition: ["transition", String],
|
|
506
|
+
// 只为 hoverStyle 而放开:`animationPlayState: 'paused'` 才能做到「悬停时暂停呼吸动画」。
|
|
507
|
+
// 写进普通 style 也合法(等于一开始就暂停),但那基本没有意义。
|
|
508
|
+
animationPlayState: ["animationPlayState", String],
|
|
509
|
+
// 毛玻璃。Safari 至今只认带前缀的写法,所以两个键一起输出,见 EXTRA_CSS_KEYS
|
|
510
|
+
backdropFilter: ["backdropFilter", String],
|
|
511
|
+
textShadow: ["textShadow", String],
|
|
512
|
+
// 只允许 0~10:越权提层是弹窗类配置最容易做的坏事(盖住宿主自己的导航 / 收银台),
|
|
513
|
+
// 层内排布十层绰绰有余,真需要压过页面其它内容的是弹窗层,那由解释器自己给 1000
|
|
514
|
+
zIndex: ["zIndex", (v) => String(clampZIndex(v))],
|
|
515
|
+
// 装饰性图层(光斑、扫光)必须让点击穿透,否则会挡住下面的按钮
|
|
516
|
+
pointerEvents: ["pointerEvents", String],
|
|
517
|
+
// 横向排布里「这一项不许被压扁」,图标位和数字位常用
|
|
518
|
+
flexShrink: ["flexShrink", String],
|
|
519
|
+
fontStyle: ["fontStyle", String],
|
|
495
520
|
// 图片
|
|
496
521
|
objectFit: ["objectFit", String],
|
|
497
522
|
// 宽度给百分比、高度按比例算。整图 banner 的热区必须靠它才能用百分比定位:
|
|
@@ -499,6 +524,9 @@ var STYLE_MAP = {
|
|
|
499
524
|
aspectRatio: ["aspectRatio", String]
|
|
500
525
|
};
|
|
501
526
|
var ALLOWED_STYLE_KEYS = Object.keys(STYLE_MAP);
|
|
527
|
+
var EXTRA_CSS_KEYS = {
|
|
528
|
+
backdropFilter: ["WebkitBackdropFilter"]
|
|
529
|
+
};
|
|
502
530
|
function toCssStyle(style) {
|
|
503
531
|
const css = {};
|
|
504
532
|
if (!style) return css;
|
|
@@ -507,7 +535,11 @@ function toCssStyle(style) {
|
|
|
507
535
|
if (!rule) return;
|
|
508
536
|
const value = style[key];
|
|
509
537
|
if (value === void 0 || value === null || value === "") return;
|
|
510
|
-
|
|
538
|
+
const result = rule[1](value);
|
|
539
|
+
css[rule[0]] = result;
|
|
540
|
+
(EXTRA_CSS_KEYS[key] || []).forEach((extra) => {
|
|
541
|
+
css[extra] = result;
|
|
542
|
+
});
|
|
511
543
|
});
|
|
512
544
|
return css;
|
|
513
545
|
}
|
|
@@ -520,11 +552,116 @@ function toRectStyle(rect) {
|
|
|
520
552
|
return css;
|
|
521
553
|
}
|
|
522
554
|
function resolveNodeStyle(node, layout) {
|
|
523
|
-
|
|
555
|
+
const css = Object.assign(
|
|
524
556
|
{},
|
|
525
557
|
layout === "absolute" ? toRectStyle(node.rect) : {},
|
|
526
558
|
toCssStyle(node.style)
|
|
527
559
|
);
|
|
560
|
+
if (css.zIndex !== void 0 && !css.position) css.position = "relative";
|
|
561
|
+
return css;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/css-value.ts
|
|
565
|
+
function sanitizeCssValue(value) {
|
|
566
|
+
if (value === void 0 || value === null) return null;
|
|
567
|
+
const text = String(value).trim();
|
|
568
|
+
if (!text) return null;
|
|
569
|
+
if (/[{}<>;@]/.test(text)) return null;
|
|
570
|
+
if (text.indexOf("/*") > -1 || text.indexOf("*/") > -1) return null;
|
|
571
|
+
if (text.indexOf("\\") > -1) return null;
|
|
572
|
+
return text;
|
|
573
|
+
}
|
|
574
|
+
function toCssProp(key) {
|
|
575
|
+
return key.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`);
|
|
576
|
+
}
|
|
577
|
+
function declarations(style) {
|
|
578
|
+
return Object.keys(style).map((key) => {
|
|
579
|
+
const value = sanitizeCssValue(style[key]);
|
|
580
|
+
return value === null ? "" : `${toCssProp(key)}:${value}`;
|
|
581
|
+
}).filter(Boolean).join(";");
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/css.ts
|
|
585
|
+
var CSS_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]{0,31}$/;
|
|
586
|
+
var ALLOWED_EASINGS = [
|
|
587
|
+
"linear",
|
|
588
|
+
"ease",
|
|
589
|
+
"ease-in",
|
|
590
|
+
"ease-out",
|
|
591
|
+
"ease-in-out",
|
|
592
|
+
"step-start",
|
|
593
|
+
"step-end"
|
|
594
|
+
];
|
|
595
|
+
var CUBIC_BEZIER_RE = /^cubic-bezier\(\s*-?\d+(\.\d+)?\s*,\s*-?\d+(\.\d+)?\s*,\s*-?\d+(\.\d+)?\s*,\s*-?\d+(\.\d+)?\s*\)$/;
|
|
596
|
+
var OFFSET_RE = /^(from|to|\d+(\.\d+)?%)$/;
|
|
597
|
+
function isValidEasing(value) {
|
|
598
|
+
if (typeof value !== "string") return false;
|
|
599
|
+
const trimmed = value.trim();
|
|
600
|
+
return ALLOWED_EASINGS.indexOf(trimmed) > -1 || CUBIC_BEZIER_RE.test(trimmed);
|
|
601
|
+
}
|
|
602
|
+
function isKeyframeOffset(value) {
|
|
603
|
+
return typeof value === "string" && OFFSET_RE.test(value.trim());
|
|
604
|
+
}
|
|
605
|
+
var scopeSeed = 0;
|
|
606
|
+
function nextScopeUid() {
|
|
607
|
+
scopeSeed += 1;
|
|
608
|
+
return scopeSeed;
|
|
609
|
+
}
|
|
610
|
+
function createCssBuilder(uid, keyframes, hover) {
|
|
611
|
+
const rootClassName = `dsl-renderer dsl-r${uid}`;
|
|
612
|
+
const scope = `.dsl-r${uid}`;
|
|
613
|
+
const defined = keyframes && typeof keyframes === "object" ? keyframes : {};
|
|
614
|
+
const hoverRules = hover ? hover.rules : [];
|
|
615
|
+
function scopedKeyframeName(name) {
|
|
616
|
+
return `dsl-kf-${uid}-${name}`;
|
|
617
|
+
}
|
|
618
|
+
function animation(animate) {
|
|
619
|
+
if (!animate || typeof animate !== "object") return void 0;
|
|
620
|
+
const name = animate.name;
|
|
621
|
+
if (typeof name !== "string" || !CSS_NAME_PATTERN.test(name)) return void 0;
|
|
622
|
+
if (!Object.prototype.hasOwnProperty.call(defined, name)) return void 0;
|
|
623
|
+
const duration = toMs(animate.duration, 0);
|
|
624
|
+
if (duration <= 0) return void 0;
|
|
625
|
+
const delay = toMs(animate.delay, 0);
|
|
626
|
+
const easing = isValidEasing(animate.easing) ? String(animate.easing).trim() : "ease";
|
|
627
|
+
const iteration = animate.iteration === "infinite" ? "infinite" : String(Math.max(1, Math.round(Number(animate.iteration) || 1)));
|
|
628
|
+
const direction = pick(animate.direction, DIRECTIONS, "normal");
|
|
629
|
+
const fill = pick(animate.fill, FILLS, "both");
|
|
630
|
+
return `${scopedKeyframeName(name)} ${duration}ms ${easing} ${delay}ms ${iteration} ${direction} ${fill}`;
|
|
631
|
+
}
|
|
632
|
+
function hoverClass(owner) {
|
|
633
|
+
return owner && hover ? hover.classOf.get(owner) : void 0;
|
|
634
|
+
}
|
|
635
|
+
function build() {
|
|
636
|
+
const rules = keyframeRules().concat(hoverRules);
|
|
637
|
+
if (!rules.length) return "";
|
|
638
|
+
rules.push(
|
|
639
|
+
`@media (prefers-reduced-motion: reduce){${scope} *{animation:none!important;transition:none!important}}`
|
|
640
|
+
);
|
|
641
|
+
return rules.join("\n");
|
|
642
|
+
}
|
|
643
|
+
function keyframeRules() {
|
|
644
|
+
return Object.keys(defined).filter((name) => CSS_NAME_PATTERN.test(name)).map((name) => {
|
|
645
|
+
const frames = defined[name];
|
|
646
|
+
if (!frames || typeof frames !== "object") return "";
|
|
647
|
+
const body = Object.keys(frames).filter(isKeyframeOffset).map((offset) => {
|
|
648
|
+
const decl = declarations(toCssStyle(frames[offset]));
|
|
649
|
+
return decl ? `${offset.trim()}{${decl}}` : "";
|
|
650
|
+
}).filter(Boolean).join("");
|
|
651
|
+
return body ? `@keyframes ${scopedKeyframeName(name)}{${body}}` : "";
|
|
652
|
+
}).filter(Boolean);
|
|
653
|
+
}
|
|
654
|
+
return { uid, rootClassName, animation, hoverClass, build };
|
|
655
|
+
}
|
|
656
|
+
var DIRECTIONS = ["normal", "reverse", "alternate", "alternate-reverse"];
|
|
657
|
+
var FILLS = ["none", "forwards", "backwards", "both"];
|
|
658
|
+
function pick(value, allowed, fallback) {
|
|
659
|
+
return typeof value === "string" && allowed.indexOf(value) > -1 ? value : fallback;
|
|
660
|
+
}
|
|
661
|
+
function toMs(value, fallback) {
|
|
662
|
+
const num = Number(value);
|
|
663
|
+
if (!isFinite(num) || num < 0) return fallback;
|
|
664
|
+
return Math.round(num);
|
|
528
665
|
}
|
|
529
666
|
|
|
530
667
|
// src/user-fields.ts
|
|
@@ -643,6 +780,116 @@ function validateUserFields(dsl, allowed) {
|
|
|
643
780
|
return issues;
|
|
644
781
|
}
|
|
645
782
|
|
|
783
|
+
// src/validate-animate.ts
|
|
784
|
+
function validateKeyframes(keyframes, add, warn) {
|
|
785
|
+
if (keyframes === void 0) return [];
|
|
786
|
+
if (typeof keyframes !== "object" || keyframes === null || Array.isArray(keyframes)) {
|
|
787
|
+
add(
|
|
788
|
+
"keyframes",
|
|
789
|
+
'keyframes \u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u5F62\u5982 { "fadeIn": { "from": {...}, "to": {...} } }'
|
|
790
|
+
);
|
|
791
|
+
return [];
|
|
792
|
+
}
|
|
793
|
+
const names = Object.keys(keyframes);
|
|
794
|
+
names.forEach((name) => {
|
|
795
|
+
const at = `keyframes.${name}`;
|
|
796
|
+
if (!CSS_NAME_PATTERN.test(name)) {
|
|
797
|
+
add(
|
|
798
|
+
at,
|
|
799
|
+
`keyframes \u540D "${name}" \u4E0D\u5408\u89C4\uFF1A\u5B57\u6BCD\u5F00\u5934\uFF0C\u53EA\u80FD\u7528\u5B57\u6BCD / \u6570\u5B57 / \u4E0B\u5212\u7EBF / \u4E2D\u5212\u7EBF\uFF0C\u6700\u957F 32 \u4E2A\u5B57\u7B26`
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
const frames = keyframes[name];
|
|
803
|
+
if (!frames || typeof frames !== "object" || Array.isArray(frames)) {
|
|
804
|
+
add(at, "\u6BCF\u7EC4 keyframes \u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u952E\u662F\u504F\u79FB\u91CF\u3001\u503C\u662F\u6837\u5F0F");
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
const offsets = Object.keys(frames);
|
|
808
|
+
if (!offsets.length) {
|
|
809
|
+
add(at, 'keyframes \u81F3\u5C11\u8981\u6709\u4E00\u4E2A\u5173\u952E\u5E27\uFF08\u5982 "from" \u548C "to"\uFF09');
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
offsets.forEach((offset) => {
|
|
813
|
+
if (!isKeyframeOffset(offset)) {
|
|
814
|
+
add(
|
|
815
|
+
`${at}.${offset}`,
|
|
816
|
+
`\u5173\u952E\u5E27\u7684\u504F\u79FB\u91CF\u53EA\u80FD\u5199 "from" / "to" / "50%" \u8FD9\u4E09\u79CD\u5F62\u5F0F\uFF0C"${offset}" \u4E0D\u5408\u6CD5`
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
const style = frames[offset];
|
|
820
|
+
if (!style || typeof style !== "object" || Array.isArray(style)) {
|
|
821
|
+
add(`${at}.${offset}`, "\u5173\u952E\u5E27\u7684\u503C\u5FC5\u987B\u662F\u6837\u5F0F\u5BF9\u8C61");
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
Object.keys(style).forEach((key) => {
|
|
825
|
+
if (ALLOWED_STYLE_KEYS.indexOf(key) === -1) {
|
|
826
|
+
warn(
|
|
827
|
+
`${at}.${offset}.${key}`,
|
|
828
|
+
`\u6837\u5F0F\u5C5E\u6027 "${key}" \u4E0D\u5728\u767D\u540D\u5355\u5185\uFF0C\u6E32\u67D3\u65F6\u4F1A\u88AB\u5FFD\u7565`
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
});
|
|
834
|
+
return names;
|
|
835
|
+
}
|
|
836
|
+
function validateAnimate(animate, path, add, keyframeNames, used) {
|
|
837
|
+
if (animate === void 0) return;
|
|
838
|
+
if (!animate || typeof animate !== "object" || Array.isArray(animate)) {
|
|
839
|
+
add(path, "animate \u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u5F62\u5982 { name, duration, easing, iteration }");
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const config = animate;
|
|
843
|
+
if (typeof config.name !== "string" || !config.name) {
|
|
844
|
+
add(`${path}.name`, "animate \u5FC5\u987B\u63D0\u4F9B name\uFF08keyframes \u540D\uFF09");
|
|
845
|
+
} else {
|
|
846
|
+
used.add(config.name);
|
|
847
|
+
if (keyframeNames.indexOf(config.name) === -1) {
|
|
848
|
+
add(
|
|
849
|
+
`${path}.name`,
|
|
850
|
+
`keyframes \u91CC\u6CA1\u6709\u5B9A\u4E49 "${config.name}"\uFF0C\u52A8\u753B\u4E0D\u4F1A\u64AD\u653E\u3002` + (keyframeNames.length ? `\u5DF2\u5B9A\u4E49\u7684\u6709\uFF1A${keyframeNames.join(" / ")}` : "\u8BF7\u5148\u5728\u9876\u5C42 keyframes \u91CC\u5B9A\u4E49\u8FD9\u7EC4\u5173\u952E\u5E27")
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
checkMs(config.duration, `${path}.duration`, add, true);
|
|
855
|
+
checkMs(config.delay, `${path}.delay`, add, false);
|
|
856
|
+
if (config.easing !== void 0 && !isValidEasing(config.easing)) {
|
|
857
|
+
add(
|
|
858
|
+
`${path}.easing`,
|
|
859
|
+
`\u7F13\u52A8 "${String(config.easing)}" \u4E0D\u5728\u767D\u540D\u5355\u5185\u3002\u53EF\u7528\uFF1A${ALLOWED_EASINGS.join(" / ")} \u6216 cubic-bezier(0.34, 1.56, 0.64, 1)`
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
if (config.iteration !== void 0 && config.iteration !== "infinite") {
|
|
863
|
+
const times = config.iteration;
|
|
864
|
+
if (typeof times !== "number" || !isFinite(times) || times <= 0 || times % 1 !== 0) {
|
|
865
|
+
add(`${path}.iteration`, "iteration \u5FC5\u987B\u662F\u6B63\u6574\u6570\uFF0C\u6216\u5B57\u7B26\u4E32 'infinite'\uFF08\u65E0\u9650\u5FAA\u73AF\uFF09");
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
checkEnum(config.direction, `${path}.direction`, DIRECTIONS2, add);
|
|
869
|
+
checkEnum(config.fill, `${path}.fill`, FILLS2, add);
|
|
870
|
+
}
|
|
871
|
+
var DIRECTIONS2 = ["normal", "reverse", "alternate", "alternate-reverse"];
|
|
872
|
+
var FILLS2 = ["none", "forwards", "backwards", "both"];
|
|
873
|
+
function checkEnum(value, path, allowed, add) {
|
|
874
|
+
if (value === void 0) return;
|
|
875
|
+
if (typeof value !== "string" || allowed.indexOf(value) === -1) {
|
|
876
|
+
add(path, `\u53EA\u80FD\u662F ${allowed.join(" / ")} \u4E4B\u4E00`);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
function checkMs(value, path, add, required) {
|
|
880
|
+
if (value === void 0) {
|
|
881
|
+
if (required) add(path, "animate \u5FC5\u987B\u63D0\u4F9B duration\uFF08\u6BEB\u79D2\uFF0C\u6B63\u6570\uFF09");
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
if (typeof value !== "number" || !isFinite(value) || value < 0) {
|
|
885
|
+
add(path, `"${String(value)}" \u4E0D\u5408\u6CD5\uFF1A\u5FC5\u987B\u662F\u975E\u8D1F\u6570\u5B57\uFF0C\u5355\u4F4D\u6BEB\u79D2`);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (required && value === 0) {
|
|
889
|
+
add(path, "duration \u5FC5\u987B\u5927\u4E8E 0\uFF0C\u5426\u5219\u52A8\u753B\u4E0D\u4F1A\u64AD\u653E");
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
646
893
|
// src/validate.ts
|
|
647
894
|
var LENGTH_STYLE_KEYS = [
|
|
648
895
|
"width",
|
|
@@ -716,14 +963,26 @@ function validate(dsl, options) {
|
|
|
716
963
|
const dataKeys = validateData(doc.data, add);
|
|
717
964
|
const stateKeys = validateState(doc.state, add);
|
|
718
965
|
validateDerived(doc.derived, dataKeys, stateKeys, add);
|
|
966
|
+
const keyframeNames = validateKeyframes(doc.keyframes, add, warn);
|
|
719
967
|
const isMulti = doc.views !== void 0;
|
|
720
968
|
if (isMulti) validateMultiView(doc, add);
|
|
721
969
|
const { views } = normalizeViews(doc);
|
|
722
970
|
const viewNames = Object.keys(views);
|
|
723
|
-
const ctx = {
|
|
971
|
+
const ctx = {
|
|
972
|
+
add,
|
|
973
|
+
warn,
|
|
974
|
+
viewNames,
|
|
975
|
+
keyframeNames,
|
|
976
|
+
usedKeyframes: /* @__PURE__ */ new Set()
|
|
977
|
+
};
|
|
724
978
|
viewNames.forEach((name) => {
|
|
725
979
|
validateView(views[name], isMulti ? `views.${name}` : "", ctx);
|
|
726
980
|
});
|
|
981
|
+
keyframeNames.forEach((name) => {
|
|
982
|
+
if (!ctx.usedKeyframes.has(name)) {
|
|
983
|
+
warn(`keyframes.${name}`, `keyframes "${name}" \u6CA1\u6709\u4EFB\u4F55 animate \u5F15\u7528\uFF0C\u4E0D\u4F1A\u751F\u6548`);
|
|
984
|
+
}
|
|
985
|
+
});
|
|
727
986
|
if (options && options.userFields) {
|
|
728
987
|
validateUserFields(doc, options.userFields).forEach(
|
|
729
988
|
(issue) => add(issue.path, issue.message)
|
|
@@ -822,8 +1081,45 @@ function validateStage(stage, add, warn, ctx, prefix) {
|
|
|
822
1081
|
if (stage.layout !== void 0 && ["absolute", "flow"].indexOf(stage.layout) === -1) {
|
|
823
1082
|
add("stage.layout", "stage.layout \u53EA\u80FD\u662F 'absolute'\uFF08\u9ED8\u8BA4\uFF09\u6216 'flow'");
|
|
824
1083
|
}
|
|
825
|
-
validateCloseButton(stage.closeButton, add, warn);
|
|
826
1084
|
const at = (key) => prefix ? `${prefix}.stage.${key}` : `stage.${key}`;
|
|
1085
|
+
if (stage.maskStyle !== void 0) {
|
|
1086
|
+
if (typeof stage.maskStyle !== "object" || stage.maskStyle === null || Array.isArray(stage.maskStyle)) {
|
|
1087
|
+
add("stage.maskStyle", "maskStyle \u5FC5\u987B\u662F\u6837\u5F0F\u5BF9\u8C61");
|
|
1088
|
+
} else {
|
|
1089
|
+
Object.keys(stage.maskStyle).forEach((key) => {
|
|
1090
|
+
if (ALLOWED_STYLE_KEYS.indexOf(key) === -1) {
|
|
1091
|
+
warn(
|
|
1092
|
+
`stage.maskStyle.${key}`,
|
|
1093
|
+
`\u6837\u5F0F\u5C5E\u6027 "${key}" \u4E0D\u5728\u767D\u540D\u5355\u5185\uFF0C\u6E32\u67D3\u65F6\u4F1A\u88AB\u5FFD\u7565`
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
if (stage.maskAction !== void 0) {
|
|
1100
|
+
validateAction(stage.maskAction, at("maskAction"), ctx);
|
|
1101
|
+
if (!stage.maskClosable) {
|
|
1102
|
+
warn(
|
|
1103
|
+
"stage.maskAction",
|
|
1104
|
+
"maskAction \u53EA\u6709\u5728 maskClosable \u4E3A true \u65F6\u624D\u4F1A\u6267\u884C\u2014\u2014\u906E\u7F69\u4E0D\u53EF\u70B9\uFF0C\u52A8\u4F5C\u5C31\u6CA1\u6709\u89E6\u53D1\u65F6\u673A"
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
validateCloseButton(stage.closeButton, add, warn, ctx, at("closeButton.action"));
|
|
1109
|
+
validateAnimate(
|
|
1110
|
+
stage.animate,
|
|
1111
|
+
"stage.animate",
|
|
1112
|
+
add,
|
|
1113
|
+
ctx.keyframeNames,
|
|
1114
|
+
ctx.usedKeyframes
|
|
1115
|
+
);
|
|
1116
|
+
validateAnimate(
|
|
1117
|
+
stage.maskAnimate,
|
|
1118
|
+
"stage.maskAnimate",
|
|
1119
|
+
add,
|
|
1120
|
+
ctx.keyframeNames,
|
|
1121
|
+
ctx.usedKeyframes
|
|
1122
|
+
);
|
|
827
1123
|
if (stage.onShow !== void 0) validateAction(stage.onShow, at("onShow"), ctx);
|
|
828
1124
|
if (stage.onClose !== void 0) validateAction(stage.onClose, at("onClose"), ctx);
|
|
829
1125
|
if (stage.height === "auto" && stage.layout !== "flow") {
|
|
@@ -833,7 +1129,7 @@ function validateStage(stage, add, warn, ctx, prefix) {
|
|
|
833
1129
|
);
|
|
834
1130
|
}
|
|
835
1131
|
}
|
|
836
|
-
function validateCloseButton(config, add, warn) {
|
|
1132
|
+
function validateCloseButton(config, add, warn, ctx, actionPath) {
|
|
837
1133
|
if (config === void 0) return;
|
|
838
1134
|
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
839
1135
|
add("stage.closeButton", "closeButton \u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
@@ -865,16 +1161,28 @@ function validateCloseButton(config, add, warn) {
|
|
|
865
1161
|
if (config.image !== void 0 && typeof config.image !== "string") {
|
|
866
1162
|
add("stage.closeButton.image", "image \u5FC5\u987B\u662F\u56FE\u7247\u94FE\u63A5\u5B57\u7B26\u4E32");
|
|
867
1163
|
}
|
|
868
|
-
|
|
869
|
-
|
|
1164
|
+
[
|
|
1165
|
+
["style", config.style],
|
|
1166
|
+
["hoverStyle", config.hoverStyle]
|
|
1167
|
+
].forEach(([field, style]) => {
|
|
1168
|
+
if (!style) return;
|
|
1169
|
+
Object.keys(style).forEach((key) => {
|
|
870
1170
|
if (ALLOWED_STYLE_KEYS.indexOf(key) === -1) {
|
|
871
1171
|
warn(
|
|
872
|
-
`stage.closeButton
|
|
1172
|
+
`stage.closeButton.${field}.${key}`,
|
|
873
1173
|
`\u6837\u5F0F\u5C5E\u6027 "${key}" \u4E0D\u5728\u767D\u540D\u5355\u5185\uFF0C\u6E32\u67D3\u65F6\u4F1A\u88AB\u5FFD\u7565`
|
|
874
1174
|
);
|
|
875
1175
|
}
|
|
876
1176
|
});
|
|
877
|
-
}
|
|
1177
|
+
});
|
|
1178
|
+
if (config.action !== void 0) validateAction(config.action, actionPath, ctx);
|
|
1179
|
+
validateAnimate(
|
|
1180
|
+
config.animate,
|
|
1181
|
+
"stage.closeButton.animate",
|
|
1182
|
+
add,
|
|
1183
|
+
ctx.keyframeNames,
|
|
1184
|
+
ctx.usedKeyframes
|
|
1185
|
+
);
|
|
878
1186
|
}
|
|
879
1187
|
function validateData(data, add) {
|
|
880
1188
|
if (data === void 0) return [];
|
|
@@ -923,8 +1231,41 @@ function validateDerived(derived, dataKeys, stateKeys, add) {
|
|
|
923
1231
|
}
|
|
924
1232
|
});
|
|
925
1233
|
}
|
|
926
|
-
function
|
|
1234
|
+
function validateNodeStyle(style, path, ctx) {
|
|
927
1235
|
const { add, warn } = ctx;
|
|
1236
|
+
if (style) {
|
|
1237
|
+
Object.keys(style).forEach((key) => {
|
|
1238
|
+
if (ALLOWED_STYLE_KEYS.indexOf(key) === -1) {
|
|
1239
|
+
warn(`${path}.${key}`, `\u6837\u5F0F\u5C5E\u6027 "${key}" \u4E0D\u5728\u767D\u540D\u5355\u5185\uFF0C\u6E32\u67D3\u65F6\u4F1A\u88AB\u5FFD\u7565`);
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
if (LENGTH_STYLE_KEYS.indexOf(key) > -1 && !checkLength(style[key])) {
|
|
1243
|
+
add(
|
|
1244
|
+
`${path}.${key}`,
|
|
1245
|
+
`"${style[key]}" \u4E0D\u662F\u5408\u6CD5\u957F\u5EA6\uFF0C\u652F\u6301\u6570\u5B57(px) / '100%' / 'auto' \u7B49`
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
if (key === "aspectRatio") {
|
|
1249
|
+
if (!checkAspectRatio(style[key])) {
|
|
1250
|
+
add(
|
|
1251
|
+
`${path}.aspectRatio`,
|
|
1252
|
+
"aspectRatio \u8981\u5199\u6210 '750 / 200' \u6216 1.5 \u8FD9\u6837\u7684\u5BBD\u9AD8\u6BD4"
|
|
1253
|
+
);
|
|
1254
|
+
} else if (ctx.viewType === "banner") {
|
|
1255
|
+
const ratio = parseAspectRatio(style[key]);
|
|
1256
|
+
if (ratio !== null && ratio < BANNER_MIN_ASPECT_RATIO) {
|
|
1257
|
+
add(
|
|
1258
|
+
`${path}.aspectRatio`,
|
|
1259
|
+
`banner \u7684\u5BBD\u9AD8\u6BD4\u4E0D\u80FD\u5C0F\u4E8E ${BANNER_MIN_ASPECT_RATIO}:1\uFF08\u5F53\u524D\u7EA6 ${ratio.toFixed(1)}:1\uFF09\u3002banner \u5751\u4F4D\u5E38\u9A7B\u9875\u9762\u9876\u90E8\uFF0C\u6BD4\u4F8B\u8D8A\u65B9\u5728\u5BBD\u5C4F\u4E0A\u8D8A\u9AD8 \u2014\u2014 1800px \u5BBD\u7684\u5751\u4F4D\u91CC\uFF0C${ratio.toFixed(1)}:1 \u4F1A\u7B97\u51FA ${Math.round(1800 / ratio)}px \u9AD8\uFF0C\u5403\u6389\u9996\u5C4F\u4E00\u5927\u5757`
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
function validateNode(node, path, layout, ctx) {
|
|
1268
|
+
const { add } = ctx;
|
|
928
1269
|
if (!node || typeof node !== "object" || Array.isArray(node)) {
|
|
929
1270
|
add(path, "\u8282\u70B9\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
930
1271
|
return;
|
|
@@ -950,36 +1291,15 @@ function validateNode(node, path, layout, ctx) {
|
|
|
950
1291
|
});
|
|
951
1292
|
}
|
|
952
1293
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
`"${node.style[key]}" \u4E0D\u662F\u5408\u6CD5\u957F\u5EA6\uFF0C\u652F\u6301\u6570\u5B57(px) / '100%' / 'auto' \u7B49`
|
|
963
|
-
);
|
|
964
|
-
}
|
|
965
|
-
if (key === "aspectRatio") {
|
|
966
|
-
if (!checkAspectRatio(node.style[key])) {
|
|
967
|
-
add(
|
|
968
|
-
`${path}.style.aspectRatio`,
|
|
969
|
-
"aspectRatio \u8981\u5199\u6210 '750 / 200' \u6216 1.5 \u8FD9\u6837\u7684\u5BBD\u9AD8\u6BD4"
|
|
970
|
-
);
|
|
971
|
-
} else if (ctx.viewType === "banner") {
|
|
972
|
-
const ratio = parseAspectRatio(node.style[key]);
|
|
973
|
-
if (ratio !== null && ratio < BANNER_MIN_ASPECT_RATIO) {
|
|
974
|
-
add(
|
|
975
|
-
`${path}.style.aspectRatio`,
|
|
976
|
-
`banner \u7684\u5BBD\u9AD8\u6BD4\u4E0D\u80FD\u5C0F\u4E8E ${BANNER_MIN_ASPECT_RATIO}:1\uFF08\u5F53\u524D\u7EA6 ${ratio.toFixed(1)}:1\uFF09\u3002banner \u5751\u4F4D\u5E38\u9A7B\u9875\u9762\u9876\u90E8\uFF0C\u6BD4\u4F8B\u8D8A\u65B9\u5728\u5BBD\u5C4F\u4E0A\u8D8A\u9AD8 \u2014\u2014 1800px \u5BBD\u7684\u5751\u4F4D\u91CC\uFF0C${ratio.toFixed(1)}:1 \u4F1A\u7B97\u51FA ${Math.round(1800 / ratio)}px \u9AD8\uFF0C\u5403\u6389\u9996\u5C4F\u4E00\u5927\u5757`
|
|
977
|
-
);
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
});
|
|
982
|
-
}
|
|
1294
|
+
validateNodeStyle(node.style, `${path}.style`, ctx);
|
|
1295
|
+
validateNodeStyle(node.hoverStyle, `${path}.hoverStyle`, ctx);
|
|
1296
|
+
validateAnimate(
|
|
1297
|
+
node.animate,
|
|
1298
|
+
`${path}.animate`,
|
|
1299
|
+
add,
|
|
1300
|
+
ctx.keyframeNames,
|
|
1301
|
+
ctx.usedKeyframes
|
|
1302
|
+
);
|
|
983
1303
|
if (node.visibleWhen !== void 0) {
|
|
984
1304
|
if (typeof node.visibleWhen !== "string") {
|
|
985
1305
|
add(`${path}.visibleWhen`, "visibleWhen \u5FC5\u987B\u662F\u8868\u8FBE\u5F0F\u5B57\u7B26\u4E32");
|
|
@@ -992,7 +1312,7 @@ function validateNode(node, path, layout, ctx) {
|
|
|
992
1312
|
validateNodeByType(node, path, ctx);
|
|
993
1313
|
}
|
|
994
1314
|
function validateNodeByType(node, path, ctx) {
|
|
995
|
-
const { add } = ctx;
|
|
1315
|
+
const { add, warn } = ctx;
|
|
996
1316
|
if (node.type === "image" && !node.src) {
|
|
997
1317
|
add(`${path}.src`, "image \u5FC5\u987B\u63D0\u4F9B src");
|
|
998
1318
|
}
|
|
@@ -1013,9 +1333,25 @@ function validateNodeByType(node, path, ctx) {
|
|
|
1013
1333
|
}
|
|
1014
1334
|
}
|
|
1015
1335
|
if (node.type === "countdown") {
|
|
1016
|
-
if (!node.to) {
|
|
1017
|
-
add(
|
|
1018
|
-
|
|
1336
|
+
if (!node.to && node.duration === void 0) {
|
|
1337
|
+
add(
|
|
1338
|
+
`${path}.to`,
|
|
1339
|
+
"countdown \u5FC5\u987B\u63D0\u4F9B to\uFF08\u7ED3\u675F\u65F6\u95F4\uFF09\u6216 duration\uFF08\u76F8\u5BF9\u65F6\u957F\uFF0C\u6BEB\u79D2\uFF0C\u4ECE\u8BE5\u89C6\u56FE\u5C55\u793A\u90A3\u4E00\u523B\u8D77\u7B97\uFF09"
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
if (node.duration !== void 0) {
|
|
1343
|
+
const duration = node.duration;
|
|
1344
|
+
if (typeof duration !== "number" || !isFinite(duration) || duration <= 0) {
|
|
1345
|
+
add(`${path}.duration`, "duration \u5FC5\u987B\u662F\u6B63\u6570\uFF0C\u5355\u4F4D\u6BEB\u79D2\uFF085 \u5206\u949F\u5C31\u5199 300000\uFF09");
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
if (node.to && node.duration !== void 0) {
|
|
1349
|
+
warn(
|
|
1350
|
+
`${path}.duration`,
|
|
1351
|
+
"\u540C\u65F6\u5199\u4E86 to \u548C duration\uFF0C\u8FD0\u884C\u65F6\u4EE5 to \u4E3A\u51C6\uFF0Cduration \u4E0D\u4F1A\u751F\u6548"
|
|
1352
|
+
);
|
|
1353
|
+
}
|
|
1354
|
+
if (!node.to) ; else if (!isValidEndTime(node.to)) {
|
|
1019
1355
|
add(
|
|
1020
1356
|
`${path}.to`,
|
|
1021
1357
|
`"${node.to}" \u4E0D\u662F\u80FD\u8BC6\u522B\u7684\u65F6\u95F4\u3002\u53EF\u4EE5\u5199 "2026-09-01 18:00:00" / "2026-09-01" / "2026\u5E749\u67081\u65E5 18\u70B9" / ISO \u683C\u5F0F / \u65F6\u95F4\u6233`
|
|
@@ -1107,4 +1443,4 @@ function formatIssues(issues) {
|
|
|
1107
1443
|
return issues.map((item) => item.path ? `${item.path}: ${item.message}` : item.message).join("\n");
|
|
1108
1444
|
}
|
|
1109
1445
|
|
|
1110
|
-
export { ACTION_TYPES, ALLOWED_STYLE_KEYS, BANNER_MIN_ASPECT_RATIO, CLOSE_POSITIONS, DEFAULT_USER_FIELDS, DSL_VERSION, NODE_TYPES, SINGLE_VIEW_NAME, TRACK_EVENT_PATTERN, USER_FIELD_LABELS, check, computeParts, createTicker, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isLength, isValidEndTime, normalizeViews, parseEndTime, resolveNodeStyle, toCssStyle, toLength, validate, validateUserFields };
|
|
1446
|
+
export { ACTION_TYPES, ALLOWED_EASINGS, ALLOWED_STYLE_KEYS, BANNER_MIN_ASPECT_RATIO, CLOSE_POSITIONS, CSS_NAME_PATTERN, DEFAULT_USER_FIELDS, DSL_VERSION, NODE_TYPES, SINGLE_VIEW_NAME, TRACK_EVENT_PATTERN, USER_FIELD_LABELS, check, computeParts, createCssBuilder, createTicker, declarations, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isKeyframeOffset, isLength, isValidEasing, isValidEndTime, nextScopeUid, normalizeViews, parseEndTime, resolveNodeStyle, sanitizeCssValue, toCssStyle, toLength, validate, validateUserFields };
|