UNPKG

@saleae/react-scrollbars-custom

Version:
1,554 lines (1,271 loc) 64.9 kB
import cnb from 'cnbuilder'; import { oneOf, func, bool, number, string, object } from 'prop-types'; import { createElement, Component, createContext } from 'react'; import { zoomLevel } from 'zoom-level'; import { DraggableCore } from 'react-draggable'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __spreadArrays() { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; } function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function (obj) { return typeof obj; }; } else { _typeof = function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } var doc = (typeof document === "undefined" ? "undefined" : _typeof(document)) === "object" ? document : null; var isUndef = function isUndef(v) { return typeof v === "undefined"; }; var isFun = function isFun(v) { return typeof v === "function"; }; var isNum = function isNum(v) { return typeof v === "number"; }; /** * @description Will return renderer result if presented, div element otherwise. * If renderer is presented it'll receive `elementRef` function which should be used as HTMLElement's ref. * * @param props {ElementPropsWithElementRefAndRenderer} * @param elementRef {ElementRef} */ var renderDivWithRenderer = function renderDivWithRenderer(props, elementRef) { if (isFun(props.renderer)) { props.elementRef = elementRef; var renderer = props.renderer; delete props.renderer; return renderer(props); } delete props.elementRef; return createElement("div", __assign({}, props, { ref: elementRef })); }; var getInnerSize = function getInnerSize(el, dimension, padding1, padding2) { var styles = getComputedStyle(el); if (styles.boxSizing === "border-box") { return Math.max(0, (parseFloat(styles[dimension]) || 0) - (parseFloat(styles[padding1]) || 0) - (parseFloat(styles[padding2]) || 0)); } return parseFloat(styles[dimension]) || 0; }; /** * @description Return element's height without padding */ var getInnerHeight = function getInnerHeight(el) { return getInnerSize(el, "height", "paddingTop", "paddingBottom"); }; /** * @description Return element's width without padding */ var getInnerWidth = function getInnerWidth(el) { return getInnerSize(el, "width", "paddingLeft", "paddingRight"); }; /** * @description Return unique UUID v4 */ var uuid = function uuid() { var uuid = ""; for (var i = 0; i < 32; i++) { if (i === 8 || i === 20) { uuid += "-" + (Math.random() * 16 | 0).toString(16); } else if (i === 12) { uuid += "-4"; } else if (i === 16) { uuid += "-" + (Math.random() * 16 | 0 & 3 | 8).toString(16); } else { uuid += (Math.random() * 16 | 0).toString(16); } } return uuid; }; /** * @description Calculate thumb size for given viewport and track parameters * * @param {number} contentSize - Scrollable content size * @param {number} viewportSize - Viewport size * @param {number} trackSize - Track size thumb can move * @param {number} minimalSize - Minimal thumb's size * @param {number} maximalSize - Maximal thumb's size */ var calcThumbSize = function calcThumbSize(contentSize, viewportSize, trackSize, minimalSize, maximalSize) { if (viewportSize >= contentSize) { return 0; } var thumbSize = viewportSize / contentSize * trackSize; isNum(maximalSize) && (thumbSize = Math.min(maximalSize, thumbSize)); isNum(minimalSize) && (thumbSize = Math.max(minimalSize, thumbSize)); return thumbSize; }; /** * @description Calculate thumb offset for given viewport, track and thumb parameters * * @param {number} contentSize - Scrollable content size * @param {number} viewportSize - Viewport size * @param {number} trackSize - Track size thumb can move * @param {number} thumbSize - Thumb size * @param {number} scroll - Scroll value to represent */ var calcThumbOffset = function calcThumbOffset(contentSize, viewportSize, trackSize, thumbSize, scroll) { if (!scroll || !thumbSize || viewportSize >= contentSize) { return 0; } return (trackSize - thumbSize) * scroll / (contentSize - viewportSize); }; /** * @description Calculate scroll for given viewport, track and thumb parameters * * @param {number} contentSize - Scrollable content size * @param {number} viewportSize - Viewport size * @param {number} trackSize - Track size thumb can move * @param {number} thumbSize - Thumb size * @param {number} thumbOffset - Thumb's offset representing the scroll */ var calcScrollForThumbOffset = function calcScrollForThumbOffset(contentSize, viewportSize, trackSize, thumbSize, thumbOffset) { if (!thumbOffset || !thumbSize || viewportSize >= contentSize) { return 0; } return thumbOffset * (contentSize - viewportSize) / (trackSize - thumbSize); }; /** * @description Returns scrollbar width specific for current environment. Can return undefined if DOM is not ready yet. */ var getScrollbarWidth = function getScrollbarWidth(force) { if (force === void 0) { force = false; } if (!doc) { return getScrollbarWidth._cache = 0; } if (!force && !isUndef(getScrollbarWidth._cache)) { return getScrollbarWidth._cache; } var el = doc.createElement("div"); el.setAttribute("style", "position:absolute;width:100px;height:100px;top:-999px;left:-999px;overflow:scroll;"); doc.body.appendChild(el); /* istanbul ignore next */ if (el.clientWidth === 0) { // Do not even cache this value because there is no calculations. Issue https://github.com/xobotyi/react-scrollbars-custom/issues/123 doc.body.removeChild(el); return; } getScrollbarWidth._cache = 100 - el.clientWidth; doc.body.removeChild(el); return getScrollbarWidth._cache; }; /** * @description Detect need of horizontal scroll reverse while RTL. */ var shouldReverseRtlScroll = function shouldReverseRtlScroll(force) { if (force === void 0) { force = false; } if (!force && !isUndef(shouldReverseRtlScroll._cache)) { return shouldReverseRtlScroll._cache; } if (!doc) { return shouldReverseRtlScroll._cache = false; } var el = doc.createElement("div"); var child = doc.createElement("div"); el.appendChild(child); el.setAttribute("style", "position:absolute;width:100px;height:100px;top:-999px;left:-999px;overflow:scroll;direction:rtl"); child.setAttribute("style", "width:1000px;height:1000px"); doc.body.appendChild(el); el.scrollLeft = -50; shouldReverseRtlScroll._cache = el.scrollLeft === -50; doc.body.removeChild(el); return shouldReverseRtlScroll._cache; }; var Emittr = /** @class */ function () { function Emittr(maxHandlers) { if (maxHandlers === void 0) { maxHandlers = 10; } this.setMaxHandlers(maxHandlers); this._handlers = Object.create(null); } Emittr._callEventHandlers = function (emitter, handlers, args) { if (!handlers.length) { return; } if (handlers.length === 1) { Reflect.apply(handlers[0], emitter, args); return; } handlers = __spreadArrays(handlers); var idx; for (idx = 0; idx < handlers.length; idx++) { Reflect.apply(handlers[idx], emitter, args); } }; Emittr.prototype.setMaxHandlers = function (count) { if (!isNum(count) || count <= 0) { throw new TypeError("Expected maxHandlers to be a positive number, got '" + count + "' of type " + _typeof(count)); } this._maxHandlers = count; return this; }; Emittr.prototype.getMaxHandlers = function () { return this._maxHandlers; }; Emittr.prototype.emit = function (name) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } if (_typeof(this._handlers[name]) !== "object" || !Array.isArray(this._handlers[name])) { return false; } Emittr._callEventHandlers(this, this._handlers[name], args); return true; }; Emittr.prototype.on = function (name, handler) { Emittr._addHandler(this, name, handler); return this; }; Emittr.prototype.prependOn = function (name, handler) { Emittr._addHandler(this, name, handler, true); return this; }; Emittr.prototype.once = function (name, handler) { if (!isFun(handler)) { throw new TypeError("Expected event handler to be a function, got " + _typeof(handler)); } Emittr._addHandler(this, name, this._wrapOnceHandler(name, handler)); return this; }; Emittr.prototype.prependOnce = function (name, handler) { if (!isFun(handler)) { throw new TypeError("Expected event handler to be a function, got " + _typeof(handler)); } Emittr._addHandler(this, name, this._wrapOnceHandler(name, handler), true); return this; }; Emittr.prototype.off = function (name, handler) { Emittr._removeHandler(this, name, handler); return this; }; Emittr.prototype.removeAllHandlers = function () { var handlers = this._handlers; this._handlers = Object.create(null); var removeHandlers = handlers["removeHandler"]; delete handlers["removeHandler"]; var idx, eventName; for (eventName in handlers) { for (idx = handlers[eventName].length - 1; idx >= 0; idx--) { Emittr._callEventHandlers(this, removeHandlers, [eventName, handlers[eventName][idx].handler || handlers[eventName][idx]]); } } return this; }; Emittr.prototype._wrapOnceHandler = function (name, handler) { var onceState = { fired: false, handler: handler, wrappedHandler: undefined, emitter: this, event: name }; var wrappedHandler = Emittr._onceWrapper.bind(onceState); onceState.wrappedHandler = wrappedHandler; wrappedHandler.handler = handler; wrappedHandler.event = name; return wrappedHandler; }; Emittr._addHandler = function (emitter, name, handler, prepend) { if (prepend === void 0) { prepend = false; } if (!isFun(handler)) { throw new TypeError("Expected event handler to be a function, got " + _typeof(handler)); } emitter._handlers[name] = emitter._handlers[name] || []; emitter.emit("addHandler", name, handler); prepend ? emitter._handlers[name].unshift(handler) : emitter._handlers[name].push(handler); return emitter; }; Emittr._onceWrapper = function _onceWrapper() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (!this.fired) { this.fired = true; this.emitter.off(this.event, this.wrappedHandler); Reflect.apply(this.handler, this.emitter, args); } }; Emittr._removeHandler = function (emitter, name, handler) { if (!isFun(handler)) { throw new TypeError("Expected event handler to be a function, got " + _typeof(handler)); } if (isUndef(emitter._handlers[name]) || !emitter._handlers[name].length) { return emitter; } var idx = -1; if (emitter._handlers[name].length === 1) { if (emitter._handlers[name][0] === handler || emitter._handlers[name][0].handler === handler) { idx = 0; handler = emitter._handlers[name][0].handler || emitter._handlers[name][0]; } } else { for (idx = emitter._handlers[name].length - 1; idx >= 0; idx--) { if (emitter._handlers[name][idx] === handler || emitter._handlers[name][idx].handler === handler) { handler = emitter._handlers[name][idx].handler || emitter._handlers[name][idx]; break; } } } if (idx === -1) { return emitter; } idx === 0 ? emitter._handlers[name].shift() : emitter._handlers[name].splice(idx, 1); emitter.emit("removeHandler", name, handler); return emitter; }; return Emittr; }(); var RAFLoop = /** @class */ function () { function RAFLoop() { var _this = this; /** * @description List of targets to update */ this.targets = []; /** * @description ID of requested animation frame. Valuable only if loop is active and has items to iterate. */ this.animationFrameID = 0; /** * @description Loop's state. */ this._isActive = false; /** * @description Start the loop if it wasn't yet. */ this.start = function () { if (!_this._isActive && _this.targets.length) { _this._isActive = true; _this.animationFrameID && cancelAnimationFrame(_this.animationFrameID); _this.animationFrameID = requestAnimationFrame(_this.rafCallback); } return _this; }; /** * @description Stop the loop if is was active. */ this.stop = function () { if (_this._isActive) { _this._isActive = false; _this.animationFrameID && cancelAnimationFrame(_this.animationFrameID); _this.animationFrameID = 0; } return _this; }; /** * @description Add target to the iteration list if it's not there. */ this.addTarget = function (target, silent) { if (silent === void 0) { silent = false; } if (_this.targets.indexOf(target) === -1) { _this.targets.push(target); _this.targets.length === 1 && !silent && _this.start(); } return _this; }; /** * @description Remove target from iteration list if it was there. */ this.removeTarget = function (target) { var idx = _this.targets.indexOf(target); if (idx !== -1) { _this.targets.splice(idx, 1); _this.targets.length === 0 && _this.stop(); } return _this; }; /** * @description Callback that called each animation frame. */ this.rafCallback = function () { if (!_this._isActive) { return 0; } for (var i = 0; i < _this.targets.length; i++) { !_this.targets[i]._unmounted && _this.targets[i].update(); } return _this.animationFrameID = requestAnimationFrame(_this.rafCallback); }; } Object.defineProperty(RAFLoop.prototype, "isActive", { /** * @description Loop's state. */ get: function get() { return this._isActive; }, enumerable: true, configurable: true }); return RAFLoop; }(); var Loop = new RAFLoop(); var AXIS_DIRECTION; (function (AXIS_DIRECTION) { AXIS_DIRECTION["X"] = "x"; AXIS_DIRECTION["Y"] = "y"; })(AXIS_DIRECTION || (AXIS_DIRECTION = {})); var AXIS_DIRECTION_PROP_TYPE = oneOf([AXIS_DIRECTION.X, AXIS_DIRECTION.Y]); var TRACK_CLICK_BEHAVIOR; (function (TRACK_CLICK_BEHAVIOR) { TRACK_CLICK_BEHAVIOR["JUMP"] = "jump"; TRACK_CLICK_BEHAVIOR["STEP"] = "step"; })(TRACK_CLICK_BEHAVIOR || (TRACK_CLICK_BEHAVIOR = {})); var TRACK_CLICK_BEHAVIOR_PROP_TYPE = oneOf([TRACK_CLICK_BEHAVIOR.JUMP, TRACK_CLICK_BEHAVIOR.STEP]); var ScrollbarThumb = /** @class */ function (_super) { __extends(ScrollbarThumb, _super); function ScrollbarThumb() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.initialOffsetX = 0; _this.initialOffsetY = 0; _this.lastDragData = { x: 0, y: 0, deltaX: 0, deltaY: 0, lastX: 0, lastY: 0 }; _this.element = null; _this.handleOnDragStart = function (ev, data) { if (!_this.element) { _this.handleOnDragStop(ev, data); return; } if (global.document) { _this.prevUserSelect = global.document.body.style.userSelect; global.document.body.style.userSelect = "none"; _this.prevOnSelectStart = global.document.onselectstart; global.document.onselectstart = ScrollbarThumb.selectStartReplacer; } _this.props.onDragStart && _this.props.onDragStart(_this.lastDragData = { x: data.x - _this.initialOffsetX, y: data.y - _this.initialOffsetY, lastX: data.lastX - _this.initialOffsetX, lastY: data.lastY - _this.initialOffsetY, deltaX: data.deltaX, deltaY: data.deltaY }); _this.element.classList.add("dragging"); }; _this.handleOnDrag = function (ev, data) { if (!_this.element) { _this.handleOnDragStop(ev, data); return; } _this.props.onDrag && _this.props.onDrag(_this.lastDragData = { x: data.x - _this.initialOffsetX, y: data.y - _this.initialOffsetY, lastX: data.lastX - _this.initialOffsetX, lastY: data.lastY - _this.initialOffsetY, deltaX: data.deltaX, deltaY: data.deltaY }, false); }; _this.handleOnDragStop = function (ev, data) { var resultData = data ? { x: data.x - _this.initialOffsetX, y: data.y - _this.initialOffsetY, lastX: data.lastX - _this.initialOffsetX, lastY: data.lastY - _this.initialOffsetY, deltaX: data.deltaX, deltaY: data.deltaY } : _this.lastDragData; _this.props.onDragEnd && _this.props.onDragEnd(resultData, true); _this.element && _this.element.classList.remove("dragging"); if (global.document) { global.document.body.style.userSelect = _this.prevUserSelect; global.document.onselectstart = _this.prevOnSelectStart; _this.prevOnSelectStart = null; } _this.initialOffsetX = 0; _this.initialOffsetY = 0; _this.lastDragData = { x: 0, y: 0, deltaX: 0, deltaY: 0, lastX: 0, lastY: 0 }; }; _this.handleOnMouseDown = function (ev) { if (!_this.element) { return; } ev.preventDefault(); ev.stopPropagation(); if (!isUndef(ev.offsetX)) { /* istanbul ignore next */ _this.initialOffsetX = ev.offsetX; /* istanbul ignore next */ _this.initialOffsetY = ev.offsetY; } else { var rect = _this.element.getBoundingClientRect(); _this.initialOffsetX = (ev.clientX || ev.touches[0].clientX) - rect.left; _this.initialOffsetY = (ev.clientY || ev.touches[0].clientY) - rect.top; } }; _this.elementRef = function (ref) { isFun(_this.props.elementRef) && _this.props.elementRef(ref); _this.element = ref; }; return _this; } ScrollbarThumb.prototype.componentDidMount = function () { if (!this.element) { this.setState(function () { throw new Error("Element was not created. Possibly you haven't provided HTMLDivElement to renderer's `elementRef` function."); }); return; } }; ScrollbarThumb.prototype.componentWillUnmount = function () { this.handleOnDragStop(); this.elementRef(null); }; ScrollbarThumb.prototype.render = function () { var _a = this.props, elementRef = _a.elementRef, axis = _a.axis, onDrag = _a.onDrag, onDragEnd = _a.onDragEnd, onDragStart = _a.onDragStart, props = __rest(_a, ["elementRef", "axis", "onDrag", "onDragEnd", "onDragStart"]); props.className = cnb("ScrollbarsCustom-Thumb", axis === AXIS_DIRECTION.X ? "ScrollbarsCustom-ThumbX" : "ScrollbarsCustom-ThumbY", props.className); if (props.renderer) { props.axis = axis; } return createElement(DraggableCore, { allowAnyClick: false, enableUserSelectHack: false, onMouseDown: this.handleOnMouseDown, onDrag: this.handleOnDrag, onStart: this.handleOnDragStart, onStop: this.handleOnDragStop, children: renderDivWithRenderer(props, this.elementRef) }); }; ScrollbarThumb.propTypes = { axis: AXIS_DIRECTION_PROP_TYPE, onDrag: func, onDragStart: func, onDragEnd: func, elementRef: func, renderer: func }; ScrollbarThumb.selectStartReplacer = function () { return false; }; return ScrollbarThumb; }(Component); var ScrollbarTrack = /** @class */ function (_super) { __extends(ScrollbarTrack, _super); function ScrollbarTrack() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.element = null; _this.elementRef = function (ref) { isFun(_this.props.elementRef) && _this.props.elementRef(ref); _this.element = ref; }; _this.handleClick = function (ev) { if (!ev || !_this.element || ev.button !== 0) { return; } if (isFun(_this.props.onClick) && ev.target === _this.element) { if (!isUndef(ev.offsetX)) { _this.props.onClick(ev, { axis: _this.props.axis, offset: _this.props.axis === AXIS_DIRECTION.X ? ev.offsetX : ev.offsetY }); } else { // support for old browsers /* istanbul ignore next */ var rect = _this.element.getBoundingClientRect(); /* istanbul ignore next */ _this.props.onClick(ev, { axis: _this.props.axis, offset: _this.props.axis === AXIS_DIRECTION.X ? (ev.clientX || ev.touches[0].clientX) - rect.left : (ev.clientY || ev.touches[0].clientY) - rect.top }); } } return true; }; return _this; } ScrollbarTrack.prototype.componentDidMount = function () { if (!this.element) { this.setState(function () { throw new Error("Element was not created. Possibly you haven't provided HTMLDivElement to renderer's `elementRef` function."); }); return; } this.element.addEventListener("click", this.handleClick); }; ScrollbarTrack.prototype.componentWillUnmount = function () { if (this.element) { this.element.removeEventListener("click", this.handleClick); this.element = null; this.elementRef(null); } }; ScrollbarTrack.prototype.render = function () { var _a = this.props, elementRef = _a.elementRef, axis = _a.axis, onClick = _a.onClick, props = __rest(_a, ["elementRef", "axis", "onClick"]); props.className = cnb("ScrollbarsCustom-Track", axis === AXIS_DIRECTION.X ? "ScrollbarsCustom-TrackX" : "ScrollbarsCustom-TrackY", props.className); if (props.renderer) { props.axis = axis; } return renderDivWithRenderer(props, this.elementRef); }; ScrollbarTrack.propTypes = { axis: AXIS_DIRECTION_PROP_TYPE, onClick: func, elementRef: func, renderer: func }; return ScrollbarTrack; }(Component); var style = { holder: { position: "relative", width: "100%", height: "100%" }, wrapper: { position: "absolute", top: 0, left: 0, bottom: 0, right: 0 }, content: { boxSizing: "border-box" }, track: { common: { position: "absolute", overflow: "hidden", borderRadius: 4, background: "rgba(0,0,0,.1)", userSelect: "none" }, x: { height: 10, width: "calc(100% - 20px)", bottom: 0, left: 10 }, y: { width: 10, height: "calc(100% - 20px)", top: 10 } }, thumb: { common: { cursor: "pointer", borderRadius: 4, background: "rgba(0,0,0,.4)" }, x: { height: "100%", width: 0 }, y: { width: "100%", height: 0 } } }; var pageZoomLevel = global.window ? zoomLevel() : 1; global.window && global.window.addEventListener("resize", function () { return pageZoomLevel = zoomLevel(); }, { passive: true }); var ScrollbarContext = createContext({ parentScrollbar: null }); var Scrollbar = /** @class */ function (_super) { __extends(Scrollbar, _super); function Scrollbar(props) { var _this = _super.call(this, props) || this; /** * @description Get current scroll-related values.<br/> * If <i>force</i> if truthy - will recalculate them instead of returning cached values. * * @return ScrollState */ _this.getScrollState = function (force) { if (force === void 0) { force = false; } if (_this.scrollValues && !force) { return __assign({}, _this.scrollValues); } var scrollState = { clientHeight: 0, clientWidth: 0, contentScrollHeight: 0, contentScrollWidth: 0, scrollHeight: 0, scrollWidth: 0, scrollTop: 0, scrollLeft: 0, scrollYBlocked: false, scrollXBlocked: false, scrollYPossible: false, scrollXPossible: false, trackYVisible: false, trackXVisible: false, zoomLevel: pageZoomLevel * 1, isRTL: undefined }; var props = _this.props; scrollState.isRTL = _this.state.isRTL; scrollState.scrollYBlocked = props.noScroll || props.noScrollY; scrollState.scrollXBlocked = props.noScroll || props.noScrollX; if (_this.scrollerElement) { scrollState.clientHeight = _this.scrollerElement.clientHeight; scrollState.clientWidth = _this.scrollerElement.clientWidth; scrollState.scrollHeight = _this.scrollerElement.scrollHeight; scrollState.scrollWidth = _this.scrollerElement.scrollWidth; scrollState.scrollTop = _this.scrollerElement.scrollTop; scrollState.scrollLeft = _this.scrollerElement.scrollLeft; scrollState.scrollYPossible = !scrollState.scrollYBlocked && scrollState.scrollHeight > scrollState.clientHeight; scrollState.scrollXPossible = !scrollState.scrollXBlocked && scrollState.scrollWidth > scrollState.clientWidth; scrollState.trackYVisible = scrollState.scrollYPossible || props.permanentTracks || props.permanentTrackY; scrollState.trackXVisible = scrollState.scrollXPossible || props.permanentTracks || props.permanentTrackX; } if (_this.contentElement) { scrollState.contentScrollHeight = _this.contentElement.scrollHeight; scrollState.contentScrollWidth = _this.contentElement.scrollWidth; } return scrollState; }; /** * @description Scroll to top border */ _this.scrollToTop = function () { if (_this.scrollerElement) { _this.scrollerElement.scrollTop = 0; } return _this; }; /** * @description Scroll to left border */ _this.scrollToLeft = function () { if (_this.scrollerElement) { _this.scrollerElement.scrollLeft = 0; } return _this; }; /** * @description Scroll to bottom border */ _this.scrollToBottom = function () { if (_this.scrollerElement) { _this.scrollerElement.scrollTop = _this.scrollerElement.scrollHeight - _this.scrollerElement.clientHeight; } return _this; }; /** * @description Scroll to right border */ _this.scrollToRight = function () { if (_this.scrollerElement) { _this.scrollerElement.scrollLeft = _this.scrollerElement.scrollWidth - _this.scrollerElement.clientWidth; } return _this; }; /** * @description Set the scrolls at given coordinates.<br/> * If coordinate is undefined - current scroll value will persist. */ _this.scrollTo = function (x, y) { if (_this.scrollerElement) { isNum(x) && (_this.scrollerElement.scrollLeft = x); isNum(y) && (_this.scrollerElement.scrollTop = y); } return _this; }; /** * @description Center the viewport at given coordinates.<br/> * If coordinate is undefined - current scroll value will persist. */ _this.centerAt = function (x, y) { if (_this.scrollerElement) { isNum(x) && (_this.scrollerElement.scrollLeft = x - _this.scrollerElement.clientWidth / 2); isNum(y) && (_this.scrollerElement.scrollTop = y - _this.scrollerElement.clientHeight / 2); } return _this; }; _this.update = function (force) { if (force === void 0) { force = false; } if (!_this.scrollerElement) { return; } // autodetect direction if not defined if (isUndef(_this.state.isRTL)) { _this.setState({ isRTL: getComputedStyle(_this.scrollerElement).direction === "rtl" }); return _this.getScrollState(); } var scrollState = _this.getScrollState(true); var prevScrollState = __assign({}, _this.scrollValues); var props = _this.props; var bitmask = 0; if (!force) { prevScrollState.clientHeight !== scrollState.clientHeight && (bitmask |= 1 << 0); prevScrollState.clientWidth !== scrollState.clientWidth && (bitmask |= 1 << 1); prevScrollState.scrollHeight !== scrollState.scrollHeight && (bitmask |= 1 << 2); prevScrollState.scrollWidth !== scrollState.scrollWidth && (bitmask |= 1 << 3); prevScrollState.scrollTop !== scrollState.scrollTop && (bitmask |= 1 << 4); prevScrollState.scrollLeft !== scrollState.scrollLeft && (bitmask |= 1 << 5); prevScrollState.scrollYBlocked !== scrollState.scrollYBlocked && (bitmask |= 1 << 6); prevScrollState.scrollXBlocked !== scrollState.scrollXBlocked && (bitmask |= 1 << 7); prevScrollState.scrollYPossible !== scrollState.scrollYPossible && (bitmask |= 1 << 8); prevScrollState.scrollXPossible !== scrollState.scrollXPossible && (bitmask |= 1 << 9); prevScrollState.trackYVisible !== scrollState.trackYVisible && (bitmask |= 1 << 10); prevScrollState.trackXVisible !== scrollState.trackXVisible && (bitmask |= 1 << 11); prevScrollState.isRTL !== scrollState.isRTL && (bitmask |= 1 << 12); prevScrollState.contentScrollHeight !== scrollState.contentScrollHeight && (bitmask |= 1 << 13); prevScrollState.contentScrollWidth !== scrollState.contentScrollWidth && (bitmask |= 1 << 14); prevScrollState.zoomLevel !== scrollState.zoomLevel && (bitmask |= 1 << 15); // if not forced and nothing has changed - skip this update if (bitmask === 0) { return prevScrollState; } } else { bitmask = 32767; } if (!props.native && _this.holderElement) { if (bitmask & 1 << 13 && (props.translateContentSizesToHolder || props.translateContentSizeYToHolder)) { _this.holderElement.style.height = scrollState.contentScrollHeight + "px"; } if (bitmask & 1 << 14 && (props.translateContentSizesToHolder || props.translateContentSizeXToHolder)) { _this.holderElement.style.width = scrollState.contentScrollWidth + "px"; } if (props.translateContentSizesToHolder || props.translateContentSizeYToHolder || props.translateContentSizeXToHolder) { if (!scrollState.clientHeight && scrollState.contentScrollHeight || !scrollState.clientWidth && scrollState.contentScrollWidth) { return; } } } // if scrollbars visibility has changed if (bitmask & 1 << 10 || bitmask & 1 << 11) { prevScrollState.scrollYBlocked = scrollState.scrollYBlocked; prevScrollState.scrollXBlocked = scrollState.scrollXBlocked; prevScrollState.scrollYPossible = scrollState.scrollYPossible; prevScrollState.scrollXPossible = scrollState.scrollXPossible; if (_this.trackYElement && bitmask & 1 << 10) { _this.trackYElement.style.display = scrollState.trackYVisible ? null : "none"; } if (_this.trackXElement && bitmask & 1 << 11) { _this.trackXElement.style.display = scrollState.trackXVisible ? null : "none"; } _this.scrollValues = prevScrollState; _this.setState({ trackYVisible: _this.scrollValues.trackYVisible = scrollState.trackYVisible, trackXVisible: _this.scrollValues.trackXVisible = scrollState.trackXVisible }); return; } (props.native ? _this.updaterNative : _this.updaterCustom)(bitmask, scrollState); _this.scrollValues = scrollState; if (!props.native && bitmask & 1 << 15) { getScrollbarWidth(true); _this.forceUpdate(); } _this.eventEmitter.emit("update", __assign({}, scrollState), prevScrollState); (bitmask & 1 << 4 || bitmask & 1 << 5) && _this.eventEmitter.emit("scroll", __assign({}, scrollState), prevScrollState); return _this.scrollValues; }; _this.updaterNative = function () { // just for future return true; }; _this.updaterCustom = function (bitmask, scrollValues) { var props = _this.props; if (_this.trackYElement) { if (_this.thumbYElement && (bitmask & 1 << 0 || bitmask & 1 << 2 || bitmask & 1 << 4 || bitmask & 1 << 6 || bitmask & 1 << 8)) { if (scrollValues.scrollYPossible) { var trackInnerSize = getInnerHeight(_this.trackYElement); var thumbSize = calcThumbSize(scrollValues.scrollHeight, scrollValues.clientHeight, trackInnerSize, props.minimalThumbYSize || props.minimalThumbSize, props.maximalThumbYSize || props.maximalThumbSize); var thumbOffset = calcThumbOffset(scrollValues.scrollHeight, scrollValues.clientHeight, trackInnerSize, thumbSize, scrollValues.scrollTop); _this.thumbYElement.style.transform = "translateY(" + thumbOffset + "px)"; _this.thumbYElement.style.height = thumbSize + "px"; _this.thumbYElement.style.display = ""; } else { _this.thumbYElement.style.transform = ""; _this.thumbYElement.style.height = "0px"; _this.thumbYElement.style.display = "none"; } } } if (_this.trackXElement) { if (_this.thumbXElement && (bitmask & 1 << 1 || bitmask & 1 << 3 || bitmask & 1 << 5 || bitmask & 1 << 7 || bitmask & 1 << 9 || bitmask & 1 << 12)) { if (scrollValues.scrollXPossible) { var trackInnerSize = getInnerWidth(_this.trackXElement); var thumbSize = calcThumbSize(scrollValues.scrollWidth, scrollValues.clientWidth, trackInnerSize, props.minimalThumbXSize || props.minimalThumbSize, props.maximalThumbXSize || props.maximalThumbSize); var thumbOffset = calcThumbOffset(scrollValues.scrollWidth, scrollValues.clientWidth, trackInnerSize, thumbSize, scrollValues.scrollLeft); if (_this.state.isRTL && shouldReverseRtlScroll()) { thumbOffset += trackInnerSize - thumbSize; } _this.thumbXElement.style.transform = "translateX(" + thumbOffset + "px)"; _this.thumbXElement.style.width = thumbSize + "px"; _this.thumbXElement.style.display = ""; } else { _this.thumbXElement.style.transform = ""; _this.thumbXElement.style.width = "0px"; _this.thumbXElement.style.display = "none"; } } } return true; }; _this.elementRefHolder = function (ref) { _this.holderElement = ref; isFun(_this.props.elementRef) && _this.props.elementRef(ref); }; _this.elementRefWrapper = function (ref) { _this.wrapperElement = ref; isFun(_this.props.wrapperProps.elementRef) && _this.props.wrapperProps.elementRef(ref); }; _this.elementRefScroller = function (ref) { _this.scrollerElement = ref; isFun(_this.props.scrollerProps.elementRef) && _this.props.scrollerProps.elementRef(ref); }; _this.elementRefContent = function (ref) { _this.contentElement = ref; isFun(_this.props.contentProps.elementRef) && _this.props.contentProps.elementRef(ref); }; _this.elementRefTrackX = function (ref) { _this.trackXElement = ref; isFun(_this.props.trackXProps.elementRef) && _this.props.trackXProps.elementRef(ref); }; _this.elementRefTrackY = function (ref) { _this.trackYElement = ref; isFun(_this.props.trackYProps.elementRef) && _this.props.trackYProps.elementRef(ref); }; _this.elementRefThumbX = function (ref) { _this.thumbXElement = ref; isFun(_this.props.thumbXProps.elementRef) && _this.props.thumbXProps.elementRef(ref); }; _this.elementRefThumbY = function (ref) { _this.thumbYElement = ref; isFun(_this.props.thumbYProps.elementRef) && _this.props.thumbYProps.elementRef(ref); }; _this.handleTrackXClick = function (ev, values) { _this.props.trackXProps.onClick && _this.props.trackXProps.onClick(ev, values); if (!_this.scrollerElement || !_this.trackXElement || !_this.thumbXElement || !_this.scrollValues || !_this.scrollValues.scrollXPossible) { return; } _this._scrollDetection(); var thumbSize = _this.thumbXElement.clientWidth; var trackInnerSize = getInnerWidth(_this.trackXElement); var thumbOffset = (_this.scrollValues.isRTL && shouldReverseRtlScroll() ? values.offset + thumbSize / 2 - trackInnerSize : values.offset - thumbSize / 2) - ( //@ts-ignore parseFloat(getComputedStyle(_this.trackXElement).paddingLeft) || 0); var target = calcScrollForThumbOffset(_this.scrollValues.scrollWidth, _this.scrollValues.clientWidth, trackInnerSize, thumbSize, thumbOffset); if (_this.props.trackClickBehavior === TRACK_CLICK_BEHAVIOR.STEP) { target = (_this.scrollValues.isRTL ? _this.scrollValues.scrollLeft > target : _this.scrollValues.scrollLeft < target) ? _this.scrollValues.scrollLeft + _this.scrollValues.clientWidth : _this.scrollValues.scrollLeft - _this.scrollValues.clientWidth; } _this.scrollerElement.scrollLeft = target; }; _this.handleTrackYClick = function (ev, values) { _this.props.trackYProps.onClick && _this.props.trackYProps.onClick(ev, values); if (!_this.scrollerElement || !_this.trackYElement || !_this.thumbYElement || !_this.scrollValues || !_this.scrollValues.scrollYPossible) { return; } _this._scrollDetection(); var thumbSize = _this.thumbYElement.clientHeight; var target = calcScrollForThumbOffset(_this.scrollValues.scrollHeight, _this.scrollValues.clientHeight, getInnerHeight(_this.trackYElement), thumbSize, values.offset - thumbSize / 2) - ( //@ts-ignore parseFloat(getComputedStyle(_this.trackYElement).paddingTop) || 0); if (_this.props.trackClickBehavior === TRACK_CLICK_BEHAVIOR.JUMP) { _this.scrollerElement.scrollTop = target; } else { _this.scrollerElement.scrollTop = _this.scrollValues.scrollTop < target ? _this.scrollValues.scrollTop + _this.scrollValues.clientHeight : _this.scrollValues.scrollTop - _this.scrollValues.clientHeight; } }; _this.handleTrackYMouseWheel = function (ev) { var props = _this.props; props.trackYProps && props.trackYProps.onWheel && props.trackYProps.onWheel(ev); if (props.disableTracksMousewheelScrolling || props.disableTrackYMousewheelScrolling) { return; } _this._scrollDetection(); if (!_this.scrollerElement || _this.scrollValues.scrollYBlocked) { return; } _this.scrollTop += ev.deltaY; }; _this.handleTrackXMouseWheel = function (ev) { var props = _this.props; props.trackXProps && props.trackXProps.onWheel && props.trackXProps.onWheel(ev); if (props.disableTracksMousewheelScrolling || props.disableTrackXMousewheelScrolling) { return; } _this._scrollDetection(); if (!_this.scrollerElement || _this.scrollValues.scrollXBlocked) { return; } _this.scrollLeft += ev.deltaX; }; _this.handleThumbXDrag = function (data, isEnd) { if (isEnd === void 0) { isEnd = false; } if (!_this.trackXElement || !_this.thumbXElement || !_this.scrollerElement || !_this.scrollValues || !_this.scrollValues.scrollXPossible) { return; } _this._scrollDetection(); var trackRect = _this.trackXElement.getBoundingClientRect(); var styles = getComputedStyle(_this.trackXElement); //@ts-ignore var paddingLeft = parseFloat(styles.paddingLeft) || 0; //@ts-ignore var paddingRight = parseFloat(styles.paddingRight) || 0; var trackInnerSize = trackRect.width - paddingLeft - paddingRight; var thumbSize = _this.thumbXElement.clientWidth; var offset = _this.scrollValues.isRTL && shouldReverseRtlScroll() ? data.x + thumbSize - trackInnerSize + paddingLeft : data.lastX - paddingLeft; _this.scrollerElement.scrollLeft = calcScrollForThumbOffset(_this.scrollValues.scrollWidth, _this.scrollValues.clientWidth, trackInnerSize, thumbSize, offset); if (isEnd && _this.props.thumbXProps && _this.props.thumbXProps.onDragEnd) { _this.props.thumbXProps.onDragEnd(data, true); } }; _this.handleThumbYDrag = function (data, isEnd) { if (isEnd === void 0) { isEnd = false; } if (!_this.scrollerElement || !_this.trackYElement || !_this.thumbYElement || !_this.scrollValues || !_this.scrollValues.scrollYPossible) { return; } _this._scrollDetection(); var trackRect = _this.trackYElement.getBoundingClientRect(); var styles = getComputedStyle(_this.trackYElement); //@ts-ignore var paddingTop = parseFloat(styles.paddingTop) || 0; //@ts-ignore var paddingBottom = parseFloat(styles.paddingBottom) || 0; var trackInnerSize = trackRect.height - paddingTop - paddingBottom; var thumbSize = _this.thumbYElement.clientHeight; var offset = data.y - paddingTop; _this.scrollerElement.scrollTop = calcScrollForThumbOffset(_this.scrollValues.scrollHeight, _this.scrollValues.clientHeight, trackInnerSize, thumbSize, offset); if (isEnd && _this.props.thumbYProps && _this.props.thumbYProps.onDragEnd) { _this.props.thumbYProps.onDragEnd(data, true); } }; _this.handleScrollerScroll = function () { _this._scrollDetection(); }; _this._scrollDetection = function () { !_this._scrollDetectionTO && _this.eventEmitter.emit("scrollStart", _this.getScrollState()); _this._scrollDetectionTO && global.window && global.window.clearTimeout(_this._scrollDetectionTO); _this._scrollDetectionTO = global.window ? global.window.setTimeout(_this._scrollDetectionCallback, _this.props.scrollDetectionThreshold || 0) : null; }; _this._scrollDetectionCallback = function () { _this._scrollDetectionTO = null; _this.eventEmitter.emit("scrollStop", _this.getScrollState()); }; _this.state = { trackXVisible: false, trackYVisible: false, isRTL: props.rtl }; _this.scrollValues = _this.getScrollState(true); _this.eventEmitter = new Emittr(15); props.onUpdate && _this.eventEmitter.on("update", props.onUpdate); props.onScroll && _this.eventEmitter.on("scroll", props.onScroll); props.onScrollStart && _this.eventEmitter.on("scrollStart", props.onScrollStart); props.onScrollStop && _this.eventEmitter.on("scrollStop", props.onScrollStop); _this.id = uuid(); return _this; } Object.defineProperty(Scrollbar.prototype, "scrollTop", { get: function get() { if (this.scrollerElement) { return this.scrollerElement.scrollTop; } return 0; }, set: function set(top) { if (this.scrollerElement) { this.scrollerElement.scrollTop = top; this.update(); } }, enumerable: true, configurable: true }); Object.defineProperty(Scrollbar.prototype, "scrollLeft", { get: function get() { if (this.scrollerElement) { return this.scrollerElement.scrollLeft; } return 0; }, set: function set(left) { if (this.scrollerElement) { this.scrollerElement.scrollLeft = left; } }, enumerable: true, configurable: true }); Object.defineProperty(Scrollbar.prototype, "scrollHeight", { get: function get() { if (this.scrollerElement) { return this.scrollerElement.scrollHeight; } return 0; }, enumerable: true, configurable: true }); Object.defineProperty(Scrollbar.prototype, "scrollWidth", { get: function get() { if (this.scrollerElement) { return this.scrollerElement.scrollWidth; } return 0; }, enumerable: true, configurable: true }); Object.defineProperty(Scrollbar.prototype, "clientHeight", { get: function get() { if (this.scrollerElement) { return this.scrollerElement.clientHeight; } return 0; }, enumerable: true, configurable: true }); Object.defineProperty(Scrollbar.prototype, "clientWidth", { get: function get() { if (this.scrollerElement) { return this.scrollerElement.clientWidth; } return 0; }, enumerable: true, configurable: true }); Scrollbar.calculateStyles = function (props, state, scrollValues, scrollbarWidth) { var _a, _b, _c, _d; var useDefaultStyles = !props.noDefaultStyles; return { holder: __assign(__assign(__assign({}, useDefaultStyles && style.holder), { position: "relative" }), props.style), wrapper: __assign(__assign(__assign({}, useDefaultStyles && __assign(__assign(__assign({}, style.wrapper), !props.disableTracksWidthCompensation && !props.disableTrackYWidthCompensation && (_a = {}, _a[state.isRTL ? "left" : "right"] = state.trackYVisible ? 10 : 0, _a)), !props.disableTracksWidthCompensation && !props.disableTrackXWidthCompensation && { bottom: state.trackXVisible ? 10 : 0 })), props.wrapperProps.style), { position: "absolute", overflow: "hidden" }), content: __assign(__assign(__assign(__assign(__assign({}, useDefaultStyles && style.content), props.translateContentSizesToHolder || props.translateContentSizeYToHolder || props.translateContentSizeXToHolder ? { display: "table-cell" } : { padding: 0.05 // needed to disable margin collapsing without flexboxes, other possible solutions here: https://stackoverflow.com/questions/19718634/how-to-disable-margin-collapsing }), useDefaultStyles && !(props.translateContentSizesToHolder || props.translateContentSizeYToHolder) && { minHeight: "100%" }), useDefaultStyles && !(props.translateContentSizesToHolder || props.translateContentSizeXToHolder) && { minWidth: "100%" }), props.contentProps.style), scroller: __assign(__assign(__assign(__assign((_b = { position: "absolute", top: 0, left: 0, bottom: 0, right: 0, paddingBottom: !scrollbarWidth && scrollValues.scrollXPossible ? props.fallbackScrollbarWidth : undefined }, _b[state.isRTL ? "paddingLeft" : "paddingRight"] = !scrollbarWidth && scrollValues.scrollYPossible ? props.fallbackScrollbarWidth : undefined, _b), props.scrollerProps.style), !isUndef(props.rtl) && { direction: props.rtl ? "rtl" : "ltr" }), props.momentum && { WebkitOverflowScrolling: "touch" }), (_c = { overflowY: scrollValues.scrollYPossible ? "scroll" : "hidden", overflowX: scrollValues.scrollXPossible ? "scroll" : "hidden", marginBottom: scrollValues.scrollXPossible ? -(scrollbarWidth || props.fallbackScrollbarWidth) - Number(scrollValues.zoomLevel !== 1) : undefined }, _c[state.isRTL ? "marginLeft" : "marginRight"] = scrollValues.scrollYPossible ? -(scrollbarWidth || props.fallbackScrollbarWidth) - Number(scrollValues.zoomLevel !== 1) : undefined, _c)), trackX: __assign(__assign(__assign(__assign({}, useDefaultStyles && style.tr