Skip to content

Commit 9d65c6e

Browse files
committed
fix(#1590): offset - position calculations
1 parent 8968a38 commit 9d65c6e

File tree

22 files changed

+138
-213
lines changed

22 files changed

+138
-213
lines changed

src/core/class/offset.js

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -404,15 +404,18 @@ Offset.prototype = {
404404
if (this.options.get('_rtl')) {
405405
const rtlW = ew > tw ? ew - tw : 0;
406406
const rtlL = rtlW > 0 ? 0 : tw - ew;
407-
element.style.left = `${tl - rtlW + rtlL + tcleft}px`;
407+
element.style.left = `${tl - rtlW + rtlL}px`;
408408
if (tcleft > this.getGlobal(element).left) {
409409
element.style.left = tcleft + 'px';
410410
}
411411
} else {
412412
const cw = t_container.offsetWidth + tcleft;
413413
const overLeft = cw <= ew ? 0 : cw - (tl + ew);
414-
if (overLeft < 0) element.style.left = `${tl + overLeft + tcleft}px`;
415-
else element.style.left = `${tl}px`;
414+
if (overLeft < 0) {
415+
element.style.left = `${tl + overLeft}px`;
416+
} else {
417+
element.style.left = `${tl}px`;
418+
}
416419
}
417420
},
418421

@@ -427,7 +430,6 @@ Offset.prototype = {
427430
* @param {"bottom"|"top"} [params.position="bottom"] Position ('bottom'|'top')
428431
* @param {*} params.inst Instance object of caller
429432
* @param {HTMLElement} [params.sibling=null] The sibling controller element
430-
* @param {boolean} [params.isWWScroll=false] Indicates if the scroll event is from the wysiwyg area
431433
* @returns {{position: "top" | "bottom"} | undefined} Success -> {position: current position}
432434
*/
433435
setAbsPosition(element, target, params) {
@@ -470,34 +472,32 @@ Offset.prototype = {
470472
const th = this.context.get('toolbar_main').offsetHeight;
471473
const containerToolbar = this.options.get('toolbar_container');
472474
const headLess = this.editor.isBalloon || this.editor.isInline || containerToolbar;
473-
const toolbarH = (containerToolbar && globalTop - wScrollY - th > 0) || (!this.editor.toolbar.isSticky && headLess) ? 0 : th + (this.editor.toolbar.isSticky ? this.options.get('toolbar_sticky') : 0);
475+
const toolbarH = (containerToolbar && globalTop - wScrollY - th > 0) || (!this.toolbar.isSticky && headLess) ? 0 : th + (this.toolbar.isSticky ? this.options.get('toolbar_sticky') : 0);
476+
const statusBarH = this.frameContext.get('statusbar')?.offsetHeight || 0;
474477

475478
// check margin
476-
const { rmt, rmb, bMargin, rt } = this._getVMargin(tmtw, tmbw, toolbarH, clientSize, targetRect, isTextSelection, params.isWWScroll, isToolbarTarget);
477-
if ((isWWTarget && ((rmb > 0 ? bMargin : rmb) + targetH <= 0 || rmt + rt + targetH - (this.editor.toolbar.isSticky && isInlineTarget ? toolbarH : 0) <= 0)) || rmt + targetH < 0) return;
479+
const { rmt, rmb, bMargin, rt } = this._getVMargin(tmtw, tmbw, toolbarH, clientSize, targetRect, isTextSelection, isToolbarTarget);
480+
if ((isWWTarget && (rmb - statusBarH + targetH <= 0 || rmt + rt + targetH - (this.toolbar.isSticky && isInlineTarget ? toolbarH : 0) <= 0)) || rmt + targetH < 0) return;
478481

479-
const isSticky = this.editor.toolbar.isSticky && this.context.get('toolbar_main').style.display !== 'none' && (!headLess || this.frameContext.get('topArea').getBoundingClientRect().top <= th);
480-
const statusBarH = this.frameContext.get('statusbar')?.offsetHeight || 0;
482+
const isSticky = this.toolbar.isSticky && this.context.get('toolbar_main').style.display !== 'none' && (!headLess || this.frameContext.get('topArea').getBoundingClientRect().top <= th);
481483
let t = addOffset.top;
482484
let y = 0;
483485
let arrowDir = '';
484486

485487
// [bottom] position
486488
if (position === 'bottom') {
487-
let trmt = rmt - (isSticky && globalTop - wScrollY <= toolbarH ? toolbarH : 0);
488-
if (isSticky && trmt + toolbarH < 0) trmt += toolbarH;
489489
arrowDir = 'up';
490490
t += targetRect.bottom + ah + wScrollY;
491-
y = rmb - (elH + ah) - statusBarH;
491+
y = rmb - (elH + ah);
492492
// change to <top> position
493493
if (y - siblingH < 0) {
494494
arrowDir = 'down';
495495
t -= targetH + elH + ah * 2;
496-
y = trmt - (elH + ah);
496+
y = rmt - (elH + ah);
497497
// sticky the <top> position
498498
if (y - siblingH < 0) {
499499
arrowDir = '';
500-
t -= y - siblingH - Math.max(1, y + elH + ah) + (!isSticky && trmt < 0 ? toolbarH : 0) - (isSticky ? this.context.get('toolbar_main').offsetTop : 0);
500+
t -= y - siblingH - Math.max(1, y + elH + ah);
501501
}
502502
}
503503
}
@@ -639,9 +639,9 @@ Offset.prototype = {
639639
const targetH = rects.height;
640640
const tmtw = rects.top;
641641
const tmbw = clientSize.h - rects.bottom;
642-
const toolbarH = !this.editor.toolbar.isSticky && (this.editor.isBalloon || this.editor.isInline) ? 0 : this.context.get('toolbar_main').offsetHeight;
642+
const toolbarH = !this.toolbar.isSticky && (this.editor.isBalloon || this.editor.isInline) ? 0 : this.context.get('toolbar_main').offsetHeight;
643643

644-
const { rmt, rmb, rt } = this._getVMargin(tmtw, tmbw, toolbarH, clientSize, rects, isTextSelection, false, false);
644+
const { rmt, rmb, rt } = this._getVMargin(tmtw, tmbw, toolbarH, clientSize, rects, isTextSelection, false);
645645
if (rmb + targetH <= 0 || rmt + rt + targetH <= 0) return;
646646

647647
element.style.visibility = '';
@@ -727,7 +727,6 @@ Offset.prototype = {
727727
* @param {{w: number, h: number}} clientSize documentElement.clientWidth, documentElement.clientHeight
728728
* @param {RectsInfo} targetRect Target rect object
729729
* @param {boolean} isTextSelection Is text selection or Range
730-
* @param {boolean} isWWScroll Indicates if the scroll event is from the wysiwyg area
731730
* @param {boolean} isToolbarTarget Indicates if the target is a toolbar element
732731
* @returns {{rmt:number, rmb:number, rt:number, tMargin:number, bMargin:number}} Margin values
733732
* - rmt: top margin to frame
@@ -736,7 +735,7 @@ Offset.prototype = {
736735
* - tMargin: top margin
737736
* - bMargin: bottom margin
738737
*/
739-
_getVMargin(tmtw, tmbw, toolbarH, clientSize, targetRect, isTextSelection, isWWScroll, isToolbarTarget) {
738+
_getVMargin(tmtw, tmbw, toolbarH, clientSize, targetRect, isTextSelection, isToolbarTarget) {
740739
const isScrollable = this.status.isScrollable();
741740
const wwRects = this.selection.getRects(this.frameContext.get('wysiwyg'), 'start').rects;
742741

@@ -754,27 +753,25 @@ Offset.prototype = {
754753
tMargin = targetRect.top;
755754
bMargin = clientSize.h - targetRect.bottom;
756755
const editorOffset = this.getGlobal();
757-
const editorScroll = this.getGlobalScroll();
758-
const statusBarH = this.frameContext.get('statusbar')?.offsetHeight || 0;
759756

760757
if (!isTextSelection) {
761-
const emt = !isToolbarTarget ? editorOffset.top - editorScroll.top - editorScroll.ts : 0;
762-
const editorH = this.frameContext.get('topArea').offsetHeight;
763-
rt = !isToolbarTarget && (this.editor.toolbar.isSticky || (isScrollable && !this.toolbar._isBalloon)) ? toolbarH : 0;
764-
rmt = targetRect.top - (targetRect.top < 0 && emt < 0 ? 0 : emt) - rt;
765-
rmb = bMargin - (isWWScroll ? editorScroll.oh - (editorH + emt) : 0) - statusBarH;
758+
const emt = editorOffset.fixedTop > 0 ? editorOffset.fixedTop : 0;
759+
const emb = _w.innerHeight - (editorOffset.fixedTop + editorOffset.height);
760+
rt = !isToolbarTarget && (this.toolbar.isSticky || (isScrollable && !this.toolbar._isBalloon)) ? toolbarH : 0;
761+
rmt = tMargin - (!isToolbarTarget ? emt : 0) - rt;
762+
rmb = bMargin - (emb > 0 ? emb : 0);
766763
} else {
767-
rt = !isToolbarTarget && !this.editor.toolbar.isSticky && !this.options.get('toolbar_container') ? toolbarH : 0;
764+
rt = !isToolbarTarget && !this.toolbar.isSticky && !this.options.get('toolbar_container') ? toolbarH : 0;
768765
const wst = !isIframe ? editorOffset.top - _w.scrollY + rt : 0;
769766
const wsb = !isIframe ? this.status.currentViewportHeight - (editorOffset.top + editorOffset.height - _w.scrollY) : 0;
770767
let st = wst;
771768
if (toolbarH > wst) {
772-
if (this.editor.toolbar.isSticky) {
769+
if (this.toolbar.isSticky) {
773770
st = toolbarH;
774771
} else {
775772
st = wst + toolbarH;
776773
}
777-
} else if (this.options.get('toolbar_container') && !this.editor.toolbar.isSticky) {
774+
} else if (this.options.get('toolbar_container') && !this.toolbar.isSticky) {
778775
toolbarH = 0;
779776
} else {
780777
st = wst + toolbarH;

src/core/event/eventManager.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -948,17 +948,16 @@ EventManager.prototype = {
948948
* @this {EventManagerThis}
949949
* @description Handles the scrolling of the editor container.
950950
* - Repositions open controllers if necessary.
951-
* @param {boolean} isWWScroll Indicates if the scroll event is from the wysiwyg area
952951
*/
953-
_scrollContainer(isWWScroll) {
952+
_scrollContainer() {
954953
if (this.menu.currentDropdownActiveButton && this.menu.currentDropdown) {
955954
this.menu._resetMenuPosition(this.menu.currentDropdownActiveButton, this.menu.currentDropdown);
956955
}
957956

958957
const openCont = this.editor.opendControllers;
959958
if (openCont.length === 0) return;
960959

961-
this.__rePositionController(openCont, isWWScroll);
960+
this.__rePositionController(openCont);
962961
},
963962

964963
/**
@@ -967,13 +966,12 @@ EventManager.prototype = {
967966
* @description Repositions the currently open controllers within the editor.
968967
* - Ensures elements are displayed in their correct positions after scrolling.
969968
* @param {Array<object>} cont List of controllers to reposition
970-
* @param {boolean} isWWScroll Indicates if the scroll event is from the wysiwyg area
971969
*/
972-
__rePositionController(cont, isWWScroll) {
970+
__rePositionController(cont) {
973971
if (_DragHandle.get('__dragMove')) _DragHandle.get('__dragMove')();
974972
for (let i = 0; i < cont.length; i++) {
975973
if (cont[i].notInCarrier) continue;
976-
cont[i].inst?._scrollReposition(isWWScroll);
974+
cont[i].inst?._scrollReposition();
977975
}
978976
},
979977

@@ -1198,7 +1196,7 @@ EventManager.prototype = {
11981196
*/
11991197
function OnScroll_wysiwyg(frameContext, eventWysiwyg, e) {
12001198
this._moveContainer(eventWysiwyg);
1201-
this._scrollContainer(true);
1199+
this._scrollContainer();
12021200

12031201
// plugin event
12041202
this._callPluginEvent('onScroll', { frameContext, event: e });
@@ -1371,7 +1369,7 @@ function OnResize_viewport() {
13711369
this.editor.menu._restoreMenuPosition();
13721370
}
13731371

1374-
this._scrollContainer(false);
1372+
this._scrollContainer();
13751373
this.__setViewportSize();
13761374
}
13771375

@@ -1389,7 +1387,7 @@ function OnScroll_window() {
13891387
this.subToolbar._setBalloonOffset(this.subToolbar._balloonOffset.position === 'top');
13901388
}
13911389

1392-
this._scrollContainer(false);
1390+
this._scrollContainer();
13931391

13941392
// document type page
13951393
if (this.frameContext.has('documentType_use_page')) {
@@ -1439,7 +1437,7 @@ function OnSelectionchange_document(_wd) {
14391437
*/
14401438
function OnScroll_Abs() {
14411439
this.menu.dropdownOff();
1442-
this._scrollContainer(false);
1440+
this._scrollContainer();
14431441
}
14441442

14451443
/**

src/modules/contracts/Controller.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,10 @@ class Controller extends CoreInjector {
269269

270270
/**
271271
* @description Reposition controller on scroll event
272-
* @param {boolean} isWWScroll Indicates if the scroll event is from the wysiwyg area
273272
*/
274-
_scrollReposition(isWWScroll) {
273+
_scrollReposition() {
275274
if (this.form.hasAttribute('data-se-hidden-by-parent') || this.form.hasAttribute('data-se-hidden-by-children')) return;
276-
if (this.#setControllerPosition(this.form, this.currentPositionTarget, false, isWWScroll)) {
275+
if (this.#setControllerPosition(this.form, this.currentPositionTarget, false)) {
277276
_w.setTimeout(() => {
278277
this.#childrenSync('show');
279278
}, 0);
@@ -397,10 +396,9 @@ class Controller extends CoreInjector {
397396
* @param {HTMLElement} controller Controller element.
398397
* @param {Node|Range} refer Element or Range that is the basis of the controller's position.
399398
* @param {boolean} [skipAutoReposition=false] If true, skips scroll/resize-based automatic positioning logic.
400-
* @param {boolean} [isWWScroll] Indicates if the scroll event is from the wysiwyg area
401399
* @returns {boolean} - view : true || hide : false
402400
*/
403-
#setControllerPosition(controller, refer, skipAutoReposition, isWWScroll) {
401+
#setControllerPosition(controller, refer, skipAutoReposition) {
404402
if (!refer) return false;
405403

406404
controller.style.visibility = 'hidden';
@@ -423,7 +421,6 @@ class Controller extends CoreInjector {
423421
isWWTarget: this.isWWTarget,
424422
inst: this,
425423
sibling: this.sibling,
426-
isWWScroll,
427424
});
428425
if (!positionResult) {
429426
this.hide();

test/unit/core/class/menu.spec.js

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ jest.mock('../../../../src/helper', () => ({
88
dom: {
99
query: {
1010
getParentElement: jest.fn(),
11-
getEventTarget: jest.fn()
11+
getEventTarget: jest.fn(),
1212
},
1313
utils: {
1414
removeClass: jest.fn(),
15-
addClass: jest.fn()
16-
}
15+
addClass: jest.fn(),
16+
},
1717
},
1818
converter: {
19-
nodeListToArray: jest.fn(nodeList => Array.from(nodeList || []))
20-
}
19+
nodeListToArray: jest.fn((nodeList) => Array.from(nodeList || [])),
20+
},
2121
}));
2222

2323
describe('Menu', () => {
@@ -32,31 +32,29 @@ describe('Menu', () => {
3232

3333
const mockMenuTray = document.createElement('div');
3434

35-
mockContext = new Map([
36-
['menuTray', mockMenuTray]
37-
]);
38-
mockContext.get = jest.fn().mockImplementation(key => {
35+
mockContext = new Map([['menuTray', mockMenuTray]]);
36+
mockContext.get = jest.fn().mockImplementation((key) => {
3937
const map = new Map([['menuTray', mockMenuTray]]);
4038
return map.get(key);
4139
});
4240

4341
mockEventManager = {
4442
addGlobalEvent: jest.fn().mockReturnValue('mock-event-id'),
45-
removeGlobalEvent: jest.fn()
43+
removeGlobalEvent: jest.fn(),
4644
};
4745

4846
mockOffset = {
49-
setRelPosition: jest.fn()
47+
setRelPosition: jest.fn(),
5048
};
5149

5250
mockEditor = {
5351
runFromTarget: jest.fn(),
5452
_preventBlur: false,
55-
_notHideToolbar: false
53+
_notHideToolbar: false,
5654
};
5755

5856
// Mock CoreInjector.call
59-
CoreInjector.mockImplementation(function(editor) {
57+
CoreInjector.mockImplementation(function (editor) {
6058
this.editor = editor;
6159
this.context = mockContext;
6260
this.eventManager = mockEventManager;
@@ -243,9 +241,6 @@ describe('Menu', () => {
243241
menu.index = 5;
244242
menu.menus = [document.createElement('div')];
245243

246-
const mockPlugin = { off: jest.fn() };
247-
menu.currentDropdownPlugin = mockPlugin;
248-
249244
menu.dropdownOff();
250245

251246
expect(menu.__removeGlobalEvent).toHaveBeenCalled();
@@ -259,7 +254,6 @@ describe('Menu', () => {
259254
expect(dom.utils.removeClass).toHaveBeenCalledWith(mockParent.children, 'on');
260255
expect(mockEditor._notHideToolbar).toBe(false);
261256
expect(mockEditor._preventBlur).toBe(false);
262-
expect(mockPlugin.off).toHaveBeenCalled();
263257
expect(menu.currentDropdownPlugin).toBeNull();
264258
});
265259

@@ -439,11 +433,7 @@ describe('Menu', () => {
439433
beforeEach(() => {
440434
const mockDropdown = document.createElement('div');
441435
menu.currentDropdown = mockDropdown;
442-
menu.menus = [
443-
document.createElement('div'),
444-
document.createElement('div'),
445-
document.createElement('div')
446-
];
436+
menu.menus = [document.createElement('div'), document.createElement('div'), document.createElement('div')];
447437
menu.index = 1;
448438
});
449439

@@ -560,7 +550,7 @@ describe('Menu', () => {
560550
const mockEvent = {
561551
code: 'ArrowUp',
562552
preventDefault: jest.fn(),
563-
stopPropagation: jest.fn()
553+
stopPropagation: jest.fn(),
564554
};
565555

566556
menu.__globalEventHandler.keydown(mockEvent);
@@ -574,7 +564,7 @@ describe('Menu', () => {
574564
const mockEvent = {
575565
code: 'ArrowDown',
576566
preventDefault: jest.fn(),
577-
stopPropagation: jest.fn()
567+
stopPropagation: jest.fn(),
578568
};
579569

580570
menu.__globalEventHandler.keydown(mockEvent);
@@ -588,7 +578,7 @@ describe('Menu', () => {
588578
const mockEvent = {
589579
code: 'Enter',
590580
preventDefault: jest.fn(),
591-
stopPropagation: jest.fn()
581+
stopPropagation: jest.fn(),
592582
};
593583

594584
const mockTarget = document.createElement('div');
@@ -611,7 +601,7 @@ describe('Menu', () => {
611601
const mockEvent = {
612602
code: 'Enter',
613603
preventDefault: jest.fn(),
614-
stopPropagation: jest.fn()
604+
stopPropagation: jest.fn(),
615605
};
616606

617607
menu.index = -1;
@@ -652,4 +642,4 @@ describe('Menu', () => {
652642
});
653643
});
654644
});
655-
});
645+
});

test/unit/core/class/toolbar.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,7 @@ describe('Toolbar', () => {
784784
expect(toolbar.triggerEvent).toHaveBeenCalledWith('onShowToolbar', {
785785
toolbar: mockToolbar,
786786
mode: 'inline',
787+
frameContext: mockFrameContext,
787788
});
788789
});
789790
});

test/unit/core/event/eventManager.working.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,11 +422,11 @@ describe('EventManager - Working Tests', () => {
422422
it('should handle files when present', async () => {
423423
mockClipboardData.files = [new File([''], 'test.txt')];
424424
mockClipboardData.getData.mockReturnValue('');
425-
eventManager._callPluginEvent = jest.fn();
425+
eventManager._callPluginEventAsync = jest.fn();
426426

427427
const result = await eventManager._setClipboardData('paste', mockEvent, mockClipboardData, mockEditor.frameContext);
428428

429-
expect(eventManager._callPluginEvent).toHaveBeenCalledWith('onFilePasteAndDrop', expect.any(Object));
429+
expect(eventManager._callPluginEventAsync).toHaveBeenCalledWith('onFilePasteAndDrop', expect.any(Object));
430430
expect(result).toBe(false);
431431
});
432432

0 commit comments

Comments
 (0)