@yaoxiu/marketing-dsl 1.2.0 → 1.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 +43 -1
- package/dist/chunk-FKZKLCVV.js +968 -0
- package/dist/docs/index.cjs +3726 -0
- package/dist/docs/index.d.cts +170 -0
- package/dist/docs/index.d.ts +170 -0
- package/dist/docs/index.js +3624 -0
- package/dist/index.cjs +118 -15
- package/dist/index.d.cts +17 -283
- package/dist/index.d.ts +17 -283
- package/dist/index.js +86 -949
- package/dist/types-BgUaJUAu.d.cts +293 -0
- package/dist/types-BgUaJUAu.d.ts +293 -0
- package/package.json +6 -1
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSL 的类型定义。
|
|
3
|
+
*
|
|
4
|
+
* 分两组:
|
|
5
|
+
* - Dsl* 是运营写的配置,也就是存进数据库的那份 JSON
|
|
6
|
+
* - Render* 是 core 算完之后交给框架层的渲染树,纯数据,跟框架无关
|
|
7
|
+
*
|
|
8
|
+
* 框架层只认 Render*,永远不该去读 Dsl*——一旦读了,解析逻辑就又漏回渲染层了。
|
|
9
|
+
*/
|
|
10
|
+
type DslViewType = 'popup' | 'banner' | 'notice';
|
|
11
|
+
type DslNodeType = 'box' | 'flex' | 'repeat' | 'image' | 'text' | 'button' | 'tabs' | 'countdown';
|
|
12
|
+
/** 长度:数字按 px,字符串原样交给 CSS,可以带 {{ }} */
|
|
13
|
+
type DslLength = number | string;
|
|
14
|
+
/** rect: [x, y, width, height],绝对定位容器里用 */
|
|
15
|
+
type DslRect = [DslLength, DslLength, DslLength, DslLength];
|
|
16
|
+
/** 样式对象,键必须在白名单内,值可以带 {{ }} */
|
|
17
|
+
type DslStyle = Record<string, unknown>;
|
|
18
|
+
interface DslActionBase {
|
|
19
|
+
type: string;
|
|
20
|
+
}
|
|
21
|
+
interface DslNavigateAction extends DslActionBase {
|
|
22
|
+
type: 'navigate';
|
|
23
|
+
url: string;
|
|
24
|
+
target?: '_self' | '_blank';
|
|
25
|
+
}
|
|
26
|
+
interface DslCloseAction extends DslActionBase {
|
|
27
|
+
type: 'close';
|
|
28
|
+
reason?: string;
|
|
29
|
+
}
|
|
30
|
+
interface DslCloseAllAction extends DslActionBase {
|
|
31
|
+
type: 'closeAll';
|
|
32
|
+
reason?: string;
|
|
33
|
+
}
|
|
34
|
+
interface DslOpenAction extends DslActionBase {
|
|
35
|
+
type: 'open';
|
|
36
|
+
view: string;
|
|
37
|
+
mode?: 'stack' | 'replace';
|
|
38
|
+
}
|
|
39
|
+
interface DslSetStateAction extends DslActionBase {
|
|
40
|
+
type: 'setState';
|
|
41
|
+
key: string;
|
|
42
|
+
value: unknown;
|
|
43
|
+
}
|
|
44
|
+
interface DslTrackAction extends DslActionBase {
|
|
45
|
+
type: 'track';
|
|
46
|
+
event: string;
|
|
47
|
+
params?: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
/** 调用宿主注册的方法,名字必须在 handlers 白名单内 */
|
|
50
|
+
interface DslCallAction extends DslActionBase {
|
|
51
|
+
type: 'call';
|
|
52
|
+
name: string;
|
|
53
|
+
params?: Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
interface DslSequenceAction extends DslActionBase {
|
|
56
|
+
type: 'sequence';
|
|
57
|
+
actions: DslAction[];
|
|
58
|
+
}
|
|
59
|
+
type DslAction = DslNavigateAction | DslCloseAction | DslCloseAllAction | DslOpenAction | DslSetStateAction | DslTrackAction | DslCallAction | DslSequenceAction;
|
|
60
|
+
interface DslNode {
|
|
61
|
+
id?: string;
|
|
62
|
+
type: DslNodeType;
|
|
63
|
+
rect?: DslRect;
|
|
64
|
+
style?: DslStyle;
|
|
65
|
+
/** 表达式,为假时整个节点不渲染 */
|
|
66
|
+
visibleWhen?: string;
|
|
67
|
+
action?: DslAction;
|
|
68
|
+
/** box / flex / countdown */
|
|
69
|
+
children?: DslNode[];
|
|
70
|
+
/** repeat / tabs:数组来源表达式 */
|
|
71
|
+
bind?: string;
|
|
72
|
+
/** repeat */
|
|
73
|
+
template?: DslNode;
|
|
74
|
+
itemName?: string;
|
|
75
|
+
indexName?: string;
|
|
76
|
+
/** tabs */
|
|
77
|
+
stateKey?: string;
|
|
78
|
+
labelField?: string;
|
|
79
|
+
itemStyle?: DslStyle;
|
|
80
|
+
activeItemStyle?: DslStyle;
|
|
81
|
+
/** image */
|
|
82
|
+
src?: string;
|
|
83
|
+
/** text */
|
|
84
|
+
content?: string;
|
|
85
|
+
/** button */
|
|
86
|
+
text?: string;
|
|
87
|
+
/** countdown */
|
|
88
|
+
to?: string;
|
|
89
|
+
precision?: 's' | 'cs';
|
|
90
|
+
as?: string;
|
|
91
|
+
format?: string;
|
|
92
|
+
endText?: string;
|
|
93
|
+
onEnd?: DslAction;
|
|
94
|
+
}
|
|
95
|
+
type DslClosePosition = 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center';
|
|
96
|
+
interface DslCloseButton {
|
|
97
|
+
show?: boolean;
|
|
98
|
+
position?: DslClosePosition;
|
|
99
|
+
/** [x, y],负值把按钮移到弹窗外面 */
|
|
100
|
+
offset?: [DslLength, DslLength];
|
|
101
|
+
size?: DslLength;
|
|
102
|
+
icon?: string;
|
|
103
|
+
image?: string;
|
|
104
|
+
style?: DslStyle;
|
|
105
|
+
}
|
|
106
|
+
interface DslStage {
|
|
107
|
+
width?: DslLength;
|
|
108
|
+
height?: DslLength;
|
|
109
|
+
/** absolute(默认)子节点用 rect 定位;flow 子节点按 flex 排 */
|
|
110
|
+
layout?: 'absolute' | 'flow';
|
|
111
|
+
mask?: boolean;
|
|
112
|
+
maskClosable?: boolean;
|
|
113
|
+
closeButton?: DslCloseButton;
|
|
114
|
+
style?: DslStyle;
|
|
115
|
+
/** 曝光埋点:数据就绪、视图真正展示时触发一次 */
|
|
116
|
+
onShow?: DslAction;
|
|
117
|
+
/** 关闭埋点:点 X、点遮罩、close 动作、被 replace 顶掉都会触发 */
|
|
118
|
+
onClose?: DslAction;
|
|
119
|
+
}
|
|
120
|
+
interface DslView {
|
|
121
|
+
type: DslViewType;
|
|
122
|
+
stage: DslStage;
|
|
123
|
+
nodes: DslNode[];
|
|
124
|
+
}
|
|
125
|
+
/** 后端数据源引用,名字必须在 sources 白名单内 */
|
|
126
|
+
interface DslSourceRef {
|
|
127
|
+
$source: string;
|
|
128
|
+
params?: Record<string, unknown>;
|
|
129
|
+
}
|
|
130
|
+
interface DslDerived {
|
|
131
|
+
/** data 里的数组字段名 */
|
|
132
|
+
list: string;
|
|
133
|
+
/** state 里的下标字段名 */
|
|
134
|
+
indexBy: string;
|
|
135
|
+
}
|
|
136
|
+
/** 一份完整配置。单视图直接写 type/stage/nodes,多视图写 views + entry */
|
|
137
|
+
interface Dsl extends Partial<DslView> {
|
|
138
|
+
version: number;
|
|
139
|
+
meta?: {
|
|
140
|
+
name?: string;
|
|
141
|
+
[key: string]: unknown;
|
|
142
|
+
};
|
|
143
|
+
data?: Record<string, unknown | DslSourceRef>;
|
|
144
|
+
state?: Record<string, unknown>;
|
|
145
|
+
derived?: Record<string, DslDerived>;
|
|
146
|
+
views?: Record<string, DslView>;
|
|
147
|
+
entry?: string;
|
|
148
|
+
}
|
|
149
|
+
/** 已经算好的行内样式,键是 CSS 属性名(驼峰),值是最终字符串 */
|
|
150
|
+
type CssStyle = Record<string, string>;
|
|
151
|
+
interface RenderElement {
|
|
152
|
+
/** 稳定 key,框架层做列表 diff 用 */
|
|
153
|
+
key: string;
|
|
154
|
+
/**
|
|
155
|
+
* 画成什么。div 带 text 就是文本节点,带 children 就是容器。
|
|
156
|
+
* svg / line 用来画矢量图标(如默认的关闭 ×),形状与尺寸都由 core 算好。
|
|
157
|
+
*/
|
|
158
|
+
tag: 'div' | 'img' | 'svg' | 'line';
|
|
159
|
+
/**
|
|
160
|
+
* 类名,如 "dsl-node dsl-text is-clickable"。
|
|
161
|
+
* 不参与样式(样式全在 style 里),纯粹是留给调试和宿主微调的钩子。
|
|
162
|
+
* 由 core 统一生成,不让各框架壳各拼一套,否则 DOM 结构会慢慢跑偏。
|
|
163
|
+
*/
|
|
164
|
+
className: string;
|
|
165
|
+
style: CssStyle;
|
|
166
|
+
text?: string;
|
|
167
|
+
src?: string;
|
|
168
|
+
/**
|
|
169
|
+
* 直接落到 DOM 上的属性(`viewBox`、`stroke-width` 这类带连字符的 SVG 属性)。
|
|
170
|
+
* 框架层必须用 setAttribute 语义写入(Vue 2 里就是 `attrs`),不能走 domProps——
|
|
171
|
+
* SVG 元素上没有对应的 JS 属性,写 property 不生效。
|
|
172
|
+
*/
|
|
173
|
+
attrs?: Record<string, string>;
|
|
174
|
+
/**
|
|
175
|
+
* 有值才是可点的,动作和上下文都已经绑好,框架层直接挂上去即可。
|
|
176
|
+
*
|
|
177
|
+
* 【框架层必须遵守】挂上去时要阻止冒泡。
|
|
178
|
+
* 父子都可点是常见排版——公告条整条点开弹窗、行末 × 关闭公告条——
|
|
179
|
+
* 不阻止的话点 × 会连带触发外层,关掉的瞬间又弹出来。
|
|
180
|
+
* 没有 onClick 的节点不要绑任何事件,让它正常冒泡到有 onClick 的祖先。
|
|
181
|
+
*/
|
|
182
|
+
onClick?: () => void;
|
|
183
|
+
children?: RenderElement[];
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 一个视图层。框架层照着这个结构套四层 div 就行,不需要自带任何 CSS:
|
|
187
|
+
*
|
|
188
|
+
* div(layerStyle)
|
|
189
|
+
* div(maskStyle) ← mask 为 true 时才画
|
|
190
|
+
* div(scrollStyle)
|
|
191
|
+
* div(stageStyle)
|
|
192
|
+
* div(clipStyle) ← 圆角裁切,关闭按钮留在外面才能溢出
|
|
193
|
+
* ...nodes
|
|
194
|
+
* closeButton ← 有配才有
|
|
195
|
+
*/
|
|
196
|
+
interface RenderLayer {
|
|
197
|
+
/** 视图名,同时用作 key */
|
|
198
|
+
name: string;
|
|
199
|
+
type: DslViewType;
|
|
200
|
+
isTop: boolean;
|
|
201
|
+
/** 画不画遮罩。只有栈顶的弹窗层才为 true */
|
|
202
|
+
mask: boolean;
|
|
203
|
+
onMaskClick?: () => void;
|
|
204
|
+
layerStyle: CssStyle;
|
|
205
|
+
maskStyle: CssStyle;
|
|
206
|
+
scrollStyle: CssStyle;
|
|
207
|
+
stageStyle: CssStyle;
|
|
208
|
+
clipStyle: CssStyle;
|
|
209
|
+
/** 关闭按钮,没配就是 undefined */
|
|
210
|
+
closeButton?: RenderElement;
|
|
211
|
+
nodes: RenderElement[];
|
|
212
|
+
}
|
|
213
|
+
interface RenderTree {
|
|
214
|
+
/** 数据没回来或加载失败时为 false,框架层什么都不画 */
|
|
215
|
+
ready: boolean;
|
|
216
|
+
/** 有弹窗层时框架层需要撑满宿主容器 */
|
|
217
|
+
hasPopup: boolean;
|
|
218
|
+
/** 根容器样式,已按 hasPopup 算好 */
|
|
219
|
+
rootStyle: CssStyle;
|
|
220
|
+
layers: RenderLayer[];
|
|
221
|
+
/** 这一轮里所有倒计时的结束时间戳,运行时据此决定要不要起定时器 */
|
|
222
|
+
countdownEndTimes: number[];
|
|
223
|
+
/** 只要有一个倒计时要厘秒,整体就按厘秒刷 */
|
|
224
|
+
countdownPrecision: 's' | 'cs';
|
|
225
|
+
}
|
|
226
|
+
type DslSource = (params: Record<string, unknown>, user: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
227
|
+
type DslHandler = (params: Record<string, unknown>) => void;
|
|
228
|
+
interface RuntimeEvents {
|
|
229
|
+
ready: {
|
|
230
|
+
keys: string[];
|
|
231
|
+
};
|
|
232
|
+
close: {
|
|
233
|
+
reason: string;
|
|
234
|
+
};
|
|
235
|
+
track: {
|
|
236
|
+
event: string;
|
|
237
|
+
params: Record<string, unknown>;
|
|
238
|
+
};
|
|
239
|
+
navigate: {
|
|
240
|
+
url: string;
|
|
241
|
+
target: string;
|
|
242
|
+
};
|
|
243
|
+
call: {
|
|
244
|
+
name: string;
|
|
245
|
+
params: Record<string, unknown>;
|
|
246
|
+
};
|
|
247
|
+
'state-change': Record<string, unknown>;
|
|
248
|
+
'view-change': {
|
|
249
|
+
view: string;
|
|
250
|
+
mode: string;
|
|
251
|
+
stack: string[];
|
|
252
|
+
closed?: string;
|
|
253
|
+
};
|
|
254
|
+
open: {
|
|
255
|
+
view: string;
|
|
256
|
+
mode: string;
|
|
257
|
+
};
|
|
258
|
+
error: {
|
|
259
|
+
type: 'unknown-source';
|
|
260
|
+
name: string;
|
|
261
|
+
} | {
|
|
262
|
+
type: 'unknown-view';
|
|
263
|
+
name: string;
|
|
264
|
+
} | {
|
|
265
|
+
type: 'unknown-handler';
|
|
266
|
+
name: string;
|
|
267
|
+
} | {
|
|
268
|
+
type: 'unknown-action';
|
|
269
|
+
action: string;
|
|
270
|
+
} | {
|
|
271
|
+
type: 'unsafe-url';
|
|
272
|
+
raw: string;
|
|
273
|
+
} | {
|
|
274
|
+
type: 'data-source-failed';
|
|
275
|
+
message: string;
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
type RuntimeEventName = keyof RuntimeEvents;
|
|
279
|
+
type RuntimeEmit = <K extends RuntimeEventName>(event: K, payload: RuntimeEvents[K]) => void;
|
|
280
|
+
interface RuntimeOptions {
|
|
281
|
+
/** 宿主注入的用户上下文,配置里用 {{ user.xxx }} 读 */
|
|
282
|
+
user?: Record<string, unknown>;
|
|
283
|
+
/** 数据源白名单 */
|
|
284
|
+
sources?: Record<string, DslSource>;
|
|
285
|
+
/** call 动作的方法白名单 */
|
|
286
|
+
handlers?: Record<string, DslHandler>;
|
|
287
|
+
/** 编辑态:动作只上报不真正执行 */
|
|
288
|
+
editMode?: boolean;
|
|
289
|
+
/** 所有事件都从这里出去,宿主自己决定怎么处理 */
|
|
290
|
+
emit?: RuntimeEmit;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export type { DslHandler as A, RuntimeEvents as B, CssStyle as C, Dsl as D, RuntimeEventName as E, RuntimeEmit as F, RuntimeOptions as R, RenderTree as a, DslView as b, DslStyle as c, DslViewType as d, DslNodeType as e, DslLength as f, DslRect as g, DslActionBase as h, DslNavigateAction as i, DslCloseAction as j, DslCloseAllAction as k, DslOpenAction as l, DslSetStateAction as m, DslTrackAction as n, DslCallAction as o, DslSequenceAction as p, DslAction as q, DslNode as r, DslClosePosition as s, DslCloseButton as t, DslStage as u, DslSourceRef as v, DslDerived as w, RenderElement as x, RenderLayer as y, DslSource as z };
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSL 的类型定义。
|
|
3
|
+
*
|
|
4
|
+
* 分两组:
|
|
5
|
+
* - Dsl* 是运营写的配置,也就是存进数据库的那份 JSON
|
|
6
|
+
* - Render* 是 core 算完之后交给框架层的渲染树,纯数据,跟框架无关
|
|
7
|
+
*
|
|
8
|
+
* 框架层只认 Render*,永远不该去读 Dsl*——一旦读了,解析逻辑就又漏回渲染层了。
|
|
9
|
+
*/
|
|
10
|
+
type DslViewType = 'popup' | 'banner' | 'notice';
|
|
11
|
+
type DslNodeType = 'box' | 'flex' | 'repeat' | 'image' | 'text' | 'button' | 'tabs' | 'countdown';
|
|
12
|
+
/** 长度:数字按 px,字符串原样交给 CSS,可以带 {{ }} */
|
|
13
|
+
type DslLength = number | string;
|
|
14
|
+
/** rect: [x, y, width, height],绝对定位容器里用 */
|
|
15
|
+
type DslRect = [DslLength, DslLength, DslLength, DslLength];
|
|
16
|
+
/** 样式对象,键必须在白名单内,值可以带 {{ }} */
|
|
17
|
+
type DslStyle = Record<string, unknown>;
|
|
18
|
+
interface DslActionBase {
|
|
19
|
+
type: string;
|
|
20
|
+
}
|
|
21
|
+
interface DslNavigateAction extends DslActionBase {
|
|
22
|
+
type: 'navigate';
|
|
23
|
+
url: string;
|
|
24
|
+
target?: '_self' | '_blank';
|
|
25
|
+
}
|
|
26
|
+
interface DslCloseAction extends DslActionBase {
|
|
27
|
+
type: 'close';
|
|
28
|
+
reason?: string;
|
|
29
|
+
}
|
|
30
|
+
interface DslCloseAllAction extends DslActionBase {
|
|
31
|
+
type: 'closeAll';
|
|
32
|
+
reason?: string;
|
|
33
|
+
}
|
|
34
|
+
interface DslOpenAction extends DslActionBase {
|
|
35
|
+
type: 'open';
|
|
36
|
+
view: string;
|
|
37
|
+
mode?: 'stack' | 'replace';
|
|
38
|
+
}
|
|
39
|
+
interface DslSetStateAction extends DslActionBase {
|
|
40
|
+
type: 'setState';
|
|
41
|
+
key: string;
|
|
42
|
+
value: unknown;
|
|
43
|
+
}
|
|
44
|
+
interface DslTrackAction extends DslActionBase {
|
|
45
|
+
type: 'track';
|
|
46
|
+
event: string;
|
|
47
|
+
params?: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
/** 调用宿主注册的方法,名字必须在 handlers 白名单内 */
|
|
50
|
+
interface DslCallAction extends DslActionBase {
|
|
51
|
+
type: 'call';
|
|
52
|
+
name: string;
|
|
53
|
+
params?: Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
interface DslSequenceAction extends DslActionBase {
|
|
56
|
+
type: 'sequence';
|
|
57
|
+
actions: DslAction[];
|
|
58
|
+
}
|
|
59
|
+
type DslAction = DslNavigateAction | DslCloseAction | DslCloseAllAction | DslOpenAction | DslSetStateAction | DslTrackAction | DslCallAction | DslSequenceAction;
|
|
60
|
+
interface DslNode {
|
|
61
|
+
id?: string;
|
|
62
|
+
type: DslNodeType;
|
|
63
|
+
rect?: DslRect;
|
|
64
|
+
style?: DslStyle;
|
|
65
|
+
/** 表达式,为假时整个节点不渲染 */
|
|
66
|
+
visibleWhen?: string;
|
|
67
|
+
action?: DslAction;
|
|
68
|
+
/** box / flex / countdown */
|
|
69
|
+
children?: DslNode[];
|
|
70
|
+
/** repeat / tabs:数组来源表达式 */
|
|
71
|
+
bind?: string;
|
|
72
|
+
/** repeat */
|
|
73
|
+
template?: DslNode;
|
|
74
|
+
itemName?: string;
|
|
75
|
+
indexName?: string;
|
|
76
|
+
/** tabs */
|
|
77
|
+
stateKey?: string;
|
|
78
|
+
labelField?: string;
|
|
79
|
+
itemStyle?: DslStyle;
|
|
80
|
+
activeItemStyle?: DslStyle;
|
|
81
|
+
/** image */
|
|
82
|
+
src?: string;
|
|
83
|
+
/** text */
|
|
84
|
+
content?: string;
|
|
85
|
+
/** button */
|
|
86
|
+
text?: string;
|
|
87
|
+
/** countdown */
|
|
88
|
+
to?: string;
|
|
89
|
+
precision?: 's' | 'cs';
|
|
90
|
+
as?: string;
|
|
91
|
+
format?: string;
|
|
92
|
+
endText?: string;
|
|
93
|
+
onEnd?: DslAction;
|
|
94
|
+
}
|
|
95
|
+
type DslClosePosition = 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center';
|
|
96
|
+
interface DslCloseButton {
|
|
97
|
+
show?: boolean;
|
|
98
|
+
position?: DslClosePosition;
|
|
99
|
+
/** [x, y],负值把按钮移到弹窗外面 */
|
|
100
|
+
offset?: [DslLength, DslLength];
|
|
101
|
+
size?: DslLength;
|
|
102
|
+
icon?: string;
|
|
103
|
+
image?: string;
|
|
104
|
+
style?: DslStyle;
|
|
105
|
+
}
|
|
106
|
+
interface DslStage {
|
|
107
|
+
width?: DslLength;
|
|
108
|
+
height?: DslLength;
|
|
109
|
+
/** absolute(默认)子节点用 rect 定位;flow 子节点按 flex 排 */
|
|
110
|
+
layout?: 'absolute' | 'flow';
|
|
111
|
+
mask?: boolean;
|
|
112
|
+
maskClosable?: boolean;
|
|
113
|
+
closeButton?: DslCloseButton;
|
|
114
|
+
style?: DslStyle;
|
|
115
|
+
/** 曝光埋点:数据就绪、视图真正展示时触发一次 */
|
|
116
|
+
onShow?: DslAction;
|
|
117
|
+
/** 关闭埋点:点 X、点遮罩、close 动作、被 replace 顶掉都会触发 */
|
|
118
|
+
onClose?: DslAction;
|
|
119
|
+
}
|
|
120
|
+
interface DslView {
|
|
121
|
+
type: DslViewType;
|
|
122
|
+
stage: DslStage;
|
|
123
|
+
nodes: DslNode[];
|
|
124
|
+
}
|
|
125
|
+
/** 后端数据源引用,名字必须在 sources 白名单内 */
|
|
126
|
+
interface DslSourceRef {
|
|
127
|
+
$source: string;
|
|
128
|
+
params?: Record<string, unknown>;
|
|
129
|
+
}
|
|
130
|
+
interface DslDerived {
|
|
131
|
+
/** data 里的数组字段名 */
|
|
132
|
+
list: string;
|
|
133
|
+
/** state 里的下标字段名 */
|
|
134
|
+
indexBy: string;
|
|
135
|
+
}
|
|
136
|
+
/** 一份完整配置。单视图直接写 type/stage/nodes,多视图写 views + entry */
|
|
137
|
+
interface Dsl extends Partial<DslView> {
|
|
138
|
+
version: number;
|
|
139
|
+
meta?: {
|
|
140
|
+
name?: string;
|
|
141
|
+
[key: string]: unknown;
|
|
142
|
+
};
|
|
143
|
+
data?: Record<string, unknown | DslSourceRef>;
|
|
144
|
+
state?: Record<string, unknown>;
|
|
145
|
+
derived?: Record<string, DslDerived>;
|
|
146
|
+
views?: Record<string, DslView>;
|
|
147
|
+
entry?: string;
|
|
148
|
+
}
|
|
149
|
+
/** 已经算好的行内样式,键是 CSS 属性名(驼峰),值是最终字符串 */
|
|
150
|
+
type CssStyle = Record<string, string>;
|
|
151
|
+
interface RenderElement {
|
|
152
|
+
/** 稳定 key,框架层做列表 diff 用 */
|
|
153
|
+
key: string;
|
|
154
|
+
/**
|
|
155
|
+
* 画成什么。div 带 text 就是文本节点,带 children 就是容器。
|
|
156
|
+
* svg / line 用来画矢量图标(如默认的关闭 ×),形状与尺寸都由 core 算好。
|
|
157
|
+
*/
|
|
158
|
+
tag: 'div' | 'img' | 'svg' | 'line';
|
|
159
|
+
/**
|
|
160
|
+
* 类名,如 "dsl-node dsl-text is-clickable"。
|
|
161
|
+
* 不参与样式(样式全在 style 里),纯粹是留给调试和宿主微调的钩子。
|
|
162
|
+
* 由 core 统一生成,不让各框架壳各拼一套,否则 DOM 结构会慢慢跑偏。
|
|
163
|
+
*/
|
|
164
|
+
className: string;
|
|
165
|
+
style: CssStyle;
|
|
166
|
+
text?: string;
|
|
167
|
+
src?: string;
|
|
168
|
+
/**
|
|
169
|
+
* 直接落到 DOM 上的属性(`viewBox`、`stroke-width` 这类带连字符的 SVG 属性)。
|
|
170
|
+
* 框架层必须用 setAttribute 语义写入(Vue 2 里就是 `attrs`),不能走 domProps——
|
|
171
|
+
* SVG 元素上没有对应的 JS 属性,写 property 不生效。
|
|
172
|
+
*/
|
|
173
|
+
attrs?: Record<string, string>;
|
|
174
|
+
/**
|
|
175
|
+
* 有值才是可点的,动作和上下文都已经绑好,框架层直接挂上去即可。
|
|
176
|
+
*
|
|
177
|
+
* 【框架层必须遵守】挂上去时要阻止冒泡。
|
|
178
|
+
* 父子都可点是常见排版——公告条整条点开弹窗、行末 × 关闭公告条——
|
|
179
|
+
* 不阻止的话点 × 会连带触发外层,关掉的瞬间又弹出来。
|
|
180
|
+
* 没有 onClick 的节点不要绑任何事件,让它正常冒泡到有 onClick 的祖先。
|
|
181
|
+
*/
|
|
182
|
+
onClick?: () => void;
|
|
183
|
+
children?: RenderElement[];
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 一个视图层。框架层照着这个结构套四层 div 就行,不需要自带任何 CSS:
|
|
187
|
+
*
|
|
188
|
+
* div(layerStyle)
|
|
189
|
+
* div(maskStyle) ← mask 为 true 时才画
|
|
190
|
+
* div(scrollStyle)
|
|
191
|
+
* div(stageStyle)
|
|
192
|
+
* div(clipStyle) ← 圆角裁切,关闭按钮留在外面才能溢出
|
|
193
|
+
* ...nodes
|
|
194
|
+
* closeButton ← 有配才有
|
|
195
|
+
*/
|
|
196
|
+
interface RenderLayer {
|
|
197
|
+
/** 视图名,同时用作 key */
|
|
198
|
+
name: string;
|
|
199
|
+
type: DslViewType;
|
|
200
|
+
isTop: boolean;
|
|
201
|
+
/** 画不画遮罩。只有栈顶的弹窗层才为 true */
|
|
202
|
+
mask: boolean;
|
|
203
|
+
onMaskClick?: () => void;
|
|
204
|
+
layerStyle: CssStyle;
|
|
205
|
+
maskStyle: CssStyle;
|
|
206
|
+
scrollStyle: CssStyle;
|
|
207
|
+
stageStyle: CssStyle;
|
|
208
|
+
clipStyle: CssStyle;
|
|
209
|
+
/** 关闭按钮,没配就是 undefined */
|
|
210
|
+
closeButton?: RenderElement;
|
|
211
|
+
nodes: RenderElement[];
|
|
212
|
+
}
|
|
213
|
+
interface RenderTree {
|
|
214
|
+
/** 数据没回来或加载失败时为 false,框架层什么都不画 */
|
|
215
|
+
ready: boolean;
|
|
216
|
+
/** 有弹窗层时框架层需要撑满宿主容器 */
|
|
217
|
+
hasPopup: boolean;
|
|
218
|
+
/** 根容器样式,已按 hasPopup 算好 */
|
|
219
|
+
rootStyle: CssStyle;
|
|
220
|
+
layers: RenderLayer[];
|
|
221
|
+
/** 这一轮里所有倒计时的结束时间戳,运行时据此决定要不要起定时器 */
|
|
222
|
+
countdownEndTimes: number[];
|
|
223
|
+
/** 只要有一个倒计时要厘秒,整体就按厘秒刷 */
|
|
224
|
+
countdownPrecision: 's' | 'cs';
|
|
225
|
+
}
|
|
226
|
+
type DslSource = (params: Record<string, unknown>, user: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
227
|
+
type DslHandler = (params: Record<string, unknown>) => void;
|
|
228
|
+
interface RuntimeEvents {
|
|
229
|
+
ready: {
|
|
230
|
+
keys: string[];
|
|
231
|
+
};
|
|
232
|
+
close: {
|
|
233
|
+
reason: string;
|
|
234
|
+
};
|
|
235
|
+
track: {
|
|
236
|
+
event: string;
|
|
237
|
+
params: Record<string, unknown>;
|
|
238
|
+
};
|
|
239
|
+
navigate: {
|
|
240
|
+
url: string;
|
|
241
|
+
target: string;
|
|
242
|
+
};
|
|
243
|
+
call: {
|
|
244
|
+
name: string;
|
|
245
|
+
params: Record<string, unknown>;
|
|
246
|
+
};
|
|
247
|
+
'state-change': Record<string, unknown>;
|
|
248
|
+
'view-change': {
|
|
249
|
+
view: string;
|
|
250
|
+
mode: string;
|
|
251
|
+
stack: string[];
|
|
252
|
+
closed?: string;
|
|
253
|
+
};
|
|
254
|
+
open: {
|
|
255
|
+
view: string;
|
|
256
|
+
mode: string;
|
|
257
|
+
};
|
|
258
|
+
error: {
|
|
259
|
+
type: 'unknown-source';
|
|
260
|
+
name: string;
|
|
261
|
+
} | {
|
|
262
|
+
type: 'unknown-view';
|
|
263
|
+
name: string;
|
|
264
|
+
} | {
|
|
265
|
+
type: 'unknown-handler';
|
|
266
|
+
name: string;
|
|
267
|
+
} | {
|
|
268
|
+
type: 'unknown-action';
|
|
269
|
+
action: string;
|
|
270
|
+
} | {
|
|
271
|
+
type: 'unsafe-url';
|
|
272
|
+
raw: string;
|
|
273
|
+
} | {
|
|
274
|
+
type: 'data-source-failed';
|
|
275
|
+
message: string;
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
type RuntimeEventName = keyof RuntimeEvents;
|
|
279
|
+
type RuntimeEmit = <K extends RuntimeEventName>(event: K, payload: RuntimeEvents[K]) => void;
|
|
280
|
+
interface RuntimeOptions {
|
|
281
|
+
/** 宿主注入的用户上下文,配置里用 {{ user.xxx }} 读 */
|
|
282
|
+
user?: Record<string, unknown>;
|
|
283
|
+
/** 数据源白名单 */
|
|
284
|
+
sources?: Record<string, DslSource>;
|
|
285
|
+
/** call 动作的方法白名单 */
|
|
286
|
+
handlers?: Record<string, DslHandler>;
|
|
287
|
+
/** 编辑态:动作只上报不真正执行 */
|
|
288
|
+
editMode?: boolean;
|
|
289
|
+
/** 所有事件都从这里出去,宿主自己决定怎么处理 */
|
|
290
|
+
emit?: RuntimeEmit;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export type { DslHandler as A, RuntimeEvents as B, CssStyle as C, Dsl as D, RuntimeEventName as E, RuntimeEmit as F, RuntimeOptions as R, RenderTree as a, DslView as b, DslStyle as c, DslViewType as d, DslNodeType as e, DslLength as f, DslRect as g, DslActionBase as h, DslNavigateAction as i, DslCloseAction as j, DslCloseAllAction as k, DslOpenAction as l, DslSetStateAction as m, DslTrackAction as n, DslCallAction as o, DslSequenceAction as p, DslAction as q, DslNode as r, DslClosePosition as s, DslCloseButton as t, DslStage as u, DslSourceRef as v, DslDerived as w, RenderElement as x, RenderLayer as y, DslSource as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaoxiu/marketing-dsl",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "营销弹窗 DSL 解释器核心,纯逻辑无框架依赖",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"types": "./dist/index.d.ts",
|
|
17
17
|
"import": "./dist/index.js",
|
|
18
18
|
"require": "./dist/index.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./docs": {
|
|
21
|
+
"types": "./dist/docs/index.d.ts",
|
|
22
|
+
"import": "./dist/docs/index.js",
|
|
23
|
+
"require": "./dist/docs/index.cjs"
|
|
19
24
|
}
|
|
20
25
|
},
|
|
21
26
|
"files": [
|