@yaoxiu/marketing-dsl 2.3.0 → 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 +521 -85
- package/dist/index.d.cts +55 -3
- package/dist/index.d.ts +55 -3
- package/dist/index.js +138 -43
- 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.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as Dsl, R as RuntimeOptions, a as RenderTree, b as DslView, c as DslStyle, C as CssStyle } from './types-
|
|
2
|
-
export {
|
|
1
|
+
import { D as Dsl, R as RuntimeOptions, a as RenderTree, b as DslView, c as DslStyle, C as CssStyle } from './types-8FNxDXp0.cjs';
|
|
2
|
+
export { s as DslAction, j as DslActionBase, h as DslAnimate, q as DslCallAction, l as DslCloseAction, m as DslCloseAllAction, v as DslCloseButton, u as DslClosePosition, y as DslDerived, E as DslHandler, G as DslInteractionEvent, F as DslInteractionTrigger, i as DslKeyframe, f as DslLength, k as DslNavigateAction, t as DslNode, e as DslNodeType, n as DslOpenAction, g as DslRect, r as DslSequenceAction, o as DslSetStateAction, B as DslSource, x as DslSourceRef, w as DslStage, p as DslTrackAction, d as DslViewType, z as RenderElement, A as RenderLayer, J as RuntimeEmit, I as RuntimeEventName, H as RuntimeEvents } from './types-8FNxDXp0.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* 运行时。
|
|
@@ -271,6 +271,58 @@ declare function safeUrl(input: unknown): string;
|
|
|
271
271
|
/** 图片额外允许 data:image/*,方便 base64 占位图 */
|
|
272
272
|
declare function safeImageUrl(input: unknown): string;
|
|
273
273
|
|
|
274
|
+
/**
|
|
275
|
+
* CSS 文本的值处理。
|
|
276
|
+
*
|
|
277
|
+
* 单独成文件是为了让 `hover.ts` 能复用而不与 `css.ts` 形成循环引用
|
|
278
|
+
* (`css.ts` 要拿 `hover.ts` 的预收集结果,反过来 `hover.ts` 只需要这几个纯函数)。
|
|
279
|
+
*
|
|
280
|
+
* 【安全】这里是整个包里唯一做字符串拼接产出样式的地方。行内样式那条路径由框架
|
|
281
|
+
* (Vue 的 style 绑定)负责转义,值再怪也只是不生效;而 CSS 文本是裸拼的,一个 `}`
|
|
282
|
+
* 就能提前闭合规则、接着写任意选择器。所以每个值都要过 `sanitizeCssValue`,
|
|
283
|
+
* 不合格的整条声明直接丢弃 —— 宁可少一条样式,也不能让配置写进任意 CSS。
|
|
284
|
+
*/
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* CSS 值安全过滤。
|
|
288
|
+
*
|
|
289
|
+
* 含 `{` `}` `<` `>` `;` `@` 或注释起始 `/*` 的值一律判为不安全,返回 `null`,
|
|
290
|
+
* 调用方据此丢弃**整条声明**(不是把字符删掉——半截值同样可能拼出意料外的效果)。
|
|
291
|
+
*
|
|
292
|
+
* @returns 安全的值字符串;不安全或空值返回 null
|
|
293
|
+
*/
|
|
294
|
+
declare function sanitizeCssValue(value: unknown): string | null;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* CSS 文本生成。
|
|
298
|
+
*
|
|
299
|
+
* 行内样式表达不了两件事:`@keyframes` 和 `:hover`。所以这一份配置除了渲染树之外,
|
|
300
|
+
* 还要产出一段 CSS 文本(`RenderTree.css`),由框架壳原样塞进一个 `<style>` 元素。
|
|
301
|
+
*
|
|
302
|
+
* 【安全】这是整个包里**唯一**做字符串拼接产出样式的地方。行内样式那条路径由框架
|
|
303
|
+
* (Vue 的 style 绑定)负责转义,值再怪也只是不生效;而 CSS 文本是裸拼的,一个 `}`
|
|
304
|
+
* 就能提前闭合规则、接着写任意选择器(改宿主页面的样式、拿 `content` 做钓鱼文案)。
|
|
305
|
+
* 所以这里的每个值都要过 `sanitizeCssValue`,每个名字都要过 `CSS_NAME_PATTERN`,
|
|
306
|
+
* 不合格的整条声明直接丢弃 —— 宁可少一条动画,也不能让配置写进任意 CSS。
|
|
307
|
+
*
|
|
308
|
+
* 【作用域】同一个页面上可能同时挂着好几份物料,keyframes 名和 hover 类名都是全局的,
|
|
309
|
+
* 重名就会互相覆盖。所以每个运行时实例领一个自增 uid,名字全部带上它。
|
|
310
|
+
*/
|
|
311
|
+
|
|
312
|
+
/** keyframes 名与作用域类名允许的字符:字母开头,字母 / 数字 / 下划线 / 中划线,最长 32 */
|
|
313
|
+
declare const CSS_NAME_PATTERN: RegExp;
|
|
314
|
+
/**
|
|
315
|
+
* 缓动函数白名单。
|
|
316
|
+
*
|
|
317
|
+
* `cubic-bezier(...)` 单独放行(四个数字参数),其余一律按关键字精确匹配 ——
|
|
318
|
+
* 不做「看着像就放行」的模糊判断,缓动值同样会被拼进 CSS 文本。
|
|
319
|
+
*/
|
|
320
|
+
declare const ALLOWED_EASINGS: string[];
|
|
321
|
+
/** 校验用:缓动值是否合法 */
|
|
322
|
+
declare function isValidEasing(value: unknown): boolean;
|
|
323
|
+
/** 校验用:关键帧的偏移键是否合法 */
|
|
324
|
+
declare function isKeyframeOffset(value: unknown): boolean;
|
|
325
|
+
|
|
274
326
|
/**
|
|
275
327
|
* 倒计时。只负责「算时间」,不负责「长什么样」。
|
|
276
328
|
*
|
|
@@ -312,4 +364,4 @@ declare function computeParts(endTime: number, now?: number): CountdownParts;
|
|
|
312
364
|
/** 不给 children 时退化成一行文本,占位符 {d} {h} {hAll} {m} {s} {cs} */
|
|
313
365
|
declare function formatParts(parts: CountdownParts, format?: string): string;
|
|
314
366
|
|
|
315
|
-
export { ACTION_TYPES, ALLOWED_STYLE_KEYS, BANNER_MIN_ASPECT_RATIO, CLOSE_POSITIONS, type CountdownParts, type CountdownPrecision, CssStyle, DEFAULT_USER_FIELDS, DSL_VERSION, Dsl, type DslRuntime, DslStyle, DslView, HOST_HANDLER_LABELS, HOST_HANDLER_NAMES, HOST_SOURCE_LABELS, HOST_SOURCE_NAMES, type HostHandlerName, type HostSourceName, type Issue, NODE_TYPES, type NormalizedViews, RenderTree, RuntimeOptions, SINGLE_VIEW_NAME, TRACK_EVENT_PATTERN, USER_FIELD_LABELS, type ValidateOptions, type ValidateResult, check, computeParts, createRuntime, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isLength, isRegisteredHostHandler, isRegisteredHostSource, isValidEndTime, normalizeViews, parseEndTime, safeImageUrl, safeUrl, toCssStyle, toLength, validate, validateUserFields };
|
|
367
|
+
export { ACTION_TYPES, ALLOWED_EASINGS, ALLOWED_STYLE_KEYS, BANNER_MIN_ASPECT_RATIO, CLOSE_POSITIONS, CSS_NAME_PATTERN, type CountdownParts, type CountdownPrecision, CssStyle, DEFAULT_USER_FIELDS, DSL_VERSION, Dsl, type DslRuntime, DslStyle, DslView, HOST_HANDLER_LABELS, HOST_HANDLER_NAMES, HOST_SOURCE_LABELS, HOST_SOURCE_NAMES, type HostHandlerName, type HostSourceName, type Issue, NODE_TYPES, type NormalizedViews, RenderTree, RuntimeOptions, SINGLE_VIEW_NAME, TRACK_EVENT_PATTERN, USER_FIELD_LABELS, type ValidateOptions, type ValidateResult, check, computeParts, createRuntime, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isKeyframeOffset, isLength, isRegisteredHostHandler, isRegisteredHostSource, isValidEasing, isValidEndTime, normalizeViews, parseEndTime, safeImageUrl, safeUrl, sanitizeCssValue, toCssStyle, toLength, validate, validateUserFields };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as Dsl, R as RuntimeOptions, a as RenderTree, b as DslView, c as DslStyle, C as CssStyle } from './types-
|
|
2
|
-
export {
|
|
1
|
+
import { D as Dsl, R as RuntimeOptions, a as RenderTree, b as DslView, c as DslStyle, C as CssStyle } from './types-8FNxDXp0.js';
|
|
2
|
+
export { s as DslAction, j as DslActionBase, h as DslAnimate, q as DslCallAction, l as DslCloseAction, m as DslCloseAllAction, v as DslCloseButton, u as DslClosePosition, y as DslDerived, E as DslHandler, G as DslInteractionEvent, F as DslInteractionTrigger, i as DslKeyframe, f as DslLength, k as DslNavigateAction, t as DslNode, e as DslNodeType, n as DslOpenAction, g as DslRect, r as DslSequenceAction, o as DslSetStateAction, B as DslSource, x as DslSourceRef, w as DslStage, p as DslTrackAction, d as DslViewType, z as RenderElement, A as RenderLayer, J as RuntimeEmit, I as RuntimeEventName, H as RuntimeEvents } from './types-8FNxDXp0.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* 运行时。
|
|
@@ -271,6 +271,58 @@ declare function safeUrl(input: unknown): string;
|
|
|
271
271
|
/** 图片额外允许 data:image/*,方便 base64 占位图 */
|
|
272
272
|
declare function safeImageUrl(input: unknown): string;
|
|
273
273
|
|
|
274
|
+
/**
|
|
275
|
+
* CSS 文本的值处理。
|
|
276
|
+
*
|
|
277
|
+
* 单独成文件是为了让 `hover.ts` 能复用而不与 `css.ts` 形成循环引用
|
|
278
|
+
* (`css.ts` 要拿 `hover.ts` 的预收集结果,反过来 `hover.ts` 只需要这几个纯函数)。
|
|
279
|
+
*
|
|
280
|
+
* 【安全】这里是整个包里唯一做字符串拼接产出样式的地方。行内样式那条路径由框架
|
|
281
|
+
* (Vue 的 style 绑定)负责转义,值再怪也只是不生效;而 CSS 文本是裸拼的,一个 `}`
|
|
282
|
+
* 就能提前闭合规则、接着写任意选择器。所以每个值都要过 `sanitizeCssValue`,
|
|
283
|
+
* 不合格的整条声明直接丢弃 —— 宁可少一条样式,也不能让配置写进任意 CSS。
|
|
284
|
+
*/
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* CSS 值安全过滤。
|
|
288
|
+
*
|
|
289
|
+
* 含 `{` `}` `<` `>` `;` `@` 或注释起始 `/*` 的值一律判为不安全,返回 `null`,
|
|
290
|
+
* 调用方据此丢弃**整条声明**(不是把字符删掉——半截值同样可能拼出意料外的效果)。
|
|
291
|
+
*
|
|
292
|
+
* @returns 安全的值字符串;不安全或空值返回 null
|
|
293
|
+
*/
|
|
294
|
+
declare function sanitizeCssValue(value: unknown): string | null;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* CSS 文本生成。
|
|
298
|
+
*
|
|
299
|
+
* 行内样式表达不了两件事:`@keyframes` 和 `:hover`。所以这一份配置除了渲染树之外,
|
|
300
|
+
* 还要产出一段 CSS 文本(`RenderTree.css`),由框架壳原样塞进一个 `<style>` 元素。
|
|
301
|
+
*
|
|
302
|
+
* 【安全】这是整个包里**唯一**做字符串拼接产出样式的地方。行内样式那条路径由框架
|
|
303
|
+
* (Vue 的 style 绑定)负责转义,值再怪也只是不生效;而 CSS 文本是裸拼的,一个 `}`
|
|
304
|
+
* 就能提前闭合规则、接着写任意选择器(改宿主页面的样式、拿 `content` 做钓鱼文案)。
|
|
305
|
+
* 所以这里的每个值都要过 `sanitizeCssValue`,每个名字都要过 `CSS_NAME_PATTERN`,
|
|
306
|
+
* 不合格的整条声明直接丢弃 —— 宁可少一条动画,也不能让配置写进任意 CSS。
|
|
307
|
+
*
|
|
308
|
+
* 【作用域】同一个页面上可能同时挂着好几份物料,keyframes 名和 hover 类名都是全局的,
|
|
309
|
+
* 重名就会互相覆盖。所以每个运行时实例领一个自增 uid,名字全部带上它。
|
|
310
|
+
*/
|
|
311
|
+
|
|
312
|
+
/** keyframes 名与作用域类名允许的字符:字母开头,字母 / 数字 / 下划线 / 中划线,最长 32 */
|
|
313
|
+
declare const CSS_NAME_PATTERN: RegExp;
|
|
314
|
+
/**
|
|
315
|
+
* 缓动函数白名单。
|
|
316
|
+
*
|
|
317
|
+
* `cubic-bezier(...)` 单独放行(四个数字参数),其余一律按关键字精确匹配 ——
|
|
318
|
+
* 不做「看着像就放行」的模糊判断,缓动值同样会被拼进 CSS 文本。
|
|
319
|
+
*/
|
|
320
|
+
declare const ALLOWED_EASINGS: string[];
|
|
321
|
+
/** 校验用:缓动值是否合法 */
|
|
322
|
+
declare function isValidEasing(value: unknown): boolean;
|
|
323
|
+
/** 校验用:关键帧的偏移键是否合法 */
|
|
324
|
+
declare function isKeyframeOffset(value: unknown): boolean;
|
|
325
|
+
|
|
274
326
|
/**
|
|
275
327
|
* 倒计时。只负责「算时间」,不负责「长什么样」。
|
|
276
328
|
*
|
|
@@ -312,4 +364,4 @@ declare function computeParts(endTime: number, now?: number): CountdownParts;
|
|
|
312
364
|
/** 不给 children 时退化成一行文本,占位符 {d} {h} {hAll} {m} {s} {cs} */
|
|
313
365
|
declare function formatParts(parts: CountdownParts, format?: string): string;
|
|
314
366
|
|
|
315
|
-
export { ACTION_TYPES, ALLOWED_STYLE_KEYS, BANNER_MIN_ASPECT_RATIO, CLOSE_POSITIONS, type CountdownParts, type CountdownPrecision, CssStyle, DEFAULT_USER_FIELDS, DSL_VERSION, Dsl, type DslRuntime, DslStyle, DslView, HOST_HANDLER_LABELS, HOST_HANDLER_NAMES, HOST_SOURCE_LABELS, HOST_SOURCE_NAMES, type HostHandlerName, type HostSourceName, type Issue, NODE_TYPES, type NormalizedViews, RenderTree, RuntimeOptions, SINGLE_VIEW_NAME, TRACK_EVENT_PATTERN, USER_FIELD_LABELS, type ValidateOptions, type ValidateResult, check, computeParts, createRuntime, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isLength, isRegisteredHostHandler, isRegisteredHostSource, isValidEndTime, normalizeViews, parseEndTime, safeImageUrl, safeUrl, toCssStyle, toLength, validate, validateUserFields };
|
|
367
|
+
export { ACTION_TYPES, ALLOWED_EASINGS, ALLOWED_STYLE_KEYS, BANNER_MIN_ASPECT_RATIO, CLOSE_POSITIONS, CSS_NAME_PATTERN, type CountdownParts, type CountdownPrecision, CssStyle, DEFAULT_USER_FIELDS, DSL_VERSION, Dsl, type DslRuntime, DslStyle, DslView, HOST_HANDLER_LABELS, HOST_HANDLER_NAMES, HOST_SOURCE_LABELS, HOST_SOURCE_NAMES, type HostHandlerName, type HostSourceName, type Issue, NODE_TYPES, type NormalizedViews, RenderTree, RuntimeOptions, SINGLE_VIEW_NAME, TRACK_EVENT_PATTERN, USER_FIELD_LABELS, type ValidateOptions, type ValidateResult, check, computeParts, createRuntime, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isKeyframeOffset, isLength, isRegisteredHostHandler, isRegisteredHostSource, isValidEasing, isValidEndTime, normalizeViews, parseEndTime, safeImageUrl, safeUrl, sanitizeCssValue, toCssStyle, toLength, validate, validateUserFields };
|
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;
|
package/dist/report/index.d.cts
CHANGED
package/dist/report/index.d.ts
CHANGED