Skip to content

Commit

Permalink
WheelEvent on a table should not prevent default action if not necess…
Browse files Browse the repository at this point in the history
…ary. (#6097)

* Fixed: scroll should not prevent default behaviour if the user reaches bottom/top or left/right edge. Added new option in Walkontable - preventWheel - it might be cofigured in initialization object. WIP. #5212

* Added: new property to prevent wheel on HOT instance, additional test specs for wheel event over cloned overlays. Changed: WOT.Viewport has destroy() method to clear its instance of the eventManager. #5212

* Reverted unnecessary changes in Viewport.js. Removed rest of the unnecessary code in overlays. #5212

* Necessary changes after merge develop into feature's brunch. #5212

* Updated: DefaultSettings definition. Added: types test. #5212
  • Loading branch information
swistach committed Jul 30, 2019
1 parent 10f7deb commit b3b05ba
Show file tree
Hide file tree
Showing 15 changed files with 559 additions and 278 deletions.
1 change: 1 addition & 0 deletions handsontable.d.ts
Expand Up @@ -1755,6 +1755,7 @@ declare namespace Handsontable {
placeholder?: string;
placeholderCellClassName?: string;
preventOverflow?: boolean | 'vertical' | 'horizontal';
preventWheel?: boolean;
readOnly?: boolean;
readOnlyCellClassName?: string;
renderAllRows?: boolean;
Expand Down
287 changes: 160 additions & 127 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/3rdparty/walkontable/css/walkontable.css
Expand Up @@ -153,7 +153,8 @@
overflow-y: auto;
}

.ht_clone_top .wtHolder {
.ht_clone_top .wtHolder,
.ht_clone_bottom .wtHolder {
overflow-x: auto;
overflow-y: hidden;
}
Expand Down
13 changes: 10 additions & 3 deletions src/3rdparty/walkontable/src/overlay/bottom.js
Expand Up @@ -60,8 +60,9 @@ class BottomOverlay extends Overlay {
const overlayRoot = this.clone.wtTable.holder.parentNode;
let headerPosition = 0;
overlayRoot.style.top = '';
const preventOverflow = this.wot.getSetting('preventOverflow');

if (this.wot.wtOverlays.leftOverlay.trimmingContainer === this.wot.rootWindow) {
if (this.trimmingContainer === this.wot.rootWindow && (!preventOverflow || preventOverflow !== 'vertical')) {
const { rootDocument, wtTable } = this.wot;
const box = wtTable.hider.getBoundingClientRect();
const bottom = Math.ceil(box.bottom);
Expand Down Expand Up @@ -90,6 +91,7 @@ class BottomOverlay extends Overlay {
this.repositionOverlay();
}
this.adjustHeaderBordersPosition(headerPosition);
this.adjustElementsSize();
}

/**
Expand All @@ -99,20 +101,25 @@ class BottomOverlay extends Overlay {
*/
setScrollPosition(pos) {
const { rootWindow } = this.wot;
let result = false;

if (this.mainTableScrollableElement === rootWindow) {
rootWindow.scrollTo(getWindowScrollLeft(rootWindow), pos);
result = true;

} else {
} else if (this.mainTableScrollableElement.scrollTop !== pos) {
this.mainTableScrollableElement.scrollTop = pos;
result = true;
}

return result;
}

/**
* Triggers onScroll hook callback
*/
onScroll() {
this.wot.getSetting('onScrollVertically');
this.wot.getSetting('onScrollHorizontally');
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/3rdparty/walkontable/src/overlay/top.js
Expand Up @@ -46,13 +46,14 @@ class TopOverlay extends Overlay {
const preventOverflow = this.wot.getSetting('preventOverflow');

if (this.trimmingContainer === this.wot.rootWindow && (!preventOverflow || preventOverflow !== 'vertical')) {
const box = this.wot.wtTable.hider.getBoundingClientRect();
const { wtTable } = this.wot;
const box = wtTable.hider.getBoundingClientRect();
const top = Math.ceil(box.top);
const bottom = Math.ceil(box.bottom);
let finalLeft;
let finalTop;

finalLeft = this.wot.wtTable.hider.style.left;
finalLeft = wtTable.hider.style.left;
finalLeft = finalLeft === '' ? 0 : finalLeft;

if (top < 0 && (bottom - overlayRoot.offsetHeight) > 0) {
Expand Down
114 changes: 44 additions & 70 deletions src/3rdparty/walkontable/src/overlays.js
Expand Up @@ -40,11 +40,15 @@ class Overlays {
this.wot.update('scrollbarWidth', this.scrollbarSize);
this.wot.update('scrollbarHeight', this.scrollbarSize);

if (rootWindow.getComputedStyle(wtTable.wtRootElement.parentNode).getPropertyValue('overflow') === 'hidden') {
this.scrollableElement = wtTable.holder;
} else {
this.scrollableElement = getScrollableElement(wtTable.TABLE);
}
const isOverflowHidden = rootWindow.getComputedStyle(wtTable.wtRootElement.parentNode).getPropertyValue('overflow') === 'hidden';

this.scrollableElement = isOverflowHidden ? wtTable.holder : getScrollableElement(wtTable.TABLE);

this.topOverlay = void 0;
this.bottomOverlay = void 0;
this.leftOverlay = void 0;
this.topLeftCornerOverlay = void 0;
this.bottomLeftCornerOverlay = void 0;

this.prepareOverlays();

Expand All @@ -57,44 +61,9 @@ class Overlays {
width: null,
height: null,
};
this.overlayScrollPositions = {
master: {
top: 0,
left: 0,
},
top: {
top: null,
left: 0,
},
bottom: {
top: null,
left: 0
},
left: {
top: 0,
left: null
}
};

this.pendingScrollCallbacks = {
master: {
top: 0,
left: 0,
},
top: {
left: 0,
},
bottom: {
left: 0,
},
left: {
top: 0,
}
};

this.verticalScrolling = false;
this.horizontalScrolling = false;
this.delegatedScrollCallback = false;

this.browserLineHeight = BODY_LINE_HEIGHT || FALLBACK_BODY_LINE_HEIGHT;

Expand Down Expand Up @@ -211,32 +180,27 @@ class Overlays {

const isHighPixelRatio = rootWindow.devicePixelRatio && rootWindow.devicePixelRatio > 1;
const isScrollOnWindow = this.scrollableElement === rootWindow;
const preventWheel = this.wot.wtSettings.getSetting('preventWheel');
const wheelEventOptions = { passive: isScrollOnWindow };

if (isHighPixelRatio || !isChrome()) {
// Resolves issue outline https://github.com/handsontable/handsontable/issues/5913
// We can keep this (removing .parentNode) to prevent stutter in retina https://github.com/handsontable/handsontable/issues/4498
this.eventManager.addEventListener(this.wot.wtTable.wtRootElement, 'wheel', event => this.onCloneWheel(event), { passive: isScrollOnWindow });
} else {
if (this.topOverlay.needFullRender) {
this.eventManager.addEventListener(this.topOverlay.clone.wtTable.holder, 'wheel', event => this.onCloneWheel(event), { passive: isScrollOnWindow });
}

if (this.bottomOverlay.needFullRender) {
this.eventManager.addEventListener(this.bottomOverlay.clone.wtTable.holder, 'wheel', event => this.onCloneWheel(event), { passive: isScrollOnWindow });
}

if (this.leftOverlay.needFullRender) {
this.eventManager.addEventListener(this.leftOverlay.clone.wtTable.holder, 'wheel', event => this.onCloneWheel(event), { passive: isScrollOnWindow });
}
if (preventWheel || isHighPixelRatio || !isChrome()) {
this.eventManager.addEventListener(this.wot.wtTable.wtRootElement, 'wheel', event => this.onCloneWheel(event, preventWheel), wheelEventOptions);
}

if (this.topLeftCornerOverlay && this.topLeftCornerOverlay.needFullRender) {
this.eventManager.addEventListener(this.topLeftCornerOverlay.clone.wtTable.holder, 'wheel', event => this.onCloneWheel(event), { passive: isScrollOnWindow });
}
const overlays = [
this.topOverlay,
this.bottomOverlay,
this.leftOverlay,
this.topLeftCornerOverlay,
this.bottomLeftCornerOverlay,
];

if (this.bottomLeftCornerOverlay && this.bottomLeftCornerOverlay.needFullRender) {
this.eventManager.addEventListener(this.bottomLeftCornerOverlay.clone.wtTable.holder, 'wheel', event => this.onCloneWheel(event), { passive: isScrollOnWindow });
overlays.forEach((overlay) => {
if (overlay && overlay.needFullRender) {
const { holder } = overlay.clone.wtTable;
this.eventManager.addEventListener(holder, 'wheel', event => this.onCloneWheel(event, preventWheel), wheelEventOptions);
}
}
});

let resizeTimeout;

Expand Down Expand Up @@ -286,13 +250,9 @@ class Overlays {
*
* @param {Event} event
*/
onCloneWheel(event) {
onCloneWheel(event, preventDefault) {
const { rootWindow } = this.wot;

if (this.scrollableElement !== rootWindow) {
event.preventDefault();
}

// There was if statement which controlled flow of this function. It avoided the execution of the next lines
// on mobile devices. It was changed. Broader description of this case is included within issue #4856.

Expand All @@ -309,7 +269,11 @@ class Overlays {
return;
}

this.translateMouseWheelToScroll(event);
const isScrollPossible = this.translateMouseWheelToScroll(event);

if (preventDefault || (this.scrollableElement !== rootWindow && isScrollPossible)) {
event.preventDefault();
}
}

/**
Expand Down Expand Up @@ -343,8 +307,10 @@ class Overlays {
deltaY += deltaY * browserLineHeight;
}

this.scrollVertically(deltaY);
this.scrollHorizontally(deltaX);
const isScrollVerticallyPossible = this.scrollVertically(deltaY);
const isScrollHorizontallyPossible = this.scrollHorizontally(deltaX);

return isScrollVerticallyPossible || isScrollHorizontallyPossible;
}

/**
Expand All @@ -353,7 +319,11 @@ class Overlays {
* @param {Number} delta Relative value to scroll.
*/
scrollVertically(delta) {
const previousScroll = this.scrollableElement.scrollTop;

this.scrollableElement.scrollTop += delta;

return previousScroll !== this.scrollableElement.scrollTop;
}

/**
Expand All @@ -362,7 +332,11 @@ class Overlays {
* @param {Number} delta Relative value to scroll.
*/
scrollHorizontally(delta) {
const previousScroll = this.scrollableElement.scrollLeft;

this.scrollableElement.scrollLeft += delta;

return previousScroll !== this.scrollableElement.scrollLeft;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/3rdparty/walkontable/src/settings.js
Expand Up @@ -28,6 +28,7 @@ class Settings {
preventOverflow() {
return false;
},
preventWheel: false,

// data source
data: void 0,
Expand Down
11 changes: 11 additions & 0 deletions src/3rdparty/walkontable/test/helpers/common.js
Expand Up @@ -78,6 +78,17 @@ export function getTotalColumns() {
return spec().data[0] ? spec().data[0].length : 0;
}

/**
* Simulates WheelEvent on the element.
*
* @param {Element} elem Element to dispatch event.
* @param {Number} deltaX Relative distance in px to scroll horizontally.
* @param {Number} deltaY Relative distance in px to scroll vertically.
*/
export function wheelOnElement(elem, deltaX = 0, deltaY = 0) {
elem.dispatchEvent(new WheelEvent('wheel', { deltaX, deltaY }));
}

beforeEach(function() {
specContext.spec = this;

Expand Down

0 comments on commit b3b05ba

Please sign in to comment.