@yaoxiu/marketing-dsl 2.2.1 → 2.4.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 +1562 -91
- package/dist/docs/index.d.cts +4 -3
- package/dist/docs/index.d.ts +4 -3
- package/dist/docs/index.js +1530 -95
- package/dist/index.cjs +542 -85
- package/dist/index.d.cts +119 -3
- package/dist/index.d.ts +119 -3
- package/dist/index.js +154 -44
- 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/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { normalizeViews, createTicker,
|
|
2
|
-
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, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isLength, isValidEndTime, normalizeViews, parseEndTime, toCssStyle, toLength, validate, validateUserFields } from './chunk-
|
|
1
|
+
import { normalizeViews, nextScopeUid, createTicker, createCssBuilder, declarations, toCssStyle, interpolateDeep, evaluate, resolveNodeStyle, interpolate, toLength, computeParts, formatParts, parseEndTime } from './chunk-DT3ZINDI.js';
|
|
2
|
+
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, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isKeyframeOffset, isLength, isValidEasing, isValidEndTime, normalizeViews, parseEndTime, sanitizeCssValue, toCssStyle, toLength, validate, validateUserFields } from './chunk-DT3ZINDI.js';
|
|
3
3
|
|
|
4
4
|
// src/url.ts
|
|
5
5
|
var SAFE_PROTOCOLS = ["http:", "https:", "mailto:", "tel:"];
|
|
@@ -103,17 +103,13 @@ function createDispatcher(options) {
|
|
|
103
103
|
// src/resolve.ts
|
|
104
104
|
function resolveTree(input) {
|
|
105
105
|
const { normalized, viewStack, ready } = input;
|
|
106
|
+
const css = createCssBuilder(input.scopeUid, input.keyframes, input.hover);
|
|
107
|
+
const pass = Object.assign({}, input, { css });
|
|
106
108
|
const layers = viewStack.map((name) => ({ name, view: normalized.views[name] })).filter((item) => !!item.view);
|
|
107
109
|
const hasPopup = layers.some((item) => item.view.type === "popup");
|
|
108
110
|
const countdowns = { endTimes: [], precision: "s" };
|
|
109
111
|
const renderLayers = ready ? layers.map(
|
|
110
|
-
(item, index) => resolveLayer(
|
|
111
|
-
item.name,
|
|
112
|
-
item.view,
|
|
113
|
-
index === layers.length - 1,
|
|
114
|
-
input,
|
|
115
|
-
countdowns
|
|
116
|
-
)
|
|
112
|
+
(item, index) => resolveLayer(item.name, item.view, index === layers.length - 1, pass, countdowns)
|
|
117
113
|
) : [];
|
|
118
114
|
return {
|
|
119
115
|
ready: ready && layers.length > 0,
|
|
@@ -124,7 +120,10 @@ function resolveTree(input) {
|
|
|
124
120
|
rootStyle: { position: "relative" },
|
|
125
121
|
layers: renderLayers,
|
|
126
122
|
countdownEndTimes: countdowns.endTimes,
|
|
127
|
-
countdownPrecision: countdowns.precision
|
|
123
|
+
countdownPrecision: countdowns.precision,
|
|
124
|
+
// build 必须在 layers 算完之后调:hover 规则是遍历节点时才登记进来的
|
|
125
|
+
css: css.build(),
|
|
126
|
+
rootClassName: css.rootClassName
|
|
128
127
|
};
|
|
129
128
|
}
|
|
130
129
|
function resolveLayer(name, view, isTop, input, countdowns) {
|
|
@@ -133,7 +132,7 @@ function resolveLayer(name, view, isTop, input, countdowns) {
|
|
|
133
132
|
const isPopup = view.type === "popup";
|
|
134
133
|
const rootLayout = stage.layout === "flow" ? "flow" : "absolute";
|
|
135
134
|
const maskClosable = !!stage.maskClosable;
|
|
136
|
-
const onMaskClick = maskClosable ? () => closeTop("mask") : void 0;
|
|
135
|
+
const onMaskClick = maskClosable ? stage.maskAction ? () => input.dispatch(stage.maskAction, context) : () => closeTop("mask") : void 0;
|
|
137
136
|
const nodes = (view.nodes || []).map(
|
|
138
137
|
(node, index) => resolveNode(node, `${name}-${index}`, rootLayout, context, input, countdowns)
|
|
139
138
|
).filter((el) => !!el);
|
|
@@ -141,20 +140,26 @@ function resolveLayer(name, view, isTop, input, countdowns) {
|
|
|
141
140
|
name,
|
|
142
141
|
type: view.type,
|
|
143
142
|
isTop,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
/**
|
|
144
|
+
* 弹窗层恒画遮罩元素,**非栈顶时只是变透明**(见下方 maskStyle)。
|
|
145
|
+
*
|
|
146
|
+
* 【为什么不是「只有栈顶才画」】那样写视觉上也对(两层实心遮罩叠加会明显变黑,
|
|
147
|
+
* 所以下层必须让位),但下层的遮罩 DOM 会被整个移除、退回栈顶时再新建一个 ——
|
|
148
|
+
* 新元素身上的 `maskAnimate` 于是又播一遍:关掉二次挽留层回到主弹窗时,
|
|
149
|
+
* 背景会重新淡入一次,看起来就是「闪了一下」。元素常驻就没这问题。
|
|
150
|
+
*/
|
|
151
|
+
mask: isPopup && stage.mask !== false,
|
|
152
|
+
// 非栈顶层整层 pointerEvents: none,遮罩点不到,这里也就不必给它挂回调
|
|
153
|
+
onMaskClick: isTop ? onMaskClick : void 0,
|
|
147
154
|
layerStyle: layerStyle(isPopup, isTop),
|
|
148
|
-
maskStyle
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
right: "0",
|
|
152
|
-
bottom: "0",
|
|
153
|
-
left: "0",
|
|
154
|
-
background: "rgba(0, 0, 0, 0.55)"
|
|
155
|
-
},
|
|
155
|
+
// 合并顺序固定:内置默认 → stage.maskStyle 覆盖 → maskAnimate 的 animation 收尾。
|
|
156
|
+
// 遮罩样式也走一遍插值,切 tab 时能跟着变(同 stage.style)
|
|
157
|
+
maskStyle: maskStyle(stage, isTop, context, input),
|
|
156
158
|
scrollStyle: scrollStyle(isPopup),
|
|
157
|
-
stageStyle:
|
|
159
|
+
stageStyle: withAnimation(
|
|
160
|
+
resolveStageStyle(stage, isPopup, context),
|
|
161
|
+
input.css.animation(stage.animate)
|
|
162
|
+
),
|
|
158
163
|
clipStyle: {
|
|
159
164
|
position: "relative",
|
|
160
165
|
height: "100%",
|
|
@@ -162,10 +167,39 @@ function resolveLayer(name, view, isTop, input, countdowns) {
|
|
|
162
167
|
overflow: "hidden",
|
|
163
168
|
boxSizing: "border-box"
|
|
164
169
|
},
|
|
165
|
-
closeButton: resolveCloseButton(stage.closeButton, name, closeTop),
|
|
170
|
+
closeButton: resolveCloseButton(stage.closeButton, name, closeTop, input, context),
|
|
166
171
|
nodes
|
|
167
172
|
};
|
|
168
173
|
}
|
|
174
|
+
function withAnimation(style, animation) {
|
|
175
|
+
if (animation) style.animation = animation;
|
|
176
|
+
return style;
|
|
177
|
+
}
|
|
178
|
+
function maskStyle(stage, isTop, context, input) {
|
|
179
|
+
const style = withAnimation(
|
|
180
|
+
Object.assign(
|
|
181
|
+
{
|
|
182
|
+
position: "absolute",
|
|
183
|
+
top: "0",
|
|
184
|
+
right: "0",
|
|
185
|
+
bottom: "0",
|
|
186
|
+
left: "0",
|
|
187
|
+
background: "rgba(0, 0, 0, 0.55)"
|
|
188
|
+
},
|
|
189
|
+
toCssStyle(interpolateDeep(stage.maskStyle, context))
|
|
190
|
+
),
|
|
191
|
+
input.css.animation(stage.maskAnimate)
|
|
192
|
+
);
|
|
193
|
+
if (isTop) return style;
|
|
194
|
+
style.background = "transparent";
|
|
195
|
+
delete style.backdropFilter;
|
|
196
|
+
delete style.WebkitBackdropFilter;
|
|
197
|
+
return style;
|
|
198
|
+
}
|
|
199
|
+
function withHover(className, owner, css) {
|
|
200
|
+
const hoverClass = css.hoverClass(owner);
|
|
201
|
+
return hoverClass ? `${className} ${hoverClass}` : className;
|
|
202
|
+
}
|
|
169
203
|
function layerStyle(isPopup, isTop) {
|
|
170
204
|
const style = isPopup ? {
|
|
171
205
|
position: "fixed",
|
|
@@ -223,7 +257,8 @@ function resolveStageStyle(stage, isPopup, context) {
|
|
|
223
257
|
}
|
|
224
258
|
return style;
|
|
225
259
|
}
|
|
226
|
-
function resolveCloseButton(config, layerName, closeTop) {
|
|
260
|
+
function resolveCloseButton(config, layerName, closeTop, input, context) {
|
|
261
|
+
const css = input.css;
|
|
227
262
|
if (!config || config.show === false) return void 0;
|
|
228
263
|
const offset = config.offset || [8, 8];
|
|
229
264
|
const size = config.size === void 0 ? 26 : config.size;
|
|
@@ -261,12 +296,14 @@ function resolveCloseButton(config, layerName, closeTop) {
|
|
|
261
296
|
style.right = toLength(offset[0]);
|
|
262
297
|
}
|
|
263
298
|
Object.assign(style, toCssStyle(config.style));
|
|
264
|
-
|
|
299
|
+
withAnimation(style, css.animation(config.animate));
|
|
300
|
+
const className = withHover("dsl-close", config, css);
|
|
301
|
+
const onClick = config.action ? () => input.dispatch(config.action, context) : () => closeTop("close-button");
|
|
265
302
|
if (iconMode === "image") {
|
|
266
303
|
return {
|
|
267
304
|
key: `${layerName}-close`,
|
|
268
305
|
tag: "div",
|
|
269
|
-
className
|
|
306
|
+
className,
|
|
270
307
|
style,
|
|
271
308
|
onClick,
|
|
272
309
|
children: [
|
|
@@ -289,7 +326,7 @@ function resolveCloseButton(config, layerName, closeTop) {
|
|
|
289
326
|
return {
|
|
290
327
|
key: `${layerName}-close`,
|
|
291
328
|
tag: "div",
|
|
292
|
-
className
|
|
329
|
+
className,
|
|
293
330
|
style,
|
|
294
331
|
onClick,
|
|
295
332
|
text: config.icon
|
|
@@ -298,7 +335,7 @@ function resolveCloseButton(config, layerName, closeTop) {
|
|
|
298
335
|
return {
|
|
299
336
|
key: `${layerName}-close`,
|
|
300
337
|
tag: "div",
|
|
301
|
-
className
|
|
338
|
+
className,
|
|
302
339
|
style,
|
|
303
340
|
onClick,
|
|
304
341
|
children: [closeIcon(`${layerName}-close-icon`, size)]
|
|
@@ -386,6 +423,12 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
386
423
|
layout
|
|
387
424
|
)
|
|
388
425
|
);
|
|
426
|
+
withAnimation(style, input.css.animation(node.animate));
|
|
427
|
+
const hoverClass = input.css.hoverClass(node);
|
|
428
|
+
const cls = (type, clickable) => {
|
|
429
|
+
const base = nodeClass(type, clickable);
|
|
430
|
+
return hoverClass ? `${base} ${hoverClass}` : base;
|
|
431
|
+
};
|
|
389
432
|
const onClick = node.action ? () => input.dispatch(node.action, context) : void 0;
|
|
390
433
|
if (onClick && !style.cursor) style.cursor = "pointer";
|
|
391
434
|
switch (node.type) {
|
|
@@ -393,7 +436,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
393
436
|
return {
|
|
394
437
|
key,
|
|
395
438
|
tag: "div",
|
|
396
|
-
className:
|
|
439
|
+
className: cls("box", !!onClick),
|
|
397
440
|
style: Object.assign({ position: "relative" }, style),
|
|
398
441
|
onClick,
|
|
399
442
|
children: resolveChildren(
|
|
@@ -409,7 +452,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
409
452
|
return {
|
|
410
453
|
key,
|
|
411
454
|
tag: "div",
|
|
412
|
-
className:
|
|
455
|
+
className: cls("flex", !!onClick),
|
|
413
456
|
style: Object.assign({ display: "flex" }, style),
|
|
414
457
|
onClick,
|
|
415
458
|
children: resolveChildren(node.children, key, "flow", context, input, countdowns)
|
|
@@ -418,7 +461,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
418
461
|
return {
|
|
419
462
|
key,
|
|
420
463
|
tag: "div",
|
|
421
|
-
className:
|
|
464
|
+
className: cls("repeat", !!onClick),
|
|
422
465
|
style: Object.assign({ display: "flex" }, style),
|
|
423
466
|
onClick,
|
|
424
467
|
children: resolveRepeat(node, key, context, input, countdowns)
|
|
@@ -427,7 +470,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
427
470
|
return {
|
|
428
471
|
key,
|
|
429
472
|
tag: "div",
|
|
430
|
-
className:
|
|
473
|
+
className: cls("tabs", false),
|
|
431
474
|
style: Object.assign({ display: "flex" }, style),
|
|
432
475
|
children: resolveTabs(node, key, context, input)
|
|
433
476
|
};
|
|
@@ -435,7 +478,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
435
478
|
return {
|
|
436
479
|
key,
|
|
437
480
|
tag: "img",
|
|
438
|
-
className:
|
|
481
|
+
className: cls("image", !!onClick),
|
|
439
482
|
src: safeImageUrl(interpolate(node.src, context)),
|
|
440
483
|
style: Object.assign({ display: "block", objectFit: "cover" }, style),
|
|
441
484
|
onClick
|
|
@@ -444,7 +487,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
444
487
|
return {
|
|
445
488
|
key,
|
|
446
489
|
tag: "div",
|
|
447
|
-
className:
|
|
490
|
+
className: cls("text", !!onClick),
|
|
448
491
|
style: Object.assign({ wordBreak: "break-word" }, style),
|
|
449
492
|
text: toText(interpolate(node.content, context)),
|
|
450
493
|
onClick
|
|
@@ -453,7 +496,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
453
496
|
return {
|
|
454
497
|
key,
|
|
455
498
|
tag: "div",
|
|
456
|
-
className:
|
|
499
|
+
className: cls("button", true),
|
|
457
500
|
style: Object.assign(
|
|
458
501
|
{
|
|
459
502
|
display: "flex",
|
|
@@ -469,7 +512,7 @@ function resolveNode(node, key, layout, context, input, countdowns) {
|
|
|
469
512
|
onClick
|
|
470
513
|
};
|
|
471
514
|
case "countdown":
|
|
472
|
-
return resolveCountdown(node, key, style, context, input, countdowns);
|
|
515
|
+
return resolveCountdown(node, key, style, context, input, countdowns, hoverClass);
|
|
473
516
|
// 配置比解释器新时,未知类型降级为不渲染,不阻断整个弹窗
|
|
474
517
|
default:
|
|
475
518
|
return void 0;
|
|
@@ -531,10 +574,23 @@ function resolveTabs(node, key, context, input) {
|
|
|
531
574
|
};
|
|
532
575
|
});
|
|
533
576
|
}
|
|
534
|
-
function
|
|
535
|
-
var _a;
|
|
577
|
+
function resolveCountdownEndTime(node, key, context, input) {
|
|
536
578
|
const endTime = parseEndTime(interpolate(node.to, context));
|
|
579
|
+
if (endTime > 0) return endTime;
|
|
580
|
+
const duration = Number(interpolate(node.duration, context));
|
|
581
|
+
if (!isFinite(duration) || duration <= 0) return 0;
|
|
582
|
+
let start = input.countdownStarts.get(key);
|
|
583
|
+
if (start === void 0) {
|
|
584
|
+
start = Date.now();
|
|
585
|
+
input.countdownStarts.set(key, start);
|
|
586
|
+
}
|
|
587
|
+
return start + duration;
|
|
588
|
+
}
|
|
589
|
+
function resolveCountdown(node, key, style, context, input, countdowns, hoverClass) {
|
|
590
|
+
var _a;
|
|
591
|
+
const endTime = resolveCountdownEndTime(node, key, context, input);
|
|
537
592
|
const parts = computeParts(endTime);
|
|
593
|
+
const cls = (base) => hoverClass ? `${base} ${hoverClass}` : base;
|
|
538
594
|
countdowns.endTimes.push(endTime);
|
|
539
595
|
if (node.precision === "cs") countdowns.precision = "cs";
|
|
540
596
|
if (endTime && node.onEnd) {
|
|
@@ -551,7 +607,7 @@ function resolveCountdown(node, key, style, context, input, countdowns) {
|
|
|
551
607
|
return {
|
|
552
608
|
key,
|
|
553
609
|
tag: "div",
|
|
554
|
-
className: "dsl-node dsl-text dsl-countdown",
|
|
610
|
+
className: cls("dsl-node dsl-text dsl-countdown"),
|
|
555
611
|
style: Object.assign({ wordBreak: "break-word" }, style),
|
|
556
612
|
text: node.endText || "\u5DF2\u7ED3\u675F"
|
|
557
613
|
};
|
|
@@ -560,7 +616,7 @@ function resolveCountdown(node, key, style, context, input, countdowns) {
|
|
|
560
616
|
return {
|
|
561
617
|
key,
|
|
562
618
|
tag: "div",
|
|
563
|
-
className: "dsl-node dsl-text dsl-countdown",
|
|
619
|
+
className: cls("dsl-node dsl-text dsl-countdown"),
|
|
564
620
|
style: Object.assign({ wordBreak: "break-word" }, style),
|
|
565
621
|
text: formatParts(parts, node.format)
|
|
566
622
|
};
|
|
@@ -569,7 +625,7 @@ function resolveCountdown(node, key, style, context, input, countdowns) {
|
|
|
569
625
|
return {
|
|
570
626
|
key,
|
|
571
627
|
tag: "div",
|
|
572
|
-
className: "dsl-node dsl-flex dsl-countdown",
|
|
628
|
+
className: cls("dsl-node dsl-flex dsl-countdown"),
|
|
573
629
|
style: Object.assign({ display: "flex" }, style),
|
|
574
630
|
children: resolveChildren(
|
|
575
631
|
node.children,
|
|
@@ -585,6 +641,37 @@ function toText(value) {
|
|
|
585
641
|
return value === void 0 || value === null ? "" : String(value);
|
|
586
642
|
}
|
|
587
643
|
|
|
644
|
+
// src/hover.ts
|
|
645
|
+
function collectHoverStyles(views, uid, context) {
|
|
646
|
+
const rules = [];
|
|
647
|
+
const classOf = /* @__PURE__ */ new WeakMap();
|
|
648
|
+
const scope = `.dsl-r${uid}`;
|
|
649
|
+
let seed = 0;
|
|
650
|
+
const register = (owner, style) => {
|
|
651
|
+
if (!owner || !style || typeof style !== "object") return;
|
|
652
|
+
const body = declarations(toCssStyle(interpolateDeep(style, context)));
|
|
653
|
+
if (!body) return;
|
|
654
|
+
seed += 1;
|
|
655
|
+
const className = `dsl-hv-${uid}-${seed}`;
|
|
656
|
+
classOf.set(owner, className);
|
|
657
|
+
rules.push(`${scope} .${className}:hover{${body}}`);
|
|
658
|
+
};
|
|
659
|
+
const walkNode = (node) => {
|
|
660
|
+
if (!node || typeof node !== "object") return;
|
|
661
|
+
register(node, node.hoverStyle);
|
|
662
|
+
(node.children || []).forEach(walkNode);
|
|
663
|
+
walkNode(node.template);
|
|
664
|
+
};
|
|
665
|
+
Object.keys(views).forEach((name) => {
|
|
666
|
+
const view = views[name];
|
|
667
|
+
if (!view || typeof view !== "object") return;
|
|
668
|
+
const closeButton = view.stage && view.stage.closeButton;
|
|
669
|
+
register(closeButton, closeButton && closeButton.hoverStyle);
|
|
670
|
+
(view.nodes || []).forEach(walkNode);
|
|
671
|
+
});
|
|
672
|
+
return { rules, classOf };
|
|
673
|
+
}
|
|
674
|
+
|
|
588
675
|
// src/runtime.ts
|
|
589
676
|
var noopEmit = () => {
|
|
590
677
|
};
|
|
@@ -596,6 +683,7 @@ function createRuntime(dsl, options = {}) {
|
|
|
596
683
|
editMode: !!options.editMode
|
|
597
684
|
};
|
|
598
685
|
const normalized = normalizeViews(dsl);
|
|
686
|
+
const scopeUid = nextScopeUid();
|
|
599
687
|
let state = Object.assign({}, dsl.state || {});
|
|
600
688
|
let resolvedData = {};
|
|
601
689
|
let viewStack = [normalized.entry];
|
|
@@ -604,6 +692,7 @@ function createRuntime(dsl, options = {}) {
|
|
|
604
692
|
let destroyed = false;
|
|
605
693
|
const listeners = /* @__PURE__ */ new Set();
|
|
606
694
|
const countdownEnds = /* @__PURE__ */ new Map();
|
|
695
|
+
const countdownStarts = /* @__PURE__ */ new Map();
|
|
607
696
|
const notify = () => {
|
|
608
697
|
if (destroyed) return;
|
|
609
698
|
listeners.forEach((fn) => fn());
|
|
@@ -737,7 +826,9 @@ function createRuntime(dsl, options = {}) {
|
|
|
737
826
|
});
|
|
738
827
|
}
|
|
739
828
|
loadData();
|
|
829
|
+
let hover;
|
|
740
830
|
function getTree() {
|
|
831
|
+
if (!hover) hover = collectHoverStyles(normalized.views, scopeUid, buildContext());
|
|
741
832
|
const tree = resolveTree({
|
|
742
833
|
normalized,
|
|
743
834
|
viewStack,
|
|
@@ -747,7 +838,11 @@ function createRuntime(dsl, options = {}) {
|
|
|
747
838
|
dispatch,
|
|
748
839
|
setState,
|
|
749
840
|
closeTop,
|
|
750
|
-
countdownEnds
|
|
841
|
+
countdownEnds,
|
|
842
|
+
countdownStarts,
|
|
843
|
+
scopeUid,
|
|
844
|
+
keyframes: dsl.keyframes,
|
|
845
|
+
hover
|
|
751
846
|
});
|
|
752
847
|
ticker.sync(tree.countdownEndTimes, tree.countdownPrecision);
|
|
753
848
|
return tree;
|
|
@@ -774,4 +869,19 @@ function createRuntime(dsl, options = {}) {
|
|
|
774
869
|
};
|
|
775
870
|
}
|
|
776
871
|
|
|
777
|
-
|
|
872
|
+
// src/host-capabilities.ts
|
|
873
|
+
var HOST_SOURCE_NAMES = ["shopAchievements", "listRenewTiers"];
|
|
874
|
+
var HOST_HANDLER_NAMES = [];
|
|
875
|
+
var HOST_SOURCE_LABELS = {
|
|
876
|
+
shopAchievements: "\u5E97\u94FA\u6210\u679C\u7EDF\u8BA1\uFF08\u5DF2\u4E0A\u67B6/\u8FD0\u884C\u4E2D/\u767D\u5E95\u56FE\u7B49\u683C\u5B50\u6570\u7EC4\uFF09",
|
|
877
|
+
listRenewTiers: "\u7EED\u8D39\u4EF7\u683C\u6863\u4F4D\u5217\u8868\uFF08\u6708\u4ED8/\u5B63\u4ED8/\u534A\u5E74/\u5E74\u4ED8\uFF0C\u542B\u4E0B\u5355\u5730\u5740\uFF09"
|
|
878
|
+
};
|
|
879
|
+
var HOST_HANDLER_LABELS = {};
|
|
880
|
+
function isRegisteredHostSource(name) {
|
|
881
|
+
return HOST_SOURCE_NAMES.indexOf(name) > -1;
|
|
882
|
+
}
|
|
883
|
+
function isRegisteredHostHandler(name) {
|
|
884
|
+
return HOST_HANDLER_NAMES.indexOf(name) > -1;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
export { HOST_HANDLER_LABELS, HOST_HANDLER_NAMES, HOST_SOURCE_LABELS, HOST_SOURCE_NAMES, createRuntime, isRegisteredHostHandler, isRegisteredHostSource, safeImageUrl, safeUrl };
|
package/dist/report/index.d.cts
CHANGED
package/dist/report/index.d.ts
CHANGED
|
@@ -15,6 +15,40 @@ type DslLength = number | string;
|
|
|
15
15
|
type DslRect = [DslLength, DslLength, DslLength, DslLength];
|
|
16
16
|
/** 样式对象,键必须在白名单内,值可以带 {{ }} */
|
|
17
17
|
type DslStyle = Record<string, unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* 动画声明。
|
|
20
|
+
*
|
|
21
|
+
* 只描述「播哪一组 keyframes、怎么播」,关键帧本身写在 `Dsl.keyframes` 里。
|
|
22
|
+
* 解释器把它落成**行内** `animation` 简写,keyframes 定义才进 `RenderTree.css`——
|
|
23
|
+
* 这样同一份配置在页面上出现多次也不会互相污染(keyframes 名带作用域前缀)。
|
|
24
|
+
*/
|
|
25
|
+
interface DslAnimate {
|
|
26
|
+
/** keyframes 名:必须在 `dsl.keyframes` 里定义过 */
|
|
27
|
+
name: string;
|
|
28
|
+
/** 时长,毫秒 */
|
|
29
|
+
duration: number;
|
|
30
|
+
/** 延迟,毫秒,默认 0。交错入场就靠它错开 */
|
|
31
|
+
delay?: number;
|
|
32
|
+
/**
|
|
33
|
+
* 缓动函数。白名单:
|
|
34
|
+
* linear / ease / ease-in / ease-out / ease-in-out / step-start / step-end /
|
|
35
|
+
* cubic-bezier(x, x, x, x)。默认 `ease`
|
|
36
|
+
*/
|
|
37
|
+
easing?: string;
|
|
38
|
+
/** 播放次数,默认 1;`'infinite'` 表示无限循环(呼吸、扫光这类) */
|
|
39
|
+
iteration?: number | 'infinite';
|
|
40
|
+
/** 播放方向,默认 `normal` */
|
|
41
|
+
direction?: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';
|
|
42
|
+
/** 填充模式,默认 `both`(入场动画必须保留终态,否则播完会弹回初始值) */
|
|
43
|
+
fill?: 'none' | 'forwards' | 'backwards' | 'both';
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 一组关键帧:偏移量 → 样式。
|
|
47
|
+
*
|
|
48
|
+
* 偏移量只接受 `'from'` / `'to'` / `'0%'` 这三种写法;
|
|
49
|
+
* 样式仍要过 style 白名单,写了白名单外的属性会被丢弃。
|
|
50
|
+
*/
|
|
51
|
+
type DslKeyframe = Record<string, DslStyle>;
|
|
18
52
|
interface DslActionBase {
|
|
19
53
|
type: string;
|
|
20
54
|
}
|
|
@@ -65,6 +99,15 @@ interface DslNode {
|
|
|
65
99
|
/** 表达式,为假时整个节点不渲染 */
|
|
66
100
|
visibleWhen?: string;
|
|
67
101
|
action?: DslAction;
|
|
102
|
+
/** 入场 / 循环动画 */
|
|
103
|
+
animate?: DslAnimate;
|
|
104
|
+
/**
|
|
105
|
+
* 鼠标悬停时叠加的样式。
|
|
106
|
+
*
|
|
107
|
+
* **只会生成 CSS 规则,不进行内联**——行内样式没有 `:hover` 这个东西,
|
|
108
|
+
* 想在行内做只能靠 JS 监听鼠标事件,那既要框架壳配合、又天然丢触屏。
|
|
109
|
+
*/
|
|
110
|
+
hoverStyle?: DslStyle;
|
|
68
111
|
/** box / flex / countdown */
|
|
69
112
|
children?: DslNode[];
|
|
70
113
|
/** repeat / tabs:数组来源表达式 */
|
|
@@ -86,6 +129,14 @@ interface DslNode {
|
|
|
86
129
|
text?: string;
|
|
87
130
|
/** countdown */
|
|
88
131
|
to?: string;
|
|
132
|
+
/**
|
|
133
|
+
* countdown 的相对时长(毫秒)。
|
|
134
|
+
*
|
|
135
|
+
* 从**该视图展示那一刻**起算,「每次打开重新倒 5 分钟」这类写法用它。
|
|
136
|
+
* 与 `to` 同时存在时 `to` 优先(绝对时间更明确)。
|
|
137
|
+
* 起算时间戳钉在运行时里,重算渲染树不会把剩余时间刷回去。
|
|
138
|
+
*/
|
|
139
|
+
duration?: number;
|
|
89
140
|
precision?: 's' | 'cs';
|
|
90
141
|
as?: string;
|
|
91
142
|
format?: string;
|
|
@@ -95,6 +146,15 @@ interface DslNode {
|
|
|
95
146
|
type DslClosePosition = 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center';
|
|
96
147
|
interface DslCloseButton {
|
|
97
148
|
show?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* 点它做什么。
|
|
151
|
+
*
|
|
152
|
+
* 不配就是默认行为「关掉当前这一层」(多视图里等于弹栈退回上一层,单视图即整体关闭)。
|
|
153
|
+
* 配了就**只**执行这个动作、不再自动关层 —— 「点 ✕ 先弹二次挽留」正是靠它:
|
|
154
|
+
* `{ "type": "open", "view": "confirm", "mode": "stack" }`。
|
|
155
|
+
* 想在动作里顺带关掉,自己写 `close` / `closeAll` 进去。
|
|
156
|
+
*/
|
|
157
|
+
action?: DslAction;
|
|
98
158
|
position?: DslClosePosition;
|
|
99
159
|
/** [x, y],负值把按钮移到弹窗外面 */
|
|
100
160
|
offset?: [DslLength, DslLength];
|
|
@@ -102,6 +162,10 @@ interface DslCloseButton {
|
|
|
102
162
|
icon?: string;
|
|
103
163
|
image?: string;
|
|
104
164
|
style?: DslStyle;
|
|
165
|
+
/** 悬停样式,只生成 CSS 规则 */
|
|
166
|
+
hoverStyle?: DslStyle;
|
|
167
|
+
/** 关闭按钮自己的动画(如延迟淡入,先让用户看内容) */
|
|
168
|
+
animate?: DslAnimate;
|
|
105
169
|
}
|
|
106
170
|
interface DslStage {
|
|
107
171
|
width?: DslLength;
|
|
@@ -110,8 +174,30 @@ interface DslStage {
|
|
|
110
174
|
layout?: 'absolute' | 'flow';
|
|
111
175
|
mask?: boolean;
|
|
112
176
|
maskClosable?: boolean;
|
|
177
|
+
/**
|
|
178
|
+
* 点遮罩做什么。
|
|
179
|
+
*
|
|
180
|
+
* 不配就是默认行为「关掉当前这一层」。配了就执行这个动作而不关层
|
|
181
|
+
* (原页面点遮罩弹二次挽留就是这么来的)。
|
|
182
|
+
*
|
|
183
|
+
* 与 `maskClosable` 各管一件事、不冲突:`maskClosable: false` 时遮罩根本不响应点击,
|
|
184
|
+
* 这个动作也不会执行;`maskAction` 只决定「点了之后做什么」。
|
|
185
|
+
*/
|
|
186
|
+
maskAction?: DslAction;
|
|
113
187
|
closeButton?: DslCloseButton;
|
|
188
|
+
/**
|
|
189
|
+
* 遮罩自身样式,走 style 白名单。
|
|
190
|
+
*
|
|
191
|
+
* 内置默认是 `rgba(0, 0, 0, 0.55)` 纯色,这里配的会**覆盖**默认值
|
|
192
|
+
* (常用 `background` 改深浅、`backdropFilter: 'blur(4px)'` 加毛玻璃)。
|
|
193
|
+
* `maskAnimate` 生成的 `animation` 仍排在最后,不会被这里的配置盖掉。
|
|
194
|
+
*/
|
|
195
|
+
maskStyle?: DslStyle;
|
|
114
196
|
style?: DslStyle;
|
|
197
|
+
/** 舞台入场动画(弹窗主体的弹入 / 放大) */
|
|
198
|
+
animate?: DslAnimate;
|
|
199
|
+
/** 遮罩入场动画(一般是淡入,和舞台分开配才能各走各的时长) */
|
|
200
|
+
maskAnimate?: DslAnimate;
|
|
115
201
|
/** 曝光埋点:数据就绪、视图真正展示时触发一次 */
|
|
116
202
|
onShow?: DslAction;
|
|
117
203
|
/** 关闭埋点:点 X、点遮罩、close 动作、被 replace 顶掉都会触发 */
|
|
@@ -145,6 +231,13 @@ interface Dsl extends Partial<DslView> {
|
|
|
145
231
|
derived?: Record<string, DslDerived>;
|
|
146
232
|
views?: Record<string, DslView>;
|
|
147
233
|
entry?: string;
|
|
234
|
+
/**
|
|
235
|
+
* 关键帧库,`{ 名字: { 偏移量: 样式 } }`。
|
|
236
|
+
*
|
|
237
|
+
* 全配置共用一份(多视图之间也共用),`animate.name` 引用这里的名字。
|
|
238
|
+
* 名字只能是 `^[a-zA-Z][a-zA-Z0-9_-]{0,31}$`——它会被拼进 CSS 文本,必须可控。
|
|
239
|
+
*/
|
|
240
|
+
keyframes?: Record<string, DslKeyframe>;
|
|
148
241
|
}
|
|
149
242
|
/** 已经算好的行内样式,键是 CSS 属性名(驼峰),值是最终字符串 */
|
|
150
243
|
type CssStyle = Record<string, string>;
|
|
@@ -198,8 +291,15 @@ interface RenderLayer {
|
|
|
198
291
|
name: string;
|
|
199
292
|
type: DslViewType;
|
|
200
293
|
isTop: boolean;
|
|
201
|
-
/**
|
|
294
|
+
/**
|
|
295
|
+
* 画不画遮罩元素。弹窗层恒为 true(`stage.mask: false` 除外),**包括非栈顶的层**。
|
|
296
|
+
*
|
|
297
|
+
* 遮挡效果由 `maskStyle` 控制:非栈顶层的底色是 transparent,实际遮挡永远只有栈顶那层
|
|
298
|
+
* (两层实心遮罩叠加会明显变黑)。元素本身常驻是刻意的 —— 按 isTop 增删这个 DOM 节点,
|
|
299
|
+
* 会让下层的 `maskAnimate` 在退回栈顶时重播一遍,表现为「关掉二次挽留层,背景闪了一下」。
|
|
300
|
+
*/
|
|
202
301
|
mask: boolean;
|
|
302
|
+
/** 点遮罩的回调。只有栈顶那层才有:非栈顶层整层 pointerEvents: none,点不到 */
|
|
203
303
|
onMaskClick?: () => void;
|
|
204
304
|
layerStyle: CssStyle;
|
|
205
305
|
maskStyle: CssStyle;
|
|
@@ -227,6 +327,20 @@ interface RenderTree {
|
|
|
227
327
|
countdownEndTimes: number[];
|
|
228
328
|
/** 只要有一个倒计时要厘秒,整体就按厘秒刷 */
|
|
229
329
|
countdownPrecision: 's' | 'cs';
|
|
330
|
+
/**
|
|
331
|
+
* 这份配置要注入页面的全部 CSS 文本(keyframes 定义 + hover 规则 + reduced-motion 兜底)。
|
|
332
|
+
*
|
|
333
|
+
* 没有动画也没有 hover 时是**空串**,此时框架壳不该建 `<style>` 元素。
|
|
334
|
+
* 内容已经过安全过滤,壳直接塞进 `<style>` 即可,**不得在壳里做任何生成或判断**。
|
|
335
|
+
*/
|
|
336
|
+
css: string;
|
|
337
|
+
/**
|
|
338
|
+
* 根容器类名,形如 `dsl-renderer dsl-r3`。
|
|
339
|
+
*
|
|
340
|
+
* 后半截是这个运行时实例的作用域前缀,`css` 里的每条规则都挂在它下面——
|
|
341
|
+
* 同一个页面上出现多份物料时,各自的 hover 规则不会串台。
|
|
342
|
+
*/
|
|
343
|
+
rootClassName: string;
|
|
230
344
|
}
|
|
231
345
|
type DslSource = (params: Record<string, unknown>, user: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
232
346
|
type DslHandler = (params: Record<string, unknown>) => void;
|
|
@@ -342,4 +456,4 @@ interface RuntimeOptions {
|
|
|
342
456
|
emit?: RuntimeEmit;
|
|
343
457
|
}
|
|
344
458
|
|
|
345
|
-
export type {
|
|
459
|
+
export type { RenderLayer as A, DslSource as B, CssStyle as C, Dsl as D, DslHandler as E, DslInteractionTrigger as F, DslInteractionEvent as G, RuntimeEvents as H, RuntimeEventName as I, RuntimeEmit as J, RuntimeOptions as R, RenderTree as a, DslView as b, DslStyle as c, DslViewType as d, DslNodeType as e, DslLength as f, DslRect as g, DslAnimate as h, DslKeyframe as i, DslActionBase as j, DslNavigateAction as k, DslCloseAction as l, DslCloseAllAction as m, DslOpenAction as n, DslSetStateAction as o, DslTrackAction as p, DslCallAction as q, DslSequenceAction as r, DslAction as s, DslNode as t, DslClosePosition as u, DslCloseButton as v, DslStage as w, DslSourceRef as x, DslDerived as y, RenderElement as z };
|