@rxdrag/website-lib-core 0.0.102 → 0.0.104
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/package.json +7 -7
- package/src/component-logic/index.ts +1 -8
- package/src/global.d.ts +7 -0
- package/src/index.ts +1 -3
- package/src/react/components/BackgroundHlsVideoPlayer.tsx +30 -1
- package/src/react/components/ContactForm/ContactForm.tsx +3 -2
- package/src/react/components/ContactForm/types.ts +1 -0
- package/src/react/components/Scroller.tsx +32 -4
- package/src/react/components/Share/socials.tsx +1 -2
- package/src/react/components/all.ts +39 -0
- package/src/react/components/index.ts +1 -2
- package/src/react/index.ts +1 -2
- package/src/component-logic/collapse.ts +0 -61
- package/src/component-logic/gsap.d.ts +0 -4
- package/src/component-logic/modal.ts +0 -45
- package/src/component-logic/motion.ts +0 -272
- package/src/component-logic/number.ts +0 -45
- package/src/component-logic/popover.ts +0 -51
- package/src/component-logic/tabs.ts +0 -10
- package/src/controller/AnimateController.ts +0 -138
- package/src/controller/AosController.ts +0 -240
- package/src/controller/CollapseController.ts +0 -130
- package/src/controller/FlipController.ts +0 -339
- package/src/controller/ModalController.ts +0 -127
- package/src/controller/NumberController.ts +0 -161
- package/src/controller/OpenableController.ts +0 -367
- package/src/controller/PageLoader.ts +0 -154
- package/src/controller/PopoverController.ts +0 -116
- package/src/controller/TabsController.ts +0 -271
- package/src/controller/applyAnimation.ts +0 -86
- package/src/controller/applyInitialState.ts +0 -79
- package/src/controller/consts.ts +0 -33
- package/src/controller/index.ts +0 -10
- package/src/controller/utils.ts +0 -48
- package/src/motion/consts.ts +0 -428
- package/src/motion/convertToGsapVars.ts +0 -102
- package/src/motion/index.ts +0 -6
- package/src/motion/normalizeAnimation.ts +0 -28
- package/src/motion/normalizeAosAnimation.ts +0 -22
- package/src/motion/normalizePopupAnimation.ts +0 -24
- package/src/motion/types.ts +0 -133
- package/src/react/hooks/index.ts +0 -1
- package/src/react/hooks/useScroll.ts +0 -30
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
import { DATA_MOTION_OPENABLE, OpenableAnimationConfig } from "../motion";
|
|
2
|
-
import { applyAnimation } from "./applyAnimation";
|
|
3
|
-
import { gsap } from "gsap/dist/gsap";
|
|
4
|
-
import {
|
|
5
|
-
EVENT_OPEN,
|
|
6
|
-
EVENT_CLOSE,
|
|
7
|
-
DATA_OPENABLE,
|
|
8
|
-
DATA_OPENABLE_ROLE,
|
|
9
|
-
OpenAble,
|
|
10
|
-
EVENT_SELECT,
|
|
11
|
-
EVENT_UNSELECT,
|
|
12
|
-
DATA_POPUP_CTA,
|
|
13
|
-
} from "./consts";
|
|
14
|
-
import { applyInitialState } from "./applyInitialState";
|
|
15
|
-
|
|
16
|
-
export type PopupEvent = {
|
|
17
|
-
//控件组的唯一标识
|
|
18
|
-
key: string;
|
|
19
|
-
//发出事件的element,popover对应Container,modal对应Trigger
|
|
20
|
-
target?: HTMLElement;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export type SelectionEvent = {
|
|
24
|
-
//控件组的唯一标识
|
|
25
|
-
key: string;
|
|
26
|
-
//发出事件的element
|
|
27
|
-
target: HTMLElement;
|
|
28
|
-
selection: string;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// 事件类型映射,用于类型安全的事件处理
|
|
32
|
-
type EventMap = {
|
|
33
|
-
[EVENT_OPEN]: PopupEvent;
|
|
34
|
-
[EVENT_CLOSE]: PopupEvent;
|
|
35
|
-
[EVENT_SELECT]: SelectionEvent;
|
|
36
|
-
[EVENT_UNSELECT]: SelectionEvent;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export class EventBus {
|
|
40
|
-
// 事件监听器存储,使用类型安全的映射
|
|
41
|
-
private eventListeners: {
|
|
42
|
-
[K in keyof EventMap]?: Array<(data: EventMap[K]) => void>;
|
|
43
|
-
} & {
|
|
44
|
-
[key: string]: Array<(data: unknown) => void>;
|
|
45
|
-
} = {};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* 触发自定义事件
|
|
49
|
-
* @param event - 事件名称
|
|
50
|
-
* @param options - 事件数据
|
|
51
|
-
*/
|
|
52
|
-
emit = <K extends keyof EventMap>(event: K, options?: EventMap[K]): void => {
|
|
53
|
-
try {
|
|
54
|
-
// 获取该事件的所有监听器
|
|
55
|
-
const listeners = this.eventListeners[event] || [];
|
|
56
|
-
|
|
57
|
-
// 调用所有监听器
|
|
58
|
-
listeners.forEach((callback) => {
|
|
59
|
-
try {
|
|
60
|
-
// 使用类型断言,因为我们知道监听器期望的类型与发送的类型匹配
|
|
61
|
-
if (event in this.eventListeners) {
|
|
62
|
-
// 对于已知的事件类型,我们可以安全地断言类型
|
|
63
|
-
(callback as (data: typeof options) => void)(options);
|
|
64
|
-
} else {
|
|
65
|
-
// 对于未知的事件类型,我们使用更通用的类型
|
|
66
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
-
callback(options as any);
|
|
68
|
-
}
|
|
69
|
-
} catch (error) {
|
|
70
|
-
console.error(`处理事件回调失败: ${event}`, error);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error(`触发事件失败: ${event}`, error);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* 监听自定义事件
|
|
80
|
-
* @param event - 事件名称
|
|
81
|
-
* @param callback - 事件回调函数
|
|
82
|
-
* @returns 取消监听的函数
|
|
83
|
-
*/
|
|
84
|
-
|
|
85
|
-
public on = <T = PopupEvent | SelectionEvent>(
|
|
86
|
-
event: string,
|
|
87
|
-
callback: (options: T) => void
|
|
88
|
-
): (() => void) => {
|
|
89
|
-
try {
|
|
90
|
-
// 确保事件监听器数组已初始化
|
|
91
|
-
if (!this.eventListeners[event]) {
|
|
92
|
-
this.eventListeners[event] = [];
|
|
93
|
-
}
|
|
94
|
-
//console.log("===$on", event, this.eventListeners);
|
|
95
|
-
|
|
96
|
-
// 将回调函数转换为适当的类型并添加到监听器列表中
|
|
97
|
-
const typedCallback = callback as unknown as (data: unknown) => void;
|
|
98
|
-
this.eventListeners[event].push(typedCallback);
|
|
99
|
-
|
|
100
|
-
// 返回取消监听的函数
|
|
101
|
-
return () => {
|
|
102
|
-
try {
|
|
103
|
-
if (this.eventListeners[event]) {
|
|
104
|
-
// 找到并移除监听器
|
|
105
|
-
const index = this.eventListeners[event].indexOf(typedCallback);
|
|
106
|
-
if (index !== -1) {
|
|
107
|
-
this.eventListeners[event].splice(index, 1);
|
|
108
|
-
|
|
109
|
-
// 如果没有更多监听器,清理数组
|
|
110
|
-
if (this.eventListeners[event].length === 0) {
|
|
111
|
-
delete this.eventListeners[event];
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error(`解除事件监听失败: ${event}`, error);
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
} catch (error) {
|
|
120
|
-
console.error(`注册事件监听失败: ${event}`, error);
|
|
121
|
-
return () => {};
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
destroy() {
|
|
126
|
-
this.eventListeners = {};
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export class OpenableController {
|
|
131
|
-
constructor(protected doc: Document | undefined) {}
|
|
132
|
-
protected unmountHandlers: (() => void)[] = [];
|
|
133
|
-
protected eventBus = new EventBus();
|
|
134
|
-
public lastCta?: string | null;
|
|
135
|
-
|
|
136
|
-
mount() {
|
|
137
|
-
this.unmount();
|
|
138
|
-
this.initOpenableAnimations();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
unmount() {
|
|
142
|
-
this.unmountHandlers.forEach((handler) => handler());
|
|
143
|
-
this.unmountHandlers = [];
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* 打开弹出层
|
|
147
|
-
* @param openableKey - 弹出层的唯一标识
|
|
148
|
-
* @param target - 触发打开的目标元素
|
|
149
|
-
*/
|
|
150
|
-
open = (openableKey: string, target: HTMLElement): void => {
|
|
151
|
-
if (!this.doc) return;
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
// 查找 Modal 容器
|
|
155
|
-
const modalContainer = this.getOpenableContainer(openableKey);
|
|
156
|
-
if (modalContainer) {
|
|
157
|
-
if (target.getAttribute(DATA_POPUP_CTA)) {
|
|
158
|
-
this.lastCta = target.getAttribute(DATA_POPUP_CTA);
|
|
159
|
-
}
|
|
160
|
-
modalContainer.classList.add("open");
|
|
161
|
-
this.eventBus.emit(EVENT_OPEN, { key: openableKey, target });
|
|
162
|
-
} else {
|
|
163
|
-
console.warn(`未找到弹出层: ${openableKey}`);
|
|
164
|
-
}
|
|
165
|
-
} catch (error) {
|
|
166
|
-
console.error(`打开弹出层失败: ${openableKey}`, error);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* 关闭弹出层
|
|
172
|
-
* @param openableKey - 弹出层的唯一标识
|
|
173
|
-
* @param target - 可选的目标元素,如果提供则使用该元素,否则查找容器
|
|
174
|
-
*/
|
|
175
|
-
close = (openableKey: string, target?: HTMLElement): void => {
|
|
176
|
-
if (!this.doc) return;
|
|
177
|
-
|
|
178
|
-
try {
|
|
179
|
-
const container = target || this.getOpenableContainer(openableKey);
|
|
180
|
-
if (!container) {
|
|
181
|
-
console.warn(`找不到弹出层容器: ${openableKey}`);
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// 移除open类
|
|
186
|
-
container.classList.remove("open");
|
|
187
|
-
|
|
188
|
-
// 触发关闭事件
|
|
189
|
-
this.eventBus.emit(EVENT_CLOSE, {
|
|
190
|
-
key: openableKey,
|
|
191
|
-
target: container,
|
|
192
|
-
});
|
|
193
|
-
} catch (error) {
|
|
194
|
-
console.error(`关闭弹出层失败: ${openableKey}`, error);
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* 获取弹出层容器元素
|
|
200
|
-
* @param openableKey - 弹出层的唯一标识
|
|
201
|
-
* @returns 弹出层容器元素,如果未找到则返回undefined
|
|
202
|
-
*/
|
|
203
|
-
getOpenableContainer = (openableKey: string): HTMLElement | undefined => {
|
|
204
|
-
if (!this.doc) return undefined;
|
|
205
|
-
|
|
206
|
-
try {
|
|
207
|
-
return this.doc.querySelector(
|
|
208
|
-
`[${DATA_OPENABLE}="${openableKey}"][${DATA_OPENABLE_ROLE}="${OpenAble.ModalContainer}"],
|
|
209
|
-
[${DATA_OPENABLE}="${openableKey}"][${DATA_OPENABLE_ROLE}="${OpenAble.PopoverContainer}"],
|
|
210
|
-
[${DATA_OPENABLE}="${openableKey}"][${DATA_OPENABLE_ROLE}="${OpenAble.CollapseContainer}"]`
|
|
211
|
-
) as HTMLElement | undefined;
|
|
212
|
-
} catch (error) {
|
|
213
|
-
console.error(`获取弹出层容器失败: ${openableKey}`, error);
|
|
214
|
-
return undefined;
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* 初始化弹窗动画
|
|
220
|
-
* @returns 清理函数
|
|
221
|
-
*/
|
|
222
|
-
initOpenableAnimations = () => {
|
|
223
|
-
const unsubscribe = this.onOpenAll((event) => {
|
|
224
|
-
const openResponseElements = this.doc?.querySelectorAll(
|
|
225
|
-
`[${DATA_MOTION_OPENABLE}]`
|
|
226
|
-
);
|
|
227
|
-
const containerElement = this.getOpenableContainer(event.key);
|
|
228
|
-
//处理循环嵌套的情况
|
|
229
|
-
const subContainers = containerElement?.querySelectorAll(
|
|
230
|
-
`[${DATA_OPENABLE_ROLE}="${OpenAble.CollapseContainer}"],
|
|
231
|
-
[${DATA_OPENABLE_ROLE}="${OpenAble.ModalContainer}"],
|
|
232
|
-
[${DATA_OPENABLE_ROLE}="${OpenAble.PopoverContainer}"]`
|
|
233
|
-
);
|
|
234
|
-
//响应open事件
|
|
235
|
-
openResponseElements?.forEach((element) => {
|
|
236
|
-
let isInSubContainer = false;
|
|
237
|
-
subContainers?.forEach((subContainer) => {
|
|
238
|
-
if (subContainer.contains(element)) {
|
|
239
|
-
isInSubContainer = true;
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
if (
|
|
244
|
-
this.doc?.defaultView &&
|
|
245
|
-
element instanceof this.doc.defaultView?.HTMLElement &&
|
|
246
|
-
containerElement?.contains(element) &&
|
|
247
|
-
!isInSubContainer
|
|
248
|
-
) {
|
|
249
|
-
if (element.dataset.motionOpenable) {
|
|
250
|
-
const animation = JSON.parse(element.dataset.motionOpenable) as
|
|
251
|
-
| OpenableAnimationConfig
|
|
252
|
-
| undefined;
|
|
253
|
-
if (animation?.initial) {
|
|
254
|
-
applyInitialState(element, animation.initial);
|
|
255
|
-
}
|
|
256
|
-
if (animation?.open) {
|
|
257
|
-
applyAnimation(element, animation.open);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
return () => {
|
|
264
|
-
const closeResponseElements = this.doc?.querySelectorAll(
|
|
265
|
-
`[${DATA_MOTION_OPENABLE}]`
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
//响应close事件
|
|
269
|
-
closeResponseElements?.forEach((element) => {
|
|
270
|
-
if (
|
|
271
|
-
this.doc?.defaultView &&
|
|
272
|
-
element instanceof this.doc?.defaultView.HTMLElement
|
|
273
|
-
) {
|
|
274
|
-
if (
|
|
275
|
-
element.dataset.motionOpenable &&
|
|
276
|
-
containerElement?.contains(element)
|
|
277
|
-
) {
|
|
278
|
-
const animation = JSON.parse(element.dataset.motionOpenable) as
|
|
279
|
-
| OpenableAnimationConfig
|
|
280
|
-
| undefined;
|
|
281
|
-
if (animation?.close) {
|
|
282
|
-
// 首先终止该元素上可能正在进行的所有动画(包括open动画)
|
|
283
|
-
gsap.killTweensOf(element);
|
|
284
|
-
|
|
285
|
-
// 然后应用close动画
|
|
286
|
-
applyAnimation(element, animation.close);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
} else {
|
|
290
|
-
console.log("===Not an HTMLElement somehow?");
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
};
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
this.unmountHandlers.push(() => {
|
|
297
|
-
unsubscribe();
|
|
298
|
-
});
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
public onOpen = (
|
|
302
|
-
openableKey: string,
|
|
303
|
-
callback: (event: PopupEvent) => VoidFunction | void
|
|
304
|
-
): (() => void) => {
|
|
305
|
-
return this.onOpenAll((event) => {
|
|
306
|
-
if (event.key === openableKey) {
|
|
307
|
-
return callback(event);
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
public onClose = (
|
|
313
|
-
openableKey: string,
|
|
314
|
-
callback: (event: PopupEvent) => void
|
|
315
|
-
) => {
|
|
316
|
-
return this.onCloseAll((event) => {
|
|
317
|
-
if (event.key === openableKey) {
|
|
318
|
-
callback(event);
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
public onCloseAll = (callback: (event: PopupEvent) => void) => {
|
|
324
|
-
return this.eventBus.on(EVENT_CLOSE, callback);
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* 用于在脚本中监听弹出层的打开/关闭事件,并处理相关动画效果
|
|
329
|
-
* 注意:此函数主要用于脚本中的事件处理,组件内部已有相应实现,无需使用
|
|
330
|
-
*
|
|
331
|
-
* @param callback - 弹出层打开时的回调函数,可返回关闭时的清理函数
|
|
332
|
-
* @returns 取消监听的函数
|
|
333
|
-
*/
|
|
334
|
-
public onOpenAll = (
|
|
335
|
-
callback: (event: PopupEvent) => VoidFunction | void
|
|
336
|
-
): (() => void) => {
|
|
337
|
-
let unsubscribe: ((event: PopupEvent) => void) | void;
|
|
338
|
-
|
|
339
|
-
const handleOpen = (event: PopupEvent) => {
|
|
340
|
-
try {
|
|
341
|
-
unsubscribe = callback?.(event);
|
|
342
|
-
} catch (error) {
|
|
343
|
-
console.error("处理弹出层打开事件失败", error);
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
const unsubscribeOpen = this.eventBus.on(EVENT_OPEN, handleOpen);
|
|
348
|
-
|
|
349
|
-
const handleClose = (event: PopupEvent) => {
|
|
350
|
-
try {
|
|
351
|
-
unsubscribe?.(event);
|
|
352
|
-
} catch (error) {
|
|
353
|
-
console.error("处理弹出层关闭事件失败", error);
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
const unsubscribeClose = this.eventBus.on(EVENT_CLOSE, handleClose);
|
|
357
|
-
|
|
358
|
-
return () => {
|
|
359
|
-
unsubscribeOpen();
|
|
360
|
-
unsubscribeClose();
|
|
361
|
-
};
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
destroy = () => {
|
|
365
|
-
this.eventBus.destroy();
|
|
366
|
-
};
|
|
367
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
export interface IPageLoader {
|
|
2
|
-
onLoaded: (callback: () => void) => () => void;
|
|
3
|
-
destroy: () => void;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export type PageLoaderKey = "runtime" | "preview" | "design";
|
|
7
|
-
export class PageLoader implements IPageLoader {
|
|
8
|
-
private static instances: Record<string, PageLoader> = {};
|
|
9
|
-
private doc?: Document;
|
|
10
|
-
private key: string;
|
|
11
|
-
private callbacks: Set<() => void> = new Set();
|
|
12
|
-
private isSwapEventRegistered = false;
|
|
13
|
-
|
|
14
|
-
private constructor(key: PageLoaderKey, doc?: Document) {
|
|
15
|
-
this.key = key;
|
|
16
|
-
this.doc = doc;
|
|
17
|
-
if (doc) {
|
|
18
|
-
this.registerMainEventListeners();
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 获取 PageLoader 实例
|
|
24
|
-
* @param key 实例的唯一标识,默认为 "preview"
|
|
25
|
-
* @param doc 文档对象
|
|
26
|
-
* @returns PageLoader 实例
|
|
27
|
-
*/
|
|
28
|
-
public static getInstance(
|
|
29
|
-
key: PageLoaderKey = "preview",
|
|
30
|
-
doc?: Document
|
|
31
|
-
): PageLoader {
|
|
32
|
-
if (!PageLoader.instances[key]) {
|
|
33
|
-
PageLoader.instances[key] = new PageLoader(key, doc);
|
|
34
|
-
} else if (doc) {
|
|
35
|
-
PageLoader.instances[key].setDoc(doc);
|
|
36
|
-
}
|
|
37
|
-
return PageLoader.instances[key];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* 销毁指定 key 的实例
|
|
42
|
-
* @param key 实例的唯一标识
|
|
43
|
-
*/
|
|
44
|
-
public static destroyInstance(key: PageLoaderKey = "preview"): void {
|
|
45
|
-
if (PageLoader.instances[key]) {
|
|
46
|
-
PageLoader.instances[key].destroy();
|
|
47
|
-
delete PageLoader.instances[key];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* 设置文档对象
|
|
53
|
-
* @param doc 文档对象
|
|
54
|
-
*/
|
|
55
|
-
private setDoc(doc?: Document) {
|
|
56
|
-
if (this.doc !== doc) {
|
|
57
|
-
this.doc = doc;
|
|
58
|
-
if (doc) {
|
|
59
|
-
this.registerMainEventListeners();
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
private registerMainEventListeners() {
|
|
65
|
-
if (!this.doc || this.isSwapEventRegistered) return;
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
// 使用标志来确保回调只执行一次
|
|
69
|
-
//let hasExecuted = false;
|
|
70
|
-
|
|
71
|
-
// 主事件处理函数,调用所有注册的回调
|
|
72
|
-
const handleEvent = () => {
|
|
73
|
-
console.log("===>页面转场事件触发,执行回调");
|
|
74
|
-
// 如果已经执行过,则不再执行
|
|
75
|
-
//if (hasExecuted) return;
|
|
76
|
-
|
|
77
|
-
//hasExecuted = true;
|
|
78
|
-
|
|
79
|
-
// 执行所有回调
|
|
80
|
-
this.callbacks.forEach((callback) => {
|
|
81
|
-
try {
|
|
82
|
-
callback();
|
|
83
|
-
} catch (error) {
|
|
84
|
-
console.error("执行回调失败", error);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// 重置执行标志,允许下一次页面转场时再次执行
|
|
89
|
-
// setTimeout(() => {
|
|
90
|
-
// hasExecuted = false;
|
|
91
|
-
// }, 100);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// 只监听一个事件,优先使用 astro:page-load
|
|
95
|
-
if (typeof window !== "undefined") {
|
|
96
|
-
// 在客户端环境中
|
|
97
|
-
this.doc.addEventListener("astro:page-load", handleEvent, {
|
|
98
|
-
//once: true,
|
|
99
|
-
});
|
|
100
|
-
// 只监听页面转场事件
|
|
101
|
-
this.doc.addEventListener("astro:after-swap", handleEvent);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
this.isSwapEventRegistered = true;
|
|
105
|
-
} catch (error) {
|
|
106
|
-
console.error("注册事件监听器失败", error);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
public onLoaded = (callback: () => void): (() => void) => {
|
|
111
|
-
// 如果不是 runtime,直接执行回调
|
|
112
|
-
if (this.key && this.key !== "runtime") {
|
|
113
|
-
callback();
|
|
114
|
-
return () => {};
|
|
115
|
-
}
|
|
116
|
-
// 检查回调是否已经注册过
|
|
117
|
-
if (this.callbacks.has(callback)) {
|
|
118
|
-
// 如果已注册,直接返回清理函数
|
|
119
|
-
return () => {
|
|
120
|
-
this.callbacks.delete(callback);
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// 添加回调到集合
|
|
125
|
-
this.callbacks.add(callback);
|
|
126
|
-
|
|
127
|
-
// 不再在这里立即执行回调,而是完全依赖事件系统
|
|
128
|
-
|
|
129
|
-
// 返回清理函数
|
|
130
|
-
return () => {
|
|
131
|
-
this.callbacks.delete(callback);
|
|
132
|
-
};
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
public destroy(): void {
|
|
136
|
-
if (this.doc && this.isSwapEventRegistered) {
|
|
137
|
-
// 移除所有事件监听器
|
|
138
|
-
const noop = () => {};
|
|
139
|
-
this.doc.removeEventListener("astro:after-swap", noop);
|
|
140
|
-
this.doc.removeEventListener("astro:page-load", noop);
|
|
141
|
-
|
|
142
|
-
// 清空回调集合
|
|
143
|
-
this.callbacks.clear();
|
|
144
|
-
this.isSwapEventRegistered = false;
|
|
145
|
-
}
|
|
146
|
-
this.doc = undefined;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// 导出单例实例,方便直接使用
|
|
151
|
-
export const pageLoader = PageLoader.getInstance(
|
|
152
|
-
"runtime",
|
|
153
|
-
typeof document !== "undefined" ? document : undefined
|
|
154
|
-
);
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { DATA_OPENABLE, DATA_OPENABLE_ROLE, OpenAble } from "./consts";
|
|
2
|
-
import { OpenableController } from "./OpenableController";
|
|
3
|
-
|
|
4
|
-
export class PopoverController extends OpenableController {
|
|
5
|
-
private static instances: Record<string, PopoverController> = {};
|
|
6
|
-
private constructor(doc?: Document) {
|
|
7
|
-
super(doc);
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* 获取 PopoverController 实例
|
|
11
|
-
* @param key 实例的唯一标识,默认为 "runtime"
|
|
12
|
-
* @param doc 文档对象
|
|
13
|
-
* @returns PopoverController 实例
|
|
14
|
-
*/
|
|
15
|
-
public static getInstance(
|
|
16
|
-
key: string = "runtime",
|
|
17
|
-
doc?: Document
|
|
18
|
-
): PopoverController {
|
|
19
|
-
if (!PopoverController.instances[key]) {
|
|
20
|
-
PopoverController.instances[key] = new PopoverController(doc);
|
|
21
|
-
} else if (doc) {
|
|
22
|
-
PopoverController.instances[key].setDoc(doc);
|
|
23
|
-
}
|
|
24
|
-
return PopoverController.instances[key];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 销毁指定 key 的实例
|
|
29
|
-
* @param key 实例的唯一标识
|
|
30
|
-
*/
|
|
31
|
-
public static destroyInstance(key: string = "runtime"): void {
|
|
32
|
-
if (PopoverController.instances[key]) {
|
|
33
|
-
PopoverController.instances[key].destroy();
|
|
34
|
-
delete PopoverController.instances[key];
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
private setDoc(doc?: Document) {
|
|
39
|
-
this.doc = doc;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
public getDocument(): Document | null {
|
|
43
|
-
return this.doc ?? null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
mount() {
|
|
47
|
-
//console.log("====> mount popover", this.doc);
|
|
48
|
-
super.mount();
|
|
49
|
-
// 获取所有的 Popover 实例
|
|
50
|
-
const popups = this.doc?.querySelectorAll(`[${DATA_OPENABLE}]`);
|
|
51
|
-
popups?.forEach((popup) => {
|
|
52
|
-
const openableKey = popup.getAttribute(DATA_OPENABLE);
|
|
53
|
-
//处理鼠标交互事件
|
|
54
|
-
if (openableKey && popup) {
|
|
55
|
-
if (
|
|
56
|
-
popup.getAttribute(DATA_OPENABLE_ROLE) === OpenAble.PopoverContainer
|
|
57
|
-
) {
|
|
58
|
-
const unsub = this.initPopover(openableKey, popup as HTMLElement);
|
|
59
|
-
this.unmountHandlers.push(unsub);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
unmount(): void {
|
|
66
|
-
super.unmount();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 初始化Popover的鼠标交互事件
|
|
71
|
-
* @param openableKey - 弹出层的唯一标识
|
|
72
|
-
* @param element - 弹出层的 DOM 元素
|
|
73
|
-
* @returns 清理函数,用于移除事件监听器
|
|
74
|
-
*/
|
|
75
|
-
public initPopover = (
|
|
76
|
-
openableKey: string,
|
|
77
|
-
element: HTMLElement
|
|
78
|
-
): (() => void) => {
|
|
79
|
-
if (!this.doc) return () => {};
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
// 创建新的事件处理函数
|
|
83
|
-
const handleMouseEnter = () => {
|
|
84
|
-
this.open(openableKey, element);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const handleMouseLeave = () => {
|
|
88
|
-
this.close(openableKey);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// 添加新的事件监听器
|
|
92
|
-
element.addEventListener("mouseenter", handleMouseEnter);
|
|
93
|
-
element.addEventListener("mouseleave", handleMouseLeave);
|
|
94
|
-
|
|
95
|
-
// 使用类型断言来解决 TypeScript 类型问题
|
|
96
|
-
const options = { passive: true };
|
|
97
|
-
element.addEventListener("touchstart", handleMouseEnter, options as EventListenerOptions);
|
|
98
|
-
element.addEventListener("touchend", handleMouseLeave, options as EventListenerOptions);
|
|
99
|
-
|
|
100
|
-
return () => {
|
|
101
|
-
element.removeEventListener("mouseenter", handleMouseEnter);
|
|
102
|
-
element.removeEventListener("mouseleave", handleMouseLeave);
|
|
103
|
-
element.removeEventListener("touchstart", handleMouseEnter, options as EventListenerOptions);
|
|
104
|
-
element.removeEventListener("touchend", handleMouseLeave, options as EventListenerOptions);
|
|
105
|
-
};
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.error(`初始化Popover事件失败: ${openableKey}`, error);
|
|
108
|
-
return () => {};
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export const popover = PopoverController.getInstance(
|
|
114
|
-
"runtime",
|
|
115
|
-
typeof document !== "undefined" ? document : undefined
|
|
116
|
-
);
|