UNPKG

35.8 kBJavaScriptView Raw
1/**
2 * SimpleBar.js - v5.2.1
3 * Scrollbars, simpler.
4 * https://grsmto.github.io/simplebar/
5 *
6 * Made by Adrien Denat from a fork by Jonathan Nicol
7 * Under MIT License
8 */
9
10import 'core-js/modules/es.array.for-each';
11import 'core-js/modules/web.dom-collections.for-each';
12import canUseDOM from 'can-use-dom';
13import 'core-js/modules/es.array.filter';
14import 'core-js/modules/es.array.iterator';
15import 'core-js/modules/es.object.assign';
16import 'core-js/modules/es.object.to-string';
17import 'core-js/modules/es.parse-int';
18import 'core-js/modules/es.string.iterator';
19import 'core-js/modules/es.weak-map';
20import 'core-js/modules/web.dom-collections.iterator';
21import throttle from 'lodash.throttle';
22import debounce from 'lodash.debounce';
23import memoize from 'lodash.memoize';
24import ResizeObserver from 'resize-observer-polyfill';
25import 'core-js/modules/es.array.reduce';
26import 'core-js/modules/es.function.name';
27import 'core-js/modules/es.regexp.exec';
28import 'core-js/modules/es.string.match';
29import 'core-js/modules/es.string.replace';
30
31var cachedScrollbarWidth = null;
32var cachedDevicePixelRatio = null;
33
34if (canUseDOM) {
35 window.addEventListener('resize', function () {
36 if (cachedDevicePixelRatio !== window.devicePixelRatio) {
37 cachedDevicePixelRatio = window.devicePixelRatio;
38 cachedScrollbarWidth = null;
39 }
40 });
41}
42
43function scrollbarWidth() {
44 if (cachedScrollbarWidth === null) {
45 if (typeof document === 'undefined') {
46 cachedScrollbarWidth = 0;
47 return cachedScrollbarWidth;
48 }
49
50 var body = document.body;
51 var box = document.createElement('div');
52 box.classList.add('simplebar-hide-scrollbar');
53 body.appendChild(box);
54 var width = box.getBoundingClientRect().right;
55 body.removeChild(box);
56 cachedScrollbarWidth = width;
57 }
58
59 return cachedScrollbarWidth;
60}
61
62// Helper function to retrieve options from element attributes
63var getOptions = function getOptions(obj) {
64 var options = Array.prototype.reduce.call(obj, function (acc, attribute) {
65 var option = attribute.name.match(/data-simplebar-(.+)/);
66
67 if (option) {
68 var key = option[1].replace(/\W+(.)/g, function (x, chr) {
69 return chr.toUpperCase();
70 });
71
72 switch (attribute.value) {
73 case 'true':
74 acc[key] = true;
75 break;
76
77 case 'false':
78 acc[key] = false;
79 break;
80
81 case undefined:
82 acc[key] = true;
83 break;
84
85 default:
86 acc[key] = attribute.value;
87 }
88 }
89
90 return acc;
91 }, {});
92 return options;
93};
94function getElementWindow(element) {
95 if (!element || !element.ownerDocument || !element.ownerDocument.defaultView) {
96 return window;
97 }
98
99 return element.ownerDocument.defaultView;
100}
101function getElementDocument(element) {
102 if (!element || !element.ownerDocument) {
103 return document;
104 }
105
106 return element.ownerDocument;
107}
108
109var SimpleBar =
110/*#__PURE__*/
111function () {
112 function SimpleBar(element, options) {
113 var _this = this;
114
115 this.onScroll = function () {
116 var elWindow = getElementWindow(_this.el);
117
118 if (!_this.scrollXTicking) {
119 elWindow.requestAnimationFrame(_this.scrollX);
120 _this.scrollXTicking = true;
121 }
122
123 if (!_this.scrollYTicking) {
124 elWindow.requestAnimationFrame(_this.scrollY);
125 _this.scrollYTicking = true;
126 }
127 };
128
129 this.scrollX = function () {
130 if (_this.axis.x.isOverflowing) {
131 _this.showScrollbar('x');
132
133 _this.positionScrollbar('x');
134 }
135
136 _this.scrollXTicking = false;
137 };
138
139 this.scrollY = function () {
140 if (_this.axis.y.isOverflowing) {
141 _this.showScrollbar('y');
142
143 _this.positionScrollbar('y');
144 }
145
146 _this.scrollYTicking = false;
147 };
148
149 this.onMouseEnter = function () {
150 _this.showScrollbar('x');
151
152 _this.showScrollbar('y');
153 };
154
155 this.onMouseMove = function (e) {
156 _this.mouseX = e.clientX;
157 _this.mouseY = e.clientY;
158
159 if (_this.axis.x.isOverflowing || _this.axis.x.forceVisible) {
160 _this.onMouseMoveForAxis('x');
161 }
162
163 if (_this.axis.y.isOverflowing || _this.axis.y.forceVisible) {
164 _this.onMouseMoveForAxis('y');
165 }
166 };
167
168 this.onMouseLeave = function () {
169 _this.onMouseMove.cancel();
170
171 if (_this.axis.x.isOverflowing || _this.axis.x.forceVisible) {
172 _this.onMouseLeaveForAxis('x');
173 }
174
175 if (_this.axis.y.isOverflowing || _this.axis.y.forceVisible) {
176 _this.onMouseLeaveForAxis('y');
177 }
178
179 _this.mouseX = -1;
180 _this.mouseY = -1;
181 };
182
183 this.onWindowResize = function () {
184 // Recalculate scrollbarWidth in case it's a zoom
185 _this.scrollbarWidth = _this.getScrollbarWidth();
186
187 _this.hideNativeScrollbar();
188 };
189
190 this.hideScrollbars = function () {
191 _this.axis.x.track.rect = _this.axis.x.track.el.getBoundingClientRect();
192 _this.axis.y.track.rect = _this.axis.y.track.el.getBoundingClientRect();
193
194 if (!_this.isWithinBounds(_this.axis.y.track.rect)) {
195 _this.axis.y.scrollbar.el.classList.remove(_this.classNames.visible);
196
197 _this.axis.y.isVisible = false;
198 }
199
200 if (!_this.isWithinBounds(_this.axis.x.track.rect)) {
201 _this.axis.x.scrollbar.el.classList.remove(_this.classNames.visible);
202
203 _this.axis.x.isVisible = false;
204 }
205 };
206
207 this.onPointerEvent = function (e) {
208 var isWithinTrackXBounds, isWithinTrackYBounds;
209 _this.axis.x.track.rect = _this.axis.x.track.el.getBoundingClientRect();
210 _this.axis.y.track.rect = _this.axis.y.track.el.getBoundingClientRect();
211
212 if (_this.axis.x.isOverflowing || _this.axis.x.forceVisible) {
213 isWithinTrackXBounds = _this.isWithinBounds(_this.axis.x.track.rect);
214 }
215
216 if (_this.axis.y.isOverflowing || _this.axis.y.forceVisible) {
217 isWithinTrackYBounds = _this.isWithinBounds(_this.axis.y.track.rect);
218 } // If any pointer event is called on the scrollbar
219
220
221 if (isWithinTrackXBounds || isWithinTrackYBounds) {
222 // Preventing the event's default action stops text being
223 // selectable during the drag.
224 e.preventDefault(); // Prevent event leaking
225
226 e.stopPropagation();
227
228 if (e.type === 'mousedown') {
229 if (isWithinTrackXBounds) {
230 _this.axis.x.scrollbar.rect = _this.axis.x.scrollbar.el.getBoundingClientRect();
231
232 if (_this.isWithinBounds(_this.axis.x.scrollbar.rect)) {
233 _this.onDragStart(e, 'x');
234 } else {
235 _this.onTrackClick(e, 'x');
236 }
237 }
238
239 if (isWithinTrackYBounds) {
240 _this.axis.y.scrollbar.rect = _this.axis.y.scrollbar.el.getBoundingClientRect();
241
242 if (_this.isWithinBounds(_this.axis.y.scrollbar.rect)) {
243 _this.onDragStart(e, 'y');
244 } else {
245 _this.onTrackClick(e, 'y');
246 }
247 }
248 }
249 }
250 };
251
252 this.drag = function (e) {
253 var eventOffset;
254 var track = _this.axis[_this.draggedAxis].track;
255 var trackSize = track.rect[_this.axis[_this.draggedAxis].sizeAttr];
256 var scrollbar = _this.axis[_this.draggedAxis].scrollbar;
257 var contentSize = _this.contentWrapperEl[_this.axis[_this.draggedAxis].scrollSizeAttr];
258 var hostSize = parseInt(_this.elStyles[_this.axis[_this.draggedAxis].sizeAttr], 10);
259 e.preventDefault();
260 e.stopPropagation();
261
262 if (_this.draggedAxis === 'y') {
263 eventOffset = e.pageY;
264 } else {
265 eventOffset = e.pageX;
266 } // Calculate how far the user's mouse is from the top/left of the scrollbar (minus the dragOffset).
267
268
269 var dragPos = eventOffset - track.rect[_this.axis[_this.draggedAxis].offsetAttr] - _this.axis[_this.draggedAxis].dragOffset; // Convert the mouse position into a percentage of the scrollbar height/width.
270
271 var dragPerc = dragPos / (trackSize - scrollbar.size); // Scroll the content by the same percentage.
272
273 var scrollPos = dragPerc * (contentSize - hostSize); // Fix browsers inconsistency on RTL
274
275 if (_this.draggedAxis === 'x') {
276 scrollPos = _this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollbarInverted ? scrollPos - (trackSize + scrollbar.size) : scrollPos;
277 scrollPos = _this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollingInverted ? -scrollPos : scrollPos;
278 }
279
280 _this.contentWrapperEl[_this.axis[_this.draggedAxis].scrollOffsetAttr] = scrollPos;
281 };
282
283 this.onEndDrag = function (e) {
284 var elDocument = getElementDocument(_this.el);
285 var elWindow = getElementWindow(_this.el);
286 e.preventDefault();
287 e.stopPropagation();
288
289 _this.el.classList.remove(_this.classNames.dragging);
290
291 elDocument.removeEventListener('mousemove', _this.drag, true);
292 elDocument.removeEventListener('mouseup', _this.onEndDrag, true);
293 _this.removePreventClickId = elWindow.setTimeout(function () {
294 // Remove these asynchronously so we still suppress click events
295 // generated simultaneously with mouseup.
296 elDocument.removeEventListener('click', _this.preventClick, true);
297 elDocument.removeEventListener('dblclick', _this.preventClick, true);
298 _this.removePreventClickId = null;
299 });
300 };
301
302 this.preventClick = function (e) {
303 e.preventDefault();
304 e.stopPropagation();
305 };
306
307 this.el = element;
308 this.minScrollbarWidth = 20;
309 this.options = Object.assign({}, SimpleBar.defaultOptions, {}, options);
310 this.classNames = Object.assign({}, SimpleBar.defaultOptions.classNames, {}, this.options.classNames);
311 this.axis = {
312 x: {
313 scrollOffsetAttr: 'scrollLeft',
314 sizeAttr: 'width',
315 scrollSizeAttr: 'scrollWidth',
316 offsetSizeAttr: 'offsetWidth',
317 offsetAttr: 'left',
318 overflowAttr: 'overflowX',
319 dragOffset: 0,
320 isOverflowing: true,
321 isVisible: false,
322 forceVisible: false,
323 track: {},
324 scrollbar: {}
325 },
326 y: {
327 scrollOffsetAttr: 'scrollTop',
328 sizeAttr: 'height',
329 scrollSizeAttr: 'scrollHeight',
330 offsetSizeAttr: 'offsetHeight',
331 offsetAttr: 'top',
332 overflowAttr: 'overflowY',
333 dragOffset: 0,
334 isOverflowing: true,
335 isVisible: false,
336 forceVisible: false,
337 track: {},
338 scrollbar: {}
339 }
340 };
341 this.removePreventClickId = null; // Don't re-instantiate over an existing one
342
343 if (SimpleBar.instances.has(this.el)) {
344 return;
345 }
346
347 this.recalculate = throttle(this.recalculate.bind(this), 64);
348 this.onMouseMove = throttle(this.onMouseMove.bind(this), 64);
349 this.hideScrollbars = debounce(this.hideScrollbars.bind(this), this.options.timeout);
350 this.onWindowResize = debounce(this.onWindowResize.bind(this), 64, {
351 leading: true
352 });
353 SimpleBar.getRtlHelpers = memoize(SimpleBar.getRtlHelpers);
354 this.init();
355 }
356 /**
357 * Static properties
358 */
359
360 /**
361 * Helper to fix browsers inconsistency on RTL:
362 * - Firefox inverts the scrollbar initial position
363 * - IE11 inverts both scrollbar position and scrolling offset
364 * Directly inspired by @KingSora's OverlayScrollbars https://github.com/KingSora/OverlayScrollbars/blob/master/js/OverlayScrollbars.js#L1634
365 */
366
367
368 SimpleBar.getRtlHelpers = function getRtlHelpers() {
369 var dummyDiv = document.createElement('div');
370 dummyDiv.innerHTML = '<div class="hs-dummy-scrollbar-size"><div style="height: 200%; width: 200%; margin: 10px 0;"></div></div>';
371 var scrollbarDummyEl = dummyDiv.firstElementChild;
372 document.body.appendChild(scrollbarDummyEl);
373 var dummyContainerChild = scrollbarDummyEl.firstElementChild;
374 scrollbarDummyEl.scrollLeft = 0;
375 var dummyContainerOffset = SimpleBar.getOffset(scrollbarDummyEl);
376 var dummyContainerChildOffset = SimpleBar.getOffset(dummyContainerChild);
377 scrollbarDummyEl.scrollLeft = 999;
378 var dummyContainerScrollOffsetAfterScroll = SimpleBar.getOffset(dummyContainerChild);
379 return {
380 // determines if the scrolling is responding with negative values
381 isRtlScrollingInverted: dummyContainerOffset.left !== dummyContainerChildOffset.left && dummyContainerChildOffset.left - dummyContainerScrollOffsetAfterScroll.left !== 0,
382 // determines if the origin scrollbar position is inverted or not (positioned on left or right)
383 isRtlScrollbarInverted: dummyContainerOffset.left !== dummyContainerChildOffset.left
384 };
385 };
386
387 SimpleBar.getOffset = function getOffset(el) {
388 var rect = el.getBoundingClientRect();
389 var elDocument = getElementDocument(el);
390 var elWindow = getElementWindow(el);
391 return {
392 top: rect.top + (elWindow.pageYOffset || elDocument.documentElement.scrollTop),
393 left: rect.left + (elWindow.pageXOffset || elDocument.documentElement.scrollLeft)
394 };
395 };
396
397 var _proto = SimpleBar.prototype;
398
399 _proto.init = function init() {
400 // Save a reference to the instance, so we know this DOM node has already been instancied
401 SimpleBar.instances.set(this.el, this); // We stop here on server-side
402
403 if (canUseDOM) {
404 this.initDOM();
405 this.scrollbarWidth = this.getScrollbarWidth();
406 this.recalculate();
407 this.initListeners();
408 }
409 };
410
411 _proto.initDOM = function initDOM() {
412 var _this2 = this;
413
414 // make sure this element doesn't have the elements yet
415 if (Array.prototype.filter.call(this.el.children, function (child) {
416 return child.classList.contains(_this2.classNames.wrapper);
417 }).length) {
418 // assume that element has his DOM already initiated
419 this.wrapperEl = this.el.querySelector("." + this.classNames.wrapper);
420 this.contentWrapperEl = this.options.scrollableNode || this.el.querySelector("." + this.classNames.contentWrapper);
421 this.contentEl = this.options.contentNode || this.el.querySelector("." + this.classNames.contentEl);
422 this.offsetEl = this.el.querySelector("." + this.classNames.offset);
423 this.maskEl = this.el.querySelector("." + this.classNames.mask);
424 this.placeholderEl = this.findChild(this.wrapperEl, "." + this.classNames.placeholder);
425 this.heightAutoObserverWrapperEl = this.el.querySelector("." + this.classNames.heightAutoObserverWrapperEl);
426 this.heightAutoObserverEl = this.el.querySelector("." + this.classNames.heightAutoObserverEl);
427 this.axis.x.track.el = this.findChild(this.el, "." + this.classNames.track + "." + this.classNames.horizontal);
428 this.axis.y.track.el = this.findChild(this.el, "." + this.classNames.track + "." + this.classNames.vertical);
429 } else {
430 // Prepare DOM
431 this.wrapperEl = document.createElement('div');
432 this.contentWrapperEl = document.createElement('div');
433 this.offsetEl = document.createElement('div');
434 this.maskEl = document.createElement('div');
435 this.contentEl = document.createElement('div');
436 this.placeholderEl = document.createElement('div');
437 this.heightAutoObserverWrapperEl = document.createElement('div');
438 this.heightAutoObserverEl = document.createElement('div');
439 this.wrapperEl.classList.add(this.classNames.wrapper);
440 this.contentWrapperEl.classList.add(this.classNames.contentWrapper);
441 this.offsetEl.classList.add(this.classNames.offset);
442 this.maskEl.classList.add(this.classNames.mask);
443 this.contentEl.classList.add(this.classNames.contentEl);
444 this.placeholderEl.classList.add(this.classNames.placeholder);
445 this.heightAutoObserverWrapperEl.classList.add(this.classNames.heightAutoObserverWrapperEl);
446 this.heightAutoObserverEl.classList.add(this.classNames.heightAutoObserverEl);
447
448 while (this.el.firstChild) {
449 this.contentEl.appendChild(this.el.firstChild);
450 }
451
452 this.contentWrapperEl.appendChild(this.contentEl);
453 this.offsetEl.appendChild(this.contentWrapperEl);
454 this.maskEl.appendChild(this.offsetEl);
455 this.heightAutoObserverWrapperEl.appendChild(this.heightAutoObserverEl);
456 this.wrapperEl.appendChild(this.heightAutoObserverWrapperEl);
457 this.wrapperEl.appendChild(this.maskEl);
458 this.wrapperEl.appendChild(this.placeholderEl);
459 this.el.appendChild(this.wrapperEl);
460 }
461
462 if (!this.axis.x.track.el || !this.axis.y.track.el) {
463 var track = document.createElement('div');
464 var scrollbar = document.createElement('div');
465 track.classList.add(this.classNames.track);
466 scrollbar.classList.add(this.classNames.scrollbar);
467 track.appendChild(scrollbar);
468 this.axis.x.track.el = track.cloneNode(true);
469 this.axis.x.track.el.classList.add(this.classNames.horizontal);
470 this.axis.y.track.el = track.cloneNode(true);
471 this.axis.y.track.el.classList.add(this.classNames.vertical);
472 this.el.appendChild(this.axis.x.track.el);
473 this.el.appendChild(this.axis.y.track.el);
474 }
475
476 this.axis.x.scrollbar.el = this.axis.x.track.el.querySelector("." + this.classNames.scrollbar);
477 this.axis.y.scrollbar.el = this.axis.y.track.el.querySelector("." + this.classNames.scrollbar);
478
479 if (!this.options.autoHide) {
480 this.axis.x.scrollbar.el.classList.add(this.classNames.visible);
481 this.axis.y.scrollbar.el.classList.add(this.classNames.visible);
482 }
483
484 this.el.setAttribute('data-simplebar', 'init');
485 };
486
487 _proto.initListeners = function initListeners() {
488 var _this3 = this;
489
490 var elWindow = getElementWindow(this.el); // Event listeners
491
492 if (this.options.autoHide) {
493 this.el.addEventListener('mouseenter', this.onMouseEnter);
494 }
495
496 ['mousedown', 'click', 'dblclick'].forEach(function (e) {
497 _this3.el.addEventListener(e, _this3.onPointerEvent, true);
498 });
499 ['touchstart', 'touchend', 'touchmove'].forEach(function (e) {
500 _this3.el.addEventListener(e, _this3.onPointerEvent, {
501 capture: true,
502 passive: true
503 });
504 });
505 this.el.addEventListener('mousemove', this.onMouseMove);
506 this.el.addEventListener('mouseleave', this.onMouseLeave);
507 this.contentWrapperEl.addEventListener('scroll', this.onScroll); // Browser zoom triggers a window resize
508
509 elWindow.addEventListener('resize', this.onWindowResize); // Hack for https://github.com/WICG/ResizeObserver/issues/38
510
511 var resizeObserverStarted = false;
512 var resizeObserver = elWindow.ResizeObserver || ResizeObserver;
513 this.resizeObserver = new resizeObserver(function () {
514 if (!resizeObserverStarted) return;
515
516 _this3.recalculate();
517 });
518 this.resizeObserver.observe(this.el);
519 this.resizeObserver.observe(this.contentEl);
520 elWindow.requestAnimationFrame(function () {
521 resizeObserverStarted = true;
522 }); // This is required to detect horizontal scroll. Vertical scroll only needs the resizeObserver.
523
524 this.mutationObserver = new elWindow.MutationObserver(this.recalculate);
525 this.mutationObserver.observe(this.contentEl, {
526 childList: true,
527 subtree: true,
528 characterData: true
529 });
530 };
531
532 _proto.recalculate = function recalculate() {
533 var elWindow = getElementWindow(this.el);
534 this.elStyles = elWindow.getComputedStyle(this.el);
535 this.isRtl = this.elStyles.direction === 'rtl';
536 var contentElOffsetWidth = this.contentEl.offsetWidth;
537 var isHeightAuto = this.heightAutoObserverEl.offsetHeight <= 1;
538 var isWidthAuto = this.heightAutoObserverEl.offsetWidth <= 1 || contentElOffsetWidth > 0;
539 var contentWrapperElOffsetWidth = this.contentWrapperEl.offsetWidth;
540 var elOverflowX = this.elStyles.overflowX;
541 var elOverflowY = this.elStyles.overflowY;
542 this.contentEl.style.padding = this.elStyles.paddingTop + " " + this.elStyles.paddingRight + " " + this.elStyles.paddingBottom + " " + this.elStyles.paddingLeft;
543 this.wrapperEl.style.margin = "-" + this.elStyles.paddingTop + " -" + this.elStyles.paddingRight + " -" + this.elStyles.paddingBottom + " -" + this.elStyles.paddingLeft;
544 var contentElScrollHeight = this.contentEl.scrollHeight;
545 var contentElScrollWidth = this.contentEl.scrollWidth;
546 this.contentWrapperEl.style.height = isHeightAuto ? 'auto' : '100%'; // Determine placeholder size
547
548 this.placeholderEl.style.width = isWidthAuto ? (contentElOffsetWidth || contentElScrollWidth) + "px" : 'auto';
549 this.placeholderEl.style.height = contentElScrollHeight + "px";
550 var contentWrapperElOffsetHeight = this.contentWrapperEl.offsetHeight;
551 this.axis.x.isOverflowing = contentElOffsetWidth !== 0 && contentElScrollWidth > contentElOffsetWidth;
552 this.axis.y.isOverflowing = contentElScrollHeight > contentWrapperElOffsetHeight; // Set isOverflowing to false if user explicitely set hidden overflow
553
554 this.axis.x.isOverflowing = elOverflowX === 'hidden' ? false : this.axis.x.isOverflowing;
555 this.axis.y.isOverflowing = elOverflowY === 'hidden' ? false : this.axis.y.isOverflowing;
556 this.axis.x.forceVisible = this.options.forceVisible === 'x' || this.options.forceVisible === true;
557 this.axis.y.forceVisible = this.options.forceVisible === 'y' || this.options.forceVisible === true;
558 this.hideNativeScrollbar(); // Set isOverflowing to false if scrollbar is not necessary (content is shorter than offset)
559
560 var offsetForXScrollbar = this.axis.x.isOverflowing ? this.scrollbarWidth : 0;
561 var offsetForYScrollbar = this.axis.y.isOverflowing ? this.scrollbarWidth : 0;
562 this.axis.x.isOverflowing = this.axis.x.isOverflowing && contentElScrollWidth > contentWrapperElOffsetWidth - offsetForYScrollbar;
563 this.axis.y.isOverflowing = this.axis.y.isOverflowing && contentElScrollHeight > contentWrapperElOffsetHeight - offsetForXScrollbar;
564 this.axis.x.scrollbar.size = this.getScrollbarSize('x');
565 this.axis.y.scrollbar.size = this.getScrollbarSize('y');
566 this.axis.x.scrollbar.el.style.width = this.axis.x.scrollbar.size + "px";
567 this.axis.y.scrollbar.el.style.height = this.axis.y.scrollbar.size + "px";
568 this.positionScrollbar('x');
569 this.positionScrollbar('y');
570 this.toggleTrackVisibility('x');
571 this.toggleTrackVisibility('y');
572 }
573 /**
574 * Calculate scrollbar size
575 */
576 ;
577
578 _proto.getScrollbarSize = function getScrollbarSize(axis) {
579 if (axis === void 0) {
580 axis = 'y';
581 }
582
583 if (!this.axis[axis].isOverflowing) {
584 return 0;
585 }
586
587 var contentSize = this.contentEl[this.axis[axis].scrollSizeAttr];
588 var trackSize = this.axis[axis].track.el[this.axis[axis].offsetSizeAttr];
589 var scrollbarSize;
590 var scrollbarRatio = trackSize / contentSize; // Calculate new height/position of drag handle.
591
592 scrollbarSize = Math.max(~~(scrollbarRatio * trackSize), this.options.scrollbarMinSize);
593
594 if (this.options.scrollbarMaxSize) {
595 scrollbarSize = Math.min(scrollbarSize, this.options.scrollbarMaxSize);
596 }
597
598 return scrollbarSize;
599 };
600
601 _proto.positionScrollbar = function positionScrollbar(axis) {
602 if (axis === void 0) {
603 axis = 'y';
604 }
605
606 if (!this.axis[axis].isOverflowing) {
607 return;
608 }
609
610 var contentSize = this.contentWrapperEl[this.axis[axis].scrollSizeAttr];
611 var trackSize = this.axis[axis].track.el[this.axis[axis].offsetSizeAttr];
612 var hostSize = parseInt(this.elStyles[this.axis[axis].sizeAttr], 10);
613 var scrollbar = this.axis[axis].scrollbar;
614 var scrollOffset = this.contentWrapperEl[this.axis[axis].scrollOffsetAttr];
615 scrollOffset = axis === 'x' && this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollingInverted ? -scrollOffset : scrollOffset;
616 var scrollPourcent = scrollOffset / (contentSize - hostSize);
617 var handleOffset = ~~((trackSize - scrollbar.size) * scrollPourcent);
618 handleOffset = axis === 'x' && this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollbarInverted ? handleOffset + (trackSize - scrollbar.size) : handleOffset;
619 scrollbar.el.style.transform = axis === 'x' ? "translate3d(" + handleOffset + "px, 0, 0)" : "translate3d(0, " + handleOffset + "px, 0)";
620 };
621
622 _proto.toggleTrackVisibility = function toggleTrackVisibility(axis) {
623 if (axis === void 0) {
624 axis = 'y';
625 }
626
627 var track = this.axis[axis].track.el;
628 var scrollbar = this.axis[axis].scrollbar.el;
629
630 if (this.axis[axis].isOverflowing || this.axis[axis].forceVisible) {
631 track.style.visibility = 'visible';
632 this.contentWrapperEl.style[this.axis[axis].overflowAttr] = 'scroll';
633 } else {
634 track.style.visibility = 'hidden';
635 this.contentWrapperEl.style[this.axis[axis].overflowAttr] = 'hidden';
636 } // Even if forceVisible is enabled, scrollbar itself should be hidden
637
638
639 if (this.axis[axis].isOverflowing) {
640 scrollbar.style.display = 'block';
641 } else {
642 scrollbar.style.display = 'none';
643 }
644 };
645
646 _proto.hideNativeScrollbar = function hideNativeScrollbar() {
647 this.offsetEl.style[this.isRtl ? 'left' : 'right'] = this.axis.y.isOverflowing || this.axis.y.forceVisible ? "-" + this.scrollbarWidth + "px" : 0;
648 this.offsetEl.style.bottom = this.axis.x.isOverflowing || this.axis.x.forceVisible ? "-" + this.scrollbarWidth + "px" : 0;
649 }
650 /**
651 * On scroll event handling
652 */
653 ;
654
655 _proto.onMouseMoveForAxis = function onMouseMoveForAxis(axis) {
656 if (axis === void 0) {
657 axis = 'y';
658 }
659
660 this.axis[axis].track.rect = this.axis[axis].track.el.getBoundingClientRect();
661 this.axis[axis].scrollbar.rect = this.axis[axis].scrollbar.el.getBoundingClientRect();
662 var isWithinScrollbarBoundsX = this.isWithinBounds(this.axis[axis].scrollbar.rect);
663
664 if (isWithinScrollbarBoundsX) {
665 this.axis[axis].scrollbar.el.classList.add(this.classNames.hover);
666 } else {
667 this.axis[axis].scrollbar.el.classList.remove(this.classNames.hover);
668 }
669
670 if (this.isWithinBounds(this.axis[axis].track.rect)) {
671 this.showScrollbar(axis);
672 this.axis[axis].track.el.classList.add(this.classNames.hover);
673 } else {
674 this.axis[axis].track.el.classList.remove(this.classNames.hover);
675 }
676 };
677
678 _proto.onMouseLeaveForAxis = function onMouseLeaveForAxis(axis) {
679 if (axis === void 0) {
680 axis = 'y';
681 }
682
683 this.axis[axis].track.el.classList.remove(this.classNames.hover);
684 this.axis[axis].scrollbar.el.classList.remove(this.classNames.hover);
685 };
686
687 /**
688 * Show scrollbar
689 */
690 _proto.showScrollbar = function showScrollbar(axis) {
691 if (axis === void 0) {
692 axis = 'y';
693 }
694
695 var scrollbar = this.axis[axis].scrollbar.el;
696
697 if (!this.axis[axis].isVisible) {
698 scrollbar.classList.add(this.classNames.visible);
699 this.axis[axis].isVisible = true;
700 }
701
702 if (this.options.autoHide) {
703 this.hideScrollbars();
704 }
705 }
706 /**
707 * Hide Scrollbar
708 */
709 ;
710
711 /**
712 * on scrollbar handle drag movement starts
713 */
714 _proto.onDragStart = function onDragStart(e, axis) {
715 if (axis === void 0) {
716 axis = 'y';
717 }
718
719 var elDocument = getElementDocument(this.el);
720 var elWindow = getElementWindow(this.el);
721 var scrollbar = this.axis[axis].scrollbar; // Measure how far the user's mouse is from the top of the scrollbar drag handle.
722
723 var eventOffset = axis === 'y' ? e.pageY : e.pageX;
724 this.axis[axis].dragOffset = eventOffset - scrollbar.rect[this.axis[axis].offsetAttr];
725 this.draggedAxis = axis;
726 this.el.classList.add(this.classNames.dragging);
727 elDocument.addEventListener('mousemove', this.drag, true);
728 elDocument.addEventListener('mouseup', this.onEndDrag, true);
729
730 if (this.removePreventClickId === null) {
731 elDocument.addEventListener('click', this.preventClick, true);
732 elDocument.addEventListener('dblclick', this.preventClick, true);
733 } else {
734 elWindow.clearTimeout(this.removePreventClickId);
735 this.removePreventClickId = null;
736 }
737 }
738 /**
739 * Drag scrollbar handle
740 */
741 ;
742
743 _proto.onTrackClick = function onTrackClick(e, axis) {
744 var _this4 = this;
745
746 if (axis === void 0) {
747 axis = 'y';
748 }
749
750 if (!this.options.clickOnTrack) return;
751 var elWindow = getElementWindow(this.el);
752 this.axis[axis].scrollbar.rect = this.axis[axis].scrollbar.el.getBoundingClientRect();
753 var scrollbar = this.axis[axis].scrollbar;
754 var scrollbarOffset = scrollbar.rect[this.axis[axis].offsetAttr];
755 var hostSize = parseInt(this.elStyles[this.axis[axis].sizeAttr], 10);
756 var scrolled = this.contentWrapperEl[this.axis[axis].scrollOffsetAttr];
757 var t = axis === 'y' ? this.mouseY - scrollbarOffset : this.mouseX - scrollbarOffset;
758 var dir = t < 0 ? -1 : 1;
759 var scrollSize = dir === -1 ? scrolled - hostSize : scrolled + hostSize;
760 var speed = 40;
761
762 var scrollTo = function scrollTo() {
763 if (dir === -1) {
764 if (scrolled > scrollSize) {
765 var _this4$contentWrapper;
766
767 scrolled -= speed;
768
769 _this4.contentWrapperEl.scrollTo((_this4$contentWrapper = {}, _this4$contentWrapper[_this4.axis[axis].offsetAttr] = scrolled, _this4$contentWrapper));
770
771 elWindow.requestAnimationFrame(scrollTo);
772 }
773 } else {
774 if (scrolled < scrollSize) {
775 var _this4$contentWrapper2;
776
777 scrolled += speed;
778
779 _this4.contentWrapperEl.scrollTo((_this4$contentWrapper2 = {}, _this4$contentWrapper2[_this4.axis[axis].offsetAttr] = scrolled, _this4$contentWrapper2));
780
781 elWindow.requestAnimationFrame(scrollTo);
782 }
783 }
784 };
785
786 scrollTo();
787 }
788 /**
789 * Getter for content element
790 */
791 ;
792
793 _proto.getContentElement = function getContentElement() {
794 return this.contentEl;
795 }
796 /**
797 * Getter for original scrolling element
798 */
799 ;
800
801 _proto.getScrollElement = function getScrollElement() {
802 return this.contentWrapperEl;
803 };
804
805 _proto.getScrollbarWidth = function getScrollbarWidth() {
806 // Try/catch for FF 56 throwing on undefined computedStyles
807 try {
808 // Detect browsers supporting CSS scrollbar styling and do not calculate
809 if (getComputedStyle(this.contentWrapperEl, '::-webkit-scrollbar').display === 'none' || 'scrollbarWidth' in document.documentElement.style || '-ms-overflow-style' in document.documentElement.style) {
810 return 0;
811 } else {
812 return scrollbarWidth();
813 }
814 } catch (e) {
815 return scrollbarWidth();
816 }
817 };
818
819 _proto.removeListeners = function removeListeners() {
820 var _this5 = this;
821
822 var elWindow = getElementWindow(this.el); // Event listeners
823
824 if (this.options.autoHide) {
825 this.el.removeEventListener('mouseenter', this.onMouseEnter);
826 }
827
828 ['mousedown', 'click', 'dblclick'].forEach(function (e) {
829 _this5.el.removeEventListener(e, _this5.onPointerEvent, true);
830 });
831 ['touchstart', 'touchend', 'touchmove'].forEach(function (e) {
832 _this5.el.removeEventListener(e, _this5.onPointerEvent, {
833 capture: true,
834 passive: true
835 });
836 });
837 this.el.removeEventListener('mousemove', this.onMouseMove);
838 this.el.removeEventListener('mouseleave', this.onMouseLeave);
839 this.contentWrapperEl.removeEventListener('scroll', this.onScroll);
840 elWindow.removeEventListener('resize', this.onWindowResize);
841 this.mutationObserver.disconnect();
842 this.resizeObserver.disconnect(); // Cancel all debounced functions
843
844 this.recalculate.cancel();
845 this.onMouseMove.cancel();
846 this.hideScrollbars.cancel();
847 this.onWindowResize.cancel();
848 }
849 /**
850 * UnMount mutation observer and delete SimpleBar instance from DOM element
851 */
852 ;
853
854 _proto.unMount = function unMount() {
855 this.removeListeners();
856 SimpleBar.instances.delete(this.el);
857 }
858 /**
859 * Check if mouse is within bounds
860 */
861 ;
862
863 _proto.isWithinBounds = function isWithinBounds(bbox) {
864 return this.mouseX >= bbox.left && this.mouseX <= bbox.left + bbox.width && this.mouseY >= bbox.top && this.mouseY <= bbox.top + bbox.height;
865 }
866 /**
867 * Find element children matches query
868 */
869 ;
870
871 _proto.findChild = function findChild(el, query) {
872 var matches = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
873 return Array.prototype.filter.call(el.children, function (child) {
874 return matches.call(child, query);
875 })[0];
876 };
877
878 return SimpleBar;
879}();
880
881SimpleBar.defaultOptions = {
882 autoHide: true,
883 forceVisible: false,
884 clickOnTrack: true,
885 classNames: {
886 contentEl: 'simplebar-content',
887 contentWrapper: 'simplebar-content-wrapper',
888 offset: 'simplebar-offset',
889 mask: 'simplebar-mask',
890 wrapper: 'simplebar-wrapper',
891 placeholder: 'simplebar-placeholder',
892 scrollbar: 'simplebar-scrollbar',
893 track: 'simplebar-track',
894 heightAutoObserverWrapperEl: 'simplebar-height-auto-observer-wrapper',
895 heightAutoObserverEl: 'simplebar-height-auto-observer',
896 visible: 'simplebar-visible',
897 horizontal: 'simplebar-horizontal',
898 vertical: 'simplebar-vertical',
899 hover: 'simplebar-hover',
900 dragging: 'simplebar-dragging'
901 },
902 scrollbarMinSize: 25,
903 scrollbarMaxSize: 0,
904 timeout: 1000
905};
906SimpleBar.instances = new WeakMap();
907
908SimpleBar.initDOMLoadedElements = function () {
909 document.removeEventListener('DOMContentLoaded', this.initDOMLoadedElements);
910 window.removeEventListener('load', this.initDOMLoadedElements);
911 Array.prototype.forEach.call(document.querySelectorAll('[data-simplebar]'), function (el) {
912 if (el.getAttribute('data-simplebar') !== 'init' && !SimpleBar.instances.has(el)) new SimpleBar(el, getOptions(el.attributes));
913 });
914};
915
916SimpleBar.removeObserver = function () {
917 this.globalObserver.disconnect();
918};
919
920SimpleBar.initHtmlApi = function () {
921 this.initDOMLoadedElements = this.initDOMLoadedElements.bind(this); // MutationObserver is IE11+
922
923 if (typeof MutationObserver !== 'undefined') {
924 // Mutation observer to observe dynamically added elements
925 this.globalObserver = new MutationObserver(SimpleBar.handleMutations);
926 this.globalObserver.observe(document, {
927 childList: true,
928 subtree: true
929 });
930 } // Taken from jQuery `ready` function
931 // Instantiate elements already present on the page
932
933
934 if (document.readyState === 'complete' || document.readyState !== 'loading' && !document.documentElement.doScroll) {
935 // Handle it asynchronously to allow scripts the opportunity to delay init
936 window.setTimeout(this.initDOMLoadedElements);
937 } else {
938 document.addEventListener('DOMContentLoaded', this.initDOMLoadedElements);
939 window.addEventListener('load', this.initDOMLoadedElements);
940 }
941};
942
943SimpleBar.handleMutations = function (mutations) {
944 mutations.forEach(function (mutation) {
945 Array.prototype.forEach.call(mutation.addedNodes, function (addedNode) {
946 if (addedNode.nodeType === 1) {
947 if (addedNode.hasAttribute('data-simplebar')) {
948 !SimpleBar.instances.has(addedNode) && new SimpleBar(addedNode, getOptions(addedNode.attributes));
949 } else {
950 Array.prototype.forEach.call(addedNode.querySelectorAll('[data-simplebar]'), function (el) {
951 if (el.getAttribute('data-simplebar') !== 'init' && !SimpleBar.instances.has(el)) new SimpleBar(el, getOptions(el.attributes));
952 });
953 }
954 }
955 });
956 Array.prototype.forEach.call(mutation.removedNodes, function (removedNode) {
957 if (removedNode.nodeType === 1) {
958 if (removedNode.hasAttribute('[data-simplebar="init"]')) {
959 SimpleBar.instances.has(removedNode) && SimpleBar.instances.get(removedNode).unMount();
960 } else {
961 Array.prototype.forEach.call(removedNode.querySelectorAll('[data-simplebar="init"]'), function (el) {
962 SimpleBar.instances.has(el) && SimpleBar.instances.get(el).unMount();
963 });
964 }
965 }
966 });
967 });
968};
969
970SimpleBar.getOptions = getOptions;
971/**
972 * HTML API
973 * Called only in a browser env.
974 */
975
976if (canUseDOM) {
977 SimpleBar.initHtmlApi();
978}
979
980export default SimpleBar;
981//# sourceMappingURL=simplebar.esm.js.map