Ghost-Admin/lib/koenig-editor/addon/lib/dnd/scroll-handler.js

111 lines
3.7 KiB
JavaScript

// adapted from draggable.js Scrollable plugin (MIT)
// https://github.com/Shopify/draggable/blob/master/src/Draggable/Plugins/Scrollable/Scrollable.js
import {
getDocumentScrollingElement,
getParentScrollableElement
} from './utils';
export const defaultOptions = {
speed: 8,
sensitivity: 50
};
export default class ScrollHandler {
constructor() {
this.options = Object.assign({}, defaultOptions);
this.currentMousePosition = null;
this.findScrollableElementFrame = null;
this.scrollableElement = null;
this.scrollAnimationFrame = null;
// bind `this` so methods can be passed to requestAnimationFrame
this._scroll = this._scroll.bind(this);
// cache browser info to avoid parsing on every animation frame
this._isSafari = navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1;
}
dragStart(draggableInfo) {
this.findScrollableElementFrame = requestAnimationFrame(() => {
this.scrollableElement = this.getScrollableElement(draggableInfo.element);
});
}
dragMove(draggableInfo) {
this.findScrollableElementFrame = requestAnimationFrame(() => {
this.scrollableElement = this.getScrollableElement(draggableInfo.target);
});
if (!this.scrollableElement) {
return;
}
this.currentMousePosition = {
clientX: draggableInfo.mousePosition.x,
clientY: draggableInfo.mousePosition.y
};
this.scrollAnimationFrame = requestAnimationFrame(this._scroll);
}
dragStop() {
cancelAnimationFrame(this.scrollAnimationFrame);
cancelAnimationFrame(this.findScrollableElementFrame);
this.currentMousePosition = null;
this.findScrollableElementFrame = null;
this.scrollableElement = null;
this.scrollAnimationFrame = null;
}
getScrollableElement(target) {
let scrollableElement = getParentScrollableElement(target);
// workaround for our particular scrolling setup
// TODO: find a way to make this configurable
if (scrollableElement === getDocumentScrollingElement()) {
scrollableElement = document.querySelector('.gh-koenig-editor');
}
return scrollableElement;
}
_scroll() {
if (!this.scrollableElement || !this.currentMousePosition) {
return;
}
cancelAnimationFrame(this.scrollAnimationFrame);
let {speed, sensitivity} = this.options;
let rect = this.scrollableElement.getBoundingClientRect();
let scrollableElement = this.scrollableElement;
let clientX = this.currentMousePosition.clientX;
let clientY = this.currentMousePosition.clientY;
let {offsetHeight, offsetWidth} = scrollableElement;
let topPosition = rect.top + offsetHeight - clientY;
let bottomPosition = clientY - rect.top;
// Safari will automatically scroll when the mouse is outside of the window
// so we want to avoid our own scrolling in that situation to avoid jank
if (topPosition < sensitivity && !(this._isSafari && topPosition < 0)) {
scrollableElement.scrollTop += speed;
} else if (bottomPosition < sensitivity && !(this._isSafari && bottomPosition < 0)) {
scrollableElement.scrollTop -= speed;
}
if (rect.left + offsetWidth - clientX < sensitivity) {
scrollableElement.scrollLeft += speed;
} else if (clientX - rect.left < sensitivity) {
scrollableElement.scrollLeft -= speed;
}
this.scrollAnimationFrame = requestAnimationFrame(this._scroll);
}
}