|
- import { __assign, __decorate } from "tslib";
- import clamp from 'lodash-es/clamp';
- import { Options } from './options';
- import { setStyle, clearEventsOn, } from './utils/';
- import { debounce, } from './decorators/';
- import { TrackController, } from './track/';
- import { getSize, update, isVisible, } from './geometry/';
- import { scrollTo, setPosition, scrollIntoView, } from './scrolling/';
- import { initPlugins, } from './plugin';
- import * as eventHandlers from './events/';
- // DO NOT use WeakMap here
- // .getAll() methods requires `scrollbarMap.values()`
- export var scrollbarMap = new Map();
- var Scrollbar = /** @class */ (function () {
- function Scrollbar(containerEl, options) {
- var _this = this;
- /**
- * Current scrolling offsets
- */
- this.offset = {
- x: 0,
- y: 0,
- };
- /**
- * Max-allowed scrolling offsets
- */
- this.limit = {
- x: Infinity,
- y: Infinity,
- };
- /**
- * Container bounding rect
- */
- this.bounding = {
- top: 0,
- right: 0,
- bottom: 0,
- left: 0,
- };
- this._plugins = [];
- this._momentum = { x: 0, y: 0 };
- this._listeners = new Set();
- this.containerEl = containerEl;
- var contentEl = this.contentEl = document.createElement('div');
- this.options = new Options(options);
- // mark as a scroll element
- containerEl.setAttribute('data-scrollbar', 'true');
- // make container focusable
- containerEl.setAttribute('tabindex', '-1');
- setStyle(containerEl, {
- overflow: 'hidden',
- outline: 'none',
- });
- // enable touch event capturing in IE, see:
- // https://github.com/idiotWu/smooth-scrollbar/issues/39
- if (window.navigator.msPointerEnabled) {
- containerEl.style.msTouchAction = 'none';
- }
- // mount content
- contentEl.className = 'scroll-content';
- Array.from(containerEl.childNodes).forEach(function (node) {
- contentEl.appendChild(node);
- });
- containerEl.appendChild(contentEl);
- // attach track
- this.track = new TrackController(this);
- // initial measuring
- this.size = this.getSize();
- // init plugins
- this._plugins = initPlugins(this, this.options.plugins);
- // preserve scroll offset
- var scrollLeft = containerEl.scrollLeft, scrollTop = containerEl.scrollTop;
- containerEl.scrollLeft = containerEl.scrollTop = 0;
- this.setPosition(scrollLeft, scrollTop, {
- withoutCallbacks: true,
- });
- var global = window;
- var MutationObserver = global.MutationObserver || global.WebKitMutationObserver || global.MozMutationObserver;
- // observe
- if (typeof MutationObserver === 'function') {
- this._observer = new MutationObserver(function () {
- _this.update();
- });
- this._observer.observe(contentEl, {
- subtree: true,
- childList: true,
- });
- }
- scrollbarMap.set(containerEl, this);
- // wait for DOM ready
- requestAnimationFrame(function () {
- _this._init();
- });
- }
- Object.defineProperty(Scrollbar.prototype, "parent", {
- /**
- * Parent scrollbar
- */
- get: function () {
- var elem = this.containerEl.parentElement;
- while (elem) {
- var parentScrollbar = scrollbarMap.get(elem);
- if (parentScrollbar) {
- return parentScrollbar;
- }
- elem = elem.parentElement;
- }
- return null;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Scrollbar.prototype, "scrollTop", {
- /**
- * Gets or sets `scrollbar.offset.y`
- */
- get: function () {
- return this.offset.y;
- },
- set: function (y) {
- this.setPosition(this.scrollLeft, y);
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(Scrollbar.prototype, "scrollLeft", {
- /**
- * Gets or sets `scrollbar.offset.x`
- */
- get: function () {
- return this.offset.x;
- },
- set: function (x) {
- this.setPosition(x, this.scrollTop);
- },
- enumerable: true,
- configurable: true
- });
- /**
- * Returns the size of the scrollbar container element
- * and the content wrapper element
- */
- Scrollbar.prototype.getSize = function () {
- return getSize(this);
- };
- /**
- * Forces scrollbar to update geometry infomation.
- *
- * By default, scrollbars are automatically updated with `100ms` debounce (or `MutationObserver` fires).
- * You can call this method to force an update when you modified contents
- */
- Scrollbar.prototype.update = function () {
- update(this);
- this._plugins.forEach(function (plugin) {
- plugin.onUpdate();
- });
- };
- /**
- * Checks if an element is visible in the current view area
- */
- Scrollbar.prototype.isVisible = function (elem) {
- return isVisible(this, elem);
- };
- /**
- * Sets the scrollbar to the given offset without easing
- */
- Scrollbar.prototype.setPosition = function (x, y, options) {
- var _this = this;
- if (x === void 0) { x = this.offset.x; }
- if (y === void 0) { y = this.offset.y; }
- if (options === void 0) { options = {}; }
- var status = setPosition(this, x, y);
- if (!status || options.withoutCallbacks) {
- return;
- }
- this._listeners.forEach(function (fn) {
- fn.call(_this, status);
- });
- };
- /**
- * Scrolls to given position with easing function
- */
- Scrollbar.prototype.scrollTo = function (x, y, duration, options) {
- if (x === void 0) { x = this.offset.x; }
- if (y === void 0) { y = this.offset.y; }
- if (duration === void 0) { duration = 0; }
- if (options === void 0) { options = {}; }
- scrollTo(this, x, y, duration, options);
- };
- /**
- * Scrolls the target element into visible area of scrollbar,
- * likes the DOM method `element.scrollIntoView().
- */
- Scrollbar.prototype.scrollIntoView = function (elem, options) {
- if (options === void 0) { options = {}; }
- scrollIntoView(this, elem, options);
- };
- /**
- * Adds scrolling listener
- */
- Scrollbar.prototype.addListener = function (fn) {
- if (typeof fn !== 'function') {
- throw new TypeError('[smooth-scrollbar] scrolling listener should be a function');
- }
- this._listeners.add(fn);
- };
- /**
- * Removes listener previously registered with `scrollbar.addListener()`
- */
- Scrollbar.prototype.removeListener = function (fn) {
- this._listeners.delete(fn);
- };
- /**
- * Adds momentum and applys delta transformers.
- */
- Scrollbar.prototype.addTransformableMomentum = function (x, y, fromEvent, callback) {
- this._updateDebounced();
- var finalDelta = this._plugins.reduce(function (delta, plugin) {
- return plugin.transformDelta(delta, fromEvent) || delta;
- }, { x: x, y: y });
- var willScroll = !this._shouldPropagateMomentum(finalDelta.x, finalDelta.y);
- if (willScroll) {
- this.addMomentum(finalDelta.x, finalDelta.y);
- }
- if (callback) {
- callback.call(this, willScroll);
- }
- };
- /**
- * Increases scrollbar's momentum
- */
- Scrollbar.prototype.addMomentum = function (x, y) {
- this.setMomentum(this._momentum.x + x, this._momentum.y + y);
- };
- /**
- * Sets scrollbar's momentum to given value
- */
- Scrollbar.prototype.setMomentum = function (x, y) {
- if (this.limit.x === 0) {
- x = 0;
- }
- if (this.limit.y === 0) {
- y = 0;
- }
- if (this.options.renderByPixels) {
- x = Math.round(x);
- y = Math.round(y);
- }
- this._momentum.x = x;
- this._momentum.y = y;
- };
- /**
- * Update options for specific plugin
- *
- * @param pluginName Name of the plugin
- * @param [options] An object includes the properties that you want to update
- */
- Scrollbar.prototype.updatePluginOptions = function (pluginName, options) {
- this._plugins.forEach(function (plugin) {
- if (plugin.name === pluginName) {
- Object.assign(plugin.options, options);
- }
- });
- };
- Scrollbar.prototype.destroy = function () {
- var _a = this, containerEl = _a.containerEl, contentEl = _a.contentEl;
- clearEventsOn(this);
- this._listeners.clear();
- this.setMomentum(0, 0);
- cancelAnimationFrame(this._renderID);
- if (this._observer) {
- this._observer.disconnect();
- }
- scrollbarMap.delete(this.containerEl);
- // restore contents
- var childNodes = Array.from(contentEl.childNodes);
- while (containerEl.firstChild) {
- containerEl.removeChild(containerEl.firstChild);
- }
- childNodes.forEach(function (el) {
- containerEl.appendChild(el);
- });
- // reset scroll position
- setStyle(containerEl, {
- overflow: '',
- });
- containerEl.scrollTop = this.scrollTop;
- containerEl.scrollLeft = this.scrollLeft;
- // invoke plugin.onDestroy
- this._plugins.forEach(function (plugin) {
- plugin.onDestroy();
- });
- this._plugins.length = 0;
- };
- Scrollbar.prototype._init = function () {
- var _this = this;
- this.update();
- // init evet handlers
- Object.keys(eventHandlers).forEach(function (prop) {
- eventHandlers[prop](_this);
- });
- // invoke `plugin.onInit`
- this._plugins.forEach(function (plugin) {
- plugin.onInit();
- });
- this._render();
- };
- Scrollbar.prototype._updateDebounced = function () {
- this.update();
- };
- // check whether to propagate monmentum to parent scrollbar
- // the following situations are considered as `true`:
- // 1. continuous scrolling is enabled (automatically disabled when overscroll is enabled)
- // 2. scrollbar reaches one side and is not about to scroll on the other direction
- Scrollbar.prototype._shouldPropagateMomentum = function (deltaX, deltaY) {
- if (deltaX === void 0) { deltaX = 0; }
- if (deltaY === void 0) { deltaY = 0; }
- var _a = this, options = _a.options, offset = _a.offset, limit = _a.limit;
- if (!options.continuousScrolling)
- return false;
- // force an update when scrollbar is "unscrollable", see #106
- if (limit.x === 0 && limit.y === 0) {
- this._updateDebounced();
- }
- var destX = clamp(deltaX + offset.x, 0, limit.x);
- var destY = clamp(deltaY + offset.y, 0, limit.y);
- var res = true;
- // offsets are not about to change
- // `&=` operator is not allowed for boolean types
- res = res && (destX === offset.x);
- res = res && (destY === offset.y);
- // current offsets are on the edge
- res = res && (offset.x === limit.x || offset.x === 0 || offset.y === limit.y || offset.y === 0);
- return res;
- };
- Scrollbar.prototype._render = function () {
- var _momentum = this._momentum;
- if (_momentum.x || _momentum.y) {
- var nextX = this._nextTick('x');
- var nextY = this._nextTick('y');
- _momentum.x = nextX.momentum;
- _momentum.y = nextY.momentum;
- this.setPosition(nextX.position, nextY.position);
- }
- var remain = __assign({}, this._momentum);
- this._plugins.forEach(function (plugin) {
- plugin.onRender(remain);
- });
- this._renderID = requestAnimationFrame(this._render.bind(this));
- };
- Scrollbar.prototype._nextTick = function (direction) {
- var _a = this, options = _a.options, offset = _a.offset, _momentum = _a._momentum;
- var current = offset[direction];
- var remain = _momentum[direction];
- if (Math.abs(remain) <= 0.1) {
- return {
- momentum: 0,
- position: current + remain,
- };
- }
- var nextMomentum = remain * (1 - options.damping);
- if (options.renderByPixels) {
- nextMomentum |= 0;
- }
- return {
- momentum: nextMomentum,
- position: current + remain - nextMomentum,
- };
- };
- __decorate([
- debounce(100, { leading: true })
- ], Scrollbar.prototype, "_updateDebounced", null);
- return Scrollbar;
- }());
- export { Scrollbar };
- //# sourceMappingURL=scrollbar.js.map
|