@whitesev/pops 3.2.1 → 3.2.2

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.
Files changed (126) hide show
  1. package/README.md +249 -249
  2. package/dist/index.amd.js +702 -667
  3. package/dist/index.amd.js.map +1 -1
  4. package/dist/index.amd.min.js +1 -1
  5. package/dist/index.amd.min.js.map +1 -1
  6. package/dist/index.cjs.js +702 -667
  7. package/dist/index.cjs.js.map +1 -1
  8. package/dist/index.cjs.min.js +1 -1
  9. package/dist/index.cjs.min.js.map +1 -1
  10. package/dist/index.esm.js +702 -667
  11. package/dist/index.esm.js.map +1 -1
  12. package/dist/index.esm.min.js +1 -1
  13. package/dist/index.esm.min.js.map +1 -1
  14. package/dist/index.iife.js +702 -667
  15. package/dist/index.iife.js.map +1 -1
  16. package/dist/index.iife.min.js +1 -1
  17. package/dist/index.iife.min.js.map +1 -1
  18. package/dist/index.system.js +702 -667
  19. package/dist/index.system.js.map +1 -1
  20. package/dist/index.system.min.js +1 -1
  21. package/dist/index.system.min.js.map +1 -1
  22. package/dist/index.umd.js +702 -667
  23. package/dist/index.umd.js.map +1 -1
  24. package/dist/index.umd.min.js +1 -1
  25. package/dist/index.umd.min.js.map +1 -1
  26. package/dist/types/src/components/folder/types/index.d.ts +43 -13
  27. package/dist/types/src/types/PopsDOMUtilsEventType.d.ts +313 -313
  28. package/dist/types/src/types/animation.d.ts +19 -19
  29. package/dist/types/src/types/button.d.ts +94 -94
  30. package/dist/types/src/types/components.d.ts +211 -211
  31. package/dist/types/src/types/event.d.ts +43 -43
  32. package/dist/types/src/types/global.d.ts +31 -31
  33. package/dist/types/src/types/icon.d.ts +32 -32
  34. package/dist/types/src/types/inst.d.ts +28 -28
  35. package/dist/types/src/types/main.d.ts +66 -66
  36. package/dist/types/src/types/mask.d.ts +52 -52
  37. package/dist/types/src/types/position.d.ts +60 -60
  38. package/package.json +27 -25
  39. package/src/Pops.ts +206 -206
  40. package/src/PopsAnimation.ts +32 -32
  41. package/src/PopsCSS.ts +54 -54
  42. package/src/PopsCore.ts +37 -37
  43. package/src/PopsIcon.ts +95 -95
  44. package/src/PopsInst.ts +21 -21
  45. package/src/components/alert/defaultConfig.ts +62 -62
  46. package/src/components/alert/index.ts +163 -163
  47. package/src/components/alert/types/index.ts +23 -23
  48. package/src/components/confirm/defaultConfig.ts +90 -90
  49. package/src/components/confirm/index.ts +165 -165
  50. package/src/components/confirm/types/index.ts +13 -17
  51. package/src/components/drawer/defaultConfig.ts +89 -89
  52. package/src/components/drawer/index.css +37 -37
  53. package/src/components/drawer/index.ts +245 -245
  54. package/src/components/drawer/types/index.ts +62 -61
  55. package/src/components/folder/defaultConfig.ts +151 -151
  56. package/src/components/folder/folderIcon.ts +28 -28
  57. package/src/components/folder/index.css +303 -303
  58. package/src/components/folder/index.ts +953 -932
  59. package/src/components/folder/types/index.ts +143 -110
  60. package/src/components/iframe/defaultConfig.ts +60 -60
  61. package/src/components/iframe/index.css +76 -76
  62. package/src/components/iframe/index.ts +331 -331
  63. package/src/components/iframe/types/index.ts +96 -96
  64. package/src/components/loading/defaultConfig.ts +29 -29
  65. package/src/components/loading/index.css +66 -66
  66. package/src/components/loading/index.ts +101 -101
  67. package/src/components/loading/types/index.ts +36 -34
  68. package/src/components/panel/css/components-select.css +84 -84
  69. package/src/components/panel/defaultConfig.ts +868 -868
  70. package/src/components/panel/handlerComponents.ts +3993 -3993
  71. package/src/components/panel/index.css +1403 -1403
  72. package/src/components/panel/index.ts +221 -221
  73. package/src/components/panel/types/components-button.ts +56 -56
  74. package/src/components/panel/types/components-common.ts +73 -73
  75. package/src/components/panel/types/components-container.ts +25 -25
  76. package/src/components/panel/types/components-deepMenu.ts +64 -64
  77. package/src/components/panel/types/components-input.ts +90 -90
  78. package/src/components/panel/types/components-own.ts +16 -16
  79. package/src/components/panel/types/components-select.ts +169 -169
  80. package/src/components/panel/types/components-selectMultiple.ts +105 -104
  81. package/src/components/panel/types/components-slider.ts +55 -55
  82. package/src/components/panel/types/components-switch.ts +33 -33
  83. package/src/components/panel/types/components-textarea.ts +45 -45
  84. package/src/components/panel/types/index.ts +244 -244
  85. package/src/components/prompt/defaultConfig.ts +94 -94
  86. package/src/components/prompt/index.css +34 -34
  87. package/src/components/prompt/index.ts +187 -215
  88. package/src/components/prompt/types/index.ts +57 -56
  89. package/src/components/rightClickMenu/defaultConfig.ts +103 -103
  90. package/src/components/rightClickMenu/index.css +115 -115
  91. package/src/components/rightClickMenu/index.ts +662 -662
  92. package/src/components/rightClickMenu/types/index.ts +145 -143
  93. package/src/components/searchSuggestion/defaultConfig.ts +63 -63
  94. package/src/components/searchSuggestion/index.ts +813 -813
  95. package/src/components/searchSuggestion/types/index.ts +244 -242
  96. package/src/components/tooltip/defaultConfig.ts +33 -33
  97. package/src/components/tooltip/index.css +199 -199
  98. package/src/components/tooltip/index.ts +617 -617
  99. package/src/components/tooltip/types/index.ts +123 -121
  100. package/src/config/CommonCSSClassName.ts +17 -17
  101. package/src/config/GlobalConfig.ts +63 -63
  102. package/src/css/animation.css +987 -987
  103. package/src/css/button.css +551 -551
  104. package/src/css/common.css +54 -54
  105. package/src/css/index.css +253 -253
  106. package/src/css/ninePalaceGridPosition.css +50 -50
  107. package/src/css/scrollbar.css +22 -22
  108. package/src/handler/PopsElementHandler.ts +303 -303
  109. package/src/handler/PopsHandler.ts +611 -611
  110. package/src/types/PopsDOMUtilsEventType.d.ts +313 -313
  111. package/src/types/animation.d.ts +19 -19
  112. package/src/types/button.d.ts +94 -94
  113. package/src/types/components.d.ts +211 -211
  114. package/src/types/event.d.ts +43 -43
  115. package/src/types/global.d.ts +31 -31
  116. package/src/types/icon.d.ts +32 -32
  117. package/src/types/inst.d.ts +28 -28
  118. package/src/types/main.d.ts +66 -66
  119. package/src/types/mask.d.ts +52 -52
  120. package/src/types/position.d.ts +60 -60
  121. package/src/utils/PopsDOMUtils.ts +2483 -2483
  122. package/src/utils/PopsDOMUtilsEventsConfig.ts +4 -4
  123. package/src/utils/PopsInstanceUtils.ts +714 -714
  124. package/src/utils/PopsMathUtils.ts +71 -71
  125. package/src/utils/PopsSafeUtils.ts +22 -22
  126. package/src/utils/PopsUtils.ts +421 -421
@@ -1,662 +1,662 @@
1
- import { OriginPrototype } from "../../PopsCore";
2
- import { GlobalConfig } from "../../config/GlobalConfig";
3
- import { PopsHandler } from "../../handler/PopsHandler";
4
- import { popsDOMUtils } from "../../utils/PopsDOMUtils";
5
- import { PopsSafeUtils } from "../../utils/PopsSafeUtils";
6
- import { popsUtils } from "../../utils/PopsUtils";
7
- import { PopsRightClickMenuDefaultConfig } from "./defaultConfig";
8
- import type {
9
- PopsRightClickMenuChildRootStoreNodeValue,
10
- PopsRightClickMenuConfig,
11
- PopsRightClickMenuDataConfig,
12
- PopsRightClickMenuItemStoreNodeValue,
13
- PopsRightClickMenuRootStoreNodeValue,
14
- } from "./types";
15
- import { PopsCSS } from "../../PopsCSS";
16
- import { PopsIcon } from "../../PopsIcon";
17
- import type { PopsType } from "../../types/main";
18
-
19
- export const PopsRightClickMenu = {
20
- init(__config__: PopsRightClickMenuConfig) {
21
- const guid = popsUtils.getRandomGUID();
22
- // 设置当前类型
23
- const popsType: PopsType = "rightClickMenu";
24
-
25
- let config = PopsRightClickMenuDefaultConfig();
26
- config = popsUtils.assign(config, GlobalConfig.getGlobalConfig());
27
- config = popsUtils.assign(config, __config__);
28
- config = PopsHandler.handleOnly(popsType, config);
29
-
30
- const { $shadowContainer, $shadowRoot } = PopsHandler.handlerShadow(config);
31
- PopsHandler.handleInit($shadowRoot, [
32
- {
33
- name: "index",
34
- css: PopsCSS.index,
35
- },
36
- {
37
- name: "anim",
38
- css: PopsCSS.anim,
39
- },
40
- {
41
- name: "common",
42
- css: PopsCSS.common,
43
- },
44
- {
45
- name: "rightClickMenu",
46
- css: PopsCSS.rightClickMenu,
47
- },
48
- ]);
49
-
50
- if (config.style != null) {
51
- const $css = popsDOMUtils.createElement(
52
- "style",
53
- {
54
- innerHTML: config.style,
55
- },
56
- {
57
- type: "text/css",
58
- }
59
- );
60
- $shadowRoot.appendChild($css);
61
- }
62
-
63
- const PopsContextMenu = {
64
- $data: {
65
- menuDataKey: "data-menu",
66
- },
67
- $el: {
68
- $root: null as any as HTMLElement,
69
- },
70
- /**
71
- * 全局点击检测
72
- * @param event
73
- */
74
- windowCheckClickEvent(event: MouseEvent | PointerEvent) {
75
- if (!PopsContextMenu.$el.$root) {
76
- return;
77
- }
78
- const $click = event.target as HTMLElement;
79
- if ($click.closest(`.pops-${popsType}`)) {
80
- return;
81
- }
82
- if ($click.className && $click.className === "pops-shadow-container" && $click.shadowRoot != null) {
83
- // pops的shadow-container
84
- PopsContextMenu.shadowRootCheckClickEvent(event);
85
- return;
86
- }
87
- PopsContextMenu.closeAllMenu(PopsContextMenu.$el.$root);
88
- },
89
- /**
90
- * target为shadowRoot或shadowRoot内的全局点击检测
91
- * @param event
92
- */
93
- shadowRootCheckClickEvent(event: MouseEvent | PointerEvent) {
94
- if (!PopsContextMenu.$el.$root) {
95
- return;
96
- }
97
- const $click = event.composedPath()[0] as HTMLElement;
98
- if ($click.closest(`.pops-${popsType}`)) {
99
- return;
100
- }
101
- PopsContextMenu.closeAllMenu(PopsContextMenu.$el.$root);
102
- },
103
- /**
104
- * 添加全局点击检测事件
105
- */
106
- addWindowCheckClickListener() {
107
- popsDOMUtils.on(globalThis, "click touchstart", PopsContextMenu.windowCheckClickEvent, {
108
- capture: true,
109
- });
110
- if (config.$target instanceof Node) {
111
- const $shadowRoot = config.$target.getRootNode();
112
- if ($shadowRoot instanceof ShadowRoot) {
113
- popsDOMUtils.on($shadowRoot, "click touchstart", PopsContextMenu.shadowRootCheckClickEvent, {
114
- capture: true,
115
- });
116
- }
117
- }
118
- },
119
- /**
120
- * 移除全局点击检测事件
121
- */
122
- removeWindowCheckClickListener() {
123
- popsDOMUtils.off(globalThis, "click touchstart", PopsContextMenu.windowCheckClickEvent, {
124
- capture: true,
125
- });
126
- if (config.$target instanceof Node) {
127
- const $shadowRoot = config.$target.getRootNode();
128
- if ($shadowRoot instanceof ShadowRoot) {
129
- popsDOMUtils.off($shadowRoot, "click touchstart", PopsContextMenu.windowCheckClickEvent, {
130
- capture: true,
131
- });
132
- }
133
- }
134
- },
135
- /**
136
- * contextmenu事件
137
- * @param event
138
- * @param selectorTarget
139
- */
140
- async contextMenuEvent(event: PointerEvent, selectorTarget: NonNullable<PopsRightClickMenuConfig["$target"]>) {
141
- if (config.preventDefault) {
142
- popsDOMUtils.preventEvent(event);
143
- }
144
- PopsHandler.handleOnly(popsType, config);
145
- if (PopsContextMenu.$el.$root) {
146
- PopsContextMenu.closeAllMenu(PopsContextMenu.$el.$root);
147
- }
148
- selectorTarget = selectorTarget ?? config.$target;
149
- const beforeShowCallBackResult = await config?.beforeShowCallBack(event);
150
- if (typeof beforeShowCallBackResult === "boolean" && !beforeShowCallBackResult) {
151
- return;
152
- }
153
- const rootElement = PopsContextMenu.showMenu(event, config.data, selectorTarget);
154
- PopsContextMenu.$el.$root = rootElement;
155
- if (config.only) {
156
- PopsHandler.handlePush(popsType, {
157
- $shadowRoot: $shadowRoot,
158
- $shadowContainer: $shadowContainer,
159
- guid: guid,
160
- $anim: rootElement,
161
- $pops: rootElement,
162
- beforeRemoveCallBack(instCommonConfig) {
163
- PopsContextMenu.closeAllMenu(instCommonConfig.$pops);
164
- },
165
- config: config,
166
- destory: () => {
167
- PopsContextMenu.closeAllMenu(rootElement);
168
- },
169
- });
170
- }
171
- },
172
- /**
173
- * 添加contextmenu事件
174
- * @param target 目标
175
- * @param selector 子元素选择器
176
- */
177
- addContextMenuEvent(target: PopsRightClickMenuConfig["$target"], selector?: string) {
178
- popsDOMUtils.on(target!, "contextmenu", selector, PopsContextMenu.contextMenuEvent);
179
- },
180
- /**
181
- * 移除contextmenu事件
182
- * @param target 目标
183
- * @param selector 子元素选择器
184
- */
185
- removeContextMenuEvent(target: HTMLElement | typeof globalThis | Window, selector?: string) {
186
- popsDOMUtils.off(target, "contextmenu", selector, PopsContextMenu.contextMenuEvent);
187
- },
188
- /**
189
- * 自动判断是否存在动画,存在动画就执行关闭动画并删除
190
- * @param $menu
191
- */
192
- animationCloseMenu($menu: HTMLElement) {
193
- /**
194
- * 动画结束触发的事件
195
- */
196
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
197
- const transitionEndEvent = (event: TransitionEvent) => {
198
- popsDOMUtils.off($menu, popsDOMUtils.getTransitionEndNameList(), transitionEndEvent, {
199
- capture: true,
200
- });
201
- $menu.remove();
202
- };
203
- if (popsDOMUtils.containsClassName($menu, `pops-${popsType}-anim-show`)) {
204
- // 有动画
205
- popsDOMUtils.on($menu, popsDOMUtils.getTransitionEndNameList(), transitionEndEvent, {
206
- capture: true,
207
- });
208
- popsDOMUtils.removeClassName($menu, `pops-${popsType}-anim-show`);
209
- } else if (
210
- popsDOMUtils.containsClassName($menu, `pops-${popsType}-anim-scale`) &&
211
- popsDOMUtils.containsClassName($menu, `pops-${popsType}-anim-scale-open`)
212
- ) {
213
- // 有动画
214
- popsDOMUtils.on($menu, popsDOMUtils.getTransitionEndNameList(), transitionEndEvent, {
215
- capture: true,
216
- });
217
- popsDOMUtils.removeClassName($menu, `pops-${popsType}-anim-scale-open`);
218
- popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-scale-not-open`);
219
- } else {
220
- // 无动画
221
- $menu.remove();
222
- }
223
- },
224
- /**
225
- * 关闭所有菜单
226
- * @param $root
227
- */
228
- closeAllMenu($root: HTMLElement) {
229
- if ($root == null) {
230
- return;
231
- }
232
- const rootElementMenuData: PopsRightClickMenuRootStoreNodeValue | PopsRightClickMenuChildRootStoreNodeValue =
233
- Reflect.get($root, PopsContextMenu.$data.menuDataKey);
234
- if ((<PopsRightClickMenuChildRootStoreNodeValue>rootElementMenuData)?.root) {
235
- $root = (<PopsRightClickMenuChildRootStoreNodeValue>rootElementMenuData).root;
236
- }
237
-
238
- const childMenuList = (<PopsRightClickMenuRootStoreNodeValue>rootElementMenuData).child as HTMLElement[];
239
- childMenuList.forEach((childMenuElement) => {
240
- this.animationCloseMenu(childMenuElement);
241
- });
242
- this.animationCloseMenu($root);
243
- PopsContextMenu.$el.$root = null as any;
244
- },
245
- /**
246
- * 获取菜单容器
247
- * @param isChildren 是否是rightClickMenu的某一项的子菜单
248
- */
249
- createMenuContainerElement(isChildren: boolean) {
250
- const $menu = popsDOMUtils.createElement(
251
- "div",
252
- {
253
- className: `pops-${popsType}`,
254
- innerHTML: /*html*/ `<ul class="pops-${popsType}-wrapper"></ul>`,
255
- },
256
- {
257
- "data-position": config.position,
258
- }
259
- );
260
- const zIndex = this.getMenuZIndex();
261
- if (zIndex > 10000) {
262
- $menu.style.zIndex = zIndex.toString();
263
- }
264
- if (isChildren) {
265
- $menu.setAttribute("is-children", "true");
266
- }
267
- // 添加动画
268
- if (config.isAnimation) {
269
- popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-grid`);
270
- }
271
- // 添加放大动画
272
- if (config.useScaleAnimation) {
273
- popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-scale`);
274
- popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-scale-not-open`);
275
- }
276
- return $menu;
277
- },
278
- /**
279
- * 动态获取配的z-index
280
- */
281
- getMenuZIndex() {
282
- return PopsHandler.handleZIndex(config.zIndex);
283
- },
284
- /**
285
- * 获取left、top偏移
286
- * @param $menu 当前生成的菜单元素
287
- * @param mousePosition 鼠标位置信息
288
- * @param isMainMenu 是否是主菜单
289
- */
290
- getOffset(
291
- $menu: HTMLElement,
292
- mousePosition: { x: number; y: number },
293
- parentInfo?: {
294
- $menu: HTMLElement;
295
- $parentItem: HTMLElement;
296
- }
297
- ) {
298
- const result = {
299
- top: 0,
300
- right: 0,
301
- bottom: 0,
302
- left: 0,
303
- };
304
- const menuElementWidth = popsDOMUtils.width($menu);
305
- const menuElementHeight = popsDOMUtils.height($menu);
306
- /**
307
- * 限制的间隙距离
308
- */
309
- const limitDistance = 1;
310
- let maxPageLeftOffset = popsDOMUtils.width(globalThis) - limitDistance;
311
- let maxPageTopOffset = popsDOMUtils.height(globalThis) - limitDistance;
312
- if (config.position === "absolute") {
313
- // 添加滚动距离
314
- maxPageLeftOffset += globalThis.scrollX;
315
- maxPageTopOffset += globalThis.scrollY;
316
- }
317
- // left最大偏移
318
- const maxLeftOffset = maxPageLeftOffset - menuElementWidth;
319
- // top最大偏移
320
- const maxTopOffset = maxPageTopOffset - menuElementHeight;
321
-
322
- const chileMenuLeftOrRightDistance = config.chileMenuLeftOrRightDistance;
323
- const childMenuTopOrBottomDistance = config.childMenuTopOrBottomDistance;
324
- let currentLeftOffset = mousePosition.x;
325
- let currentTopOffset = mousePosition.y;
326
- currentLeftOffset = currentLeftOffset < 0 ? 0 : currentLeftOffset;
327
- currentTopOffset = currentTopOffset < 0 ? 0 : currentTopOffset;
328
-
329
- // 不允许超出left最大值
330
- if (config.limitPositionXInView && currentLeftOffset + chileMenuLeftOrRightDistance >= maxLeftOffset) {
331
- // 超过,那么子菜单将会在放在左边
332
- // 偏移计算方式就是父菜单的右偏移+父菜单的宽度
333
- if (parentInfo) {
334
- // 子菜单
335
- const mainMenuOffset = popsDOMUtils.offset(parentInfo.$menu);
336
- currentLeftOffset = maxPageLeftOffset - mainMenuOffset.left - chileMenuLeftOrRightDistance + limitDistance;
337
- } else {
338
- // 主菜单 默认的
339
- currentLeftOffset = limitDistance + chileMenuLeftOrRightDistance;
340
- }
341
- if (currentLeftOffset < 0) {
342
- currentLeftOffset = 0;
343
- } else if (currentLeftOffset > maxLeftOffset) {
344
- currentLeftOffset = maxLeftOffset;
345
- }
346
- // 去除左偏移,变为右偏移
347
- result.right = currentLeftOffset;
348
- Reflect.deleteProperty(result, "left");
349
- } else {
350
- // 右边
351
- currentLeftOffset = currentLeftOffset + chileMenuLeftOrRightDistance;
352
- result.left = currentLeftOffset;
353
- Reflect.deleteProperty(result, "right");
354
- }
355
- // 不允许超出top最大值
356
- if (config.limitPositionYInView && currentTopOffset + childMenuTopOrBottomDistance >= maxTopOffset) {
357
- // 超过,那么子菜单将会在放在上面
358
- if (parentInfo) {
359
- // 以项的top偏移为基准
360
- const parentItemOffset = popsDOMUtils.offset(parentInfo.$parentItem, false);
361
- currentTopOffset =
362
- maxPageTopOffset - parentItemOffset.bottom - childMenuTopOrBottomDistance + limitDistance;
363
- } else {
364
- currentTopOffset = limitDistance + childMenuTopOrBottomDistance;
365
- }
366
- if (currentTopOffset < 0) {
367
- currentTopOffset = limitDistance;
368
- } else if (currentTopOffset > maxTopOffset) {
369
- currentTopOffset = maxTopOffset;
370
- }
371
- // 去除上偏移,变为下偏移
372
- result.bottom = currentTopOffset;
373
- Reflect.deleteProperty(result, "top");
374
- } else {
375
- currentTopOffset = currentTopOffset + childMenuTopOrBottomDistance;
376
- // 底部偏移
377
- result.top = currentTopOffset;
378
- Reflect.deleteProperty(result, "bottom");
379
- }
380
- return result;
381
- },
382
- /**
383
- * 显示菜单
384
- * @param menuEvent 触发的事件
385
- * @param dataConfig
386
- * @param $listenerRootNode 右键菜单监听的元素
387
- */
388
- showMenu(
389
- menuEvent: PointerEvent,
390
- dataConfig: PopsRightClickMenuDataConfig[],
391
- $listenerRootNode: NonNullable<PopsRightClickMenuConfig["$target"]>
392
- ) {
393
- const menuElement = this.createMenuContainerElement(false);
394
- Reflect.set(menuElement, PopsContextMenu.$data.menuDataKey, {
395
- child: [],
396
- } as PopsRightClickMenuRootStoreNodeValue);
397
- // 添加子元素
398
- PopsContextMenu.addMenuLiELement(menuEvent, menuElement, menuElement, dataConfig, $listenerRootNode);
399
- // 添加到页面
400
- popsDOMUtils.append($shadowRoot, menuElement);
401
- // 判断容器是否存在
402
- if (!document.contains($shadowContainer)) {
403
- if (typeof config.beforeAppendToPageCallBack === "function") {
404
- config.beforeAppendToPageCallBack($shadowRoot, $shadowContainer);
405
- }
406
- popsDOMUtils.appendBody($shadowContainer);
407
- }
408
- this.handlerShowMenuCSS(menuElement, menuEvent);
409
- return menuElement;
410
- },
411
- /**
412
- * 显示子菜单
413
- * @param menuEvent 事件
414
- * @param posInfo 位置信息
415
- * @param dataConfig
416
- * @param $root 根菜单元素
417
- * @param $targetLi 父li项元素
418
- * @param $listenerRootNode 右键菜单监听的元素
419
- */
420
- showClildMenu(
421
- menuEvent: PointerEvent,
422
- posInfo: {
423
- clientX: number;
424
- clientY: number;
425
- },
426
- dataConfig: PopsRightClickMenuDataConfig[],
427
- $root: HTMLDivElement,
428
- $targetLi: HTMLLIElement,
429
- $listenerRootNode: NonNullable<PopsRightClickMenuConfig["$target"]>
430
- ) {
431
- const menuElement = this.createMenuContainerElement(true);
432
- Reflect.set(menuElement, PopsContextMenu.$data.menuDataKey, {
433
- parent: $targetLi,
434
- root: $root,
435
- } as PopsRightClickMenuItemStoreNodeValue);
436
- // 根菜单数据
437
- const rootElementMenuData: PopsRightClickMenuRootStoreNodeValue = Reflect.get(
438
- $root,
439
- PopsContextMenu.$data.menuDataKey
440
- );
441
- rootElementMenuData.child.push(menuElement);
442
- // 添加子元素
443
- PopsContextMenu.addMenuLiELement(menuEvent, $root, menuElement, dataConfig, $listenerRootNode);
444
- // 添加到页面
445
- popsDOMUtils.append($shadowRoot, menuElement);
446
- const $parentMenu = $targetLi.closest<HTMLElement>(".pops-rightClickMenu")!;
447
- this.handlerShowMenuCSS(menuElement, posInfo, {
448
- $menu: $parentMenu,
449
- $parentItem: $targetLi,
450
- });
451
- return menuElement;
452
- },
453
- /**
454
- * 处理菜单显示的css样式(添加到页面后)
455
- * @param $menu 菜单元素
456
- * @param posInfo 菜单位置信息
457
- * @param parentInfo 配置子菜单的父级信息
458
- */
459
- handlerShowMenuCSS(
460
- $menu: HTMLElement,
461
- posInfo: {
462
- clientX: number;
463
- clientY: number;
464
- },
465
- parentInfo?: {
466
- $menu: HTMLElement;
467
- $parentItem: HTMLElement;
468
- }
469
- ) {
470
- const offset = this.getOffset(
471
- $menu,
472
- {
473
- x: posInfo.clientX,
474
- y: posInfo.clientY,
475
- },
476
- parentInfo
477
- );
478
- // 显示
479
- popsDOMUtils.css($menu, {
480
- ...offset,
481
- });
482
- // 过渡动画
483
- if (config.isAnimation) {
484
- popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-show`);
485
- }
486
- if (config.useScaleAnimation) {
487
- popsDOMUtils.removeClassName($menu, `pops-${popsType}-anim-scale-not-open`);
488
- popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-scale-open`);
489
- }
490
- },
491
- /**
492
- * 获取菜单项的元素
493
- * @param menuEvent 事件
494
- * @param $root 根元素
495
- * @param $menu 菜单元素
496
- * @param dataConfig 配置
497
- * @param $listenerRootNode 右键菜单监听的元素
498
- */
499
- addMenuLiELement(
500
- menuEvent: PointerEvent,
501
- $root: HTMLDivElement,
502
- $menu: HTMLDivElement,
503
- dataConfig: PopsRightClickMenuDataConfig[],
504
- $listenerRootNode: NonNullable<PopsRightClickMenuConfig["$target"]>
505
- ) {
506
- const menuEventTarget = menuEvent.target;
507
- const menuULElement = $menu.querySelector<HTMLUListElement>("ul")!;
508
- dataConfig.forEach((item) => {
509
- const menuLiElement = popsDOMUtils.parseTextToDOM<HTMLLIElement>(`<li></li>`);
510
- // 判断有无图标,有就添加进去
511
- if (typeof item.icon === "string" && item.icon.trim() !== "") {
512
- const iconSVGHTML = PopsIcon.getIcon(item.icon) ?? item.icon;
513
- const iconElement = popsDOMUtils.parseTextToDOM(
514
- /*html*/ `<i class="pops-${popsType}-icon" is-loading="${item.iconIsLoading ?? false}">${iconSVGHTML}</i>`
515
- );
516
- menuLiElement.appendChild(iconElement);
517
- }
518
- // 插入文字
519
- const text = typeof item.text === "function" ? item.text() : item.text;
520
- menuLiElement.insertAdjacentHTML("beforeend", PopsSafeUtils.getSafeHTML(`<span>${text}</span>`));
521
- // 如果存在子数据,显示
522
- if (item.item && Array.isArray(item.item)) {
523
- popsDOMUtils.addClassName(menuLiElement, `pops-${popsType}-item`);
524
- }
525
- // 鼠标|触摸 移入事件
526
- // 在移动端会先触发touchstart再然后mouseenter
527
- let isEmitTouchEvent = false;
528
- /**
529
- * 鼠标|触摸 移入事件
530
- */
531
- function liElementHoverEvent(event: MouseEvent | TouchEvent) {
532
- if (event.type === "touchstart") {
533
- isEmitTouchEvent = true;
534
- }
535
- if (isEmitTouchEvent && event.type === "mouseenter") {
536
- return;
537
- }
538
- Array.from(menuULElement.children as any as HTMLLIElement[]).forEach((liElement) => {
539
- popsDOMUtils.removeClassName(liElement, `pops-${popsType}-is-visited`);
540
- const li_menuData: PopsRightClickMenuItemStoreNodeValue = Reflect.get(
541
- liElement,
542
- PopsContextMenu.$data.menuDataKey
543
- );
544
- if (!li_menuData) {
545
- return;
546
- }
547
- function removeElement($el: HTMLElement | undefined | null) {
548
- if (!$el) return;
549
- $el.querySelectorAll<HTMLLIElement>("ul li").forEach(($ele) => {
550
- const menuData: PopsRightClickMenuItemStoreNodeValue = Reflect.get(
551
- $ele,
552
- PopsContextMenu.$data.menuDataKey
553
- );
554
- if (menuData?.child) {
555
- removeElement(menuData.child);
556
- }
557
- });
558
- $el.remove();
559
- }
560
- // 遍历根元素的上的__menuData__.child,判断
561
- removeElement(li_menuData.child);
562
- });
563
- // 清理根元素上的children不存在于页面中的元素
564
- const root_menuData: PopsRightClickMenuRootStoreNodeValue = Reflect.get(
565
- $root,
566
- PopsContextMenu.$data.menuDataKey
567
- );
568
- for (let index = 0; index < root_menuData.child.length; index++) {
569
- const element = root_menuData.child[index];
570
- if (!$shadowRoot.contains(element)) {
571
- root_menuData.child.splice(index, 1);
572
- index--;
573
- }
574
- }
575
- popsDOMUtils.addClassName(menuLiElement, `pops-${popsType}-is-visited`);
576
- if (!item.item) {
577
- return;
578
- }
579
- const rect = menuLiElement.getBoundingClientRect();
580
- const childMenu = PopsContextMenu.showClildMenu(
581
- menuEvent,
582
- {
583
- clientX: rect.left + popsDOMUtils.outerWidth(menuLiElement),
584
- clientY: rect.top,
585
- },
586
- item.item,
587
- $root,
588
- menuLiElement,
589
- $listenerRootNode
590
- );
591
- Reflect.set(menuLiElement, PopsContextMenu.$data.menuDataKey, {
592
- child: childMenu,
593
- } as PopsRightClickMenuItemStoreNodeValue);
594
- }
595
- /**
596
- * 点击事件
597
- * @param clickEvent
598
- */
599
- async function liElementClickEvent(clickEvent: MouseEvent | PointerEvent) {
600
- if (typeof item.callback === "function") {
601
- try {
602
- OriginPrototype.Object.defineProperty(menuEvent, "target", {
603
- get() {
604
- return menuEventTarget;
605
- },
606
- });
607
- } catch {
608
- // 忽略
609
- }
610
- const callbackResult = await item.callback(
611
- clickEvent as PointerEvent,
612
- menuEvent,
613
- menuLiElement,
614
- $listenerRootNode
615
- );
616
- if (typeof callbackResult === "boolean" && callbackResult == false) {
617
- return;
618
- }
619
- }
620
- // 取消绑定的鼠标/触摸事件,防止关闭的时候再次触发
621
- Array.from(menuULElement.children as any as HTMLLIElement[]).forEach((liEle) => {
622
- popsDOMUtils.off(liEle, "mouseenter touchstart");
623
- });
624
- PopsContextMenu.closeAllMenu($root);
625
- }
626
- popsDOMUtils.on(menuLiElement, "mouseenter touchstart", liElementHoverEvent);
627
- // 项-点击事件
628
- popsDOMUtils.on(menuLiElement, "click", liElementClickEvent);
629
- menuULElement.appendChild(menuLiElement);
630
- });
631
- },
632
- };
633
-
634
- // 添加右键菜单事件
635
- PopsContextMenu.addContextMenuEvent(config.$target, config.targetSelector!);
636
- // 添加全局点击检测
637
- PopsContextMenu.addWindowCheckClickListener();
638
- return {
639
- guid: guid,
640
- config: config as DeepRequired<PopsRightClickMenuConfig>,
641
- addWindowCheckClickListener: PopsContextMenu.addWindowCheckClickListener,
642
- removeWindowCheckClickListener: PopsContextMenu.removeWindowCheckClickListener,
643
- addContextMenuEvent: PopsContextMenu.addContextMenuEvent,
644
- removeContextMenuEvent: PopsContextMenu.removeContextMenuEvent,
645
- /**
646
- * 移除初始化时的添加的监听事件
647
- */
648
- removeInitEventListener: {
649
- contextMenu() {
650
- PopsContextMenu.removeContextMenuEvent(config.$target as Window, config.targetSelector!);
651
- },
652
- windowClick() {
653
- PopsContextMenu.removeWindowCheckClickListener();
654
- },
655
- },
656
- /**
657
- * 操作弹出菜单的对象
658
- */
659
- PopsContextMenu: PopsContextMenu,
660
- };
661
- },
662
- };
1
+ import { OriginPrototype } from "../../PopsCore";
2
+ import { GlobalConfig } from "../../config/GlobalConfig";
3
+ import { PopsHandler } from "../../handler/PopsHandler";
4
+ import { popsDOMUtils } from "../../utils/PopsDOMUtils";
5
+ import { PopsSafeUtils } from "../../utils/PopsSafeUtils";
6
+ import { popsUtils } from "../../utils/PopsUtils";
7
+ import { PopsRightClickMenuDefaultConfig } from "./defaultConfig";
8
+ import type {
9
+ PopsRightClickMenuChildRootStoreNodeValue,
10
+ PopsRightClickMenuConfig,
11
+ PopsRightClickMenuDataConfig,
12
+ PopsRightClickMenuItemStoreNodeValue,
13
+ PopsRightClickMenuRootStoreNodeValue,
14
+ } from "./types";
15
+ import { PopsCSS } from "../../PopsCSS";
16
+ import { PopsIcon } from "../../PopsIcon";
17
+ import type { PopsType } from "../../types/main";
18
+
19
+ export const PopsRightClickMenu = {
20
+ init(__config__: PopsRightClickMenuConfig) {
21
+ const guid = popsUtils.getRandomGUID();
22
+ // 设置当前类型
23
+ const popsType: PopsType = "rightClickMenu";
24
+
25
+ let config = PopsRightClickMenuDefaultConfig();
26
+ config = popsUtils.assign(config, GlobalConfig.getGlobalConfig());
27
+ config = popsUtils.assign(config, __config__);
28
+ config = PopsHandler.handleOnly(popsType, config);
29
+
30
+ const { $shadowContainer, $shadowRoot } = PopsHandler.handlerShadow(config);
31
+ PopsHandler.handleInit($shadowRoot, [
32
+ {
33
+ name: "index",
34
+ css: PopsCSS.index,
35
+ },
36
+ {
37
+ name: "anim",
38
+ css: PopsCSS.anim,
39
+ },
40
+ {
41
+ name: "common",
42
+ css: PopsCSS.common,
43
+ },
44
+ {
45
+ name: "rightClickMenu",
46
+ css: PopsCSS.rightClickMenu,
47
+ },
48
+ ]);
49
+
50
+ if (config.style != null) {
51
+ const $css = popsDOMUtils.createElement(
52
+ "style",
53
+ {
54
+ innerHTML: config.style,
55
+ },
56
+ {
57
+ type: "text/css",
58
+ }
59
+ );
60
+ $shadowRoot.appendChild($css);
61
+ }
62
+
63
+ const PopsContextMenu = {
64
+ $data: {
65
+ menuDataKey: "data-menu",
66
+ },
67
+ $el: {
68
+ $root: null as any as HTMLElement,
69
+ },
70
+ /**
71
+ * 全局点击检测
72
+ * @param event
73
+ */
74
+ windowCheckClickEvent(event: MouseEvent | PointerEvent) {
75
+ if (!PopsContextMenu.$el.$root) {
76
+ return;
77
+ }
78
+ const $click = event.target as HTMLElement;
79
+ if ($click.closest(`.pops-${popsType}`)) {
80
+ return;
81
+ }
82
+ if ($click.className && $click.className === "pops-shadow-container" && $click.shadowRoot != null) {
83
+ // pops的shadow-container
84
+ PopsContextMenu.shadowRootCheckClickEvent(event);
85
+ return;
86
+ }
87
+ PopsContextMenu.closeAllMenu(PopsContextMenu.$el.$root);
88
+ },
89
+ /**
90
+ * target为shadowRoot或shadowRoot内的全局点击检测
91
+ * @param event
92
+ */
93
+ shadowRootCheckClickEvent(event: MouseEvent | PointerEvent) {
94
+ if (!PopsContextMenu.$el.$root) {
95
+ return;
96
+ }
97
+ const $click = event.composedPath()[0] as HTMLElement;
98
+ if ($click.closest(`.pops-${popsType}`)) {
99
+ return;
100
+ }
101
+ PopsContextMenu.closeAllMenu(PopsContextMenu.$el.$root);
102
+ },
103
+ /**
104
+ * 添加全局点击检测事件
105
+ */
106
+ addWindowCheckClickListener() {
107
+ popsDOMUtils.on(globalThis, "click touchstart", PopsContextMenu.windowCheckClickEvent, {
108
+ capture: true,
109
+ });
110
+ if (config.$target instanceof Node) {
111
+ const $shadowRoot = config.$target.getRootNode();
112
+ if ($shadowRoot instanceof ShadowRoot) {
113
+ popsDOMUtils.on($shadowRoot, "click touchstart", PopsContextMenu.shadowRootCheckClickEvent, {
114
+ capture: true,
115
+ });
116
+ }
117
+ }
118
+ },
119
+ /**
120
+ * 移除全局点击检测事件
121
+ */
122
+ removeWindowCheckClickListener() {
123
+ popsDOMUtils.off(globalThis, "click touchstart", PopsContextMenu.windowCheckClickEvent, {
124
+ capture: true,
125
+ });
126
+ if (config.$target instanceof Node) {
127
+ const $shadowRoot = config.$target.getRootNode();
128
+ if ($shadowRoot instanceof ShadowRoot) {
129
+ popsDOMUtils.off($shadowRoot, "click touchstart", PopsContextMenu.windowCheckClickEvent, {
130
+ capture: true,
131
+ });
132
+ }
133
+ }
134
+ },
135
+ /**
136
+ * contextmenu事件
137
+ * @param event
138
+ * @param selectorTarget
139
+ */
140
+ async contextMenuEvent(event: PointerEvent, selectorTarget: NonNullable<PopsRightClickMenuConfig["$target"]>) {
141
+ if (config.preventDefault) {
142
+ popsDOMUtils.preventEvent(event);
143
+ }
144
+ PopsHandler.handleOnly(popsType, config);
145
+ if (PopsContextMenu.$el.$root) {
146
+ PopsContextMenu.closeAllMenu(PopsContextMenu.$el.$root);
147
+ }
148
+ selectorTarget = selectorTarget ?? config.$target;
149
+ const beforeShowCallBackResult = await config?.beforeShowCallBack(event);
150
+ if (typeof beforeShowCallBackResult === "boolean" && !beforeShowCallBackResult) {
151
+ return;
152
+ }
153
+ const rootElement = PopsContextMenu.showMenu(event, config.data, selectorTarget);
154
+ PopsContextMenu.$el.$root = rootElement;
155
+ if (config.only) {
156
+ PopsHandler.handlePush(popsType, {
157
+ $shadowRoot: $shadowRoot,
158
+ $shadowContainer: $shadowContainer,
159
+ guid: guid,
160
+ $anim: rootElement,
161
+ $pops: rootElement,
162
+ beforeRemoveCallBack(instCommonConfig) {
163
+ PopsContextMenu.closeAllMenu(instCommonConfig.$pops);
164
+ },
165
+ config: config,
166
+ destory: () => {
167
+ PopsContextMenu.closeAllMenu(rootElement);
168
+ },
169
+ });
170
+ }
171
+ },
172
+ /**
173
+ * 添加contextmenu事件
174
+ * @param target 目标
175
+ * @param selector 子元素选择器
176
+ */
177
+ addContextMenuEvent(target: PopsRightClickMenuConfig["$target"], selector?: string) {
178
+ popsDOMUtils.on(target!, "contextmenu", selector, PopsContextMenu.contextMenuEvent);
179
+ },
180
+ /**
181
+ * 移除contextmenu事件
182
+ * @param target 目标
183
+ * @param selector 子元素选择器
184
+ */
185
+ removeContextMenuEvent(target: HTMLElement | typeof globalThis | Window, selector?: string) {
186
+ popsDOMUtils.off(target, "contextmenu", selector, PopsContextMenu.contextMenuEvent);
187
+ },
188
+ /**
189
+ * 自动判断是否存在动画,存在动画就执行关闭动画并删除
190
+ * @param $menu
191
+ */
192
+ animationCloseMenu($menu: HTMLElement) {
193
+ /**
194
+ * 动画结束触发的事件
195
+ */
196
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
197
+ const transitionEndEvent = (event: TransitionEvent) => {
198
+ popsDOMUtils.off($menu, popsDOMUtils.getTransitionEndNameList(), transitionEndEvent, {
199
+ capture: true,
200
+ });
201
+ $menu.remove();
202
+ };
203
+ if (popsDOMUtils.containsClassName($menu, `pops-${popsType}-anim-show`)) {
204
+ // 有动画
205
+ popsDOMUtils.on($menu, popsDOMUtils.getTransitionEndNameList(), transitionEndEvent, {
206
+ capture: true,
207
+ });
208
+ popsDOMUtils.removeClassName($menu, `pops-${popsType}-anim-show`);
209
+ } else if (
210
+ popsDOMUtils.containsClassName($menu, `pops-${popsType}-anim-scale`) &&
211
+ popsDOMUtils.containsClassName($menu, `pops-${popsType}-anim-scale-open`)
212
+ ) {
213
+ // 有动画
214
+ popsDOMUtils.on($menu, popsDOMUtils.getTransitionEndNameList(), transitionEndEvent, {
215
+ capture: true,
216
+ });
217
+ popsDOMUtils.removeClassName($menu, `pops-${popsType}-anim-scale-open`);
218
+ popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-scale-not-open`);
219
+ } else {
220
+ // 无动画
221
+ $menu.remove();
222
+ }
223
+ },
224
+ /**
225
+ * 关闭所有菜单
226
+ * @param $root
227
+ */
228
+ closeAllMenu($root: HTMLElement) {
229
+ if ($root == null) {
230
+ return;
231
+ }
232
+ const rootElementMenuData: PopsRightClickMenuRootStoreNodeValue | PopsRightClickMenuChildRootStoreNodeValue =
233
+ Reflect.get($root, PopsContextMenu.$data.menuDataKey);
234
+ if ((<PopsRightClickMenuChildRootStoreNodeValue>rootElementMenuData)?.root) {
235
+ $root = (<PopsRightClickMenuChildRootStoreNodeValue>rootElementMenuData).root;
236
+ }
237
+
238
+ const childMenuList = (<PopsRightClickMenuRootStoreNodeValue>rootElementMenuData).child as HTMLElement[];
239
+ childMenuList.forEach((childMenuElement) => {
240
+ this.animationCloseMenu(childMenuElement);
241
+ });
242
+ this.animationCloseMenu($root);
243
+ PopsContextMenu.$el.$root = null as any;
244
+ },
245
+ /**
246
+ * 获取菜单容器
247
+ * @param isChildren 是否是rightClickMenu的某一项的子菜单
248
+ */
249
+ createMenuContainerElement(isChildren: boolean) {
250
+ const $menu = popsDOMUtils.createElement(
251
+ "div",
252
+ {
253
+ className: `pops-${popsType}`,
254
+ innerHTML: /*html*/ `<ul class="pops-${popsType}-wrapper"></ul>`,
255
+ },
256
+ {
257
+ "data-position": config.position,
258
+ }
259
+ );
260
+ const zIndex = this.getMenuZIndex();
261
+ if (zIndex > 10000) {
262
+ $menu.style.zIndex = zIndex.toString();
263
+ }
264
+ if (isChildren) {
265
+ $menu.setAttribute("is-children", "true");
266
+ }
267
+ // 添加动画
268
+ if (config.isAnimation) {
269
+ popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-grid`);
270
+ }
271
+ // 添加放大动画
272
+ if (config.useScaleAnimation) {
273
+ popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-scale`);
274
+ popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-scale-not-open`);
275
+ }
276
+ return $menu;
277
+ },
278
+ /**
279
+ * 动态获取配的z-index
280
+ */
281
+ getMenuZIndex() {
282
+ return PopsHandler.handleZIndex(config.zIndex);
283
+ },
284
+ /**
285
+ * 获取left、top偏移
286
+ * @param $menu 当前生成的菜单元素
287
+ * @param mousePosition 鼠标位置信息
288
+ * @param isMainMenu 是否是主菜单
289
+ */
290
+ getOffset(
291
+ $menu: HTMLElement,
292
+ mousePosition: { x: number; y: number },
293
+ parentInfo?: {
294
+ $menu: HTMLElement;
295
+ $parentItem: HTMLElement;
296
+ }
297
+ ) {
298
+ const result = {
299
+ top: 0,
300
+ right: 0,
301
+ bottom: 0,
302
+ left: 0,
303
+ };
304
+ const menuElementWidth = popsDOMUtils.width($menu);
305
+ const menuElementHeight = popsDOMUtils.height($menu);
306
+ /**
307
+ * 限制的间隙距离
308
+ */
309
+ const limitDistance = 1;
310
+ let maxPageLeftOffset = popsDOMUtils.width(globalThis) - limitDistance;
311
+ let maxPageTopOffset = popsDOMUtils.height(globalThis) - limitDistance;
312
+ if (config.position === "absolute") {
313
+ // 添加滚动距离
314
+ maxPageLeftOffset += globalThis.scrollX;
315
+ maxPageTopOffset += globalThis.scrollY;
316
+ }
317
+ // left最大偏移
318
+ const maxLeftOffset = maxPageLeftOffset - menuElementWidth;
319
+ // top最大偏移
320
+ const maxTopOffset = maxPageTopOffset - menuElementHeight;
321
+
322
+ const chileMenuLeftOrRightDistance = config.chileMenuLeftOrRightDistance;
323
+ const childMenuTopOrBottomDistance = config.childMenuTopOrBottomDistance;
324
+ let currentLeftOffset = mousePosition.x;
325
+ let currentTopOffset = mousePosition.y;
326
+ currentLeftOffset = currentLeftOffset < 0 ? 0 : currentLeftOffset;
327
+ currentTopOffset = currentTopOffset < 0 ? 0 : currentTopOffset;
328
+
329
+ // 不允许超出left最大值
330
+ if (config.limitPositionXInView && currentLeftOffset + chileMenuLeftOrRightDistance >= maxLeftOffset) {
331
+ // 超过,那么子菜单将会在放在左边
332
+ // 偏移计算方式就是父菜单的右偏移+父菜单的宽度
333
+ if (parentInfo) {
334
+ // 子菜单
335
+ const mainMenuOffset = popsDOMUtils.offset(parentInfo.$menu);
336
+ currentLeftOffset = maxPageLeftOffset - mainMenuOffset.left - chileMenuLeftOrRightDistance + limitDistance;
337
+ } else {
338
+ // 主菜单 默认的
339
+ currentLeftOffset = limitDistance + chileMenuLeftOrRightDistance;
340
+ }
341
+ if (currentLeftOffset < 0) {
342
+ currentLeftOffset = 0;
343
+ } else if (currentLeftOffset > maxLeftOffset) {
344
+ currentLeftOffset = maxLeftOffset;
345
+ }
346
+ // 去除左偏移,变为右偏移
347
+ result.right = currentLeftOffset;
348
+ Reflect.deleteProperty(result, "left");
349
+ } else {
350
+ // 右边
351
+ currentLeftOffset = currentLeftOffset + chileMenuLeftOrRightDistance;
352
+ result.left = currentLeftOffset;
353
+ Reflect.deleteProperty(result, "right");
354
+ }
355
+ // 不允许超出top最大值
356
+ if (config.limitPositionYInView && currentTopOffset + childMenuTopOrBottomDistance >= maxTopOffset) {
357
+ // 超过,那么子菜单将会在放在上面
358
+ if (parentInfo) {
359
+ // 以项的top偏移为基准
360
+ const parentItemOffset = popsDOMUtils.offset(parentInfo.$parentItem, false);
361
+ currentTopOffset =
362
+ maxPageTopOffset - parentItemOffset.bottom - childMenuTopOrBottomDistance + limitDistance;
363
+ } else {
364
+ currentTopOffset = limitDistance + childMenuTopOrBottomDistance;
365
+ }
366
+ if (currentTopOffset < 0) {
367
+ currentTopOffset = limitDistance;
368
+ } else if (currentTopOffset > maxTopOffset) {
369
+ currentTopOffset = maxTopOffset;
370
+ }
371
+ // 去除上偏移,变为下偏移
372
+ result.bottom = currentTopOffset;
373
+ Reflect.deleteProperty(result, "top");
374
+ } else {
375
+ currentTopOffset = currentTopOffset + childMenuTopOrBottomDistance;
376
+ // 底部偏移
377
+ result.top = currentTopOffset;
378
+ Reflect.deleteProperty(result, "bottom");
379
+ }
380
+ return result;
381
+ },
382
+ /**
383
+ * 显示菜单
384
+ * @param menuEvent 触发的事件
385
+ * @param dataConfig
386
+ * @param $listenerRootNode 右键菜单监听的元素
387
+ */
388
+ showMenu(
389
+ menuEvent: PointerEvent,
390
+ dataConfig: PopsRightClickMenuDataConfig[],
391
+ $listenerRootNode: NonNullable<PopsRightClickMenuConfig["$target"]>
392
+ ) {
393
+ const menuElement = this.createMenuContainerElement(false);
394
+ Reflect.set(menuElement, PopsContextMenu.$data.menuDataKey, {
395
+ child: [],
396
+ } as PopsRightClickMenuRootStoreNodeValue);
397
+ // 添加子元素
398
+ PopsContextMenu.addMenuLiELement(menuEvent, menuElement, menuElement, dataConfig, $listenerRootNode);
399
+ // 添加到页面
400
+ popsDOMUtils.append($shadowRoot, menuElement);
401
+ // 判断容器是否存在
402
+ if (!document.contains($shadowContainer)) {
403
+ if (typeof config.beforeAppendToPageCallBack === "function") {
404
+ config.beforeAppendToPageCallBack($shadowRoot, $shadowContainer);
405
+ }
406
+ popsDOMUtils.appendBody($shadowContainer);
407
+ }
408
+ this.handlerShowMenuCSS(menuElement, menuEvent);
409
+ return menuElement;
410
+ },
411
+ /**
412
+ * 显示子菜单
413
+ * @param menuEvent 事件
414
+ * @param posInfo 位置信息
415
+ * @param dataConfig
416
+ * @param $root 根菜单元素
417
+ * @param $targetLi 父li项元素
418
+ * @param $listenerRootNode 右键菜单监听的元素
419
+ */
420
+ showClildMenu(
421
+ menuEvent: PointerEvent,
422
+ posInfo: {
423
+ clientX: number;
424
+ clientY: number;
425
+ },
426
+ dataConfig: PopsRightClickMenuDataConfig[],
427
+ $root: HTMLDivElement,
428
+ $targetLi: HTMLLIElement,
429
+ $listenerRootNode: NonNullable<PopsRightClickMenuConfig["$target"]>
430
+ ) {
431
+ const menuElement = this.createMenuContainerElement(true);
432
+ Reflect.set(menuElement, PopsContextMenu.$data.menuDataKey, {
433
+ parent: $targetLi,
434
+ root: $root,
435
+ } as PopsRightClickMenuItemStoreNodeValue);
436
+ // 根菜单数据
437
+ const rootElementMenuData: PopsRightClickMenuRootStoreNodeValue = Reflect.get(
438
+ $root,
439
+ PopsContextMenu.$data.menuDataKey
440
+ );
441
+ rootElementMenuData.child.push(menuElement);
442
+ // 添加子元素
443
+ PopsContextMenu.addMenuLiELement(menuEvent, $root, menuElement, dataConfig, $listenerRootNode);
444
+ // 添加到页面
445
+ popsDOMUtils.append($shadowRoot, menuElement);
446
+ const $parentMenu = $targetLi.closest<HTMLElement>(".pops-rightClickMenu")!;
447
+ this.handlerShowMenuCSS(menuElement, posInfo, {
448
+ $menu: $parentMenu,
449
+ $parentItem: $targetLi,
450
+ });
451
+ return menuElement;
452
+ },
453
+ /**
454
+ * 处理菜单显示的css样式(添加到页面后)
455
+ * @param $menu 菜单元素
456
+ * @param posInfo 菜单位置信息
457
+ * @param parentInfo 配置子菜单的父级信息
458
+ */
459
+ handlerShowMenuCSS(
460
+ $menu: HTMLElement,
461
+ posInfo: {
462
+ clientX: number;
463
+ clientY: number;
464
+ },
465
+ parentInfo?: {
466
+ $menu: HTMLElement;
467
+ $parentItem: HTMLElement;
468
+ }
469
+ ) {
470
+ const offset = this.getOffset(
471
+ $menu,
472
+ {
473
+ x: posInfo.clientX,
474
+ y: posInfo.clientY,
475
+ },
476
+ parentInfo
477
+ );
478
+ // 显示
479
+ popsDOMUtils.css($menu, {
480
+ ...offset,
481
+ });
482
+ // 过渡动画
483
+ if (config.isAnimation) {
484
+ popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-show`);
485
+ }
486
+ if (config.useScaleAnimation) {
487
+ popsDOMUtils.removeClassName($menu, `pops-${popsType}-anim-scale-not-open`);
488
+ popsDOMUtils.addClassName($menu, `pops-${popsType}-anim-scale-open`);
489
+ }
490
+ },
491
+ /**
492
+ * 获取菜单项的元素
493
+ * @param menuEvent 事件
494
+ * @param $root 根元素
495
+ * @param $menu 菜单元素
496
+ * @param dataConfig 配置
497
+ * @param $listenerRootNode 右键菜单监听的元素
498
+ */
499
+ addMenuLiELement(
500
+ menuEvent: PointerEvent,
501
+ $root: HTMLDivElement,
502
+ $menu: HTMLDivElement,
503
+ dataConfig: PopsRightClickMenuDataConfig[],
504
+ $listenerRootNode: NonNullable<PopsRightClickMenuConfig["$target"]>
505
+ ) {
506
+ const menuEventTarget = menuEvent.target;
507
+ const menuULElement = $menu.querySelector<HTMLUListElement>("ul")!;
508
+ dataConfig.forEach((item) => {
509
+ const menuLiElement = popsDOMUtils.parseTextToDOM<HTMLLIElement>(`<li></li>`);
510
+ // 判断有无图标,有就添加进去
511
+ if (typeof item.icon === "string" && item.icon.trim() !== "") {
512
+ const iconSVGHTML = PopsIcon.getIcon(item.icon) ?? item.icon;
513
+ const iconElement = popsDOMUtils.parseTextToDOM(
514
+ /*html*/ `<i class="pops-${popsType}-icon" is-loading="${item.iconIsLoading ?? false}">${iconSVGHTML}</i>`
515
+ );
516
+ menuLiElement.appendChild(iconElement);
517
+ }
518
+ // 插入文字
519
+ const text = typeof item.text === "function" ? item.text() : item.text;
520
+ menuLiElement.insertAdjacentHTML("beforeend", PopsSafeUtils.getSafeHTML(`<span>${text}</span>`));
521
+ // 如果存在子数据,显示
522
+ if (item.item && Array.isArray(item.item)) {
523
+ popsDOMUtils.addClassName(menuLiElement, `pops-${popsType}-item`);
524
+ }
525
+ // 鼠标|触摸 移入事件
526
+ // 在移动端会先触发touchstart再然后mouseenter
527
+ let isEmitTouchEvent = false;
528
+ /**
529
+ * 鼠标|触摸 移入事件
530
+ */
531
+ function liElementHoverEvent(event: MouseEvent | TouchEvent) {
532
+ if (event.type === "touchstart") {
533
+ isEmitTouchEvent = true;
534
+ }
535
+ if (isEmitTouchEvent && event.type === "mouseenter") {
536
+ return;
537
+ }
538
+ Array.from(menuULElement.children as any as HTMLLIElement[]).forEach((liElement) => {
539
+ popsDOMUtils.removeClassName(liElement, `pops-${popsType}-is-visited`);
540
+ const li_menuData: PopsRightClickMenuItemStoreNodeValue = Reflect.get(
541
+ liElement,
542
+ PopsContextMenu.$data.menuDataKey
543
+ );
544
+ if (!li_menuData) {
545
+ return;
546
+ }
547
+ function removeElement($el: HTMLElement | undefined | null) {
548
+ if (!$el) return;
549
+ $el.querySelectorAll<HTMLLIElement>("ul li").forEach(($ele) => {
550
+ const menuData: PopsRightClickMenuItemStoreNodeValue = Reflect.get(
551
+ $ele,
552
+ PopsContextMenu.$data.menuDataKey
553
+ );
554
+ if (menuData?.child) {
555
+ removeElement(menuData.child);
556
+ }
557
+ });
558
+ $el.remove();
559
+ }
560
+ // 遍历根元素的上的__menuData__.child,判断
561
+ removeElement(li_menuData.child);
562
+ });
563
+ // 清理根元素上的children不存在于页面中的元素
564
+ const root_menuData: PopsRightClickMenuRootStoreNodeValue = Reflect.get(
565
+ $root,
566
+ PopsContextMenu.$data.menuDataKey
567
+ );
568
+ for (let index = 0; index < root_menuData.child.length; index++) {
569
+ const element = root_menuData.child[index];
570
+ if (!$shadowRoot.contains(element)) {
571
+ root_menuData.child.splice(index, 1);
572
+ index--;
573
+ }
574
+ }
575
+ popsDOMUtils.addClassName(menuLiElement, `pops-${popsType}-is-visited`);
576
+ if (!item.item) {
577
+ return;
578
+ }
579
+ const rect = menuLiElement.getBoundingClientRect();
580
+ const childMenu = PopsContextMenu.showClildMenu(
581
+ menuEvent,
582
+ {
583
+ clientX: rect.left + popsDOMUtils.outerWidth(menuLiElement),
584
+ clientY: rect.top,
585
+ },
586
+ item.item,
587
+ $root,
588
+ menuLiElement,
589
+ $listenerRootNode
590
+ );
591
+ Reflect.set(menuLiElement, PopsContextMenu.$data.menuDataKey, {
592
+ child: childMenu,
593
+ } as PopsRightClickMenuItemStoreNodeValue);
594
+ }
595
+ /**
596
+ * 点击事件
597
+ * @param clickEvent
598
+ */
599
+ async function liElementClickEvent(clickEvent: MouseEvent | PointerEvent) {
600
+ if (typeof item.callback === "function") {
601
+ try {
602
+ OriginPrototype.Object.defineProperty(menuEvent, "target", {
603
+ get() {
604
+ return menuEventTarget;
605
+ },
606
+ });
607
+ } catch {
608
+ // 忽略
609
+ }
610
+ const callbackResult = await item.callback(
611
+ clickEvent as PointerEvent,
612
+ menuEvent,
613
+ menuLiElement,
614
+ $listenerRootNode
615
+ );
616
+ if (typeof callbackResult === "boolean" && callbackResult == false) {
617
+ return;
618
+ }
619
+ }
620
+ // 取消绑定的鼠标/触摸事件,防止关闭的时候再次触发
621
+ Array.from(menuULElement.children as any as HTMLLIElement[]).forEach((liEle) => {
622
+ popsDOMUtils.off(liEle, "mouseenter touchstart");
623
+ });
624
+ PopsContextMenu.closeAllMenu($root);
625
+ }
626
+ popsDOMUtils.on(menuLiElement, "mouseenter touchstart", liElementHoverEvent);
627
+ // 项-点击事件
628
+ popsDOMUtils.on(menuLiElement, "click", liElementClickEvent);
629
+ menuULElement.appendChild(menuLiElement);
630
+ });
631
+ },
632
+ };
633
+
634
+ // 添加右键菜单事件
635
+ PopsContextMenu.addContextMenuEvent(config.$target, config.targetSelector!);
636
+ // 添加全局点击检测
637
+ PopsContextMenu.addWindowCheckClickListener();
638
+ return {
639
+ guid: guid,
640
+ config: config as DeepRequired<PopsRightClickMenuConfig>,
641
+ addWindowCheckClickListener: PopsContextMenu.addWindowCheckClickListener,
642
+ removeWindowCheckClickListener: PopsContextMenu.removeWindowCheckClickListener,
643
+ addContextMenuEvent: PopsContextMenu.addContextMenuEvent,
644
+ removeContextMenuEvent: PopsContextMenu.removeContextMenuEvent,
645
+ /**
646
+ * 移除初始化时的添加的监听事件
647
+ */
648
+ removeInitEventListener: {
649
+ contextMenu() {
650
+ PopsContextMenu.removeContextMenuEvent(config.$target as Window, config.targetSelector!);
651
+ },
652
+ windowClick() {
653
+ PopsContextMenu.removeWindowCheckClickListener();
654
+ },
655
+ },
656
+ /**
657
+ * 操作弹出菜单的对象
658
+ */
659
+ PopsContextMenu: PopsContextMenu,
660
+ };
661
+ },
662
+ };