@@ -4,7 +4,10 @@ import VeRadio from "vue-easytable/packages/ve-radio";
4
4
import { COMPS_NAME , EMIT_EVENTS } from "./util/constant" ;
5
5
import { clsName } from "./util/index" ;
6
6
import { getRandomId } from "../../src/utils/random" ;
7
- import { getViewportOffset } from "../../src/utils/dom" ;
7
+ import {
8
+ getViewportOffset ,
9
+ getViewportOffsetWithinContainer ,
10
+ } from "../../src/utils/dom" ;
8
11
9
12
export default {
10
13
name : COMPS_NAME . VE_DROPDOWN ,
@@ -101,6 +104,13 @@ export default {
101
104
type : Number ,
102
105
default : 5 ,
103
106
} ,
107
+ // popper append to element
108
+ popperAppendTo : {
109
+ type : [ String , HTMLElement ] ,
110
+ default : function ( ) {
111
+ return document . body ;
112
+ } ,
113
+ } ,
104
114
} ,
105
115
data ( ) {
106
116
return {
@@ -113,6 +123,10 @@ export default {
113
123
rootId : "" ,
114
124
// dropdown items panel id
115
125
dropdownItemsPanelId : "" ,
126
+ // 弹出被添加到的目标元素
127
+ popperAppendToEl : null ,
128
+ // 弹出被添加到的目标元素标签名称
129
+ appendToElTagName : null ,
116
130
} ;
117
131
} ,
118
132
computed : {
@@ -250,7 +264,12 @@ export default {
250
264
251
265
// change dropdown panel position
252
266
changDropdownPanelPosition ( ) {
253
- const { defaultInstance, rootId } = this ;
267
+ const {
268
+ defaultInstance,
269
+ rootId,
270
+ popperAppendToEl,
271
+ appendToElTagName,
272
+ } = this ;
254
273
255
274
let rootEl = document . querySelector ( `#${ rootId } ` ) ;
256
275
@@ -261,40 +280,61 @@ export default {
261
280
const triggerEl = this . $el . querySelector ( ".ve-dropdown-dt" ) ;
262
281
const { height : triggerElHeight } =
263
282
triggerEl . getBoundingClientRect ( ) ;
264
- // const {
265
- // left: triggerElLeft,
266
- // top: triggerElTop,
267
- // right: triggerElRight,
268
- // bottom: triggerElBottom,
269
- // } = getMousePosition(event);
283
+
284
+ if ( ! popperAppendToEl ) {
285
+ return false ;
286
+ }
287
+
288
+ // is append to body
289
+ const isAppendToBody = appendToElTagName === "BODY" ;
270
290
271
291
const {
272
292
offsetLeft : triggerElLeft ,
273
293
offsetTop : triggerElTop ,
274
294
right : triggerElRight ,
275
295
bottom : triggerElBottom ,
276
- } = getViewportOffset ( triggerEl ) ;
296
+ } = isAppendToBody
297
+ ? getViewportOffset ( triggerEl )
298
+ : getViewportOffsetWithinContainer (
299
+ triggerEl ,
300
+ popperAppendToEl ,
301
+ ) ;
277
302
278
303
let panelX = 0 ;
279
304
let panelY = 0 ;
280
305
306
+ // 如果不是添加到body 需要考虑外层容器滚动调的影响
307
+ let scrollLeft = 0 ;
308
+ let scrollTop = 0 ;
309
+ if ( ! isAppendToBody ) {
310
+ scrollLeft = popperAppendToEl . scrollLeft ;
311
+ scrollTop = popperAppendToEl . scrollTop ;
312
+ }
313
+
281
314
// 右方宽度够显示
282
315
if ( triggerElRight >= currentPanelWidth ) {
283
- panelX = triggerElLeft ;
316
+ panelX = triggerElLeft + scrollLeft ;
284
317
}
285
318
// 右方宽度不够显示在鼠标点击左方
286
319
else {
287
- panelX = triggerElLeft - currentPanelWidth ;
320
+ panelX = triggerElLeft - currentPanelWidth + scrollLeft ;
288
321
}
289
322
290
323
// 下方高度够显示
291
324
if ( triggerElBottom >= currentPanelHeight ) {
292
- panelY = triggerElTop + triggerElHeight + defaultInstance ;
325
+ panelY =
326
+ triggerElTop +
327
+ triggerElHeight +
328
+ defaultInstance +
329
+ scrollTop ;
293
330
}
294
331
// 下方高度不够显示在鼠标点击上方
295
332
else {
296
333
panelY =
297
- triggerElTop - currentPanelHeight - defaultInstance ;
334
+ triggerElTop -
335
+ currentPanelHeight -
336
+ defaultInstance +
337
+ scrollTop ;
298
338
}
299
339
300
340
rootEl . style . left = panelX + "px" ;
@@ -386,8 +426,13 @@ export default {
386
426
return clsName ( getRandomId ( ) ) ;
387
427
} ,
388
428
389
- // add root contextmenu panel to body
390
- addRootElementToBody ( ) {
429
+ /*
430
+ add root contextmenu panel to element
431
+ 如果不指定则添加到 body
432
+ */
433
+ addRootElementToElement ( ) {
434
+ const { popperAppendTo } = this ;
435
+
391
436
this . rootId = this . getRandomIdWithPrefix ( ) ;
392
437
this . dropdownItemsPanelId = this . getRandomIdWithPrefix ( ) ;
393
438
@@ -402,7 +447,19 @@ export default {
402
447
403
448
containerEl . setAttribute ( "id" , this . rootId ) ;
404
449
405
- document . body . appendChild ( containerEl ) ;
450
+ if (
451
+ typeof popperAppendTo === "string" &&
452
+ popperAppendTo . length > 0
453
+ ) {
454
+ this . popperAppendToEl =
455
+ document . querySelector ( popperAppendTo ) ;
456
+ } else {
457
+ this . popperAppendToEl = popperAppendTo ;
458
+ }
459
+
460
+ this . appendToElTagName = this . popperAppendToEl . tagName ;
461
+
462
+ this . popperAppendToEl . appendChild ( containerEl ) ;
406
463
} ) ;
407
464
}
408
465
} ,
@@ -412,16 +469,34 @@ export default {
412
469
this . init ( ) ;
413
470
} ,
414
471
mounted ( ) {
415
- this . addRootElementToBody ( ) ;
416
-
417
- document . addEventListener ( "scroll" , this . changDropdownPanelPosition ) ;
472
+ this . addRootElementToElement ( ) ;
473
+
474
+ this . $nextTick ( ( ) => {
475
+ const targetEl =
476
+ this . appendToElTagName === "BODY"
477
+ ? document
478
+ : this . popperAppendToEl ;
479
+ targetEl . addEventListener (
480
+ "scroll" ,
481
+ this . changDropdownPanelPosition ,
482
+ ) ;
483
+ } ) ;
418
484
window . addEventListener ( "resize" , this . changDropdownPanelPosition ) ;
419
485
} ,
420
486
421
487
destroyed ( ) {
422
488
this . removeOrEmptyRootPanel ( ) ;
423
489
424
- document . removeEventListener ( "scroll" , this . changDropdownPanelPosition ) ;
490
+ this . $nextTick ( ( ) => {
491
+ const targetEl =
492
+ this . appendToElTagName === "BODY"
493
+ ? document
494
+ : this . popperAppendToEl ;
495
+ targetEl . removeEventListener (
496
+ "scroll" ,
497
+ this . changDropdownPanelPosition ,
498
+ ) ;
499
+ } ) ;
425
500
window . removeEventListener ( "resize" , this . changDropdownPanelPosition ) ;
426
501
} ,
427
502
0 commit comments