@yaoxiu/marketing-dsl 2.7.0 → 2.9.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 CHANGED
@@ -111,7 +111,7 @@ const unsubscribe = runtime.subscribe(() => {
111
111
  // 还会把用户已经切到的 tab 打回默认值。
112
112
  runtime.update({ user: { shopName: '麦爆了旗舰店' } });
113
113
 
114
- // 卸载时必须调,否则倒计时定时器不会停
114
+ // 卸载时必须调,否则倒计时 / 自动轮播的定时器不会停
115
115
  runtime.destroy();
116
116
  unsubscribe();
117
117
  ```
@@ -143,6 +143,8 @@ unsubscribe();
143
143
  countdownPrecision: 's',
144
144
  css: '', // 要注入的 CSS 文本(keyframes + hover 规则),没有动画时是空串
145
145
  rootClassName: 'dsl-renderer dsl-r3', // 根容器类名,后半截是这份物料的作用域前缀
146
+ onRootEnter: fn, // 可选:只有配了 pauseOnHover 的自动轮播才有,见下
147
+ onRootLeave: fn,
146
148
  }
147
149
  ```
148
150
 
@@ -167,6 +169,28 @@ unsubscribe();
167
169
 
168
170
  父子都可点是常见排版——公告条整条点开弹窗、行末 × 关闭公告条——不阻止的话点 × 会连带触发外层,关掉的瞬间又弹出来。没有 `onClick` 的节点不要绑任何事件,让它正常冒泡到有 `onClick` 的祖先。
169
171
 
172
+ ### 自动轮播(`derived.xxx.autoplay`)
173
+
174
+ ```jsonc
175
+ "state": { "slide": 0 },
176
+ "derived": { "cur": { "list": "slides", "indexBy": "slide",
177
+ "autoplay": { "interval": 4000, "pauseOnHover": true } } }
178
+ ```
179
+
180
+ 配了就开启、始终循环(没有 `enabled` / `loop`):每 `interval` 毫秒把 `state[indexBy]` 推到
181
+ `(当前 + 1) % 列表长度`。定时逻辑在 `src/autoplay.ts`,`runtime.ts` 只接线。
182
+
183
+ - **与点击同一条路径**:到点调的就是运行时的 `setState`,`state-change` 照常派发,文案 / 图片 /
184
+ tabs 指示器 / 链接 / 运营配的 `transition` 与手动点击效果完全一致,不引入额外切换动画
185
+ - 任何 `setState` 改了同一个 key(用户点 tab)都会**重新计时**
186
+ - 列表长度 ≤1、数据源未就绪或失败时不播;数据源回来后开始;`editMode` 下照样播
187
+ - `destroy()` 清掉全部轮播定时器,之后不会再 `setState`
188
+ - `pauseOnHover` 需要壳配合:渲染树此时带 `onRootEnter` / `onRootLeave`,
189
+ 壳在根容器上绑 `mouseenter` / `mouseleave` 原样调用即可(**有才绑、不拦冒泡、不加判断**)
190
+
191
+ 校验:`autoplay` 须是对象;`interval` 须是 ≥300 的数字(单位毫秒,否则 error);
192
+ `pauseOnHover` 须是布尔;其余字段只告警(运行时忽略)。字段表导出为 `AUTOPLAY_FIELDS`。
193
+
170
194
  ## 运行时事件(`emit`)
171
195
 
172
196
  | 事件 | 何时抛 | 宿主拿它干嘛 |
@@ -399,6 +399,109 @@ function createTicker(options) {
399
399
  return { sync, stop };
400
400
  }
401
401
 
402
+ // src/autoplay.ts
403
+ var DERIVED_FIELDS = ["list", "indexBy", "autoplay"];
404
+ var AUTOPLAY_FIELDS = ["interval", "pauseOnHover"];
405
+ var AUTOPLAY_MIN_INTERVAL = 300;
406
+ function collectEntries(derived) {
407
+ const entries = [];
408
+ Object.keys(derived || {}).forEach((name) => {
409
+ const config = (derived || {})[name];
410
+ const autoplay = config && config.autoplay;
411
+ if (!autoplay || typeof autoplay !== "object") return;
412
+ const interval = Number(autoplay.interval);
413
+ if (!isFinite(interval) || interval < AUTOPLAY_MIN_INTERVAL) return;
414
+ entries.push({
415
+ list: config.list,
416
+ indexBy: config.indexBy,
417
+ interval,
418
+ pauseOnHover: autoplay.pauseOnHover === true,
419
+ timer: null
420
+ });
421
+ });
422
+ return entries;
423
+ }
424
+ function createAutoplay(options) {
425
+ const entries = collectEntries(options.derived);
426
+ let started = false;
427
+ let hovering = false;
428
+ let stopped = false;
429
+ function clear(entry) {
430
+ if (entry.timer) {
431
+ clearTimeout(entry.timer);
432
+ entry.timer = null;
433
+ }
434
+ }
435
+ function advance(entry) {
436
+ entry.timer = null;
437
+ const length = options.getLength(entry.list);
438
+ if (length <= 1) return;
439
+ const index = options.getIndex(entry.indexBy);
440
+ options.setState(entry.indexBy, (index + 1) % length);
441
+ }
442
+ function schedule(entry) {
443
+ clear(entry);
444
+ if (stopped || !started) return;
445
+ if (hovering && entry.pauseOnHover) return;
446
+ if (options.getLength(entry.list) <= 1) return;
447
+ entry.timer = setTimeout(() => advance(entry), entry.interval);
448
+ }
449
+ return {
450
+ pausable: entries.some((entry) => entry.pauseOnHover),
451
+ start() {
452
+ started = true;
453
+ entries.forEach(schedule);
454
+ },
455
+ reset(key) {
456
+ entries.forEach((entry) => {
457
+ if (entry.indexBy === key) schedule(entry);
458
+ });
459
+ },
460
+ pause() {
461
+ hovering = true;
462
+ entries.forEach((entry) => {
463
+ if (entry.pauseOnHover) clear(entry);
464
+ });
465
+ },
466
+ resume() {
467
+ hovering = false;
468
+ entries.forEach((entry) => {
469
+ if (entry.pauseOnHover) schedule(entry);
470
+ });
471
+ },
472
+ stop() {
473
+ stopped = true;
474
+ entries.forEach(clear);
475
+ }
476
+ };
477
+ }
478
+ function validateAutoplay(autoplay, path, add, warn) {
479
+ if (autoplay === void 0) return;
480
+ if (!autoplay || typeof autoplay !== "object" || Array.isArray(autoplay)) {
481
+ add(path, 'autoplay \u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u5F62\u5982 { "interval": 4000, "pauseOnHover": true }');
482
+ return;
483
+ }
484
+ const config = autoplay;
485
+ const interval = config.interval;
486
+ if (typeof interval !== "number" || !isFinite(interval) || interval < AUTOPLAY_MIN_INTERVAL) {
487
+ add(
488
+ `${path}.interval`,
489
+ `interval \u5FC5\u987B\u662F\u4E0D\u5C0F\u4E8E ${AUTOPLAY_MIN_INTERVAL} \u7684\u6570\u5B57\uFF0C\u5355\u4F4D\u6BEB\u79D2\uFF08\u5982 4000 \u8868\u793A 4 \u79D2\uFF09\uFF0C\u5F53\u524D\u4E3A ${JSON.stringify(interval)}`
490
+ );
491
+ }
492
+ if (config.pauseOnHover !== void 0 && typeof config.pauseOnHover !== "boolean") {
493
+ add(`${path}.pauseOnHover`, "pauseOnHover \u5FC5\u987B\u662F true \u6216 false");
494
+ }
495
+ Object.keys(config).forEach((key) => {
496
+ if (AUTOPLAY_FIELDS.indexOf(key) === -1) {
497
+ warn(
498
+ `${path}.${key}`,
499
+ `autoplay \u4E0D\u8BA4\u8BC6 "${key}"\uFF0C\u4F1A\u88AB\u5FFD\u7565\u3002\u53EF\u7528\uFF1A${AUTOPLAY_FIELDS.join(" / ")}\uFF08\u914D\u4E86 autoplay \u5C31\u5F00\u542F\u4E14\u59CB\u7EC8\u5FAA\u73AF\uFF0C\u4E0D\u9700\u8981 enabled / loop\uFF09`
500
+ );
501
+ }
502
+ });
503
+ }
504
+
402
505
  // src/views.ts
403
506
  var SINGLE_VIEW_NAME = "main";
404
507
  function normalizeViews(dsl) {
@@ -962,7 +1065,7 @@ function validate(dsl, options) {
962
1065
  }
963
1066
  const dataKeys = validateData(doc.data, add);
964
1067
  const stateKeys = validateState(doc.state, add);
965
- validateDerived(doc.derived, dataKeys, stateKeys, add);
1068
+ validateDerived(doc.derived, dataKeys, stateKeys, add, warn);
966
1069
  const keyframeNames = validateKeyframes(doc.keyframes, add, warn);
967
1070
  const isMulti = doc.views !== void 0;
968
1071
  if (isMulti) validateMultiView(doc, add);
@@ -1211,7 +1314,7 @@ function validateState(state, add) {
1211
1314
  }
1212
1315
  return Object.keys(state);
1213
1316
  }
1214
- function validateDerived(derived, dataKeys, stateKeys, add) {
1317
+ function validateDerived(derived, dataKeys, stateKeys, add, warn) {
1215
1318
  if (derived === void 0) return;
1216
1319
  if (typeof derived !== "object" || derived === null || Array.isArray(derived)) {
1217
1320
  add("derived", "derived \u5FC5\u987B\u662F\u5BF9\u8C61");
@@ -1229,6 +1332,7 @@ function validateDerived(derived, dataKeys, stateKeys, add) {
1229
1332
  } else if (stateKeys.indexOf(item.indexBy) === -1) {
1230
1333
  add(`derived.${key}.indexBy`, `state \u4E2D\u4E0D\u5B58\u5728 "${item.indexBy}"`);
1231
1334
  }
1335
+ validateAutoplay(item.autoplay, `derived.${key}.autoplay`, add, warn);
1232
1336
  });
1233
1337
  }
1234
1338
  function validateNodeStyle(style, path, ctx) {
@@ -1443,4 +1547,4 @@ function formatIssues(issues) {
1443
1547
  return issues.map((item) => item.path ? `${item.path}: ${item.message}` : item.message).join("\n");
1444
1548
  }
1445
1549
 
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 };
1550
+ export { ACTION_TYPES, ALLOWED_EASINGS, ALLOWED_STYLE_KEYS, AUTOPLAY_FIELDS, AUTOPLAY_MIN_INTERVAL, BANNER_MIN_ASPECT_RATIO, CLOSE_POSITIONS, CSS_NAME_PATTERN, DEFAULT_USER_FIELDS, DERIVED_FIELDS, DSL_VERSION, NODE_TYPES, SINGLE_VIEW_NAME, TRACK_EVENT_PATTERN, USER_FIELD_LABELS, check, computeParts, createAutoplay, createCssBuilder, createTicker, declarations, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isKeyframeOffset, isLength, isValidEasing, isValidEndTime, nextScopeUid, normalizeViews, parseEndTime, resolveNodeStyle, sanitizeCssValue, toCssStyle, toLength, validate, validateAutoplay, validateUserFields };
@@ -129,6 +129,11 @@ var ALLOWED_EASINGS = [
129
129
  "step-end"
130
130
  ];
131
131
 
132
+ // src/autoplay.ts
133
+ var DERIVED_FIELDS = ["list", "indexBy", "autoplay"];
134
+ var AUTOPLAY_FIELDS = ["interval", "pauseOnHover"];
135
+ var AUTOPLAY_MIN_INTERVAL = 300;
136
+
132
137
  // src/validate.ts
133
138
  var DSL_VERSION = 1;
134
139
  var NODE_TYPES = [
@@ -477,9 +482,14 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6`
477
482
  "`close` \u7684 `extra.name`\uFF08`reason` \u4ECD\u662F\u5173\u95ED\u539F\u56E0\uFF09"
478
483
  ],
479
484
  [
480
- "\u5173\u95ED\u6309\u94AE\u7684 `action` \u91CC\uFF08\u548C `close` \u5199\u5728\u540C\u4E00\u4E2A `sequence`\uFF09",
485
+ "\u5173\u95ED\u6309\u94AE\u7684 `action` \u91CC\uFF08\u548C `close` \u5199\u5728\u540C\u4E00\u4E2A `sequence`\uFF0C\u4E14**\u4E0D\u8DF3\u8F6C**\uFF09",
481
486
  "**\u4E0D\u7B97\u70B9\u51FB**\uFF0C\u5E76\u8FDB\u90A3\u6761\u5173\u95ED",
482
487
  "`close` \u7684 `extra.name`"
488
+ ],
489
+ [
490
+ "\u4E3B\u6309\u94AE\u7684 `action` \u91CC\uFF08`sequence` \u91CC\u65E2\u6709 `close` / `closeAll`\uFF0C\u53C8\u6709 `navigate`\uFF09",
491
+ "**\u7B97\u4E00\u6761 `click`**\uFF08\u8DF3\u8D70\u4E86\u5C31\u662F\u8F6C\u5316\uFF09",
492
+ "`extra.name`\uFF1B\u8DF3\u8F6C\u5730\u5740\u5E76\u8FDB `extra.url`"
483
493
  ]
484
494
  ]
485
495
  },
@@ -491,7 +501,12 @@ countdown_end // \u5012\u8BA1\u65F6\u5F52\u96F6`
491
501
  {
492
502
  t: "note",
493
503
  tone: "warn",
494
- text: '**\u5173\u95ED\u52A8\u4F5C\u4E0D\u4EA7\u751F\u70B9\u51FB\u3002**\u53EA\u8981\u8FD9\u4E00\u4E0B\u6267\u884C\u4E86 `close` / `closeAll`\uFF0C\u5B83\u7684\u672C\u8D28\u5C31\u662F\u300C\u5173\u6389\u5B83\u300D\u800C\u4E0D\u662F\u300C\u70B9\u4E86\u5B83\u300D\u2014\u2014\u540E\u7AEF\u7B97\u70B9\u51FB\u7387\u65F6\u5206\u6BCD\u662F\u66DD\u5149\u3001\u5206\u5B50\u662F\u70B9\u51FB\uFF0C\u628A\u5173\u95ED\u7B97\u8FDB\u70B9\u51FB\u4F1A\u8BA9\u70B9\u51FB\u7387\u865A\u9AD8\u3002\u4F60\u914D\u5728\u5173\u95ED\u6309\u94AE\u4E0A\u7684\u57CB\u70B9\u540D\u4E0D\u4F1A\u4E22\uFF0C\u5B83\u8FDB\u4E86 `close` \u7684 `extra`\u3002\u60F3\u533A\u5206\u300C\u4ECE\u54EA\u4E2A\u5165\u53E3\u5173\u7684\u300D\uFF0C\u66F4\u63A8\u8350\u7528 `close` \u52A8\u4F5C\u7684 `reason`\uFF08\u5982 `reason: "user-dismiss"`\uFF09\u3002'
504
+ text: '**\u7EAF\u5173\u95ED\u52A8\u4F5C\u4E0D\u4EA7\u751F\u70B9\u51FB\u3002**\u8FD9\u4E00\u4E0B\u6267\u884C\u4E86 `close` / `closeAll`\u3001\u53C8\u6CA1\u6709\u8DF3\u8F6C\uFF0C\u5B83\u7684\u672C\u8D28\u5C31\u662F\u300C\u5173\u6389\u5B83\u300D\u800C\u4E0D\u662F\u300C\u70B9\u4E86\u5B83\u300D\u2014\u2014\u540E\u7AEF\u7B97\u70B9\u51FB\u7387\u65F6\u5206\u6BCD\u662F\u66DD\u5149\u3001\u5206\u5B50\u662F\u70B9\u51FB\uFF0C\u628A\u5173\u95ED\u7B97\u8FDB\u70B9\u51FB\u4F1A\u8BA9\u70B9\u51FB\u7387\u865A\u9AD8\u3002\u4F60\u914D\u5728\u5173\u95ED\u6309\u94AE\u4E0A\u7684\u57CB\u70B9\u540D\u4E0D\u4F1A\u4E22\uFF0C\u5B83\u8FDB\u4E86 `close` \u7684 `extra`\u3002\u60F3\u533A\u5206\u300C\u4ECE\u54EA\u4E2A\u5165\u53E3\u5173\u7684\u300D\uFF0C\u66F4\u63A8\u8350\u7528 `close` \u52A8\u4F5C\u7684 `reason`\uFF08\u5982 `reason: "user-dismiss"`\uFF09\u3002'
505
+ },
506
+ {
507
+ t: "note",
508
+ tone: "info",
509
+ text: "**\u4F46\u300C\u70B9\u4E86\u5C31\u8DF3\u8D70\u300D\u7684\u6309\u94AE\u5373\u4F7F\u5E26\u5173\u95ED\u4E5F\u7B97\u4E00\u6B21\u70B9\u51FB\u3002**\u4E3B CTA \u5E38\u5199\u6210 `sequence: [track, closeAll, navigate]`\uFF08\u8DF3\u8D70\u524D\u987A\u624B\u628A\u5F39\u7A97\u6536\u6389\uFF09\uFF0C\u540C\u4E00\u6B21\u624B\u52BF\u91CC\u53EA\u8981\u6709 `navigate`\uFF0C\u5C31\u7167\u5E38\u4E0A\u62A5\u4E00\u6761 `click`\u2014\u2014\u7528\u6237\u771F\u7684\u8DF3\u8FC7\u53BB\u4E86\uFF0C\u8FD9\u662F\u8F6C\u5316\u884C\u4E3A\uFF0C\u5173\u95ED\u53EA\u662F\u5B83\u7684\u526F\u4F5C\u7528\u3002\u8FD9\u6761 `click` \u7684 `extra` \u91CC\u65E2\u6709\u4F60\u914D\u7684\u57CB\u70B9\u540D\uFF0C\u4E5F\u6709\u8DF3\u8F6C\u5730\u5740\uFF1B\u8FD9\u4E00\u4E0B\u4E0D\u4F1A\u518D\u628A\u57CB\u70B9\u540D\u5E76\u8FDB `close`\uFF0C\u540C\u4E00\u4E2A\u540D\u5B57\u4E0D\u4F1A\u88AB\u62A5\u4E24\u904D\u3002"
495
510
  },
496
511
  {
497
512
  t: "note",
@@ -852,6 +867,18 @@ var userFieldRows = DEFAULT_USER_FIELDS.map((field) => [
852
867
  `\`user.${field}\``,
853
868
  USER_FIELD_LABELS[field] || ""
854
869
  ]);
870
+ var derivedRows = [
871
+ ["list", "`data` \u91CC\u7684\u6570\u7EC4\u5B57\u6BB5\u540D"],
872
+ ["indexBy", "`state` \u91CC\u7684\u4E0B\u6807\u5B57\u6BB5\u540D"],
873
+ ["autoplay", "\u53EF\u9009\u3002\u81EA\u52A8\u8F6E\u64AD\uFF0C\u89C1\u4E0B"]
874
+ ];
875
+ var autoplayRows = [
876
+ [
877
+ "interval",
878
+ `\u5FC5\u586B\u3002\u5207\u6362\u95F4\u9694\uFF0C**\u5355\u4F4D\u6BEB\u79D2**\uFF0C\u4E0D\u5C0F\u4E8E ${AUTOPLAY_MIN_INTERVAL}\uFF08\u5982 \`4000\` = 4 \u79D2\uFF09`
879
+ ],
880
+ ["pauseOnHover", "\u53EF\u9009\uFF0C\u9ED8\u8BA4 `false`\u3002\u9F20\u6807\u60AC\u505C\u5728\u6574\u4E2A\u7269\u6599\u4E0A\u65F6\u6682\u505C\uFF0C\u79FB\u5F00\u540E\u91CD\u65B0\u8BA1\u65F6"]
881
+ ];
855
882
  var sections4 = [
856
883
  {
857
884
  id: "data",
@@ -921,9 +948,42 @@ var sections4 = [
921
948
  {
922
949
  t: "table",
923
950
  head: ["\u5B57\u6BB5", "\u8BF4\u660E"],
924
- rows: [
925
- ["list", "`data` \u91CC\u7684\u6570\u7EC4\u5B57\u6BB5\u540D"],
926
- ["indexBy", "`state` \u91CC\u7684\u4E0B\u6807\u5B57\u6BB5\u540D"]
951
+ rows: derivedRows.concat(
952
+ reconcileRows({ rows: derivedRows, actual: DERIVED_FIELDS, label: "\u65B0\u589E\u5B57\u6BB5" })
953
+ )
954
+ },
955
+ { t: "h3", text: "derived.autoplay\uFF1A\u81EA\u52A8\u8F6E\u64AD" },
956
+ {
957
+ t: "p",
958
+ text: "\u516C\u544A\u6761\u8F6E\u64AD\u6587\u6848\u3001\u8F6E\u64AD\u56FE\u8FD9\u7C7B\u300C\u9694\u51E0\u79D2\u81EA\u52A8\u6362\u4E0B\u4E00\u6761\u300D\u7684\u9700\u6C42\uFF0C\u5728 derived \u4E0A\u52A0 `autoplay` \u5373\u53EF\u3002\u914D\u4E86\u5C31\u5F00\u542F\u3001\u59CB\u7EC8\u5FAA\u73AF\uFF1A\u6BCF\u9694 `interval` \u6BEB\u79D2\u628A `state[indexBy]` \u52A0 1\uFF0C\u5230\u672B\u5C3E\u56DE\u5230 0\u3002\u5B83\u6539\u72B6\u6001\u8D70\u7684\u5C31\u662F\u70B9 tab \u90A3\u4E00\u6761\u8DEF\uFF0C\u6240\u4EE5\u6587\u6848\u3001\u56FE\u7247\u3001tabs \u5706\u70B9\u6307\u793A\u5668\u3001\u8DF3\u8F6C\u94FE\u63A5\u4F1A\u4E00\u8D77\u5207\uFF0C\u6548\u679C\u548C\u7528\u6237\u624B\u52A8\u70B9\u5B8C\u5168\u4E00\u6837\u2014\u2014\u4E0D\u4F1A\u51ED\u7A7A\u591A\u51FA\u5207\u6362\u52A8\u753B\uFF0C\u60F3\u8981\u8FC7\u6E21\u6548\u679C\u81EA\u5DF1\u5728 `style` \u91CC\u914D `transition`\u3002"
959
+ },
960
+ {
961
+ t: "code",
962
+ code: `"data": { "slides": [ { "text": "\u2026", "url": "\u2026" }, { \u2026 }, { \u2026 } ] },
963
+ "state": { "slide": 0 },
964
+ "derived": { "cur": { "list": "slides", "indexBy": "slide",
965
+ "autoplay": { "interval": 4000, "pauseOnHover": true } } }
966
+
967
+ // \u6587\u6848 {{ cur.text }}\u3001\u94FE\u63A5 {{ cur.url }} \u81EA\u52A8\u8DDF\u7740\u5F53\u524D\u9879\u8D70\uFF1B
968
+ // \u5706\u70B9\u6307\u793A\u5668\u5C31\u662F\u4E00\u4E2A tabs\uFF1A{ "type": "tabs", "bind": "slides", "stateKey": "slide" }`
969
+ },
970
+ {
971
+ t: "table",
972
+ head: ["\u5B57\u6BB5", "\u8BF4\u660E"],
973
+ rows: autoplayRows.concat(
974
+ reconcileRows({
975
+ rows: autoplayRows,
976
+ actual: AUTOPLAY_FIELDS,
977
+ label: "\u65B0\u589E\u5B57\u6BB5"
978
+ })
979
+ )
980
+ },
981
+ {
982
+ t: "list",
983
+ items: [
984
+ "\u7528\u6237\u70B9\u4E86 tab\uFF08\u6216\u4EFB\u4F55 `setState` \u6539\u4E86\u540C\u4E00\u4E2A key\uFF09\u4F1A**\u91CD\u65B0\u8BA1\u65F6**\uFF0C\u4E0D\u4F1A\u521A\u70B9\u5B8C\u5C31\u88AB\u5207\u8D70",
985
+ "\u5217\u8868\u53EA\u6709 0~1 \u6761\u3001\u6216\u6570\u636E\u6E90\u8FD8\u6CA1\u56DE\u6765\u65F6\u4E0D\u64AD\uFF1B\u6570\u636E\u56DE\u6765\u540E\u81EA\u52A8\u5F00\u59CB",
986
+ "\u6CA1\u6709 `enabled` / `loop` \u5F00\u5173\uFF1A\u4E0D\u60F3\u8F6E\u64AD\u5C31\u522B\u5199 `autoplay`\uFF0C\u5199\u4E86\u5C31\u4E00\u76F4\u5FAA\u73AF"
927
987
  ]
928
988
  }
929
989
  ]
@@ -1851,6 +1911,15 @@ var sections_default = sections10;
1851
1911
 
1852
1912
  // src/docs/meta.ts
1853
1913
  var changelog = [
1914
+ {
1915
+ date: "2026-09-22",
1916
+ items: [
1917
+ '\u65B0\u589E derived.autoplay \u81EA\u52A8\u8F6E\u64AD\uFF08\u7B2C 8 \u8282\uFF09\uFF1A`{ "interval": 4000, "pauseOnHover": true }` \u6302\u5728 derived \u4E0A\u5373\u53EF\u8BA9\u516C\u544A\u6761\u6587\u6848 / \u8F6E\u64AD\u56FE\u6BCF\u9694\u51E0\u79D2\u81EA\u52A8\u6362\u4E0B\u4E00\u6761\u3001\u5230\u672B\u5C3E\u56DE\u5230\u7B2C\u4E00\u6761\u3002\u5207\u6362\u8D70\u7684\u662F\u548C\u70B9 tab \u540C\u4E00\u6761\u8DEF\uFF0C\u6587\u6848\u3001\u56FE\u7247\u3001\u5706\u70B9\u6307\u793A\u5668\u3001\u8DF3\u8F6C\u94FE\u63A5\u4E00\u8D77\u5207\uFF1B\u7528\u6237\u624B\u52A8\u70B9\u8FC7\u4F1A\u91CD\u65B0\u8BA1\u65F6\uFF0C\u5217\u8868\u4E0D\u8DB3 2 \u6761\u6216\u6570\u636E\u6CA1\u56DE\u6765\u65F6\u4E0D\u64AD',
1918
+ "autoplay \u4FDD\u5B58\u65F6\u4F1A\u6821\u9A8C\uFF1Ainterval \u5FC5\u987B\u662F\u4E0D\u5C0F\u4E8E 300 \u7684\u6BEB\u79D2\u6570\uFF08\u5199\u6210 4 \u8868\u793A 4 \u6BEB\u79D2\uFF0C\u4F1A\u88AB\u62E6\u4E0B\uFF09\uFF0CpauseOnHover \u5FC5\u987B\u662F\u5E03\u5C14\uFF1B\u6CA1\u6709 enabled / loop \u5B57\u6BB5\uFF0C\u5199\u4E86\u53EA\u544A\u8B66\u5E76\u88AB\u5FFD\u7565",
1919
+ "\u65B0\u589E\u793A\u4F8B \u246D \u516C\u544A\u6761\u8F6E\u64AD\u6218\u62A5\uFF1A3 \u6761\u6587\u6848 + tabs \u5706\u70B9\u6307\u793A\u5668 + \u8DF3\u8F6C\u94FE\u63A5\u8DDF\u968F\u5F53\u524D\u9879\uFF0C\u8981\u505A\u8F6E\u64AD\u6284\u8FD9\u4EFD",
1920
+ "\u6846\u67B6\u58F3\u65B0\u589E\u4E00\u6761\u4E49\u52A1\uFF1A\u6E32\u67D3\u6811\u5E26 onRootEnter / onRootLeave \u65F6\u5728\u6839\u5BB9\u5668\u4E0A\u7ED1 mouseenter / mouseleave\uFF08Vue 2 \u58F3\u5DF2\u8DDF\u4E0A\uFF09"
1921
+ ]
1922
+ },
1854
1923
  {
1855
1924
  date: "2026-09-01",
1856
1925
  items: [
@@ -5326,6 +5395,142 @@ var quotaLow = {
5326
5395
  };
5327
5396
  var quota_low_default = quotaLow;
5328
5397
 
5398
+ // src/docs/examples/notice-carousel.ts
5399
+ var slides = [
5400
+ {
5401
+ key: "move",
5402
+ text: "\u{1F389} \u4ECA\u65E5\u5DF2\u6709 12,806 \u5BB6\u5E97\u94FA\u7528\u9EA6\u7206\u4E86\u5B8C\u6210\u5546\u54C1\u642C\u5BB6",
5403
+ url: "https://maibaole.example.com/move",
5404
+ // tabs 的标签字段,圆点不显示文字,留空串
5405
+ dot: ""
5406
+ },
5407
+ {
5408
+ key: "ai",
5409
+ text: "\u{1F525} AI \u667A\u80FD\u4F18\u5316\u672C\u5468\u7D2F\u8BA1\u6539\u5199\u6807\u9898 38 \u4E07\u6761\uFF0C\u70B9\u51FB\u7387\u5E73\u5747\u63D0\u5347 17%",
5410
+ url: "https://maibaole.example.com/ai-optimize",
5411
+ dot: ""
5412
+ },
5413
+ {
5414
+ key: "renew",
5415
+ text: "\u{1F4B0} \u4E13\u4E1A\u7248\u9650\u65F6 8 \u6298\uFF0C\u7EED\u8D39\u7ACB\u7701 180 \u5143",
5416
+ url: "https://maibaole.example.com/renew",
5417
+ dot: ""
5418
+ }
5419
+ ];
5420
+ var noticeCarousel = {
5421
+ version: 1,
5422
+ type: "notice",
5423
+ meta: { name: "\u516C\u544A\u6761\u8F6E\u64AD\u6218\u62A5\uFF08\u81EA\u52A8\u8F6E\u64AD\uFF09" },
5424
+ data: { slides },
5425
+ state: { slide: 0 },
5426
+ derived: {
5427
+ cur: {
5428
+ list: "slides",
5429
+ indexBy: "slide",
5430
+ // 4 秒一条,悬停暂停;单位是毫秒
5431
+ autoplay: { interval: 4e3, pauseOnHover: true }
5432
+ }
5433
+ },
5434
+ stage: {
5435
+ width: "100%",
5436
+ height: "auto",
5437
+ layout: "flow",
5438
+ // 通栏类型不写 maxWidth,宽度交给宿主坑位
5439
+ style: { radius: 6, background: "#fff8e6" },
5440
+ onShow: { type: "track", event: "notice_show", params: { notice: "carousel" } },
5441
+ onClose: {
5442
+ type: "track",
5443
+ event: "notice_dismiss",
5444
+ params: { notice: "carousel", reason: "{{ closeReason }}" }
5445
+ }
5446
+ },
5447
+ nodes: [
5448
+ {
5449
+ type: "flex",
5450
+ style: { align: "center", gap: 10, padding: "10px 14px" },
5451
+ children: [
5452
+ {
5453
+ // 文案跟着当前项走;定高单行,换条时公告条高度不跳
5454
+ type: "text",
5455
+ content: "{{ cur.text }}",
5456
+ style: {
5457
+ flex: "1",
5458
+ fontSize: 14,
5459
+ color: "#8a6212",
5460
+ lineHeight: 22,
5461
+ height: 22,
5462
+ overflow: "hidden",
5463
+ whiteSpace: "nowrap"
5464
+ }
5465
+ },
5466
+ {
5467
+ // 圆点指示器:当前项拉长成胶囊,宽度变化用 transition 过渡
5468
+ type: "tabs",
5469
+ bind: "slides",
5470
+ stateKey: "slide",
5471
+ labelField: "dot",
5472
+ style: { direction: "row", align: "center", gap: 6 },
5473
+ itemStyle: {
5474
+ width: 6,
5475
+ height: 6,
5476
+ radius: 3,
5477
+ background: "rgba(176,138,62,0.35)",
5478
+ cursor: "pointer",
5479
+ transition: "width 0.3s ease"
5480
+ },
5481
+ activeItemStyle: { width: 16, background: "#f5a623" },
5482
+ action: {
5483
+ type: "track",
5484
+ event: "notice_dot_click",
5485
+ params: { notice: "carousel" }
5486
+ }
5487
+ },
5488
+ {
5489
+ // 链接跟着当前项走:看到哪条、点的就是哪条
5490
+ type: "button",
5491
+ text: "\u53BB\u770B\u770B",
5492
+ style: {
5493
+ width: 64,
5494
+ height: 26,
5495
+ radius: 13,
5496
+ background: "#f5a623",
5497
+ color: "#fff",
5498
+ fontSize: 12,
5499
+ cursor: "pointer"
5500
+ },
5501
+ action: {
5502
+ type: "sequence",
5503
+ actions: [
5504
+ {
5505
+ type: "track",
5506
+ event: "notice_click",
5507
+ params: { notice: "carousel", slide: "{{ cur.key }}" }
5508
+ },
5509
+ { type: "navigate", url: "{{ cur.url }}", target: "_blank" }
5510
+ ]
5511
+ }
5512
+ },
5513
+ {
5514
+ type: "text",
5515
+ content: "\u2715",
5516
+ style: {
5517
+ alignSelf: "center",
5518
+ width: 22,
5519
+ height: 22,
5520
+ textAlign: "center",
5521
+ lineHeight: 22,
5522
+ color: "#b08a3e",
5523
+ fontSize: 13,
5524
+ cursor: "pointer"
5525
+ },
5526
+ action: { type: "close", reason: "user-close" }
5527
+ }
5528
+ ]
5529
+ }
5530
+ ]
5531
+ };
5532
+ var notice_carousel_default = noticeCarousel;
5533
+
5329
5534
  // src/docs/examples/index.ts
5330
5535
  var HOTSPOT_BG = "rgba(255,68,51,0.16)";
5331
5536
  var imageHotspot = {
@@ -5591,6 +5796,11 @@ var examples = [
5591
5796
  label: "\u246C \u590D\u523B\u8FD0\u8425 demo \xB7 \u642C\u5BB6\u6B21\u6570\u5C06\u5C3D\uFF08\u989D\u5EA6\u8FDB\u5EA6\u6761\uFF09",
5592
5797
  value: "quotaLow",
5593
5798
  dsl: quota_low_default
5799
+ },
5800
+ {
5801
+ label: "\u246D \u516C\u544A\u6761\u8F6E\u64AD\u6218\u62A5\uFF08\u81EA\u52A8\u8F6E\u64AD + \u5706\u70B9\u6307\u793A\u5668\uFF09",
5802
+ value: "noticeCarousel",
5803
+ dsl: notice_carousel_default
5594
5804
  }
5595
5805
  ];
5596
5806
  var examples_default = examples;
@@ -5769,6 +5979,7 @@ function checklistSection() {
5769
5979
  - [ ] \u6240\u6709 \`style\` \u7684\u5C5E\u6027\u540D\u90FD\u5728\u767D\u540D\u5355\u91CC
5770
5980
  - [ ] \u6CA1\u6709\u4EFB\u4F55 JS \u4EE3\u7801\u3001\`javascript:\` \u94FE\u63A5\u3001\u51FD\u6570\u8C03\u7528
5771
5981
  - [ ] \`derived.list\` \u6307\u5411\u7684\u5B57\u6BB5\u5728 \`data\` \u91CC\u771F\u5B9E\u5B58\u5728\uFF0C\`derived.indexBy\` \u5728 \`state\` \u91CC\u5B58\u5728
5982
+ - [ ] \u7528\u4E86 \`derived.autoplay\` \u7684\u8BDD\uFF1A\`interval\` \u662F \u2265${AUTOPLAY_MIN_INTERVAL} \u7684\u6BEB\u79D2\u6570\u5B57\uFF08\u4E0D\u662F\u79D2\uFF09\uFF0C\u6CA1\u6709\u5199 \`enabled\` / \`loop\` \u8FD9\u7C7B\u4E0D\u5B58\u5728\u7684\u5B57\u6BB5
5772
5983
  - [ ] \u7528\u4E86 \`views\` \u7684\u8BDD\uFF1A\`entry\` \u5B58\u5728\u4E8E \`views\`\uFF0C\u9876\u5C42\u6CA1\u6709\u6B8B\u7559 \`stage\`/\`nodes\`\uFF0C\u6240\u6709 \`open\` \u7684 \`view\` \u90FD\u80FD\u5BF9\u4E0A
5773
5984
  - [ ] \`repeat.bind\` \u6307\u5411\u7684\u662F\u6570\u7EC4
5774
5985
  - [ ] \u6BCF\u4E2A\u53EF\u70B9\u51FB\u5143\u7D20\u90FD\u6709 \`action\`\uFF0C\u4E14\u5E26 \`track\` \u57CB\u70B9
@@ -6018,6 +6229,18 @@ function dataSection() {
6018
6229
  \u4E4B\u540E\u4EFB\u610F\u8282\u70B9\u91CC\u7528 \`{{ current.price }}\` \`{{ current.url }}\`\uFF0C
6019
6230
  \u5207\u6362\u65F6\u4EF7\u683C\u3001\u94FE\u63A5\u3001\u57CB\u70B9\u53C2\u6570\u5168\u90E8\u81EA\u52A8\u8054\u52A8\uFF0C\u7248\u5F0F\u53EA\u9700\u8981\u6392\u4E00\u904D\u3002
6020
6231
 
6232
+ **\u81EA\u52A8\u8F6E\u64AD**\uFF08\u516C\u544A\u6761\u8F6E\u64AD\u6587\u6848 / \u6218\u62A5\u6EDA\u52A8\u3001\u8F6E\u64AD\u56FE\uFF09\u7528 \`derived.xxx.autoplay\`\uFF0C\u4E0D\u8981\u81EA\u5DF1\u62FC\u5B9A\u65F6\u903B\u8F91\uFF1A
6233
+
6234
+ \`\`\`jsonc
6235
+ "state": { "slide": 0 },
6236
+ "derived": { "cur": { "list": "slides", "indexBy": "slide",
6237
+ "autoplay": { "interval": 4000, "pauseOnHover": true } } }
6238
+ \`\`\`
6239
+
6240
+ - \`interval\` \u5FC5\u586B\uFF0C**\u5355\u4F4D\u6BEB\u79D2**\uFF0C\u4E0D\u5C0F\u4E8E ${AUTOPLAY_MIN_INTERVAL}\uFF1B\`pauseOnHover\` \u53EF\u9009\u5E03\u5C14\uFF08\u60AC\u505C\u6682\u505C\uFF09
6241
+ - \u53EA\u6709\u8FD9\u4E24\u4E2A\u5B57\u6BB5\uFF0C**\u6CA1\u6709** \`enabled\` / \`loop\`\uFF1A\u914D\u4E86\u5C31\u5F00\u542F\u3001\u59CB\u7EC8\u5FAA\u73AF
6242
+ - \u5207\u6362\u6548\u679C\u4E0E\u70B9 tab \u5B8C\u5168\u4E00\u6837\uFF0C\u6587\u6848 / \u56FE\u7247 / \u94FE\u63A5\u90FD\u4ECE \`{{ cur.xxx }}\` \u53D6\uFF1B\u8981\u5706\u70B9\u6307\u793A\u5668\u5C31\u653E\u4E00\u4E2A \`tabs\`\uFF08\`bind\` \u540C\u4E00\u4E2A\u5217\u8868\u3001\`stateKey\` \u540C\u4E00\u4E2A state\uFF09
6243
+
6021
6244
  \u6570\u636E\u8981\u4ECE\u540E\u7AEF\u53D6\u65F6\u5199 \`"tiers": { "$source": "\u6570\u636E\u6E90\u540D", "params": {} }\`\uFF0C
6022
6245
  \u6570\u636E\u6E90\u540D\u5FC5\u987B\u662F\u540E\u7AEF\u5DF2\u6CE8\u518C\u7684\uFF0C**\u4E0D\u80FD\u5199\u63A5\u53E3\u5730\u5740**\u3002\u7528\u6237\u6CA1\u8BF4\u5C31\u7528\u5199\u6B7B\u7684\u6570\u636E\u3002
6023
6246
 
@@ -1,4 +1,4 @@
1
- import { D as Dsl } from '../types-8FNxDXp0.cjs';
1
+ import { D as Dsl } from '../types-BapBTTio.cjs';
2
2
 
3
3
  /**
4
4
  * 开发文档的数据结构。
@@ -97,7 +97,8 @@ declare const meta: DocMeta;
97
97
  * ①② 是最小骨架,③④ 是运营 demo 真实弹窗的完整复刻,⑤⑥ 是多视图,⑦ 是换肤换布局,
98
98
  * ⑧ 是通栏 banner(宽度由页面决定、只能声明比例),⑨⑩ 是商品编辑抽屉里两个**固定尺寸**坑位,
99
99
  * ⑪⑫ 是搬家成功页两个线上弹窗的一比一复刻(动画、hover、相对时长倒计时的完整用法),
100
- * ⑬ 是额度进度条(用表达式算填充宽度,要画任何比例条都抄这份)。
100
+ * ⑬ 是额度进度条(用表达式算填充宽度,要画任何比例条都抄这份),
101
+ * ⑭ 是公告条自动轮播(derived.autoplay + tabs 圆点指示器)。
101
102
  * 后端数据源($source)用法见 ③。
102
103
  */
103
104
 
@@ -1,4 +1,4 @@
1
- import { D as Dsl } from '../types-8FNxDXp0.js';
1
+ import { D as Dsl } from '../types-BapBTTio.js';
2
2
 
3
3
  /**
4
4
  * 开发文档的数据结构。
@@ -97,7 +97,8 @@ declare const meta: DocMeta;
97
97
  * ①② 是最小骨架,③④ 是运营 demo 真实弹窗的完整复刻,⑤⑥ 是多视图,⑦ 是换肤换布局,
98
98
  * ⑧ 是通栏 banner(宽度由页面决定、只能声明比例),⑨⑩ 是商品编辑抽屉里两个**固定尺寸**坑位,
99
99
  * ⑪⑫ 是搬家成功页两个线上弹窗的一比一复刻(动画、hover、相对时长倒计时的完整用法),
100
- * ⑬ 是额度进度条(用表达式算填充宽度,要画任何比例条都抄这份)。
100
+ * ⑬ 是额度进度条(用表达式算填充宽度,要画任何比例条都抄这份),
101
+ * ⑭ 是公告条自动轮播(derived.autoplay + tabs 圆点指示器)。
101
102
  * 后端数据源($source)用法见 ③。
102
103
  */
103
104