@qingwu-ui/scroll-fab 0.9.0-beta.2 → 0.9.0-beta.3

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
@@ -4,6 +4,9 @@
4
4
 
5
5
  - **绕圈翻转**:桌面悬停 / 触屏按住推进描边进度,绕满一圈在「滚动到底部 / 返回顶部」间纯切换(不附带滚动),未绕满移开/松手则描边衰减倒转
6
6
  - **点击与描边正交**:轻点恒直接执行当前模式动作;触屏绕满后的释放被消费,避免同一手势既翻转又滚动
7
+ - **键盘语义正确**:Enter/Space 执行当前模式动作(原生按钮激活);双模式下方向键 `ArrowUp/ArrowDown` 翻转模式
8
+ - **单模式 / 阈值显隐**:`modes` 允许只跑「返回顶部」,`showThreshold` 滚过阈值才出现、不打扰首屏
9
+ - **进度回调**:`onScroll(pct)` 实时吐出 0..1 滚动进度
7
10
  - **Lenis 可插拔**:内置 rAF 缓动,可选接入 [Lenis](https://github.com/darkroomengineering/lenis)(未安装静默回退)
8
11
  - **异形形状**:银杏叶等自定义外形,进度描边沿外扩轮廓完全包裹
9
12
  - 程序滚动支持 wheel / touch / keydown 打断、懒加载变长追击
@@ -68,6 +71,19 @@ new ScrollFab({
68
71
 
69
72
  异形模式下圆形外壳自动退场(背景/边框/圆角),阴影改用 `drop-shadow` 跟随叶片真实形状;命中区仍为矩形以保证 44px 触控目标。
70
73
 
74
+ ### 单模式 / 阈值显隐 / 进度回调
75
+
76
+ ```ts
77
+ // 只跑「返回顶部」:首屏不打扰,滚过 400px 才出现;实时读滚动进度
78
+ new ScrollFab({
79
+ modes: ["to-top"],
80
+ showThreshold: 400,
81
+ onScroll: (pct) => console.log(`${Math.round(pct * 100)}%`),
82
+ });
83
+ ```
84
+
85
+ 单模式没有绕环翻转与描边,视觉上即普通按钮;键盘 Enter/Space 执行动作。
86
+
71
87
  ## API
72
88
 
73
89
  ```ts
@@ -90,6 +106,9 @@ interface ScrollFabOptions {
90
106
  ariaLabelToBottom?: string;
91
107
  ariaLabelToTop?: string;
92
108
  onModeChange?: (mode: "to-bottom" | "to-top") => void;
109
+ modes?: ("to-bottom" | "to-top")[]; // 缺省双模式;初始取首项;单模式无绕环/描边;空配置抛 TypeError
110
+ showThreshold?: number; // 显示所需最小 scrollPos px,默认 0(行为不变),负数按 0
111
+ onScroll?: (pct: number) => void; // 0..1 小数,rAF 节流,隐藏期间照吐,构造/refresh 后补发
93
112
  }
94
113
  ```
95
114
 
package/dist/index.cjs CHANGED
@@ -79,6 +79,8 @@ const RADIUS = 21;
79
79
  const FALLBACK_PATH_LEN = 100;
80
80
  const DEFAULT_LABEL_BOTTOM = "滚动到底部(悬停或按住绕圈可切换为返回顶部)";
81
81
  const DEFAULT_LABEL_TOP = "返回顶部(悬停或按住绕圈可切换为滚动到底部)";
82
+ const STATIC_LABEL_BOTTOM = "滚动到底部";
83
+ const STATIC_LABEL_TOP = "返回顶部";
82
84
  function easeInOutCubic(p) {
83
85
  return p < .5 ? 4 * p * p * p : 1 - (-2 * p + 2) ** 3 / 2;
84
86
  }
@@ -102,13 +104,17 @@ var ScrollFab = class {
102
104
  labelTop;
103
105
  onModeChange;
104
106
  onLenisReady;
107
+ onScroll;
108
+ modes;
109
+ single;
110
+ threshold;
105
111
  /** 外部注入:宿主实例,组件永不销毁 */
106
112
  externalLenis = null;
107
113
  /** 自建配置:null = 不走 lenis;true 形态也归一成对象 */
108
114
  ownLenisConfig = null;
109
115
  ownLenis = null;
110
116
  ownLenisPromise = null;
111
- mode = "to-bottom";
117
+ mode;
112
118
  progress = 0;
113
119
  hovering = false;
114
120
  pressing = false;
@@ -128,6 +134,8 @@ var ScrollFab = class {
128
134
  ro = null;
129
135
  actionSeq = 0;
130
136
  destroyed = false;
137
+ emitRaf = null;
138
+ lastPct = -1;
131
139
  constructor(options = {}) {
132
140
  this.targetEl = options.target ?? null;
133
141
  this.shape = options.shape ?? null;
@@ -135,10 +143,15 @@ var ScrollFab = class {
135
143
  this.decayMs = Math.max(100, options.decayDuration ?? 300);
136
144
  this.cancelPx = Math.max(4, options.cancelThreshold ?? 10);
137
145
  this.animate = options.animate ?? true;
138
- this.labelBottom = options.ariaLabelToBottom ?? DEFAULT_LABEL_BOTTOM;
139
- this.labelTop = options.ariaLabelToTop ?? DEFAULT_LABEL_TOP;
146
+ this.threshold = Math.max(0, options.showThreshold ?? 0);
140
147
  this.onModeChange = options.onModeChange;
141
148
  this.onLenisReady = options.onLenisReady;
149
+ this.onScroll = options.onScroll;
150
+ this.modes = this.normalizeModes(options.modes);
151
+ this.single = this.modes.length === 1;
152
+ this.mode = this.modes[0];
153
+ this.labelBottom = options.ariaLabelToBottom ?? (this.single ? STATIC_LABEL_BOTTOM : DEFAULT_LABEL_BOTTOM);
154
+ this.labelTop = options.ariaLabelToTop ?? (this.single ? STATIC_LABEL_TOP : DEFAULT_LABEL_TOP);
142
155
  if (isLenisInstance(options.lenis)) this.externalLenis = options.lenis;
143
156
  else if (options.lenis != null && options.lenis !== false) {
144
157
  const cfg = options.lenis === true ? {} : options.lenis;
@@ -152,7 +165,7 @@ var ScrollFab = class {
152
165
  this.el.className = `qsf-root${options.className ? ` ${options.className}` : ""}`;
153
166
  this.el.dataset.mode = this.mode;
154
167
  if (this.shape) this.el.dataset.shape = "on";
155
- this.el.setAttribute("aria-label", this.labelBottom);
168
+ this.el.setAttribute("aria-label", this.mode === "to-bottom" ? this.labelBottom : this.labelTop);
156
169
  this.el.style.setProperty("--qsf-size", `${Math.max(44, options.size ?? 48)}px`);
157
170
  if (options.zIndex != null) this.el.style.setProperty("--qsf-z", String(options.zIndex));
158
171
  if (options.position?.right != null) this.el.style.setProperty("--qsf-right", `${options.position.right}px`);
@@ -197,12 +210,14 @@ var ScrollFab = class {
197
210
  else this.icon.innerHTML = ICON_CHEVRON_DOWN;
198
211
  this.el.append(svg, this.icon);
199
212
  document.body.append(this.el);
200
- this.el.addEventListener("pointerenter", this.onEnter);
201
- this.el.addEventListener("pointerleave", this.onLeave);
202
- this.el.addEventListener("pointerdown", this.onDown);
203
- this.el.addEventListener("pointermove", this.onMove);
204
- this.el.addEventListener("pointerup", this.onUp);
205
- this.el.addEventListener("pointercancel", this.onCancel);
213
+ if (!this.single) {
214
+ this.el.addEventListener("pointerenter", this.onEnter);
215
+ this.el.addEventListener("pointerleave", this.onLeave);
216
+ this.el.addEventListener("pointerdown", this.onDown);
217
+ this.el.addEventListener("pointermove", this.onMove);
218
+ this.el.addEventListener("pointerup", this.onUp);
219
+ this.el.addEventListener("pointercancel", this.onCancel);
220
+ }
206
221
  this.el.addEventListener("keydown", this.onKey);
207
222
  this.el.addEventListener("click", this.onClick);
208
223
  window.addEventListener("resize", this.onRecheck);
@@ -212,14 +227,17 @@ var ScrollFab = class {
212
227
  this.ro.observe(this.targetEl ?? document.documentElement);
213
228
  }
214
229
  this.syncVisible();
230
+ this.emitScroll();
215
231
  }
216
232
  /** 当前模式 */
217
233
  get currentMode() {
218
234
  return this.mode;
219
235
  }
220
- /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估) */
236
+ /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估),并强制补发一次进度 */
221
237
  refresh() {
222
- this.onRecheck();
238
+ this.syncVisible();
239
+ this.lastPct = -1;
240
+ this.emitScroll();
223
241
  }
224
242
  scrollToBottom() {
225
243
  this.animateScroll("bottom");
@@ -229,12 +247,14 @@ var ScrollFab = class {
229
247
  }
230
248
  destroy() {
231
249
  this.destroyed = true;
232
- this.el.removeEventListener("pointerenter", this.onEnter);
233
- this.el.removeEventListener("pointerleave", this.onLeave);
234
- this.el.removeEventListener("pointerdown", this.onDown);
235
- this.el.removeEventListener("pointermove", this.onMove);
236
- this.el.removeEventListener("pointerup", this.onUp);
237
- this.el.removeEventListener("pointercancel", this.onCancel);
250
+ if (!this.single) {
251
+ this.el.removeEventListener("pointerenter", this.onEnter);
252
+ this.el.removeEventListener("pointerleave", this.onLeave);
253
+ this.el.removeEventListener("pointerdown", this.onDown);
254
+ this.el.removeEventListener("pointermove", this.onMove);
255
+ this.el.removeEventListener("pointerup", this.onUp);
256
+ this.el.removeEventListener("pointercancel", this.onCancel);
257
+ }
238
258
  this.el.removeEventListener("keydown", this.onKey);
239
259
  this.el.removeEventListener("click", this.onClick);
240
260
  window.removeEventListener("resize", this.onRecheck);
@@ -246,10 +266,20 @@ var ScrollFab = class {
246
266
  this.ownLenis = null;
247
267
  if (this.ringRaf != null) cancelAnimationFrame(this.ringRaf);
248
268
  this.ringRaf = null;
269
+ if (this.emitRaf != null) cancelAnimationFrame(this.emitRaf);
270
+ this.emitRaf = null;
249
271
  if (this.flashTimer != null) clearTimeout(this.flashTimer);
250
272
  if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);
251
273
  this.el.remove();
252
274
  }
275
+ /** 规范化 modes:缺省双模式;去重、过滤非法值;结果为空直接抛错 */
276
+ normalizeModes(input) {
277
+ if (!input) return ["to-bottom", "to-top"];
278
+ const out = [];
279
+ for (const m of input) if ((m === "to-bottom" || m === "to-top") && !out.includes(m)) out.push(m);
280
+ if (out.length === 0) throw new TypeError("ScrollFab: modes 至少需要一个有效模式('to-bottom' | 'to-top')");
281
+ return out;
282
+ }
253
283
  makeCircle(cls) {
254
284
  const c = document.createElementNS(SVG_NS, "circle");
255
285
  c.setAttribute("class", cls);
@@ -323,9 +353,12 @@ var ScrollFab = class {
323
353
  this.doAction();
324
354
  };
325
355
  onKey = (e) => {
326
- if (e.repeat || e.key !== "Enter" && e.key !== " ") return;
356
+ if (e.repeat || e.key !== "ArrowUp" && e.key !== "ArrowDown") return;
357
+ if (this.single) return;
358
+ const want = e.key === "ArrowUp" ? "to-top" : "to-bottom";
359
+ if (want === this.mode) return;
327
360
  e.preventDefault();
328
- this.setMode(this.mode === "to-bottom" ? "to-top" : "to-bottom");
361
+ this.setMode(want);
329
362
  };
330
363
  holding() {
331
364
  return this.hovering || this.pressing;
@@ -588,9 +621,26 @@ var ScrollFab = class {
588
621
  }
589
622
  onRecheck = () => {
590
623
  this.syncVisible();
624
+ this.scheduleScroll();
591
625
  };
592
626
  syncVisible() {
593
- this.el.hidden = this.maxScroll() <= 1;
627
+ this.el.hidden = this.maxScroll() <= 1 || this.scrollPos() < this.threshold;
628
+ }
629
+ /** rAF 节流:一帧内多次重估只吐一次;按钮隐藏期间照常调度 */
630
+ scheduleScroll() {
631
+ if (this.emitRaf != null || this.destroyed) return;
632
+ this.emitRaf = requestAnimationFrame(() => {
633
+ this.emitRaf = null;
634
+ this.emitScroll();
635
+ });
636
+ }
637
+ emitScroll() {
638
+ if (this.destroyed) return;
639
+ const max = this.maxScroll();
640
+ const pct = max <= 0 ? 0 : Math.min(1, Math.max(0, this.scrollPos() / max));
641
+ if (pct === this.lastPct) return;
642
+ this.lastPct = pct;
643
+ this.onScroll?.(pct);
594
644
  }
595
645
  };
596
646
  //#endregion
package/dist/index.d.cts CHANGED
@@ -86,6 +86,18 @@ interface ScrollFabOptions {
86
86
  ariaLabelToTop?: string;
87
87
  /** 模式翻转回调 */
88
88
  onModeChange?: (mode: ScrollFabMode) => void;
89
+ /**
90
+ * 启用的模式集合(按数组顺序);缺省 ["to-bottom", "to-top"]。
91
+ * 初始模式取首项;只传一个即为单模式(无绕环翻转、无描边、静态 aria 文案);空数组/全非法抛 TypeError。
92
+ */
93
+ modes?: ScrollFabMode[];
94
+ /** 显示所需的最小已滚动距离 px:scrollPos < 该值时按钮隐藏(默认 0,行为不变);负数按 0 */
95
+ showThreshold?: number;
96
+ /**
97
+ * 滚动进度回调:0..1 小数(maxScroll 为 0 时吐 0),scroll/resize/程序滚动时 rAF 节流触发,
98
+ * 按钮隐藏期间照常吐,构造完成与 refresh() 后各补发一次。
99
+ */
100
+ onScroll?: (pct: number) => void;
89
101
  }
90
102
  //#endregion
91
103
  //#region src/scroll-fab.d.ts
@@ -106,6 +118,10 @@ declare class ScrollFab {
106
118
  private readonly labelTop;
107
119
  private readonly onModeChange?;
108
120
  private readonly onLenisReady?;
121
+ private readonly onScroll?;
122
+ private readonly modes;
123
+ private readonly single;
124
+ private readonly threshold;
109
125
  /** 外部注入:宿主实例,组件永不销毁 */
110
126
  private externalLenis;
111
127
  /** 自建配置:null = 不走 lenis;true 形态也归一成对象 */
@@ -132,14 +148,18 @@ declare class ScrollFab {
132
148
  private ro;
133
149
  private actionSeq;
134
150
  private destroyed;
151
+ private emitRaf;
152
+ private lastPct;
135
153
  constructor(options?: ScrollFabOptions);
136
154
  /** 当前模式 */
137
155
  get currentMode(): ScrollFabMode;
138
- /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估) */
156
+ /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估),并强制补发一次进度 */
139
157
  refresh(): void;
140
158
  scrollToBottom(): void;
141
159
  scrollToTop(): void;
142
160
  destroy(): void;
161
+ /** 规范化 modes:缺省双模式;去重、过滤非法值;结果为空直接抛错 */
162
+ private normalizeModes;
143
163
  private makeCircle;
144
164
  private makePath;
145
165
  private onEnter;
@@ -190,6 +210,9 @@ declare class ScrollFab {
190
210
  private setScrollPos;
191
211
  private onRecheck;
192
212
  private syncVisible;
213
+ /** rAF 节流:一帧内多次重估只吐一次;按钮隐藏期间照常调度 */
214
+ private scheduleScroll;
215
+ private emitScroll;
193
216
  }
194
217
  //#endregion
195
218
  export { type LenisLike, ScrollFab, type ScrollFabLenisConfig, type ScrollFabMode, type ScrollFabOptions, type ScrollFabPosition, type ScrollFabShape };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/scroll-fab.ts"],"mappings":";KAAY;;UAGK;EACf;EACA;EACA;EACA;;;UAIe;EACf,WACE,0BAA0B,aAC1B,UAAU;EAEZ,OAAO;EACP;EACA;EACA;EACA,MAAM,eAAe,cAAc;EACnC,OAAO,eAAe,cAAc;EACpC;;;UAIe;EACf,UAAU,cAAc;EACxB,UAAU;EACV;EACA,UAAU;;EAEV;EACA;EACA;EACA;GACC;;;;;;;;UASc;;EAEf;;EAEA;;EAEA;;EAEA;;UAGe;;EAEf,SAAS;;EAET;;EAEA;;EAEA;;EAEA;;EAEA,mBAAmB;;EAEnB,WAAW;;EAEX;;EAEA;;EAEA;;;;;;;;EAQA,kBAAkB,uBAAuB;;EAEzC,QAAQ;;EAER,gBAAgB,OAAO;;EAEvB;;EAEA;;EAEA,gBAAgB,MAAM;;;;cCjEX;WACF,IAAI;UAEL;UACA;UACA;mBACS;UACT;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;;UAGT;;UAEA;UACA;UACA;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;EAER,YAAY,UAAS;;MA2GjB,eAAe;;EAKnB;EAIA;EAIA;EAIA;UAyBQ;UASA;UAaA;UAMA;UAMA;UAeA;UAUA;UAeA;UAOA;UAUA;UAQA;UAIA;UAMA;;UAoBA;UAYA;UAOA;UAUA;UAIA;;UAyBA;;;;;;UA6BA;;UA6BA;UAqBA;;UAQA;UAMA;UASA;UAmCA;UAMA;UAIA;;UAKA;UAMA;;UAOA;UAMA;UAMA;UAeA;UAKA;UAIA;UAKA;UAKA;UAIA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/scroll-fab.ts"],"mappings":";KAAY;;UAGK;EACf;EACA;EACA;EACA;;;UAIe;EACf,WACE,0BAA0B,aAC1B,UAAU;EAEZ,OAAO;EACP;EACA;EACA;EACA,MAAM,eAAe,cAAc;EACnC,OAAO,eAAe,cAAc;EACpC;;;UAIe;EACf,UAAU,cAAc;EACxB,UAAU;EACV;EACA,UAAU;;EAEV;EACA;EACA;EACA;GACC;;;;;;;;UASc;;EAEf;;EAEA;;EAEA;;EAEA;;UAGe;;EAEf,SAAS;;EAET;;EAEA;;EAEA;;EAEA;;EAEA,mBAAmB;;EAEnB,WAAW;;EAEX;;EAEA;;EAEA;;;;;;;;EAQA,kBAAkB,uBAAuB;;EAEzC,QAAQ;;EAER,gBAAgB,OAAO;;EAEvB;;EAEA;;EAEA,gBAAgB,MAAM;;;;;EAKtB,QAAQ;;EAER;;;;;EAKA,YAAY;;;;cC3ED;WACF,IAAI;UAEL;UACA;UACA;mBACS;UACT;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;;UAGT;;UAEA;UACA;UACA;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;EAER,YAAY,UAAS;;MA2HjB,eAAe;;EAKnB;EAMA;EAIA;EAIA;;UA8BQ;UAcA;UASA;UAcA;UAMA;UAMA;UAeA;UAUA;UAeA;UAOA;UAUA;UAYA;UAIA;UAMA;;UAoBA;UAYA;UAOA;UAUA;UAIA;;UAyBA;;;;;;UA6BA;;UA6BA;UAqBA;;UAQA;UAMA;UASA;UAmCA;UAMA;UAIA;;UAKA;UAMA;;UAOA;UAMA;UAMA;UAeA;UAKA;UAIA;UAKA;UAKA;UAKA;;UAKA;UAQA"}
package/dist/index.d.mts CHANGED
@@ -86,6 +86,18 @@ interface ScrollFabOptions {
86
86
  ariaLabelToTop?: string;
87
87
  /** 模式翻转回调 */
88
88
  onModeChange?: (mode: ScrollFabMode) => void;
89
+ /**
90
+ * 启用的模式集合(按数组顺序);缺省 ["to-bottom", "to-top"]。
91
+ * 初始模式取首项;只传一个即为单模式(无绕环翻转、无描边、静态 aria 文案);空数组/全非法抛 TypeError。
92
+ */
93
+ modes?: ScrollFabMode[];
94
+ /** 显示所需的最小已滚动距离 px:scrollPos < 该值时按钮隐藏(默认 0,行为不变);负数按 0 */
95
+ showThreshold?: number;
96
+ /**
97
+ * 滚动进度回调:0..1 小数(maxScroll 为 0 时吐 0),scroll/resize/程序滚动时 rAF 节流触发,
98
+ * 按钮隐藏期间照常吐,构造完成与 refresh() 后各补发一次。
99
+ */
100
+ onScroll?: (pct: number) => void;
89
101
  }
90
102
  //#endregion
91
103
  //#region src/scroll-fab.d.ts
@@ -106,6 +118,10 @@ declare class ScrollFab {
106
118
  private readonly labelTop;
107
119
  private readonly onModeChange?;
108
120
  private readonly onLenisReady?;
121
+ private readonly onScroll?;
122
+ private readonly modes;
123
+ private readonly single;
124
+ private readonly threshold;
109
125
  /** 外部注入:宿主实例,组件永不销毁 */
110
126
  private externalLenis;
111
127
  /** 自建配置:null = 不走 lenis;true 形态也归一成对象 */
@@ -132,14 +148,18 @@ declare class ScrollFab {
132
148
  private ro;
133
149
  private actionSeq;
134
150
  private destroyed;
151
+ private emitRaf;
152
+ private lastPct;
135
153
  constructor(options?: ScrollFabOptions);
136
154
  /** 当前模式 */
137
155
  get currentMode(): ScrollFabMode;
138
- /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估) */
156
+ /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估),并强制补发一次进度 */
139
157
  refresh(): void;
140
158
  scrollToBottom(): void;
141
159
  scrollToTop(): void;
142
160
  destroy(): void;
161
+ /** 规范化 modes:缺省双模式;去重、过滤非法值;结果为空直接抛错 */
162
+ private normalizeModes;
143
163
  private makeCircle;
144
164
  private makePath;
145
165
  private onEnter;
@@ -190,6 +210,9 @@ declare class ScrollFab {
190
210
  private setScrollPos;
191
211
  private onRecheck;
192
212
  private syncVisible;
213
+ /** rAF 节流:一帧内多次重估只吐一次;按钮隐藏期间照常调度 */
214
+ private scheduleScroll;
215
+ private emitScroll;
193
216
  }
194
217
  //#endregion
195
218
  export { type LenisLike, ScrollFab, type ScrollFabLenisConfig, type ScrollFabMode, type ScrollFabOptions, type ScrollFabPosition, type ScrollFabShape };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/scroll-fab.ts"],"mappings":";KAAY;;UAGK;EACf;EACA;EACA;EACA;;;UAIe;EACf,WACE,0BAA0B,aAC1B,UAAU;EAEZ,OAAO;EACP;EACA;EACA;EACA,MAAM,eAAe,cAAc;EACnC,OAAO,eAAe,cAAc;EACpC;;;UAIe;EACf,UAAU,cAAc;EACxB,UAAU;EACV;EACA,UAAU;;EAEV;EACA;EACA;EACA;GACC;;;;;;;;UASc;;EAEf;;EAEA;;EAEA;;EAEA;;UAGe;;EAEf,SAAS;;EAET;;EAEA;;EAEA;;EAEA;;EAEA,mBAAmB;;EAEnB,WAAW;;EAEX;;EAEA;;EAEA;;;;;;;;EAQA,kBAAkB,uBAAuB;;EAEzC,QAAQ;;EAER,gBAAgB,OAAO;;EAEvB;;EAEA;;EAEA,gBAAgB,MAAM;;;;cCjEX;WACF,IAAI;UAEL;UACA;UACA;mBACS;UACT;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;;UAGT;;UAEA;UACA;UACA;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;EAER,YAAY,UAAS;;MA2GjB,eAAe;;EAKnB;EAIA;EAIA;EAIA;UAyBQ;UASA;UAaA;UAMA;UAMA;UAeA;UAUA;UAeA;UAOA;UAUA;UAQA;UAIA;UAMA;;UAoBA;UAYA;UAOA;UAUA;UAIA;;UAyBA;;;;;;UA6BA;;UA6BA;UAqBA;;UAQA;UAMA;UASA;UAmCA;UAMA;UAIA;;UAKA;UAMA;;UAOA;UAMA;UAMA;UAeA;UAKA;UAIA;UAKA;UAKA;UAIA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/scroll-fab.ts"],"mappings":";KAAY;;UAGK;EACf;EACA;EACA;EACA;;;UAIe;EACf,WACE,0BAA0B,aAC1B,UAAU;EAEZ,OAAO;EACP;EACA;EACA;EACA,MAAM,eAAe,cAAc;EACnC,OAAO,eAAe,cAAc;EACpC;;;UAIe;EACf,UAAU,cAAc;EACxB,UAAU;EACV;EACA,UAAU;;EAEV;EACA;EACA;EACA;GACC;;;;;;;;UASc;;EAEf;;EAEA;;EAEA;;EAEA;;UAGe;;EAEf,SAAS;;EAET;;EAEA;;EAEA;;EAEA;;EAEA,mBAAmB;;EAEnB,WAAW;;EAEX;;EAEA;;EAEA;;;;;;;;EAQA,kBAAkB,uBAAuB;;EAEzC,QAAQ;;EAER,gBAAgB,OAAO;;EAEvB;;EAEA;;EAEA,gBAAgB,MAAM;;;;;EAKtB,QAAQ;;EAER;;;;;EAKA,YAAY;;;;cC3ED;WACF,IAAI;UAEL;UACA;UACA;mBACS;UACT;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;;UAGT;;UAEA;UACA;UACA;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;EAER,YAAY,UAAS;;MA2HjB,eAAe;;EAKnB;EAMA;EAIA;EAIA;;UA8BQ;UAcA;UASA;UAcA;UAMA;UAMA;UAeA;UAUA;UAeA;UAOA;UAUA;UAYA;UAIA;UAMA;;UAoBA;UAYA;UAOA;UAUA;UAIA;;UAyBA;;;;;;UA6BA;;UA6BA;UAqBA;;UAQA;UAMA;UASA;UAmCA;UAMA;UAIA;;UAKA;UAMA;;UAOA;UAMA;UAMA;UAeA;UAKA;UAIA;UAKA;UAKA;UAKA;;UAKA;UAQA"}
package/dist/index.mjs CHANGED
@@ -78,6 +78,8 @@ const RADIUS = 21;
78
78
  const FALLBACK_PATH_LEN = 100;
79
79
  const DEFAULT_LABEL_BOTTOM = "滚动到底部(悬停或按住绕圈可切换为返回顶部)";
80
80
  const DEFAULT_LABEL_TOP = "返回顶部(悬停或按住绕圈可切换为滚动到底部)";
81
+ const STATIC_LABEL_BOTTOM = "滚动到底部";
82
+ const STATIC_LABEL_TOP = "返回顶部";
81
83
  function easeInOutCubic(p) {
82
84
  return p < .5 ? 4 * p * p * p : 1 - (-2 * p + 2) ** 3 / 2;
83
85
  }
@@ -101,13 +103,17 @@ var ScrollFab = class {
101
103
  labelTop;
102
104
  onModeChange;
103
105
  onLenisReady;
106
+ onScroll;
107
+ modes;
108
+ single;
109
+ threshold;
104
110
  /** 外部注入:宿主实例,组件永不销毁 */
105
111
  externalLenis = null;
106
112
  /** 自建配置:null = 不走 lenis;true 形态也归一成对象 */
107
113
  ownLenisConfig = null;
108
114
  ownLenis = null;
109
115
  ownLenisPromise = null;
110
- mode = "to-bottom";
116
+ mode;
111
117
  progress = 0;
112
118
  hovering = false;
113
119
  pressing = false;
@@ -127,6 +133,8 @@ var ScrollFab = class {
127
133
  ro = null;
128
134
  actionSeq = 0;
129
135
  destroyed = false;
136
+ emitRaf = null;
137
+ lastPct = -1;
130
138
  constructor(options = {}) {
131
139
  this.targetEl = options.target ?? null;
132
140
  this.shape = options.shape ?? null;
@@ -134,10 +142,15 @@ var ScrollFab = class {
134
142
  this.decayMs = Math.max(100, options.decayDuration ?? 300);
135
143
  this.cancelPx = Math.max(4, options.cancelThreshold ?? 10);
136
144
  this.animate = options.animate ?? true;
137
- this.labelBottom = options.ariaLabelToBottom ?? DEFAULT_LABEL_BOTTOM;
138
- this.labelTop = options.ariaLabelToTop ?? DEFAULT_LABEL_TOP;
145
+ this.threshold = Math.max(0, options.showThreshold ?? 0);
139
146
  this.onModeChange = options.onModeChange;
140
147
  this.onLenisReady = options.onLenisReady;
148
+ this.onScroll = options.onScroll;
149
+ this.modes = this.normalizeModes(options.modes);
150
+ this.single = this.modes.length === 1;
151
+ this.mode = this.modes[0];
152
+ this.labelBottom = options.ariaLabelToBottom ?? (this.single ? STATIC_LABEL_BOTTOM : DEFAULT_LABEL_BOTTOM);
153
+ this.labelTop = options.ariaLabelToTop ?? (this.single ? STATIC_LABEL_TOP : DEFAULT_LABEL_TOP);
141
154
  if (isLenisInstance(options.lenis)) this.externalLenis = options.lenis;
142
155
  else if (options.lenis != null && options.lenis !== false) {
143
156
  const cfg = options.lenis === true ? {} : options.lenis;
@@ -151,7 +164,7 @@ var ScrollFab = class {
151
164
  this.el.className = `qsf-root${options.className ? ` ${options.className}` : ""}`;
152
165
  this.el.dataset.mode = this.mode;
153
166
  if (this.shape) this.el.dataset.shape = "on";
154
- this.el.setAttribute("aria-label", this.labelBottom);
167
+ this.el.setAttribute("aria-label", this.mode === "to-bottom" ? this.labelBottom : this.labelTop);
155
168
  this.el.style.setProperty("--qsf-size", `${Math.max(44, options.size ?? 48)}px`);
156
169
  if (options.zIndex != null) this.el.style.setProperty("--qsf-z", String(options.zIndex));
157
170
  if (options.position?.right != null) this.el.style.setProperty("--qsf-right", `${options.position.right}px`);
@@ -196,12 +209,14 @@ var ScrollFab = class {
196
209
  else this.icon.innerHTML = ICON_CHEVRON_DOWN;
197
210
  this.el.append(svg, this.icon);
198
211
  document.body.append(this.el);
199
- this.el.addEventListener("pointerenter", this.onEnter);
200
- this.el.addEventListener("pointerleave", this.onLeave);
201
- this.el.addEventListener("pointerdown", this.onDown);
202
- this.el.addEventListener("pointermove", this.onMove);
203
- this.el.addEventListener("pointerup", this.onUp);
204
- this.el.addEventListener("pointercancel", this.onCancel);
212
+ if (!this.single) {
213
+ this.el.addEventListener("pointerenter", this.onEnter);
214
+ this.el.addEventListener("pointerleave", this.onLeave);
215
+ this.el.addEventListener("pointerdown", this.onDown);
216
+ this.el.addEventListener("pointermove", this.onMove);
217
+ this.el.addEventListener("pointerup", this.onUp);
218
+ this.el.addEventListener("pointercancel", this.onCancel);
219
+ }
205
220
  this.el.addEventListener("keydown", this.onKey);
206
221
  this.el.addEventListener("click", this.onClick);
207
222
  window.addEventListener("resize", this.onRecheck);
@@ -211,14 +226,17 @@ var ScrollFab = class {
211
226
  this.ro.observe(this.targetEl ?? document.documentElement);
212
227
  }
213
228
  this.syncVisible();
229
+ this.emitScroll();
214
230
  }
215
231
  /** 当前模式 */
216
232
  get currentMode() {
217
233
  return this.mode;
218
234
  }
219
- /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估) */
235
+ /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估),并强制补发一次进度 */
220
236
  refresh() {
221
- this.onRecheck();
237
+ this.syncVisible();
238
+ this.lastPct = -1;
239
+ this.emitScroll();
222
240
  }
223
241
  scrollToBottom() {
224
242
  this.animateScroll("bottom");
@@ -228,12 +246,14 @@ var ScrollFab = class {
228
246
  }
229
247
  destroy() {
230
248
  this.destroyed = true;
231
- this.el.removeEventListener("pointerenter", this.onEnter);
232
- this.el.removeEventListener("pointerleave", this.onLeave);
233
- this.el.removeEventListener("pointerdown", this.onDown);
234
- this.el.removeEventListener("pointermove", this.onMove);
235
- this.el.removeEventListener("pointerup", this.onUp);
236
- this.el.removeEventListener("pointercancel", this.onCancel);
249
+ if (!this.single) {
250
+ this.el.removeEventListener("pointerenter", this.onEnter);
251
+ this.el.removeEventListener("pointerleave", this.onLeave);
252
+ this.el.removeEventListener("pointerdown", this.onDown);
253
+ this.el.removeEventListener("pointermove", this.onMove);
254
+ this.el.removeEventListener("pointerup", this.onUp);
255
+ this.el.removeEventListener("pointercancel", this.onCancel);
256
+ }
237
257
  this.el.removeEventListener("keydown", this.onKey);
238
258
  this.el.removeEventListener("click", this.onClick);
239
259
  window.removeEventListener("resize", this.onRecheck);
@@ -245,10 +265,20 @@ var ScrollFab = class {
245
265
  this.ownLenis = null;
246
266
  if (this.ringRaf != null) cancelAnimationFrame(this.ringRaf);
247
267
  this.ringRaf = null;
268
+ if (this.emitRaf != null) cancelAnimationFrame(this.emitRaf);
269
+ this.emitRaf = null;
248
270
  if (this.flashTimer != null) clearTimeout(this.flashTimer);
249
271
  if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);
250
272
  this.el.remove();
251
273
  }
274
+ /** 规范化 modes:缺省双模式;去重、过滤非法值;结果为空直接抛错 */
275
+ normalizeModes(input) {
276
+ if (!input) return ["to-bottom", "to-top"];
277
+ const out = [];
278
+ for (const m of input) if ((m === "to-bottom" || m === "to-top") && !out.includes(m)) out.push(m);
279
+ if (out.length === 0) throw new TypeError("ScrollFab: modes 至少需要一个有效模式('to-bottom' | 'to-top')");
280
+ return out;
281
+ }
252
282
  makeCircle(cls) {
253
283
  const c = document.createElementNS(SVG_NS, "circle");
254
284
  c.setAttribute("class", cls);
@@ -322,9 +352,12 @@ var ScrollFab = class {
322
352
  this.doAction();
323
353
  };
324
354
  onKey = (e) => {
325
- if (e.repeat || e.key !== "Enter" && e.key !== " ") return;
355
+ if (e.repeat || e.key !== "ArrowUp" && e.key !== "ArrowDown") return;
356
+ if (this.single) return;
357
+ const want = e.key === "ArrowUp" ? "to-top" : "to-bottom";
358
+ if (want === this.mode) return;
326
359
  e.preventDefault();
327
- this.setMode(this.mode === "to-bottom" ? "to-top" : "to-bottom");
360
+ this.setMode(want);
328
361
  };
329
362
  holding() {
330
363
  return this.hovering || this.pressing;
@@ -587,9 +620,26 @@ var ScrollFab = class {
587
620
  }
588
621
  onRecheck = () => {
589
622
  this.syncVisible();
623
+ this.scheduleScroll();
590
624
  };
591
625
  syncVisible() {
592
- this.el.hidden = this.maxScroll() <= 1;
626
+ this.el.hidden = this.maxScroll() <= 1 || this.scrollPos() < this.threshold;
627
+ }
628
+ /** rAF 节流:一帧内多次重估只吐一次;按钮隐藏期间照常调度 */
629
+ scheduleScroll() {
630
+ if (this.emitRaf != null || this.destroyed) return;
631
+ this.emitRaf = requestAnimationFrame(() => {
632
+ this.emitRaf = null;
633
+ this.emitScroll();
634
+ });
635
+ }
636
+ emitScroll() {
637
+ if (this.destroyed) return;
638
+ const max = this.maxScroll();
639
+ const pct = max <= 0 ? 0 : Math.min(1, Math.max(0, this.scrollPos() / max));
640
+ if (pct === this.lastPct) return;
641
+ this.lastPct = pct;
642
+ this.onScroll?.(pct);
593
643
  }
594
644
  };
595
645
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../icon/icons.ts","../src/scroll-fab.ts"],"sourcesContent":["/* ============================================================\n 青梧 UI · 图标库\n ============================================================\n 零依赖 · 纯字符串常量 · tree-shakeable named exports\n 所有图标统一使用:viewBox=\"0 0 24 24\", stroke=\"currentColor\",\n stroke-width=\"2\", stroke-linecap=\"round\", stroke-linejoin=\"round\".\n\n 使用方式:\n import { ICON_CALENDAR, ICON_SEARCH } from \"@/icon/icons\";\n element.innerHTML = ICON_CALENDAR;\n 或者通过 dangerouslySetInnerHTML 渲染。\n ============================================================ */\n\nconst S = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S16 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S18 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S20 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S36 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\n/* ---- 通用 UI 图标 ---- */\n\nexport const ICON_CALENDAR = S(\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M8 2v4M16 2v4M3 10h18\"/>',\n);\n\nexport const ICON_CALENDAR_DOT = S(\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"/><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/><line x1=\"8\" y1=\"14\" x2=\"8.01\" y2=\"14\"/>',\n);\n\nexport const ICON_SEARCH = S('<circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21-4.35-4.35\"/>');\n\nexport const ICON_UPLOAD = S(\n '<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"17 8 12 3 7 8\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/>',\n);\n\nexport const ICON_UPLOAD_ARROW = S(\n '<path d=\"M12 16V4\"/><path d=\"m6 9 6-5 6 5\"/><path d=\"M4 20h16\"/>',\n);\n\nexport const ICON_MENU = S('<path d=\"M3 7h18M3 12h18M3 17h18\"/>');\n\nexport const ICON_BELL = S(\n '<path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9\"/><path d=\"M10.3 21a1.94 1.94 0 0 0 3.4 0\"/>',\n);\n\nexport const ICON_CLOSE = S(\n '<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>',\n);\n\nexport const ICON_CHECK = S('<polyline points=\"20 6 9 17 4 12\"/>');\n\nexport const ICON_PLUS = S(\n '<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n);\n\nexport const ICON_MINUS = S('<line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>');\n\nexport const ICON_BOX = S(\n '<path d=\"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z\"/>',\n);\n\nexport const ICON_EDIT = S(\n '<path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/>',\n);\n\nexport const ICON_FILE = S(\n '<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/>',\n);\n\nexport const ICON_FOLDER = S(\n '<path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\"/>',\n);\n\nexport const ICON_INFO = S(\n '<circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"/>',\n);\n\nexport const ICON_WARNING = S(\n '<path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/>',\n);\n\nexport const ICON_TRASH = S(\n '<polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/><line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/>',\n);\n\nexport const ICON_DOWNLOAD = S(\n '<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/>',\n);\n\nexport const ICON_COPY = S(\n '<rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/>',\n);\n\nexport const ICON_EYE = S(\n '<path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>',\n);\n\nexport const ICON_STAR = S(\n '<polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/>',\n);\n\nexport const ICON_STAR_FILLED = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" aria-hidden=\"true\"><polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/></svg>`;\n\nexport const ICON_BOOKMARK = S('<path d=\"M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z\"/>');\n\nexport const ICON_EXTERNAL_LINK = S(\n '<path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/><polyline points=\"15 3 21 3 21 9\"/><line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/>',\n);\nexport const ICON_GITHUB = S(\n '<path d=\"M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4\"/><path d=\"M9 18c-4.51 2-5-2-7-2\"/>',\n);\n\n/* ---- 导航箭头 ---- */\n\nexport const ICON_CHEVRON_LEFT = S16('<path d=\"m15 18-6-6 6-6\"/>');\nexport const ICON_CHEVRON_RIGHT = S16('<path d=\"m9 18 6-6-6-6\"/>');\nexport const ICON_CHEVRON_UP = S('<polyline points=\"18 15 12 9 6 15\"/>');\nexport const ICON_CHEVRON_DOWN = S('<polyline points=\"6 9 12 15 18 9\"/>');\nexport const ICON_CODE = S(\n '<polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/>',\n);\n\n/* ---- 全屏 ---- */\n\nexport const ICON_FULLSCREEN = S(\n '<polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\"/><line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\"/>',\n);\nexport const ICON_FULLSCREEN_EXIT = S(\n '<polyline points=\"4 8 4 3 9 3\"/><polyline points=\"20 16 20 21 15 21\"/><line x1=\"4\" y1=\"3\" x2=\"11\" y2=\"10\"/><line x1=\"20\" y1=\"21\" x2=\"13\" y2=\"14\"/>',\n);\n\n/* ---- 功能图标 ---- */\n\nexport const ICON_CLOCK = S('<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 6v6l4 2\"/>');\nexport const ICON_TYPE = S(\n '<polyline points=\"4 7 4 4 20 4 20 7\"/><line x1=\"9\" y1=\"20\" x2=\"15\" y2=\"20\"/><line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/>',\n);\nexport const ICON_USERS = S(\n '<path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/><path d=\"M23 21v-2a4 4 0 0 0-3-3.87\"/><path d=\"M16 3.13a4 4 0 0 1 0 7.75\"/>',\n);\nexport const ICON_MICROPHONE = S(\n '<path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"/><path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/><line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"/><line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\"/>',\n);\nexport const ICON_MUSIC = S(\n '<path d=\"M9 18V5l12-2v13\"/><circle cx=\"6\" cy=\"18\" r=\"3\"/><circle cx=\"18\" cy=\"16\" r=\"3\"/>',\n);\nexport const ICON_VIDEO = S(\n '<polygon points=\"23 7 16 12 23 17 23 7\"/><rect x=\"1\" y=\"5\" width=\"15\" height=\"14\" rx=\"2\" ry=\"2\"/>',\n);\nexport const ICON_IMAGE = S(\n '<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/>',\n);\n\n/* ---- 主题 ---- */\n\nexport const ICON_SUN = S(\n '<circle cx=\"12\" cy=\"12\" r=\"5\"/><line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"3\"/><line x1=\"12\" y1=\"21\" x2=\"12\" y2=\"23\"/><line x1=\"4.22\" y1=\"4.22\" x2=\"5.64\" y2=\"5.64\"/><line x1=\"18.36\" y1=\"18.36\" x2=\"19.78\" y2=\"19.78\"/><line x1=\"1\" y1=\"12\" x2=\"3\" y2=\"12\"/><line x1=\"21\" y1=\"12\" x2=\"23\" y2=\"12\"/><line x1=\"4.22\" y1=\"19.78\" x2=\"5.64\" y2=\"18.36\"/><line x1=\"18.36\" y1=\"5.64\" x2=\"19.78\" y2=\"4.22\"/>',\n);\nexport const ICON_MOON = S('<path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/>');\n\n/* ---- 侧边栏 ---- */\n\nexport const ICON_SIDEBAR_TOGGLE = S(\n '<rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\"/><path d=\"M9 3v18\"/><path d=\"m14 9-3 3 3 3\"/>',\n);\n\n/* ---- 编辑器图标 ---- */\n\nexport const ICON_LIST = S(\n '<line x1=\"8\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"8\" y1=\"18\" x2=\"21\" y2=\"18\"/><line x1=\"3\" y1=\"6\" x2=\"3.01\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"3.01\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"3.01\" y2=\"18\"/>',\n);\nexport const ICON_AI = S(\n '<path d=\"M12 2l2.4 7.2h7.6l-6 4.8 2.4 7.2-6.4-4.8-6.4 4.8 2.4-7.2-6-4.8h7.6z\"/>',\n);\nexport const ICON_BOLT = S('<polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/>');\nexport const ICON_REFRESH = S(\n '<polyline points=\"23 4 23 10 17 10\"/><polyline points=\"1 20 1 14 7 14\"/><path d=\"M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15\"/>',\n);\nexport const ICON_RETRY = S(\n '<polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/>',\n);\nexport const ICON_SETTINGS = S(\n '<circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z\"/>',\n);\nexport const ICON_HISTORY = S(\n '<path d=\"M3 3v5h5\"/><path d=\"M3.05 13A9 9 0 1 0 6.11 6.11L3 8\"/><path d=\"M12 7v5l4 2\"/>',\n);\nexport const ICON_PUSH = S(\n '<line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>',\n);\nexport const ICON_TRANSLATE = S(\n '<path d=\"M5 8l6 6\"/><path d=\"M4 14l6-6 2-3\"/><path d=\"M2 5h12\"/><path d=\"M7 2h1\"/><path d=\"M22 22l-5-10-5 10\"/><path d=\"M14 18h6\"/>',\n);\nexport const ICON_SUMMARY = S(\n '<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/>',\n);\nexport const ICON_RENAME = S('<path d=\"M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z\"/>');\nexport const ICON_SELECTION = S(\n '<circle cx=\"12\" cy=\"12\" r=\"1\"/><path d=\"M20 12a8 8 0 1 1-8-8\"/><path d=\"m16 2 4 4-4 4\"/>',\n);\nexport const ICON_TAG = S(\n '<path d=\"M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z\"/><line x1=\"7\" y1=\"7\" x2=\"7.01\" y2=\"7\"/>',\n);\nexport const ICON_GRIP = S(\n '<circle cx=\"9\" cy=\"12\" r=\"1\"/><circle cx=\"15\" cy=\"12\" r=\"1\"/><circle cx=\"9\" cy=\"6\" r=\"1\"/><circle cx=\"15\" cy=\"6\" r=\"1\"/><circle cx=\"9\" cy=\"18\" r=\"1\"/><circle cx=\"15\" cy=\"18\" r=\"1\"/>',\n);\nexport const ICON_DIAGRAM = S(\n '<rect x=\"2\" y=\"2\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"14\" y=\"2\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"2\" y=\"14\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"14\" y=\"14\" width=\"8\" height=\"8\" rx=\"1\"/>',\n);\nexport const ICON_LANGUAGE = S(\n '<circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"2\" y1=\"12\" x2=\"22\" y2=\"12\"/><path d=\"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z\"/>',\n);\nexport const ICON_PAPERCLIP = S(\n '<path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\"/>',\n);\nexport const ICON_BROKEN_IMAGE =\n '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"2\" width=\"20\" height=\"20\" rx=\"2\"/><path d=\"M2 14l4-4 4 4 4-4 4 4 4-4\"/><path d=\"M6 6h.01M18 6h.01\"/></svg>';\n\n/* ---- 包级别图标(用于 upload/search/calendar 包) ---- */\n\nexport const ICO_UPLOAD = S36('<path d=\"M12 16V4\"/><path d=\"m6 9 6-5 6 5\"/><path d=\"M4 20h16\"/>');\nconst S18c = (d: string) =>\n `<svg class=\"qs-ico\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S20c = (d: string) =>\n `<svg class=\"qw-cal-svg\" xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nexport const ICO_SEARCH = S18c('<circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m20 20-3.2-3.2\"/>');\nexport const ICO_MENU = S18('<path d=\"M4 7h16M4 12h16M4 17h16\"/>');\nexport const ICO_CALENDAR = S20c(\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M8 2v4M16 2v4M3 10h18\"/>',\n);\nexport const ICO_ARROW_LEFT = S16('<path d=\"m15 18-6-6 6-6\"/>');\nexport const ICO_ARROW_RIGHT = S16('<path d=\"m9 18 6-6-6-6\"/>');\n\n/* ---- 搜索插图(大尺寸 art) ---- */\n\nexport const SEARCH_ART =\n '<svg viewBox=\"0 0 160 140\" role=\"img\" aria-label=\"Search illustration\">' +\n '<ellipse class=\"qs-art-shadow\" cx=\"80\" cy=\"124\" rx=\"46\" ry=\"6\"/>' +\n '<g class=\"qs-art-float\">' +\n '<rect class=\"qs-art-paper\" x=\"40\" y=\"20\" width=\"80\" height=\"82\" rx=\"13\"/>' +\n '<line class=\"qs-art-row-accent\" x1=\"54\" y1=\"42\" x2=\"92\" y2=\"42\"/>' +\n '<line class=\"qs-art-row\" x1=\"54\" y1=\"56\" x2=\"106\" y2=\"56\"/>' +\n '<line class=\"qs-art-row\" x1=\"54\" y1=\"70\" x2=\"84\" y2=\"70\"/>' +\n '<circle class=\"qs-art-glass\" cx=\"96\" cy=\"86\" r=\"23\"/>' +\n '<line class=\"qs-art-handle\" x1=\"113\" y1=\"103\" x2=\"129\" y2=\"119\"/>' +\n '<text class=\"qs-art-seal\" x=\"96\" y=\"94\" text-anchor=\"middle\" font-size=\"24\">寻</text>' +\n \"</g>\" +\n '<circle class=\"qs-art-dot-a qs-art-twinkle\" cx=\"34\" cy=\"32\" r=\"3\"/>' +\n '<circle class=\"qs-art-dot-b qs-art-twinkle\" cx=\"126\" cy=\"40\" r=\"2.6\" style=\"animation-delay:.8s\"/>' +\n '<circle class=\"qs-art-dot-c qs-art-twinkle\" cx=\"30\" cy=\"98\" r=\"2.4\" style=\"animation-delay:1.5s\"/>' +\n \"</svg>\";\n","/** 青梧UI ScrollFab 悬浮滚动栏:默认“滚动到底部”,桌面悬停/触屏按住绕满描边一圈翻转为“返回顶部”,点击(轻点)直接执行当前模式动作 */\n\nimport { ICON_CHEVRON_DOWN } from \"../../../icon/icons\";\nimport type {\n LenisLike,\n ScrollFabLenisConfig,\n ScrollFabMode,\n ScrollFabOptions,\n ScrollFabShape,\n} from \"./types\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\nconst RADIUS = 21;\nconst FALLBACK_PATH_LEN = 100;\nconst DEFAULT_LABEL_BOTTOM = \"滚动到底部(悬停或按住绕圈可切换为返回顶部)\";\nconst DEFAULT_LABEL_TOP = \"返回顶部(悬停或按住绕圈可切换为滚动到底部)\";\n\nfunction easeInOutCubic(p: number): number {\n return p < 0.5 ? 4 * p * p * p : 1 - (-2 * p + 2) ** 3 / 2;\n}\n\nfunction isLenisInstance(v: unknown): v is LenisLike {\n return (\n typeof v === \"object\" &&\n v !== null &&\n typeof (v as LenisLike).scrollTo === \"function\"\n );\n}\n\nexport class ScrollFab {\n readonly el: HTMLButtonElement;\n\n private ring: SVGGeometryElement;\n private track: SVGGeometryElement;\n private fillPath: SVGPathElement | null = null;\n private readonly icon: HTMLElement;\n private len: number;\n\n private readonly targetEl: HTMLElement | null;\n private readonly shape: ScrollFabShape | null;\n private readonly ringMs: number;\n private readonly decayMs: number;\n private readonly cancelPx: number;\n private readonly animate: boolean;\n private readonly labelBottom: string;\n private readonly labelTop: string;\n private readonly onModeChange?: (mode: ScrollFabMode) => void;\n private readonly onLenisReady?: (lenis: LenisLike) => void;\n\n /** 外部注入:宿主实例,组件永不销毁 */\n private externalLenis: LenisLike | null = null;\n /** 自建配置:null = 不走 lenis;true 形态也归一成对象 */\n private ownLenisConfig: ScrollFabLenisConfig | null = null;\n private ownLenis: LenisLike | null = null;\n private ownLenisPromise: Promise<LenisLike | null> | null = null;\n\n private mode: ScrollFabMode = \"to-bottom\";\n private progress = 0;\n private hovering = false;\n private pressing = false;\n private flipped = false;\n private pointerId: number | null = null;\n private startX = 0;\n private startY = 0;\n private ringRaf: number | null = null;\n private lastTs: number | null = null;\n private scrollRaf: number | null = null;\n private lenisLoopRaf: number | null = null;\n private lenisScrolling = false;\n private lenisDest: \"bottom\" | \"top\" | null = null;\n private flashTimer: ReturnType<typeof setTimeout> | null = null;\n private tapResetTimer: ReturnType<typeof setTimeout> | null = null;\n private touchTap = false;\n private ro: ResizeObserver | null = null;\n private actionSeq = 0;\n private destroyed = false;\n\n constructor(options: ScrollFabOptions = {}) {\n this.targetEl = options.target ?? null;\n this.shape = options.shape ?? null;\n this.ringMs = Math.max(100, Math.min(5000, options.ringDuration ?? 800));\n this.decayMs = Math.max(100, options.decayDuration ?? 300);\n this.cancelPx = Math.max(4, options.cancelThreshold ?? 10);\n this.animate = options.animate ?? true;\n this.labelBottom = options.ariaLabelToBottom ?? DEFAULT_LABEL_BOTTOM;\n this.labelTop = options.ariaLabelToTop ?? DEFAULT_LABEL_TOP;\n this.onModeChange = options.onModeChange;\n this.onLenisReady = options.onLenisReady;\n\n if (isLenisInstance(options.lenis)) {\n this.externalLenis = options.lenis;\n } else if (options.lenis != null && options.lenis !== false) {\n const cfg = options.lenis === true ? {} : options.lenis;\n // 自建实例默认不劫持宿主滚轮:只驱动按钮触发的程序滚动\n this.ownLenisConfig = { smoothWheel: false, ...cfg };\n }\n\n this.el = document.createElement(\"button\");\n this.el.type = \"button\";\n this.el.className = `qsf-root${options.className ? ` ${options.className}` : \"\"}`;\n this.el.dataset.mode = this.mode;\n if (this.shape) this.el.dataset.shape = \"on\";\n this.el.setAttribute(\"aria-label\", this.labelBottom);\n this.el.style.setProperty(\"--qsf-size\", `${Math.max(44, options.size ?? 48)}px`);\n if (options.zIndex != null) this.el.style.setProperty(\"--qsf-z\", String(options.zIndex));\n if (options.position?.right != null)\n this.el.style.setProperty(\"--qsf-right\", `${options.position.right}px`);\n if (options.position?.bottom != null)\n this.el.style.setProperty(\"--qsf-bottom\", `${options.position.bottom}px`);\n if (options.position?.left != null)\n this.el.style.setProperty(\"--qsf-left\", `${options.position.left}px`);\n if (options.position?.top != null)\n this.el.style.setProperty(\"--qsf-top\", `${options.position.top}px`);\n\n const svg = document.createElementNS(SVG_NS, \"svg\");\n svg.setAttribute(\"class\", \"qsf-svg\");\n svg.setAttribute(\"viewBox\", this.shape?.viewBox ?? \"0 0 48 48\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n\n if (this.shape) {\n this.fillPath = document.createElementNS(SVG_NS, \"path\");\n this.fillPath.setAttribute(\"class\", \"qsf-shape-fill\");\n this.fillPath.setAttribute(\"d\", this.shape.fill);\n this.track = this.makePath(\"qsf-track\", this.shape.outline);\n this.ring = this.makePath(\"qsf-bar\", this.shape.outline);\n if (this.shape.outlineTransform) {\n this.track.setAttribute(\"transform\", this.shape.outlineTransform);\n this.ring.setAttribute(\"transform\", this.shape.outlineTransform);\n }\n svg.append(this.fillPath, this.track, this.ring);\n } else {\n this.track = this.makeCircle(\"qsf-track\");\n this.ring = this.makeCircle(\"qsf-bar\");\n svg.append(this.track, this.ring);\n }\n\n let len: number;\n if (this.shape) {\n len = FALLBACK_PATH_LEN;\n } else {\n len = 2 * Math.PI * RADIUS;\n }\n try {\n // 浏览器对未渲染 SVG 抛错(happy-dom 返回 NaN),失败时用回退值\n const measured = this.ring.getTotalLength();\n if (Number.isFinite(measured) && measured > 0) len = measured;\n } catch {\n /* 几何/经验回退 */\n }\n this.len = len;\n this.ring.style.strokeDasharray = String(this.len);\n this.renderRing();\n\n this.icon = document.createElement(\"span\");\n this.icon.className = \"qsf-icon\";\n this.icon.setAttribute(\"aria-hidden\", \"true\");\n if (options.content != null) {\n if (typeof options.content === \"string\") this.icon.innerHTML = options.content;\n else this.icon.append(options.content);\n } else {\n this.icon.innerHTML = ICON_CHEVRON_DOWN;\n }\n\n this.el.append(svg, this.icon);\n document.body.append(this.el);\n\n this.el.addEventListener(\"pointerenter\", this.onEnter);\n this.el.addEventListener(\"pointerleave\", this.onLeave);\n this.el.addEventListener(\"pointerdown\", this.onDown);\n this.el.addEventListener(\"pointermove\", this.onMove);\n this.el.addEventListener(\"pointerup\", this.onUp);\n this.el.addEventListener(\"pointercancel\", this.onCancel);\n this.el.addEventListener(\"keydown\", this.onKey);\n this.el.addEventListener(\"click\", this.onClick);\n window.addEventListener(\"resize\", this.onRecheck);\n (this.targetEl ?? window).addEventListener(\"scroll\", this.onRecheck, { passive: true });\n if (typeof ResizeObserver === \"function\") {\n this.ro = new ResizeObserver(this.onRecheck);\n this.ro.observe(this.targetEl ?? document.documentElement);\n }\n this.syncVisible();\n }\n\n /** 当前模式 */\n get currentMode(): ScrollFabMode {\n return this.mode;\n }\n\n /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估) */\n refresh(): void {\n this.onRecheck();\n }\n\n scrollToBottom(): void {\n this.animateScroll(\"bottom\");\n }\n\n scrollToTop(): void {\n this.animateScroll(\"top\");\n }\n\n destroy(): void {\n this.destroyed = true;\n this.el.removeEventListener(\"pointerenter\", this.onEnter);\n this.el.removeEventListener(\"pointerleave\", this.onLeave);\n this.el.removeEventListener(\"pointerdown\", this.onDown);\n this.el.removeEventListener(\"pointermove\", this.onMove);\n this.el.removeEventListener(\"pointerup\", this.onUp);\n this.el.removeEventListener(\"pointercancel\", this.onCancel);\n this.el.removeEventListener(\"keydown\", this.onKey);\n this.el.removeEventListener(\"click\", this.onClick);\n window.removeEventListener(\"resize\", this.onRecheck);\n (this.targetEl ?? window).removeEventListener(\"scroll\", this.onRecheck);\n this.ro?.disconnect();\n this.cancelScroll();\n this.stopLenisLoop();\n // 只销毁自建实例;外部宿主实例交还宿主\n this.ownLenis?.destroy?.();\n this.ownLenis = null;\n if (this.ringRaf != null) cancelAnimationFrame(this.ringRaf);\n this.ringRaf = null;\n if (this.flashTimer != null) clearTimeout(this.flashTimer);\n if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);\n this.el.remove();\n }\n\n private makeCircle(cls: string): SVGCircleElement {\n const c = document.createElementNS(SVG_NS, \"circle\");\n c.setAttribute(\"class\", cls);\n c.setAttribute(\"cx\", \"24\");\n c.setAttribute(\"cy\", \"24\");\n c.setAttribute(\"r\", String(RADIUS));\n return c;\n }\n\n private makePath(cls: string, d: string): SVGPathElement {\n const p = document.createElementNS(SVG_NS, \"path\");\n p.setAttribute(\"class\", cls);\n p.setAttribute(\"d\", d);\n return p;\n }\n\n /* ---------------- 输入状态机 ----------------\n * 桌面(mouse):pointerenter 推进描边 / pointerleave 衰减倒转;click 恒为动作\n * 触屏(非 mouse):pointerdown 按住推进(首指针独占,位移超阈值放弃并放行手势);\n * 轻点 = 动作;绕满翻转过 → 本次释放被消费(不在同一手势里翻转+滚动)\n */\n\n private onEnter = (e: PointerEvent): void => {\n if (e.pointerType !== \"mouse\") return;\n this.hovering = true;\n this.kickRing();\n };\n\n private onLeave = (e: PointerEvent): void => {\n if (e.pointerType !== \"mouse\") return;\n this.hovering = false;\n this.flipped = false;\n };\n\n private onDown = (e: PointerEvent): void => {\n if (e.button !== 0 || this.pointerId !== null) return;\n // 桌面描边由 hover 驱动,鼠标按压不接管\n if (e.pointerType === \"mouse\") return;\n e.preventDefault();\n this.touchTap = true;\n if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);\n this.pointerId = e.pointerId;\n this.startX = e.clientX;\n this.startY = e.clientY;\n this.pressing = true;\n this.flipped = false;\n this.kickRing();\n };\n\n private onMove = (e: PointerEvent): void => {\n if (e.pointerId !== this.pointerId || !this.pressing) return;\n // 超阈值 = 放弃描边且放行页面手势(不拦截 move,浏览器原生滚动照常)\n if (Math.hypot(e.clientX - this.startX, e.clientY - this.startY) > this.cancelPx) {\n this.pressing = false;\n this.pointerId = null;\n this.flipped = false;\n }\n };\n\n private onUp = (e: PointerEvent): void => {\n if (e.pointerId !== this.pointerId) return;\n this.pointerId = null;\n this.pressing = false;\n const consumed = this.flipped;\n this.flipped = false;\n // ghost click 兜底标记延迟复位,避免吞掉下一次真实鼠标点击\n this.tapResetTimer = setTimeout(() => {\n this.touchTap = false;\n this.tapResetTimer = null;\n }, 400);\n if (consumed) return;\n this.doAction();\n };\n\n private onCancel = (e: PointerEvent): void => {\n if (e.pointerId !== this.pointerId) return;\n this.pointerId = null;\n this.pressing = false;\n this.flipped = false;\n };\n\n private onClick = (e: MouseEvent): void => {\n // 触屏 pointerdown 已 preventDefault,正常不合成 click;此处兜底吞掉,防双触发\n if (this.touchTap) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n this.doAction();\n };\n\n private onKey = (e: KeyboardEvent): void => {\n if (e.repeat || (e.key !== \"Enter\" && e.key !== \" \")) return;\n e.preventDefault();\n this.setMode(this.mode === \"to-bottom\" ? \"to-top\" : \"to-bottom\");\n };\n\n /* ---------------- 描边 rAF:悬停/按住推进 · 离开/松手衰减倒转 ---------------- */\n\n private holding(): boolean {\n return this.hovering || this.pressing;\n }\n\n private kickRing(): void {\n if (this.ringRaf != null) return;\n this.lastTs = null;\n this.ringRaf = requestAnimationFrame(this.stepRing);\n }\n\n private stepRing = (ts: number): void => {\n const dt = this.lastTs == null ? 0 : ts - this.lastTs;\n this.lastTs = ts;\n if (this.holding()) {\n if (!this.flipped) {\n this.progress = Math.min(1, this.progress + dt / this.ringMs);\n if (this.progress >= 1) this.completeRing();\n }\n } else {\n this.progress = Math.max(0, this.progress - dt / this.decayMs);\n }\n this.renderRing();\n if (this.holding() || this.progress > 0) {\n this.ringRaf = requestAnimationFrame(this.stepRing);\n } else {\n this.ringRaf = null;\n }\n };\n\n /** 绕满一圈 = 纯模式翻转(不附带任何滚动) */\n private completeRing(): void {\n this.flipped = true;\n const next: ScrollFabMode = this.mode === \"to-bottom\" ? \"to-top\" : \"to-bottom\";\n this.setMode(next);\n this.el.classList.add(\"is-complete\");\n if (this.flashTimer != null) clearTimeout(this.flashTimer);\n this.flashTimer = setTimeout(() => {\n this.el.classList.remove(\"is-complete\");\n this.flashTimer = null;\n }, 350);\n }\n\n private renderRing(): void {\n const vis = this.progress > 0;\n this.track.toggleAttribute(\"hidden\", !vis);\n this.ring.toggleAttribute(\"hidden\", !vis);\n this.ring.style.strokeDashoffset = String(this.len * (1 - this.progress));\n }\n\n private setMode(mode: ScrollFabMode): void {\n if (mode === this.mode) return;\n this.mode = mode;\n this.el.dataset.mode = mode;\n this.el.setAttribute(\"aria-label\", mode === \"to-bottom\" ? this.labelBottom : this.labelTop);\n this.onModeChange?.(mode);\n }\n\n /* ---------------- 程序滚动:Lenis 后端可插拔,缺省内置 rAF ---------------- */\n\n private doAction(): void {\n this.animateScroll(this.mode === \"to-bottom\" ? \"bottom\" : \"top\");\n }\n\n private animateScroll(dest: \"bottom\" | \"top\"): void {\n const seq = ++this.actionSeq;\n this.cancelScroll();\n if (!this.animate || this.reducedMotion()) {\n this.setScrollPos(this.targetPos(dest));\n return;\n }\n if (this.externalLenis) {\n this.scrollWithLenis(this.externalLenis, dest, false);\n return;\n }\n if (this.ownLenisConfig) {\n const seq = ++this.actionSeq;\n void this.resolveOwnLenis().then((lenis) => {\n // await 期间可能已被新动作或 destroy 取代\n if (this.destroyed || seq !== this.actionSeq) return;\n if (lenis) this.scrollWithLenis(lenis, dest, true);\n else this.animateRaf(dest);\n });\n return;\n }\n this.animateRaf(dest);\n }\n\n /** 首次点击时动态 import lenis 并自建实例;未安装(或加载失败)→ null 永久回退 rAF */\n private resolveOwnLenis(): Promise<LenisLike | null> {\n if (this.ownLenis) return Promise.resolve(this.ownLenis);\n if (this.ownLenisPromise) return this.ownLenisPromise;\n this.ownLenisPromise = import(\"lenis\")\n .then((mod) => {\n const LenisCtor =\n (mod as { default?: new (opts: Record<string, unknown>) => LenisLike }).default ??\n (mod as unknown as new (opts: Record<string, unknown>) => LenisLike);\n const cfg: Record<string, unknown> = { ...this.ownLenisConfig };\n // target 容器映射为 Lenis wrapper;window 形态留空(Lenis 默认即 window)\n if (this.targetEl) cfg.wrapper = this.targetEl;\n this.ownLenis = new LenisCtor(cfg);\n this.onLenisReady?.(this.ownLenis);\n return this.ownLenis;\n })\n .catch(() => {\n // lenis 未安装/不可用:放弃 lenis 路线,避免每次点击重复尝试\n this.ownLenisConfig = null;\n this.ownLenisPromise = null;\n return null;\n });\n return this.ownLenisPromise;\n }\n\n /**\n * 用 Lenis 执行程序滚动。\n * 自建实例(own):组件自跑 raf loop(Lenis 需外部驱动 raf),保留 wheel/touch/keydown 打断与到底追击;\n * 外部实例:宿主自带 raf loop 与手势处理,组件只发 scrollTo,不追击不打断。\n */\n private scrollWithLenis(\n lenis: LenisLike,\n dest: \"bottom\" | \"top\",\n own: boolean,\n ): void {\n const immediate = !this.animate || this.reducedMotion();\n const target = this.targetPos(dest);\n this.lenisDest = dest;\n if (own) {\n // 上次打断可能调过 stop,发起前复位\n (lenis as LenisLike & { start?: () => void }).start?.();\n lenis.scrollTo(target, {\n immediate,\n onComplete: () => this.endLenisScroll(),\n });\n if (!immediate) {\n this.lenisScrolling = true;\n this.attachInterrupt();\n this.startLenisLoop(lenis, dest);\n } else {\n this.endLenisScroll();\n }\n } else {\n lenis.scrollTo(target, { immediate });\n this.lenisDest = null;\n }\n }\n\n /** 自建实例的 raf 驱动 + 懒加载到底追击(对齐内置 rAF 语义) */\n private startLenisLoop(lenis: LenisLike, dest: \"bottom\" | \"top\"): void {\n let lastEnd = this.targetPos(dest);\n const loop = (time: number): void => {\n if (!this.lenisScrolling) return;\n if (dest === \"bottom\") {\n const live = this.targetPos(\"bottom\");\n if (Math.abs(live - lastEnd) > 2) {\n // 内容变长:追击新底端(force 覆盖当前程序动画)\n lastEnd = live;\n lenis.scrollTo(live, {\n force: true,\n onComplete: () => this.endLenisScroll(),\n });\n }\n }\n lenis.raf?.(time);\n this.lenisLoopRaf = requestAnimationFrame(loop);\n };\n this.lenisLoopRaf = requestAnimationFrame(loop);\n }\n\n private stopLenisLoop(): void {\n if (this.lenisLoopRaf != null) {\n cancelAnimationFrame(this.lenisLoopRaf);\n this.lenisLoopRaf = null;\n }\n }\n\n /** 打断自建 Lenis 程序滚动:stop 冻结动画(下次发起前 start 复位) */\n private onLenisInterrupt = (): void => {\n if (!this.lenisScrolling) return;\n (this.ownLenis as LenisLike & { stop?: () => void } | null)?.stop?.();\n this.endLenisScroll();\n };\n\n private endLenisScroll(): void {\n this.lenisScrolling = false;\n this.lenisDest = null;\n this.stopLenisLoop();\n this.detachInterrupt();\n }\n\n /* ---------------- 内置 rAF:缓动 + 打断 + 懒加载追击 ---------------- */\n\n private animateRaf(dest: \"bottom\" | \"top\"): void {\n this.attachRafInterrupt();\n let init = false;\n let start = 0;\n let end = 0;\n let t0 = 0;\n let dur = 0;\n const step = (ts: number): void => {\n if (!init) {\n init = true;\n start = this.scrollPos();\n end = this.targetPos(dest);\n t0 = ts;\n dur = Math.min(1200, Math.max(400, Math.abs(end - start) / 3));\n } else if (dest === \"bottom\") {\n // 追击:懒加载变长时以当前实测底端为准重设行程\n const live = this.targetPos(dest);\n if (Math.abs(live - end) > 2) {\n end = live;\n start = this.scrollPos();\n t0 = ts;\n dur = Math.min(1200, Math.max(400, Math.abs(end - start) / 3));\n }\n }\n const p = Math.min(1, (ts - t0) / dur);\n this.setScrollPos(start + (end - start) * easeInOutCubic(p));\n if (p < 1) this.scrollRaf = requestAnimationFrame(step);\n else {\n this.scrollRaf = null;\n this.detachRafInterrupt();\n }\n };\n this.scrollRaf = requestAnimationFrame(step);\n }\n\n private reducedMotion(): boolean {\n return (\n typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n );\n }\n\n private onInterrupt = (): void => {\n this.cancelScroll();\n };\n\n private interruptSource(): Window | HTMLElement {\n return this.targetEl ?? window;\n }\n\n /** 自建 Lenis 路线的打断监听 */\n private attachInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.addEventListener(ev, this.onLenisInterrupt, { passive: true });\n }\n\n private detachInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.removeEventListener(ev, this.onLenisInterrupt);\n }\n\n /** 内置 rAF 路线的打断监听 */\n private attachRafInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.addEventListener(ev, this.onInterrupt, { passive: true });\n }\n\n private detachRafInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.removeEventListener(ev, this.onInterrupt);\n }\n\n private cancelScroll(): void {\n if (this.scrollRaf != null) {\n cancelAnimationFrame(this.scrollRaf);\n this.scrollRaf = null;\n }\n if (this.lenisScrolling) this.endLenisScroll();\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"]) {\n src.removeEventListener(ev, this.onInterrupt);\n src.removeEventListener(ev, this.onLenisInterrupt);\n }\n }\n\n /* ---------------- 度量与可见性(可滚动才渲染) ---------------- */\n\n private maxScroll(): number {\n if (this.targetEl) return Math.max(0, this.targetEl.scrollHeight - this.targetEl.clientHeight);\n return Math.max(0, document.documentElement.scrollHeight - window.innerHeight);\n }\n\n private targetPos(dest: \"bottom\" | \"top\"): number {\n return dest === \"bottom\" ? this.maxScroll() : 0;\n }\n\n private scrollPos(): number {\n if (this.targetEl) return this.targetEl.scrollTop;\n return window.scrollY || document.documentElement.scrollTop || 0;\n }\n\n private setScrollPos(v: number): void {\n if (this.targetEl) this.targetEl.scrollTop = v;\n else window.scrollTo(0, v);\n }\n\n private onRecheck = (): void => {\n this.syncVisible();\n };\n\n private syncVisible(): void {\n this.el.hidden = this.maxScroll() <= 1;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../icon/icons.ts","../src/scroll-fab.ts"],"sourcesContent":["/* ============================================================\n 青梧 UI · 图标库\n ============================================================\n 零依赖 · 纯字符串常量 · tree-shakeable named exports\n 所有图标统一使用:viewBox=\"0 0 24 24\", stroke=\"currentColor\",\n stroke-width=\"2\", stroke-linecap=\"round\", stroke-linejoin=\"round\".\n\n 使用方式:\n import { ICON_CALENDAR, ICON_SEARCH } from \"@/icon/icons\";\n element.innerHTML = ICON_CALENDAR;\n 或者通过 dangerouslySetInnerHTML 渲染。\n ============================================================ */\n\nconst S = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S16 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S18 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S20 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S36 = (d: string) =>\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\n/* ---- 通用 UI 图标 ---- */\n\nexport const ICON_CALENDAR = S(\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M8 2v4M16 2v4M3 10h18\"/>',\n);\n\nexport const ICON_CALENDAR_DOT = S(\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"/><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/><line x1=\"8\" y1=\"14\" x2=\"8.01\" y2=\"14\"/>',\n);\n\nexport const ICON_SEARCH = S('<circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21-4.35-4.35\"/>');\n\nexport const ICON_UPLOAD = S(\n '<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"17 8 12 3 7 8\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/>',\n);\n\nexport const ICON_UPLOAD_ARROW = S(\n '<path d=\"M12 16V4\"/><path d=\"m6 9 6-5 6 5\"/><path d=\"M4 20h16\"/>',\n);\n\nexport const ICON_MENU = S('<path d=\"M3 7h18M3 12h18M3 17h18\"/>');\n\nexport const ICON_BELL = S(\n '<path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9\"/><path d=\"M10.3 21a1.94 1.94 0 0 0 3.4 0\"/>',\n);\n\nexport const ICON_CLOSE = S(\n '<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>',\n);\n\nexport const ICON_CHECK = S('<polyline points=\"20 6 9 17 4 12\"/>');\n\nexport const ICON_PLUS = S(\n '<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n);\n\nexport const ICON_MINUS = S('<line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>');\n\nexport const ICON_BOX = S(\n '<path d=\"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z\"/>',\n);\n\nexport const ICON_EDIT = S(\n '<path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/>',\n);\n\nexport const ICON_FILE = S(\n '<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/>',\n);\n\nexport const ICON_FOLDER = S(\n '<path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\"/>',\n);\n\nexport const ICON_INFO = S(\n '<circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"/>',\n);\n\nexport const ICON_WARNING = S(\n '<path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/>',\n);\n\nexport const ICON_TRASH = S(\n '<polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/><line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/>',\n);\n\nexport const ICON_DOWNLOAD = S(\n '<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/>',\n);\n\nexport const ICON_COPY = S(\n '<rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/>',\n);\n\nexport const ICON_EYE = S(\n '<path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>',\n);\n\nexport const ICON_STAR = S(\n '<polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/>',\n);\n\nexport const ICON_STAR_FILLED = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" aria-hidden=\"true\"><polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/></svg>`;\n\nexport const ICON_BOOKMARK = S('<path d=\"M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z\"/>');\n\nexport const ICON_EXTERNAL_LINK = S(\n '<path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/><polyline points=\"15 3 21 3 21 9\"/><line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/>',\n);\nexport const ICON_GITHUB = S(\n '<path d=\"M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4\"/><path d=\"M9 18c-4.51 2-5-2-7-2\"/>',\n);\n\n/* ---- 导航箭头 ---- */\n\nexport const ICON_CHEVRON_LEFT = S16('<path d=\"m15 18-6-6 6-6\"/>');\nexport const ICON_CHEVRON_RIGHT = S16('<path d=\"m9 18 6-6-6-6\"/>');\nexport const ICON_CHEVRON_UP = S('<polyline points=\"18 15 12 9 6 15\"/>');\nexport const ICON_CHEVRON_DOWN = S('<polyline points=\"6 9 12 15 18 9\"/>');\nexport const ICON_CODE = S(\n '<polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/>',\n);\n\n/* ---- 全屏 ---- */\n\nexport const ICON_FULLSCREEN = S(\n '<polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\"/><line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\"/>',\n);\nexport const ICON_FULLSCREEN_EXIT = S(\n '<polyline points=\"4 8 4 3 9 3\"/><polyline points=\"20 16 20 21 15 21\"/><line x1=\"4\" y1=\"3\" x2=\"11\" y2=\"10\"/><line x1=\"20\" y1=\"21\" x2=\"13\" y2=\"14\"/>',\n);\n\n/* ---- 功能图标 ---- */\n\nexport const ICON_CLOCK = S('<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 6v6l4 2\"/>');\nexport const ICON_TYPE = S(\n '<polyline points=\"4 7 4 4 20 4 20 7\"/><line x1=\"9\" y1=\"20\" x2=\"15\" y2=\"20\"/><line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/>',\n);\nexport const ICON_USERS = S(\n '<path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/><path d=\"M23 21v-2a4 4 0 0 0-3-3.87\"/><path d=\"M16 3.13a4 4 0 0 1 0 7.75\"/>',\n);\nexport const ICON_MICROPHONE = S(\n '<path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"/><path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/><line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"/><line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\"/>',\n);\nexport const ICON_MUSIC = S(\n '<path d=\"M9 18V5l12-2v13\"/><circle cx=\"6\" cy=\"18\" r=\"3\"/><circle cx=\"18\" cy=\"16\" r=\"3\"/>',\n);\nexport const ICON_VIDEO = S(\n '<polygon points=\"23 7 16 12 23 17 23 7\"/><rect x=\"1\" y=\"5\" width=\"15\" height=\"14\" rx=\"2\" ry=\"2\"/>',\n);\nexport const ICON_IMAGE = S(\n '<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/>',\n);\n\n/* ---- 主题 ---- */\n\nexport const ICON_SUN = S(\n '<circle cx=\"12\" cy=\"12\" r=\"5\"/><line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"3\"/><line x1=\"12\" y1=\"21\" x2=\"12\" y2=\"23\"/><line x1=\"4.22\" y1=\"4.22\" x2=\"5.64\" y2=\"5.64\"/><line x1=\"18.36\" y1=\"18.36\" x2=\"19.78\" y2=\"19.78\"/><line x1=\"1\" y1=\"12\" x2=\"3\" y2=\"12\"/><line x1=\"21\" y1=\"12\" x2=\"23\" y2=\"12\"/><line x1=\"4.22\" y1=\"19.78\" x2=\"5.64\" y2=\"18.36\"/><line x1=\"18.36\" y1=\"5.64\" x2=\"19.78\" y2=\"4.22\"/>',\n);\nexport const ICON_MOON = S('<path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/>');\n\n/* ---- 侧边栏 ---- */\n\nexport const ICON_SIDEBAR_TOGGLE = S(\n '<rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\"/><path d=\"M9 3v18\"/><path d=\"m14 9-3 3 3 3\"/>',\n);\n\n/* ---- 编辑器图标 ---- */\n\nexport const ICON_LIST = S(\n '<line x1=\"8\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"8\" y1=\"18\" x2=\"21\" y2=\"18\"/><line x1=\"3\" y1=\"6\" x2=\"3.01\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"3.01\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"3.01\" y2=\"18\"/>',\n);\nexport const ICON_AI = S(\n '<path d=\"M12 2l2.4 7.2h7.6l-6 4.8 2.4 7.2-6.4-4.8-6.4 4.8 2.4-7.2-6-4.8h7.6z\"/>',\n);\nexport const ICON_BOLT = S('<polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/>');\nexport const ICON_REFRESH = S(\n '<polyline points=\"23 4 23 10 17 10\"/><polyline points=\"1 20 1 14 7 14\"/><path d=\"M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15\"/>',\n);\nexport const ICON_RETRY = S(\n '<polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/>',\n);\nexport const ICON_SETTINGS = S(\n '<circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z\"/>',\n);\nexport const ICON_HISTORY = S(\n '<path d=\"M3 3v5h5\"/><path d=\"M3.05 13A9 9 0 1 0 6.11 6.11L3 8\"/><path d=\"M12 7v5l4 2\"/>',\n);\nexport const ICON_PUSH = S(\n '<line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>',\n);\nexport const ICON_TRANSLATE = S(\n '<path d=\"M5 8l6 6\"/><path d=\"M4 14l6-6 2-3\"/><path d=\"M2 5h12\"/><path d=\"M7 2h1\"/><path d=\"M22 22l-5-10-5 10\"/><path d=\"M14 18h6\"/>',\n);\nexport const ICON_SUMMARY = S(\n '<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/>',\n);\nexport const ICON_RENAME = S('<path d=\"M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z\"/>');\nexport const ICON_SELECTION = S(\n '<circle cx=\"12\" cy=\"12\" r=\"1\"/><path d=\"M20 12a8 8 0 1 1-8-8\"/><path d=\"m16 2 4 4-4 4\"/>',\n);\nexport const ICON_TAG = S(\n '<path d=\"M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z\"/><line x1=\"7\" y1=\"7\" x2=\"7.01\" y2=\"7\"/>',\n);\nexport const ICON_GRIP = S(\n '<circle cx=\"9\" cy=\"12\" r=\"1\"/><circle cx=\"15\" cy=\"12\" r=\"1\"/><circle cx=\"9\" cy=\"6\" r=\"1\"/><circle cx=\"15\" cy=\"6\" r=\"1\"/><circle cx=\"9\" cy=\"18\" r=\"1\"/><circle cx=\"15\" cy=\"18\" r=\"1\"/>',\n);\nexport const ICON_DIAGRAM = S(\n '<rect x=\"2\" y=\"2\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"14\" y=\"2\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"2\" y=\"14\" width=\"8\" height=\"8\" rx=\"1\"/><rect x=\"14\" y=\"14\" width=\"8\" height=\"8\" rx=\"1\"/>',\n);\nexport const ICON_LANGUAGE = S(\n '<circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"2\" y1=\"12\" x2=\"22\" y2=\"12\"/><path d=\"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z\"/>',\n);\nexport const ICON_PAPERCLIP = S(\n '<path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\"/>',\n);\nexport const ICON_BROKEN_IMAGE =\n '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"2\" width=\"20\" height=\"20\" rx=\"2\"/><path d=\"M2 14l4-4 4 4 4-4 4 4 4-4\"/><path d=\"M6 6h.01M18 6h.01\"/></svg>';\n\n/* ---- 包级别图标(用于 upload/search/calendar 包) ---- */\n\nexport const ICO_UPLOAD = S36('<path d=\"M12 16V4\"/><path d=\"m6 9 6-5 6 5\"/><path d=\"M4 20h16\"/>');\nconst S18c = (d: string) =>\n `<svg class=\"qs-ico\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nconst S20c = (d: string) =>\n `<svg class=\"qw-cal-svg\" xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${d}</svg>`;\n\nexport const ICO_SEARCH = S18c('<circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m20 20-3.2-3.2\"/>');\nexport const ICO_MENU = S18('<path d=\"M4 7h16M4 12h16M4 17h16\"/>');\nexport const ICO_CALENDAR = S20c(\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M8 2v4M16 2v4M3 10h18\"/>',\n);\nexport const ICO_ARROW_LEFT = S16('<path d=\"m15 18-6-6 6-6\"/>');\nexport const ICO_ARROW_RIGHT = S16('<path d=\"m9 18 6-6-6-6\"/>');\n\n/* ---- 搜索插图(大尺寸 art) ---- */\n\nexport const SEARCH_ART =\n '<svg viewBox=\"0 0 160 140\" role=\"img\" aria-label=\"Search illustration\">' +\n '<ellipse class=\"qs-art-shadow\" cx=\"80\" cy=\"124\" rx=\"46\" ry=\"6\"/>' +\n '<g class=\"qs-art-float\">' +\n '<rect class=\"qs-art-paper\" x=\"40\" y=\"20\" width=\"80\" height=\"82\" rx=\"13\"/>' +\n '<line class=\"qs-art-row-accent\" x1=\"54\" y1=\"42\" x2=\"92\" y2=\"42\"/>' +\n '<line class=\"qs-art-row\" x1=\"54\" y1=\"56\" x2=\"106\" y2=\"56\"/>' +\n '<line class=\"qs-art-row\" x1=\"54\" y1=\"70\" x2=\"84\" y2=\"70\"/>' +\n '<circle class=\"qs-art-glass\" cx=\"96\" cy=\"86\" r=\"23\"/>' +\n '<line class=\"qs-art-handle\" x1=\"113\" y1=\"103\" x2=\"129\" y2=\"119\"/>' +\n '<text class=\"qs-art-seal\" x=\"96\" y=\"94\" text-anchor=\"middle\" font-size=\"24\">寻</text>' +\n \"</g>\" +\n '<circle class=\"qs-art-dot-a qs-art-twinkle\" cx=\"34\" cy=\"32\" r=\"3\"/>' +\n '<circle class=\"qs-art-dot-b qs-art-twinkle\" cx=\"126\" cy=\"40\" r=\"2.6\" style=\"animation-delay:.8s\"/>' +\n '<circle class=\"qs-art-dot-c qs-art-twinkle\" cx=\"30\" cy=\"98\" r=\"2.4\" style=\"animation-delay:1.5s\"/>' +\n \"</svg>\";\n","/** 青梧UI ScrollFab 悬浮滚动栏:默认“滚动到底部”,桌面悬停/触屏按住绕满描边一圈翻转为“返回顶部”,点击(轻点)直接执行当前模式动作 */\n\nimport { ICON_CHEVRON_DOWN } from \"../../../icon/icons\";\nimport type {\n LenisLike,\n ScrollFabLenisConfig,\n ScrollFabMode,\n ScrollFabOptions,\n ScrollFabShape,\n} from \"./types\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\nconst RADIUS = 21;\nconst FALLBACK_PATH_LEN = 100;\nconst DEFAULT_LABEL_BOTTOM = \"滚动到底部(悬停或按住绕圈可切换为返回顶部)\";\nconst DEFAULT_LABEL_TOP = \"返回顶部(悬停或按住绕圈可切换为滚动到底部)\";\nconst STATIC_LABEL_BOTTOM = \"滚动到底部\";\nconst STATIC_LABEL_TOP = \"返回顶部\";\n\nfunction easeInOutCubic(p: number): number {\n return p < 0.5 ? 4 * p * p * p : 1 - (-2 * p + 2) ** 3 / 2;\n}\n\nfunction isLenisInstance(v: unknown): v is LenisLike {\n return (\n typeof v === \"object\" &&\n v !== null &&\n typeof (v as LenisLike).scrollTo === \"function\"\n );\n}\n\nexport class ScrollFab {\n readonly el: HTMLButtonElement;\n\n private ring: SVGGeometryElement;\n private track: SVGGeometryElement;\n private fillPath: SVGPathElement | null = null;\n private readonly icon: HTMLElement;\n private len: number;\n\n private readonly targetEl: HTMLElement | null;\n private readonly shape: ScrollFabShape | null;\n private readonly ringMs: number;\n private readonly decayMs: number;\n private readonly cancelPx: number;\n private readonly animate: boolean;\n private readonly labelBottom: string;\n private readonly labelTop: string;\n private readonly onModeChange?: (mode: ScrollFabMode) => void;\n private readonly onLenisReady?: (lenis: LenisLike) => void;\n private readonly onScroll?: (pct: number) => void;\n private readonly modes: ScrollFabMode[];\n private readonly single: boolean;\n private readonly threshold: number;\n\n /** 外部注入:宿主实例,组件永不销毁 */\n private externalLenis: LenisLike | null = null;\n /** 自建配置:null = 不走 lenis;true 形态也归一成对象 */\n private ownLenisConfig: ScrollFabLenisConfig | null = null;\n private ownLenis: LenisLike | null = null;\n private ownLenisPromise: Promise<LenisLike | null> | null = null;\n\n private mode: ScrollFabMode;\n private progress = 0;\n private hovering = false;\n private pressing = false;\n private flipped = false;\n private pointerId: number | null = null;\n private startX = 0;\n private startY = 0;\n private ringRaf: number | null = null;\n private lastTs: number | null = null;\n private scrollRaf: number | null = null;\n private lenisLoopRaf: number | null = null;\n private lenisScrolling = false;\n private lenisDest: \"bottom\" | \"top\" | null = null;\n private flashTimer: ReturnType<typeof setTimeout> | null = null;\n private tapResetTimer: ReturnType<typeof setTimeout> | null = null;\n private touchTap = false;\n private ro: ResizeObserver | null = null;\n private actionSeq = 0;\n private destroyed = false;\n private emitRaf: number | null = null;\n private lastPct = -1;\n\n constructor(options: ScrollFabOptions = {}) {\n this.targetEl = options.target ?? null;\n this.shape = options.shape ?? null;\n this.ringMs = Math.max(100, Math.min(5000, options.ringDuration ?? 800));\n this.decayMs = Math.max(100, options.decayDuration ?? 300);\n this.cancelPx = Math.max(4, options.cancelThreshold ?? 10);\n this.animate = options.animate ?? true;\n this.threshold = Math.max(0, options.showThreshold ?? 0);\n this.onModeChange = options.onModeChange;\n this.onLenisReady = options.onLenisReady;\n this.onScroll = options.onScroll;\n\n this.modes = this.normalizeModes(options.modes);\n this.single = this.modes.length === 1;\n this.mode = this.modes[0]!;\n\n this.labelBottom =\n options.ariaLabelToBottom ?? (this.single ? STATIC_LABEL_BOTTOM : DEFAULT_LABEL_BOTTOM);\n this.labelTop = options.ariaLabelToTop ?? (this.single ? STATIC_LABEL_TOP : DEFAULT_LABEL_TOP);\n\n if (isLenisInstance(options.lenis)) {\n this.externalLenis = options.lenis;\n } else if (options.lenis != null && options.lenis !== false) {\n const cfg = options.lenis === true ? {} : options.lenis;\n // 自建实例默认不劫持宿主滚轮:只驱动按钮触发的程序滚动\n this.ownLenisConfig = { smoothWheel: false, ...cfg };\n }\n\n this.el = document.createElement(\"button\");\n this.el.type = \"button\";\n this.el.className = `qsf-root${options.className ? ` ${options.className}` : \"\"}`;\n this.el.dataset.mode = this.mode;\n if (this.shape) this.el.dataset.shape = \"on\";\n this.el.setAttribute(\n \"aria-label\",\n this.mode === \"to-bottom\" ? this.labelBottom : this.labelTop,\n );\n this.el.style.setProperty(\"--qsf-size\", `${Math.max(44, options.size ?? 48)}px`);\n if (options.zIndex != null) this.el.style.setProperty(\"--qsf-z\", String(options.zIndex));\n if (options.position?.right != null)\n this.el.style.setProperty(\"--qsf-right\", `${options.position.right}px`);\n if (options.position?.bottom != null)\n this.el.style.setProperty(\"--qsf-bottom\", `${options.position.bottom}px`);\n if (options.position?.left != null)\n this.el.style.setProperty(\"--qsf-left\", `${options.position.left}px`);\n if (options.position?.top != null)\n this.el.style.setProperty(\"--qsf-top\", `${options.position.top}px`);\n\n const svg = document.createElementNS(SVG_NS, \"svg\");\n svg.setAttribute(\"class\", \"qsf-svg\");\n svg.setAttribute(\"viewBox\", this.shape?.viewBox ?? \"0 0 48 48\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n\n if (this.shape) {\n this.fillPath = document.createElementNS(SVG_NS, \"path\");\n this.fillPath.setAttribute(\"class\", \"qsf-shape-fill\");\n this.fillPath.setAttribute(\"d\", this.shape.fill);\n this.track = this.makePath(\"qsf-track\", this.shape.outline);\n this.ring = this.makePath(\"qsf-bar\", this.shape.outline);\n if (this.shape.outlineTransform) {\n this.track.setAttribute(\"transform\", this.shape.outlineTransform);\n this.ring.setAttribute(\"transform\", this.shape.outlineTransform);\n }\n svg.append(this.fillPath, this.track, this.ring);\n } else {\n this.track = this.makeCircle(\"qsf-track\");\n this.ring = this.makeCircle(\"qsf-bar\");\n svg.append(this.track, this.ring);\n }\n\n let len: number;\n if (this.shape) {\n len = FALLBACK_PATH_LEN;\n } else {\n len = 2 * Math.PI * RADIUS;\n }\n try {\n // 浏览器对未渲染 SVG 抛错(happy-dom 返回 NaN),失败时用回退值\n const measured = this.ring.getTotalLength();\n if (Number.isFinite(measured) && measured > 0) len = measured;\n } catch {\n /* 几何/经验回退 */\n }\n this.len = len;\n this.ring.style.strokeDasharray = String(this.len);\n this.renderRing();\n\n this.icon = document.createElement(\"span\");\n this.icon.className = \"qsf-icon\";\n this.icon.setAttribute(\"aria-hidden\", \"true\");\n if (options.content != null) {\n if (typeof options.content === \"string\") this.icon.innerHTML = options.content;\n else this.icon.append(options.content);\n } else {\n this.icon.innerHTML = ICON_CHEVRON_DOWN;\n }\n\n this.el.append(svg, this.icon);\n document.body.append(this.el);\n\n // 单模式没有绕环翻转:不绑绕环手势,只保留按钮激活与(双模式的)方向键翻转\n if (!this.single) {\n this.el.addEventListener(\"pointerenter\", this.onEnter);\n this.el.addEventListener(\"pointerleave\", this.onLeave);\n this.el.addEventListener(\"pointerdown\", this.onDown);\n this.el.addEventListener(\"pointermove\", this.onMove);\n this.el.addEventListener(\"pointerup\", this.onUp);\n this.el.addEventListener(\"pointercancel\", this.onCancel);\n }\n this.el.addEventListener(\"keydown\", this.onKey);\n this.el.addEventListener(\"click\", this.onClick);\n window.addEventListener(\"resize\", this.onRecheck);\n (this.targetEl ?? window).addEventListener(\"scroll\", this.onRecheck, { passive: true });\n if (typeof ResizeObserver === \"function\") {\n this.ro = new ResizeObserver(this.onRecheck);\n this.ro.observe(this.targetEl ?? document.documentElement);\n }\n this.syncVisible();\n // 初始补发一次进度(同步吐出,宿主无需先滚再读)\n this.emitScroll();\n }\n\n /** 当前模式 */\n get currentMode(): ScrollFabMode {\n return this.mode;\n }\n\n /** 内容懒加载等外部高度变化后可显式触发重估(滚动/resize 时也会自动重估),并强制补发一次进度 */\n refresh(): void {\n this.syncVisible();\n this.lastPct = -1;\n this.emitScroll();\n }\n\n scrollToBottom(): void {\n this.animateScroll(\"bottom\");\n }\n\n scrollToTop(): void {\n this.animateScroll(\"top\");\n }\n\n destroy(): void {\n this.destroyed = true;\n if (!this.single) {\n this.el.removeEventListener(\"pointerenter\", this.onEnter);\n this.el.removeEventListener(\"pointerleave\", this.onLeave);\n this.el.removeEventListener(\"pointerdown\", this.onDown);\n this.el.removeEventListener(\"pointermove\", this.onMove);\n this.el.removeEventListener(\"pointerup\", this.onUp);\n this.el.removeEventListener(\"pointercancel\", this.onCancel);\n }\n this.el.removeEventListener(\"keydown\", this.onKey);\n this.el.removeEventListener(\"click\", this.onClick);\n window.removeEventListener(\"resize\", this.onRecheck);\n (this.targetEl ?? window).removeEventListener(\"scroll\", this.onRecheck);\n this.ro?.disconnect();\n this.cancelScroll();\n this.stopLenisLoop();\n // 只销毁自建实例;外部宿主实例交还宿主\n this.ownLenis?.destroy?.();\n this.ownLenis = null;\n if (this.ringRaf != null) cancelAnimationFrame(this.ringRaf);\n this.ringRaf = null;\n if (this.emitRaf != null) cancelAnimationFrame(this.emitRaf);\n this.emitRaf = null;\n if (this.flashTimer != null) clearTimeout(this.flashTimer);\n if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);\n this.el.remove();\n }\n\n /** 规范化 modes:缺省双模式;去重、过滤非法值;结果为空直接抛错 */\n private normalizeModes(input?: ScrollFabMode[]): ScrollFabMode[] {\n if (!input) return [\"to-bottom\", \"to-top\"];\n const out: ScrollFabMode[] = [];\n for (const m of input) {\n if ((m === \"to-bottom\" || m === \"to-top\") && !out.includes(m)) out.push(m);\n }\n if (out.length === 0) {\n throw new TypeError(\n \"ScrollFab: modes 至少需要一个有效模式('to-bottom' | 'to-top')\",\n );\n }\n return out;\n }\n\n private makeCircle(cls: string): SVGCircleElement {\n const c = document.createElementNS(SVG_NS, \"circle\");\n c.setAttribute(\"class\", cls);\n c.setAttribute(\"cx\", \"24\");\n c.setAttribute(\"cy\", \"24\");\n c.setAttribute(\"r\", String(RADIUS));\n return c;\n }\n\n private makePath(cls: string, d: string): SVGPathElement {\n const p = document.createElementNS(SVG_NS, \"path\");\n p.setAttribute(\"class\", cls);\n p.setAttribute(\"d\", d);\n return p;\n }\n\n /* ---------------- 输入状态机 ----------------\n * 桌面(mouse):pointerenter 推进描边 / pointerleave 衰减倒转;click 恒为动作\n * 触屏(非 mouse):pointerdown 按住推进(首指针独占,位移超阈值放弃并放行手势);\n * 轻点 = 动作;绕满翻转过 → 本次释放被消费(不在同一手势里翻转+滚动)\n * 键盘:Enter/Space 走原生按钮激活(click → doAction);双模式下方向键翻转模式\n */\n\n private onEnter = (e: PointerEvent): void => {\n if (e.pointerType !== \"mouse\") return;\n this.hovering = true;\n this.kickRing();\n };\n\n private onLeave = (e: PointerEvent): void => {\n if (e.pointerType !== \"mouse\") return;\n this.hovering = false;\n this.flipped = false;\n };\n\n private onDown = (e: PointerEvent): void => {\n if (e.button !== 0 || this.pointerId !== null) return;\n // 桌面描边由 hover 驱动,鼠标按压不接管\n if (e.pointerType === \"mouse\") return;\n e.preventDefault();\n this.touchTap = true;\n if (this.tapResetTimer != null) clearTimeout(this.tapResetTimer);\n this.pointerId = e.pointerId;\n this.startX = e.clientX;\n this.startY = e.clientY;\n this.pressing = true;\n this.flipped = false;\n this.kickRing();\n };\n\n private onMove = (e: PointerEvent): void => {\n if (e.pointerId !== this.pointerId || !this.pressing) return;\n // 超阈值 = 放弃描边且放行页面手势(不拦截 move,浏览器原生滚动照常)\n if (Math.hypot(e.clientX - this.startX, e.clientY - this.startY) > this.cancelPx) {\n this.pressing = false;\n this.pointerId = null;\n this.flipped = false;\n }\n };\n\n private onUp = (e: PointerEvent): void => {\n if (e.pointerId !== this.pointerId) return;\n this.pointerId = null;\n this.pressing = false;\n const consumed = this.flipped;\n this.flipped = false;\n // ghost click 兜底标记延迟复位,避免吞掉下一次真实鼠标点击\n this.tapResetTimer = setTimeout(() => {\n this.touchTap = false;\n this.tapResetTimer = null;\n }, 400);\n if (consumed) return;\n this.doAction();\n };\n\n private onCancel = (e: PointerEvent): void => {\n if (e.pointerId !== this.pointerId) return;\n this.pointerId = null;\n this.pressing = false;\n this.flipped = false;\n };\n\n private onClick = (e: MouseEvent): void => {\n // 触屏 pointerdown 已 preventDefault,正常不合成 click;此处兜底吞掉,防双触发\n if (this.touchTap) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n this.doAction();\n };\n\n private onKey = (e: KeyboardEvent): void => {\n if (e.repeat || (e.key !== \"ArrowUp\" && e.key !== \"ArrowDown\")) return;\n if (this.single) return;\n const want: ScrollFabMode = e.key === \"ArrowUp\" ? \"to-top\" : \"to-bottom\";\n if (want === this.mode) return;\n // 翻转与滚动正交:拦截方向键的原生页面滚动,只做模式翻转\n e.preventDefault();\n this.setMode(want);\n };\n\n /* ---------------- 描边 rAF:悬停/按住推进 · 离开/松手衰减倒转 ---------------- */\n\n private holding(): boolean {\n return this.hovering || this.pressing;\n }\n\n private kickRing(): void {\n if (this.ringRaf != null) return;\n this.lastTs = null;\n this.ringRaf = requestAnimationFrame(this.stepRing);\n }\n\n private stepRing = (ts: number): void => {\n const dt = this.lastTs == null ? 0 : ts - this.lastTs;\n this.lastTs = ts;\n if (this.holding()) {\n if (!this.flipped) {\n this.progress = Math.min(1, this.progress + dt / this.ringMs);\n if (this.progress >= 1) this.completeRing();\n }\n } else {\n this.progress = Math.max(0, this.progress - dt / this.decayMs);\n }\n this.renderRing();\n if (this.holding() || this.progress > 0) {\n this.ringRaf = requestAnimationFrame(this.stepRing);\n } else {\n this.ringRaf = null;\n }\n };\n\n /** 绕满一圈 = 纯模式翻转(不附带任何滚动) */\n private completeRing(): void {\n this.flipped = true;\n const next: ScrollFabMode = this.mode === \"to-bottom\" ? \"to-top\" : \"to-bottom\";\n this.setMode(next);\n this.el.classList.add(\"is-complete\");\n if (this.flashTimer != null) clearTimeout(this.flashTimer);\n this.flashTimer = setTimeout(() => {\n this.el.classList.remove(\"is-complete\");\n this.flashTimer = null;\n }, 350);\n }\n\n private renderRing(): void {\n const vis = this.progress > 0;\n this.track.toggleAttribute(\"hidden\", !vis);\n this.ring.toggleAttribute(\"hidden\", !vis);\n this.ring.style.strokeDashoffset = String(this.len * (1 - this.progress));\n }\n\n private setMode(mode: ScrollFabMode): void {\n if (mode === this.mode) return;\n this.mode = mode;\n this.el.dataset.mode = mode;\n this.el.setAttribute(\"aria-label\", mode === \"to-bottom\" ? this.labelBottom : this.labelTop);\n this.onModeChange?.(mode);\n }\n\n /* ---------------- 程序滚动:Lenis 后端可插拔,缺省内置 rAF ---------------- */\n\n private doAction(): void {\n this.animateScroll(this.mode === \"to-bottom\" ? \"bottom\" : \"top\");\n }\n\n private animateScroll(dest: \"bottom\" | \"top\"): void {\n const seq = ++this.actionSeq;\n this.cancelScroll();\n if (!this.animate || this.reducedMotion()) {\n this.setScrollPos(this.targetPos(dest));\n return;\n }\n if (this.externalLenis) {\n this.scrollWithLenis(this.externalLenis, dest, false);\n return;\n }\n if (this.ownLenisConfig) {\n const seq = ++this.actionSeq;\n void this.resolveOwnLenis().then((lenis) => {\n // await 期间可能已被新动作或 destroy 取代\n if (this.destroyed || seq !== this.actionSeq) return;\n if (lenis) this.scrollWithLenis(lenis, dest, true);\n else this.animateRaf(dest);\n });\n return;\n }\n this.animateRaf(dest);\n }\n\n /** 首次点击时动态 import lenis 并自建实例;未安装(或加载失败)→ null 永久回退 rAF */\n private resolveOwnLenis(): Promise<LenisLike | null> {\n if (this.ownLenis) return Promise.resolve(this.ownLenis);\n if (this.ownLenisPromise) return this.ownLenisPromise;\n this.ownLenisPromise = import(\"lenis\")\n .then((mod) => {\n const LenisCtor =\n (mod as { default?: new (opts: Record<string, unknown>) => LenisLike }).default ??\n (mod as unknown as new (opts: Record<string,unknown>) => LenisLike);\n const cfg: Record<string, unknown> = { ...this.ownLenisConfig };\n // target 容器映射为 Lenis wrapper;window 形态留空(Lenis 默认即 window)\n if (this.targetEl) cfg.wrapper = this.targetEl;\n this.ownLenis = new LenisCtor(cfg);\n this.onLenisReady?.(this.ownLenis);\n return this.ownLenis\n })\n .catch(() => {\n // lenis 未安装/不可用:放弃 lenis 路线,避免每次点击重复尝试\n this.ownLenisConfig = null;\n this.ownLenisPromise = null;\n return null;\n });\n return this.ownLenisPromise;\n }\n\n /**\n * 用 Lenis 执行程序滚动。\n * 自建实例(own):组件自跑 raf loop(Lenis 需外部驱动 raf),保留 wheel/touch/keydown 打断与到底追击;\n * 外部实例:宿主自带 raf loop 与手势处理,组件只发 scrollTo,不追击不打断。\n */\n private scrollWithLenis(\n lenis: LenisLike,\n dest: \"bottom\" | \"top\",\n own: boolean,\n ): void {\n const immediate = !this.animate || this.reducedMotion();\n const target = this.targetPos(dest);\n this.lenisDest = dest;\n if (own) {\n // 上次打断可能调过 stop,发起前复位\n (lenis as LenisLike & { start?: () => void }).start?.();\n lenis.scrollTo(target, {\n immediate,\n onComplete: () => this.endLenisScroll(),\n });\n if (!immediate) {\n this.lenisScrolling = true;\n this.attachInterrupt();\n this.startLenisLoop(lenis, dest);\n } else {\n this.endLenisScroll();\n }\n } else {\n lenis.scrollTo(target, { immediate });\n this.lenisDest = null;\n }\n }\n\n /** 自建实例的 raf 驱动 + 懒加载到底追击(对齐内置 rAF 语义) */\n private startLenisLoop(lenis: LenisLike, dest: \"bottom\" | \"top\"): void {\n let lastEnd = this.targetPos(dest);\n const loop = (time: number): void => {\n if (!this.lenisScrolling) return;\n if (dest === \"bottom\") {\n const live = this.targetPos(\"bottom\");\n if (Math.abs(live - lastEnd) > 2) {\n // 内容变长:追击新底端(force 覆盖当前程序动画)\n lastEnd = live;\n lenis.scrollTo(live, {\n force: true,\n onComplete: () => this.endLenisScroll(),\n });\n }\n }\n lenis.raf?.(time);\n this.lenisLoopRaf = requestAnimationFrame(loop);\n };\n this.lenisLoopRaf = requestAnimationFrame(loop);\n }\n\n private stopLenisLoop(): void {\n if (this.lenisLoopRaf != null) {\n cancelAnimationFrame(this.lenisLoopRaf);\n this.lenisLoopRaf = null;\n }\n }\n\n /** 打断自建 Lenis 程序滚动:stop 冻结动画(下次发起前 start 复位) */\n private onLenisInterrupt = (): void => {\n if (!this.lenisScrolling) return;\n (this.ownLenis as (LenisLike & { stop?: () => void }) | null)?.stop?.();\n this.endLenisScroll();\n };\n\n private endLenisScroll(): void {\n this.lenisScrolling = false;\n this.lenisDest = null;\n this.stopLenisLoop();\n this.detachInterrupt();\n }\n\n /* ---------------- 内置 rAF:缓动 + 打断 + 懒加载追击 ---------------- */\n\n private animateRaf(dest: \"bottom\" | \"top\"): void {\n this.attachRafInterrupt();\n let init = false;\n let start = 0;\n let end = 0;\n let t0 = 0;\n let dur = 0;\n const step = (ts: number): void => {\n if (!init) {\n init = true;\n start = this.scrollPos();\n end = this.targetPos(dest);\n t0 = ts;\n dur = Math.min(1200, Math.max(400, Math.abs(end - start) / 3));\n } else if (dest === \"bottom\") {\n // 追击:懒加载变长时以当前实测底端为准重设行程\n const live = this.targetPos(dest);\n if (Math.abs(live - end) > 2) {\n end = live;\n start = this.scrollPos();\n t0 = ts;\n dur = Math.min(1200, Math.max(400, Math.abs(end - start) / 3));\n }\n }\n const p = Math.min(1, (ts - t0) / dur);\n this.setScrollPos(start + (end - start) * easeInOutCubic(p));\n if (p < 1) this.scrollRaf = requestAnimationFrame(step);\n else {\n this.scrollRaf = null;\n this.detachRafInterrupt();\n }\n };\n this.scrollRaf = requestAnimationFrame(step);\n }\n\n private reducedMotion(): boolean {\n return (\n typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n );\n }\n\n private onInterrupt = (): void => {\n this.cancelScroll();\n };\n\n private interruptSource(): Window | HTMLElement {\n return this.targetEl ?? window;\n }\n\n /** 自建 Lenis 路线的打断监听 */\n private attachInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.addEventListener(ev, this.onLenisInterrupt, { passive: true });\n }\n\n private detachInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.removeEventListener(ev, this.onLenisInterrupt);\n }\n\n /** 内置 rAF 路线的打断监听 */\n private attachRafInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.addEventListener(ev, this.onInterrupt, { passive: true });\n }\n\n private detachRafInterrupt(): void {\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"])\n src.removeEventListener(ev, this.onInterrupt);\n }\n\n private cancelScroll(): void {\n if (this.scrollRaf != null) {\n cancelAnimationFrame(this.scrollRaf);\n this.scrollRaf = null;\n }\n if (this.lenisScrolling) this.endLenisScroll();\n const src = this.interruptSource();\n for (const ev of [\"wheel\", \"touchstart\", \"keydown\"]) {\n src.removeEventListener(ev, this.onInterrupt);\n src.removeEventListener(ev, this.onLenisInterrupt);\n }\n }\n\n /* ---------------- 度量、显隐与进度(可滚动且越过阈值才渲染) ---------------- */\n\n private maxScroll(): number {\n if (this.targetEl) return Math.max(0, this.targetEl.scrollHeight - this.targetEl.clientHeight);\n return Math.max(0, document.documentElement.scrollHeight - window.innerHeight);\n }\n\n private targetPos(dest: \"bottom\" | \"top\"): number {\n return dest === \"bottom\" ? this.maxScroll() : 0;\n }\n\n private scrollPos(): number {\n if (this.targetEl) return this.targetEl.scrollTop;\n return window.scrollY || document.documentElement.scrollTop || 0;\n }\n\n private setScrollPos(v: number): void {\n if (this.targetEl) this.targetEl.scrollTop = v;\n else window.scrollTo(0, v);\n }\n\n private onRecheck = (): void => {\n this.syncVisible();\n this.scheduleScroll();\n };\n\n private syncVisible(): void {\n this.el.hidden = this.maxScroll() <= 1 || this.scrollPos() < this.threshold;\n }\n\n /** rAF 节流:一帧内多次重估只吐一次;按钮隐藏期间照常调度 */\n private scheduleScroll(): void {\n if (this.emitRaf != null || this.destroyed) return;\n this.emitRaf = requestAnimationFrame(() => {\n this.emitRaf = null;\n this.emitScroll();\n });\n }\n\n private emitScroll(): void {\n if (this.destroyed) return;\n const max = this.maxScroll();\n const pct = max <= 0 ? 0 : Math.min(1, Math.max(0, this.scrollPos() / max));\n if (pct === this.lastPct) return;\n this.lastPct = pct;\n this.onScroll?.(pct);\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qingwu-ui/scroll-fab",
3
- "version": "0.9.0-beta.2",
4
- "description": "青梧 UI 悬浮滚动栏 —— 描边绕圈翻转「滚到底/回顶部」、Lenis 可插拔(未装回退 rAF)、异形形状(银杏叶等进度完全包裹)、零强依赖",
3
+ "version": "0.9.0-beta.3",
4
+ "description": "青梧 UI 悬浮滚动栏 —— 描边绕圈翻转「滚到底/回顶部」、Lenis 可插拔(未装回退 rAF)、异形形状(银杏叶等进度完全包裹)、单模式/阈值显隐/onScroll 进度回调、零强依赖",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "sideEffects": [