initial commit
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Jonathan Nicol
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,367 @@
|
||||
# SimpleBar [![npm package][npm-badge]][npm] ![size-badge]
|
||||
|
||||
:warning: SimpleBar v5 is here! There are some **breaking changes!** Make sure to check out [the changelog](https://github.com/Grsmto/simplebar/releases) before updating.
|
||||
|
||||
SimpleBar is a plugin that tries to solve a long time problem: how to get custom scrollbars for your web-app while keeping a good user experience?
|
||||
SimpleBar **does NOT implement a custom scroll behaviour**. It keeps the **native** `overflow: auto` scroll and **only** replace the scrollbar visual appearance.
|
||||
|
||||
SimpleBar is meant to be as easy to use as possible and lightweight. If you want something more advanced I recommend [KingSora](https://github.com/KingSora) 's [Overlay Scrollbars](https://kingsora.github.io/OverlayScrollbars/).
|
||||
|
||||
### Installation
|
||||
|
||||
**- Via npm**
|
||||
`npm install simplebar --save`
|
||||
|
||||
**- Via Yarn**
|
||||
`yarn add simplebar`
|
||||
|
||||
**- Via `<script>` tag**
|
||||
|
||||
```html
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/simplebar@latest/dist/simplebar.css"
|
||||
/>
|
||||
<script src="https://unpkg.com/simplebar@latest/dist/simplebar.min.js"></script>
|
||||
<!-- or -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.css"
|
||||
/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.min.js"></script>
|
||||
```
|
||||
|
||||
note: you should replace `@latest` to the latest version (ex `@5.3.3`), if you want to lock to a specific version.
|
||||
You can find the full list of modules available [there](https://unpkg.com/simplebar@latest/dist/).
|
||||
|
||||
### Usage
|
||||
|
||||
Check out the [React](https://github.com/Grsmto/simplebar/blob/master/examples/react/src/App.js) and [Vue](https://github.com/Grsmto/simplebar/blob/master/examples/vue/src/App.vue) examples.
|
||||
|
||||
If you are using Gatsby, please see [#345](https://github.com/Grsmto/simplebar/issues/345).
|
||||
|
||||
If you are using a module loader (like Webpack) you first need to load SimpleBar:
|
||||
|
||||
```js
|
||||
import 'simplebar'; // or "import SimpleBar from 'simplebar';" if you want to use it manually.
|
||||
import 'simplebar/dist/simplebar.css';
|
||||
```
|
||||
|
||||
Set `data-simplebar` on the element you want your custom scrollbar. You're done.
|
||||
|
||||
```html
|
||||
<div data-simplebar></div>
|
||||
```
|
||||
|
||||
**Don't forget to import both css and js in your project!**
|
||||
|
||||
### Noscript support
|
||||
|
||||
To make sure your elements are scrollable when JavaScript is disabled, it's important to include this snippet in your `<head>` to reset scrolling:
|
||||
|
||||
```js
|
||||
<noscript>
|
||||
<style>
|
||||
/**
|
||||
* Reinstate scrolling for non-JS clients
|
||||
*/
|
||||
.simplebar-content-wrapper {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
```
|
||||
|
||||
### :warning: Warning!
|
||||
|
||||
SimpleBar is **not intended to be used on the `body` element!** I don't recommend wrapping your entire web page inside a custom scroll as it will often badly affect the user experience (slower scroll performance compared to the native body scroll, no native scroll behaviours like click on track, etc.). Do it at your own risk! SimpleBar is meant to improve the experience of **internal web page scrolling**; such as a chat box or a small scrolling area. **Please read the [caveats](#5-caveats) section first to be aware of the limitations!**
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
If you are experiencing issues when setting up SimpleBar, it is most likely because your styles are clashing with SimpleBar ones. Make sure the element you are setting SimpleBar on does not override any SimpleBar css properties! **We recommend to not style that element at all and use an inner element instead.**
|
||||
|
||||
### Sponsors
|
||||
|
||||
Thanks to BrowserStack for sponsoring open source projects and letting us test SimpleBar for free.
|
||||
<a href="https://www.browserstack.com" target="_blank">
|
||||
<img src="https://user-images.githubusercontent.com/15015324/45184727-368fbf80-b1fe-11e8-8827-08dbc80b0fb1.png" width="200">
|
||||
</a>
|
||||
|
||||
---
|
||||
|
||||
1. [Documentation](#1-documentation)
|
||||
2. [Browsers support](#2-browsers-support)
|
||||
3. [Demo](#3-demo)
|
||||
4. [How it works](#4-how-it-works)
|
||||
5. [Caveats](#5-caveats)
|
||||
6. [Changelog](#6-changelog)
|
||||
7. [Credits](#7-credits)
|
||||
|
||||
## 1. Documentation
|
||||
|
||||
### Other usages
|
||||
|
||||
You can start SimpleBar manually if you need to:
|
||||
|
||||
```js
|
||||
new SimpleBar(document.getElementById('myElement'));
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```js
|
||||
Array.prototype.forEach.call(
|
||||
document.querySelectorAll('.myElements'),
|
||||
el => new SimpleBar()
|
||||
);
|
||||
```
|
||||
|
||||
If you want to use jQuery:
|
||||
|
||||
```js
|
||||
new SimpleBar($('#myElement')[0]);
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```js
|
||||
$('.myElements').each(element, new SimpleBar());
|
||||
```
|
||||
|
||||
### Styling
|
||||
|
||||
The default styling is applied with CSS. There is no "built-in" way to style the scrollbar, you just need to override the default CSS.
|
||||
|
||||
Ex, to change the color of the scrollbar:
|
||||
```css
|
||||
.simplebar-scrollbar::before {
|
||||
background-color: red;
|
||||
}
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
Options can be applied to the plugin during initialization:
|
||||
|
||||
```js
|
||||
new SimpleBar(document.getElementById('myElement'), {
|
||||
option1: value1,
|
||||
option2: value2
|
||||
});
|
||||
```
|
||||
|
||||
or using data-attributes:
|
||||
|
||||
```html
|
||||
<div data-simplebar data-simplebar-auto-hide="false"></div>
|
||||
```
|
||||
|
||||
Available options are:
|
||||
|
||||
#### autoHide
|
||||
|
||||
By default SimpleBar automatically hides the scrollbar if the user is not scrolling (it emulates Mac OSX Lion's scrollbar). You can make the scrollbar always visible by setting the `autoHide` option to `false`:
|
||||
|
||||
```js
|
||||
new SimpleBar(document.getElementById('myElement'), { autoHide: false });
|
||||
```
|
||||
|
||||
Default value is `true`.
|
||||
|
||||
You can also control the animation via CSS as it's a simple CSS opacity transition.
|
||||
|
||||
#### classNames
|
||||
|
||||
It is possible to change the default class names that SimpleBar uses. To get your own styles to work refer to `simplebar.css` to get an idea how to setup your css.
|
||||
|
||||
- `content` represents the wrapper for the content being scrolled.
|
||||
- `scrollContent` represents the container containing the elements being scrolled.
|
||||
- `scrollbar` defines the style of the scrollbar with which the user can interact to scroll the content.
|
||||
- `track` styles the area surrounding the `scrollbar`.
|
||||
|
||||
```js
|
||||
classNames: {
|
||||
// defaults
|
||||
content: 'simplebar-content',
|
||||
scrollContent: 'simplebar-scroll-content',
|
||||
scrollbar: 'simplebar-scrollbar',
|
||||
track: 'simplebar-track'
|
||||
}
|
||||
```
|
||||
|
||||
#### forceVisible
|
||||
|
||||
You can force the track to be visible (same behaviour as `overflow: scroll`) using the `forceVisible` option:
|
||||
|
||||
```
|
||||
forceVisible: true|'x'|'y' (default to `false`)
|
||||
```
|
||||
|
||||
By default, SimpleBar behave like `overflow: auto`.
|
||||
|
||||
### ariaLabel
|
||||
|
||||
You can set custom aria-label attribute for users with screen reader using the `ariaLabel` option:
|
||||
|
||||
```
|
||||
ariaLabel: 'Your label' (default to `scrollable content`)
|
||||
```
|
||||
|
||||
#### direction (RTL support)
|
||||
|
||||
You can activate RTL support by passing the `direction` option:
|
||||
|
||||
```
|
||||
direction: 'rtl' (default to `ltr`)
|
||||
```
|
||||
|
||||
You will need both `data-simplebar-direction='rtl'` and a css rule with `direction: rtl`.
|
||||
|
||||
#### timeout
|
||||
|
||||
Define the delay until the scrollbar hides. Has no effect if `autoHide` is `false`.
|
||||
|
||||
Default value is `1000`.
|
||||
|
||||
#### clickOnTrack
|
||||
|
||||
Controls the click on track behaviour.
|
||||
|
||||
Default to `true`.
|
||||
|
||||
#### scrollbarMinSize / scrollbarMaxSize
|
||||
|
||||
Controls the min and max size of the scrollbar in `px`.
|
||||
|
||||
Default for `scrollbarMinSize` is `25`.
|
||||
Default for `scrollbarMaxSize` is `0` (no max size).
|
||||
|
||||
### Apply scroll vertically only
|
||||
|
||||
Simply define in css `overflow-x: hidden` on your element.
|
||||
|
||||
### Notifying the plugin of content changes
|
||||
|
||||
#### Note: you shouldn't need to use these functions as SimpleBar takes care of that automatically. This is for advanced usage only.
|
||||
|
||||
If later on you dynamically modify your content, for instance changing its height or width, or adding or removing content, you should recalculate the scrollbars like so:
|
||||
|
||||
```js
|
||||
const simpleBar = new SimpleBar(document.getElementById('myElement'));
|
||||
simpleBar.recalculate();
|
||||
```
|
||||
|
||||
### Trigger programmatical scrolling
|
||||
|
||||
If you want to access to the original scroll element, you can retrieve it via a getter:
|
||||
|
||||
```js
|
||||
const simpleBar = new SimpleBar(document.getElementById('myElement'));
|
||||
simpleBar.getScrollElement();
|
||||
```
|
||||
|
||||
### Subscribe to `scroll` event
|
||||
|
||||
You can subscribe to the `scroll` event, just like you do with native scrolling elements:
|
||||
|
||||
```js
|
||||
const simpleBar = new SimpleBar(document.getElementById('myElement'));
|
||||
simpleBar.getScrollElement().addEventListener('scroll', function(...));
|
||||
```
|
||||
|
||||
### Add content dynamically
|
||||
|
||||
You can retrieve the element containing data like this:
|
||||
|
||||
```js
|
||||
const simpleBar = new SimpleBar(document.getElementById('myElement'));
|
||||
simpleBar.getContentElement();
|
||||
```
|
||||
|
||||
### Disable Mutation Observer (core package only)
|
||||
|
||||
```js
|
||||
SimpleBar.removeObserver();
|
||||
```
|
||||
|
||||
### Retrieve SimpleBar instance from data-simplebar nodes
|
||||
|
||||
```js
|
||||
SimpleBar.instances.get(document.querySelector('[data-simplebar]']))
|
||||
```
|
||||
|
||||
### Unmount/destroy
|
||||
|
||||
```js
|
||||
const simpleBar = new SimpleBar(document.getElementById('myElement'));
|
||||
|
||||
simpleBar.unMount()
|
||||
```
|
||||
|
||||
:warning: **Calling this function will not restore the default scrollbar!**
|
||||
|
||||
A common usecase is to only want SimpleBar on desktop/wider screens, but instead of trying to mount/unmount the plugin based on screen size, you should instead simply never mount it in the first place.
|
||||
|
||||
For example if you want it only on desktop you can first test for screen size using `matchMedia`:
|
||||
|
||||
```js
|
||||
if (window.matchMedia('(min-width: 600px)').matches) {
|
||||
new SimpleBar(..)
|
||||
}
|
||||
```
|
||||
|
||||
### Non-JS fallback
|
||||
|
||||
SimpleBar hides the browser's default scrollbars, which obviously is undesirable if the user has JavaScript disabled. To restore the browser's scrollbars you can include the following `noscript` element in your document's `head`:
|
||||
|
||||
```html
|
||||
<noscript>
|
||||
<style>
|
||||
[data-simplebar] {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
```
|
||||
|
||||
## 2. Browsers support
|
||||
|
||||
SimpleBar has been tested on the following browsers: Chrome, Firefox, Safari, Edge, IE11.
|
||||
|
||||
Notice: IE10 doesn't support `MutationObserver` so you will still need to instantiate SimpleBar manually and call `recalculate()` as needed (or you can just use a polyfill for `MutationObserver`).
|
||||
|
||||
If you want to apply SimpleBar on an SVG element on IE11, you will need a [polyfill for `classList`](https://github.com/eligrey/classList.js/blob/master/classList.js).
|
||||
|
||||
IE9 is not supported anymore (because we use `translate3d` to position the scrollbar) so please use SimpleBar v1 if you really need it.
|
||||
|
||||
## 3. Demo
|
||||
|
||||
https://grsmto.github.io/simplebar/
|
||||
|
||||
## 4. How it works
|
||||
|
||||
SimpleBar only does one thing: replace the browser's default scrollbars with a custom CSS-styled scrollbar without sacrificing performance. Unlike most other plugins, SimpleBar doesn't mimic scroll behaviour with Javascript, which typically causes jank and strange scrolling behaviour. You keep the awesomeness of native scrolling… with a custom scrollbar!
|
||||
Design your scrollbar how you like, with CSS, across all browsers.
|
||||
|
||||
For the most part SimpleBar uses the browser's native scrolling functionality, but replaces the conventional scrollbar with a custom CSS-styled scrollbar. The plugin listens for scroll events and redraws the custom scrollbar accordingly.
|
||||
|
||||
Key to this technique is hiding the native browser scrollbar. The scrollable element is made slightly wider/taller than its containing element, effectively hiding the scrollbar from view.
|
||||
|
||||
## 5. Caveats
|
||||
|
||||
- SimpleBar can't be used on the `<body>`, `<textarea>`, `<table>` or `<select>` elements. `<iframe>` should be working (depends of your usecase). If you are looking to support these, I suggest taking a look at [OverLayScrollbars](https://kingsora.github.io/OverlayScrollbars).
|
||||
- SimpleBar doesn't currently support `overflow: visible`. Which means any children of your scrolling div will be clipped (like with `overflow: hidden`).
|
||||
|
||||
Please take a look at [this comparison table](https://kingsora.github.io/OverlayScrollbars/#!faq) to see what SimpleBar does compare to others.
|
||||
|
||||
### Community plugins
|
||||
|
||||
**Ruby On Rails**
|
||||
To include SimpleBar in the Ruby On Rails asset pipeline, use the [simplebar-rails](https://github.com/thutterer/simplebar-rails) gem.
|
||||
|
||||
**Ember.js**
|
||||
To use SimpleBar with the Ember.js framework, use the [ember-simplebars](https://github.com/fpauser/ember-simplebar) addon.
|
||||
|
||||
[npm-badge]: https://img.shields.io/npm/v/simplebar.svg?style=flat-square
|
||||
[npm]: https://www.npmjs.org/package/simplebar
|
||||
[size-badge]: http://img.badgesize.io/Grsmto/simplebar/master/packages/simplebar/src/simplebar.js?compression=gzip&&style=flat-square
|
||||
@@ -0,0 +1,897 @@
|
||||
/**
|
||||
* SimpleBar.js - v5.3.6
|
||||
* Scrollbars, simpler.
|
||||
* https://grsmto.github.io/simplebar/
|
||||
*
|
||||
* Made by Adrien Denat from a fork by Jonathan Nicol
|
||||
* Under MIT License
|
||||
*/
|
||||
|
||||
import 'core-js/modules/es.array.filter';
|
||||
import 'core-js/modules/es.array.for-each';
|
||||
import 'core-js/modules/es.array.iterator';
|
||||
import 'core-js/modules/es.object.assign';
|
||||
import 'core-js/modules/es.object.to-string';
|
||||
import 'core-js/modules/es.parse-int';
|
||||
import 'core-js/modules/es.string.iterator';
|
||||
import 'core-js/modules/es.weak-map';
|
||||
import 'core-js/modules/web.dom-collections.iterator';
|
||||
import throttle from 'lodash.throttle';
|
||||
import debounce from 'lodash.debounce';
|
||||
import memoize from 'lodash.memoize';
|
||||
import { ResizeObserver } from '@juggle/resize-observer';
|
||||
import canUseDOM from 'can-use-dom';
|
||||
import 'core-js/modules/es.array.reduce';
|
||||
import 'core-js/modules/es.function.name';
|
||||
import 'core-js/modules/es.regexp.exec';
|
||||
import 'core-js/modules/es.string.match';
|
||||
import 'core-js/modules/es.string.replace';
|
||||
|
||||
function getElementWindow(element) {
|
||||
if (!element || !element.ownerDocument || !element.ownerDocument.defaultView) {
|
||||
return window;
|
||||
}
|
||||
|
||||
return element.ownerDocument.defaultView;
|
||||
}
|
||||
function getElementDocument(element) {
|
||||
if (!element || !element.ownerDocument) {
|
||||
return document;
|
||||
}
|
||||
|
||||
return element.ownerDocument;
|
||||
}
|
||||
|
||||
var cachedScrollbarWidth = null;
|
||||
var cachedDevicePixelRatio = null;
|
||||
|
||||
if (canUseDOM) {
|
||||
window.addEventListener('resize', function () {
|
||||
if (cachedDevicePixelRatio !== window.devicePixelRatio) {
|
||||
cachedDevicePixelRatio = window.devicePixelRatio;
|
||||
cachedScrollbarWidth = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function scrollbarWidth(el) {
|
||||
if (cachedScrollbarWidth === null) {
|
||||
var document = getElementDocument(el);
|
||||
|
||||
if (typeof document === 'undefined') {
|
||||
cachedScrollbarWidth = 0;
|
||||
return cachedScrollbarWidth;
|
||||
}
|
||||
|
||||
var body = document.body;
|
||||
var box = document.createElement('div');
|
||||
box.classList.add('simplebar-hide-scrollbar');
|
||||
body.appendChild(box);
|
||||
var width = box.getBoundingClientRect().right;
|
||||
body.removeChild(box);
|
||||
cachedScrollbarWidth = width;
|
||||
}
|
||||
|
||||
return cachedScrollbarWidth;
|
||||
}
|
||||
|
||||
var SimpleBar =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
function SimpleBar(element, options) {
|
||||
var _this = this;
|
||||
|
||||
this.onScroll = function () {
|
||||
var elWindow = getElementWindow(_this.el);
|
||||
|
||||
if (!_this.scrollXTicking) {
|
||||
elWindow.requestAnimationFrame(_this.scrollX);
|
||||
_this.scrollXTicking = true;
|
||||
}
|
||||
|
||||
if (!_this.scrollYTicking) {
|
||||
elWindow.requestAnimationFrame(_this.scrollY);
|
||||
_this.scrollYTicking = true;
|
||||
}
|
||||
};
|
||||
|
||||
this.scrollX = function () {
|
||||
if (_this.axis.x.isOverflowing) {
|
||||
_this.showScrollbar('x');
|
||||
|
||||
_this.positionScrollbar('x');
|
||||
}
|
||||
|
||||
_this.scrollXTicking = false;
|
||||
};
|
||||
|
||||
this.scrollY = function () {
|
||||
if (_this.axis.y.isOverflowing) {
|
||||
_this.showScrollbar('y');
|
||||
|
||||
_this.positionScrollbar('y');
|
||||
}
|
||||
|
||||
_this.scrollYTicking = false;
|
||||
};
|
||||
|
||||
this.onMouseEnter = function () {
|
||||
_this.showScrollbar('x');
|
||||
|
||||
_this.showScrollbar('y');
|
||||
};
|
||||
|
||||
this.onMouseMove = function (e) {
|
||||
_this.mouseX = e.clientX;
|
||||
_this.mouseY = e.clientY;
|
||||
|
||||
if (_this.axis.x.isOverflowing || _this.axis.x.forceVisible) {
|
||||
_this.onMouseMoveForAxis('x');
|
||||
}
|
||||
|
||||
if (_this.axis.y.isOverflowing || _this.axis.y.forceVisible) {
|
||||
_this.onMouseMoveForAxis('y');
|
||||
}
|
||||
};
|
||||
|
||||
this.onMouseLeave = function () {
|
||||
_this.onMouseMove.cancel();
|
||||
|
||||
if (_this.axis.x.isOverflowing || _this.axis.x.forceVisible) {
|
||||
_this.onMouseLeaveForAxis('x');
|
||||
}
|
||||
|
||||
if (_this.axis.y.isOverflowing || _this.axis.y.forceVisible) {
|
||||
_this.onMouseLeaveForAxis('y');
|
||||
}
|
||||
|
||||
_this.mouseX = -1;
|
||||
_this.mouseY = -1;
|
||||
};
|
||||
|
||||
this.onWindowResize = function () {
|
||||
// Recalculate scrollbarWidth in case it's a zoom
|
||||
_this.scrollbarWidth = _this.getScrollbarWidth();
|
||||
|
||||
_this.hideNativeScrollbar();
|
||||
};
|
||||
|
||||
this.hideScrollbars = function () {
|
||||
_this.axis.x.track.rect = _this.axis.x.track.el.getBoundingClientRect();
|
||||
_this.axis.y.track.rect = _this.axis.y.track.el.getBoundingClientRect();
|
||||
|
||||
if (!_this.isWithinBounds(_this.axis.y.track.rect)) {
|
||||
_this.axis.y.scrollbar.el.classList.remove(_this.classNames.visible);
|
||||
|
||||
_this.axis.y.isVisible = false;
|
||||
}
|
||||
|
||||
if (!_this.isWithinBounds(_this.axis.x.track.rect)) {
|
||||
_this.axis.x.scrollbar.el.classList.remove(_this.classNames.visible);
|
||||
|
||||
_this.axis.x.isVisible = false;
|
||||
}
|
||||
};
|
||||
|
||||
this.onPointerEvent = function (e) {
|
||||
var isWithinTrackXBounds, isWithinTrackYBounds;
|
||||
_this.axis.x.track.rect = _this.axis.x.track.el.getBoundingClientRect();
|
||||
_this.axis.y.track.rect = _this.axis.y.track.el.getBoundingClientRect();
|
||||
|
||||
if (_this.axis.x.isOverflowing || _this.axis.x.forceVisible) {
|
||||
isWithinTrackXBounds = _this.isWithinBounds(_this.axis.x.track.rect);
|
||||
}
|
||||
|
||||
if (_this.axis.y.isOverflowing || _this.axis.y.forceVisible) {
|
||||
isWithinTrackYBounds = _this.isWithinBounds(_this.axis.y.track.rect);
|
||||
} // If any pointer event is called on the scrollbar
|
||||
|
||||
|
||||
if (isWithinTrackXBounds || isWithinTrackYBounds) {
|
||||
// Preventing the event's default action stops text being
|
||||
// selectable during the drag.
|
||||
e.preventDefault(); // Prevent event leaking
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
if (e.type === 'mousedown') {
|
||||
if (isWithinTrackXBounds) {
|
||||
_this.axis.x.scrollbar.rect = _this.axis.x.scrollbar.el.getBoundingClientRect();
|
||||
|
||||
if (_this.isWithinBounds(_this.axis.x.scrollbar.rect)) {
|
||||
_this.onDragStart(e, 'x');
|
||||
} else {
|
||||
_this.onTrackClick(e, 'x');
|
||||
}
|
||||
}
|
||||
|
||||
if (isWithinTrackYBounds) {
|
||||
_this.axis.y.scrollbar.rect = _this.axis.y.scrollbar.el.getBoundingClientRect();
|
||||
|
||||
if (_this.isWithinBounds(_this.axis.y.scrollbar.rect)) {
|
||||
_this.onDragStart(e, 'y');
|
||||
} else {
|
||||
_this.onTrackClick(e, 'y');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.drag = function (e) {
|
||||
var eventOffset;
|
||||
var track = _this.axis[_this.draggedAxis].track;
|
||||
var trackSize = track.rect[_this.axis[_this.draggedAxis].sizeAttr];
|
||||
var scrollbar = _this.axis[_this.draggedAxis].scrollbar;
|
||||
var contentSize = _this.contentWrapperEl[_this.axis[_this.draggedAxis].scrollSizeAttr];
|
||||
var hostSize = parseInt(_this.elStyles[_this.axis[_this.draggedAxis].sizeAttr], 10);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (_this.draggedAxis === 'y') {
|
||||
eventOffset = e.pageY;
|
||||
} else {
|
||||
eventOffset = e.pageX;
|
||||
} // Calculate how far the user's mouse is from the top/left of the scrollbar (minus the dragOffset).
|
||||
|
||||
|
||||
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.
|
||||
|
||||
var dragPerc = dragPos / (trackSize - scrollbar.size); // Scroll the content by the same percentage.
|
||||
|
||||
var scrollPos = dragPerc * (contentSize - hostSize); // Fix browsers inconsistency on RTL
|
||||
|
||||
if (_this.draggedAxis === 'x') {
|
||||
scrollPos = _this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollbarInverted ? scrollPos - (trackSize + scrollbar.size) : scrollPos;
|
||||
scrollPos = _this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollingInverted ? -scrollPos : scrollPos;
|
||||
}
|
||||
|
||||
_this.contentWrapperEl[_this.axis[_this.draggedAxis].scrollOffsetAttr] = scrollPos;
|
||||
};
|
||||
|
||||
this.onEndDrag = function (e) {
|
||||
var elDocument = getElementDocument(_this.el);
|
||||
var elWindow = getElementWindow(_this.el);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
_this.el.classList.remove(_this.classNames.dragging);
|
||||
|
||||
elDocument.removeEventListener('mousemove', _this.drag, true);
|
||||
elDocument.removeEventListener('mouseup', _this.onEndDrag, true);
|
||||
_this.removePreventClickId = elWindow.setTimeout(function () {
|
||||
// Remove these asynchronously so we still suppress click events
|
||||
// generated simultaneously with mouseup.
|
||||
elDocument.removeEventListener('click', _this.preventClick, true);
|
||||
elDocument.removeEventListener('dblclick', _this.preventClick, true);
|
||||
_this.removePreventClickId = null;
|
||||
});
|
||||
};
|
||||
|
||||
this.preventClick = function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
this.el = element;
|
||||
this.minScrollbarWidth = 20;
|
||||
this.options = Object.assign({}, SimpleBar.defaultOptions, {}, options);
|
||||
this.classNames = Object.assign({}, SimpleBar.defaultOptions.classNames, {}, this.options.classNames);
|
||||
this.axis = {
|
||||
x: {
|
||||
scrollOffsetAttr: 'scrollLeft',
|
||||
sizeAttr: 'width',
|
||||
scrollSizeAttr: 'scrollWidth',
|
||||
offsetSizeAttr: 'offsetWidth',
|
||||
offsetAttr: 'left',
|
||||
overflowAttr: 'overflowX',
|
||||
dragOffset: 0,
|
||||
isOverflowing: true,
|
||||
isVisible: false,
|
||||
forceVisible: false,
|
||||
track: {},
|
||||
scrollbar: {}
|
||||
},
|
||||
y: {
|
||||
scrollOffsetAttr: 'scrollTop',
|
||||
sizeAttr: 'height',
|
||||
scrollSizeAttr: 'scrollHeight',
|
||||
offsetSizeAttr: 'offsetHeight',
|
||||
offsetAttr: 'top',
|
||||
overflowAttr: 'overflowY',
|
||||
dragOffset: 0,
|
||||
isOverflowing: true,
|
||||
isVisible: false,
|
||||
forceVisible: false,
|
||||
track: {},
|
||||
scrollbar: {}
|
||||
}
|
||||
};
|
||||
this.removePreventClickId = null; // Don't re-instantiate over an existing one
|
||||
|
||||
if (SimpleBar.instances.has(this.el)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.recalculate = throttle(this.recalculate.bind(this), 64);
|
||||
this.onMouseMove = throttle(this.onMouseMove.bind(this), 64);
|
||||
this.hideScrollbars = debounce(this.hideScrollbars.bind(this), this.options.timeout);
|
||||
this.onWindowResize = debounce(this.onWindowResize.bind(this), 64, {
|
||||
leading: true
|
||||
});
|
||||
SimpleBar.getRtlHelpers = memoize(SimpleBar.getRtlHelpers);
|
||||
this.init();
|
||||
}
|
||||
/**
|
||||
* Static properties
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper to fix browsers inconsistency on RTL:
|
||||
* - Firefox inverts the scrollbar initial position
|
||||
* - IE11 inverts both scrollbar position and scrolling offset
|
||||
* Directly inspired by @KingSora's OverlayScrollbars https://github.com/KingSora/OverlayScrollbars/blob/master/js/OverlayScrollbars.js#L1634
|
||||
*/
|
||||
|
||||
|
||||
SimpleBar.getRtlHelpers = function getRtlHelpers() {
|
||||
var dummyDiv = document.createElement('div');
|
||||
dummyDiv.innerHTML = '<div class="hs-dummy-scrollbar-size"><div style="height: 200%; width: 200%; margin: 10px 0;"></div></div>';
|
||||
var scrollbarDummyEl = dummyDiv.firstElementChild;
|
||||
document.body.appendChild(scrollbarDummyEl);
|
||||
var dummyContainerChild = scrollbarDummyEl.firstElementChild;
|
||||
scrollbarDummyEl.scrollLeft = 0;
|
||||
var dummyContainerOffset = SimpleBar.getOffset(scrollbarDummyEl);
|
||||
var dummyContainerChildOffset = SimpleBar.getOffset(dummyContainerChild);
|
||||
scrollbarDummyEl.scrollLeft = 999;
|
||||
var dummyContainerScrollOffsetAfterScroll = SimpleBar.getOffset(dummyContainerChild);
|
||||
return {
|
||||
// determines if the scrolling is responding with negative values
|
||||
isRtlScrollingInverted: dummyContainerOffset.left !== dummyContainerChildOffset.left && dummyContainerChildOffset.left - dummyContainerScrollOffsetAfterScroll.left !== 0,
|
||||
// determines if the origin scrollbar position is inverted or not (positioned on left or right)
|
||||
isRtlScrollbarInverted: dummyContainerOffset.left !== dummyContainerChildOffset.left
|
||||
};
|
||||
};
|
||||
|
||||
SimpleBar.getOffset = function getOffset(el) {
|
||||
var rect = el.getBoundingClientRect();
|
||||
var elDocument = getElementDocument(el);
|
||||
var elWindow = getElementWindow(el);
|
||||
return {
|
||||
top: rect.top + (elWindow.pageYOffset || elDocument.documentElement.scrollTop),
|
||||
left: rect.left + (elWindow.pageXOffset || elDocument.documentElement.scrollLeft)
|
||||
};
|
||||
};
|
||||
|
||||
var _proto = SimpleBar.prototype;
|
||||
|
||||
_proto.init = function init() {
|
||||
// Save a reference to the instance, so we know this DOM node has already been instancied
|
||||
SimpleBar.instances.set(this.el, this); // We stop here on server-side
|
||||
|
||||
if (canUseDOM) {
|
||||
this.initDOM();
|
||||
this.setAccessibilityAttributes();
|
||||
this.scrollbarWidth = this.getScrollbarWidth();
|
||||
this.recalculate();
|
||||
this.initListeners();
|
||||
}
|
||||
};
|
||||
|
||||
_proto.initDOM = function initDOM() {
|
||||
var _this2 = this;
|
||||
|
||||
// make sure this element doesn't have the elements yet
|
||||
if (Array.prototype.filter.call(this.el.children, function (child) {
|
||||
return child.classList.contains(_this2.classNames.wrapper);
|
||||
}).length) {
|
||||
// assume that element has his DOM already initiated
|
||||
this.wrapperEl = this.el.querySelector("." + this.classNames.wrapper);
|
||||
this.contentWrapperEl = this.options.scrollableNode || this.el.querySelector("." + this.classNames.contentWrapper);
|
||||
this.contentEl = this.options.contentNode || this.el.querySelector("." + this.classNames.contentEl);
|
||||
this.offsetEl = this.el.querySelector("." + this.classNames.offset);
|
||||
this.maskEl = this.el.querySelector("." + this.classNames.mask);
|
||||
this.placeholderEl = this.findChild(this.wrapperEl, "." + this.classNames.placeholder);
|
||||
this.heightAutoObserverWrapperEl = this.el.querySelector("." + this.classNames.heightAutoObserverWrapperEl);
|
||||
this.heightAutoObserverEl = this.el.querySelector("." + this.classNames.heightAutoObserverEl);
|
||||
this.axis.x.track.el = this.findChild(this.el, "." + this.classNames.track + "." + this.classNames.horizontal);
|
||||
this.axis.y.track.el = this.findChild(this.el, "." + this.classNames.track + "." + this.classNames.vertical);
|
||||
} else {
|
||||
// Prepare DOM
|
||||
this.wrapperEl = document.createElement('div');
|
||||
this.contentWrapperEl = document.createElement('div');
|
||||
this.offsetEl = document.createElement('div');
|
||||
this.maskEl = document.createElement('div');
|
||||
this.contentEl = document.createElement('div');
|
||||
this.placeholderEl = document.createElement('div');
|
||||
this.heightAutoObserverWrapperEl = document.createElement('div');
|
||||
this.heightAutoObserverEl = document.createElement('div');
|
||||
this.wrapperEl.classList.add(this.classNames.wrapper);
|
||||
this.contentWrapperEl.classList.add(this.classNames.contentWrapper);
|
||||
this.offsetEl.classList.add(this.classNames.offset);
|
||||
this.maskEl.classList.add(this.classNames.mask);
|
||||
this.contentEl.classList.add(this.classNames.contentEl);
|
||||
this.placeholderEl.classList.add(this.classNames.placeholder);
|
||||
this.heightAutoObserverWrapperEl.classList.add(this.classNames.heightAutoObserverWrapperEl);
|
||||
this.heightAutoObserverEl.classList.add(this.classNames.heightAutoObserverEl);
|
||||
|
||||
while (this.el.firstChild) {
|
||||
this.contentEl.appendChild(this.el.firstChild);
|
||||
}
|
||||
|
||||
this.contentWrapperEl.appendChild(this.contentEl);
|
||||
this.offsetEl.appendChild(this.contentWrapperEl);
|
||||
this.maskEl.appendChild(this.offsetEl);
|
||||
this.heightAutoObserverWrapperEl.appendChild(this.heightAutoObserverEl);
|
||||
this.wrapperEl.appendChild(this.heightAutoObserverWrapperEl);
|
||||
this.wrapperEl.appendChild(this.maskEl);
|
||||
this.wrapperEl.appendChild(this.placeholderEl);
|
||||
this.el.appendChild(this.wrapperEl);
|
||||
}
|
||||
|
||||
if (!this.axis.x.track.el || !this.axis.y.track.el) {
|
||||
var track = document.createElement('div');
|
||||
var scrollbar = document.createElement('div');
|
||||
track.classList.add(this.classNames.track);
|
||||
scrollbar.classList.add(this.classNames.scrollbar);
|
||||
track.appendChild(scrollbar);
|
||||
this.axis.x.track.el = track.cloneNode(true);
|
||||
this.axis.x.track.el.classList.add(this.classNames.horizontal);
|
||||
this.axis.y.track.el = track.cloneNode(true);
|
||||
this.axis.y.track.el.classList.add(this.classNames.vertical);
|
||||
this.el.appendChild(this.axis.x.track.el);
|
||||
this.el.appendChild(this.axis.y.track.el);
|
||||
}
|
||||
|
||||
this.axis.x.scrollbar.el = this.axis.x.track.el.querySelector("." + this.classNames.scrollbar);
|
||||
this.axis.y.scrollbar.el = this.axis.y.track.el.querySelector("." + this.classNames.scrollbar);
|
||||
|
||||
if (!this.options.autoHide) {
|
||||
this.axis.x.scrollbar.el.classList.add(this.classNames.visible);
|
||||
this.axis.y.scrollbar.el.classList.add(this.classNames.visible);
|
||||
}
|
||||
|
||||
this.el.setAttribute('data-simplebar', 'init');
|
||||
};
|
||||
|
||||
_proto.setAccessibilityAttributes = function setAccessibilityAttributes() {
|
||||
var ariaLabel = this.options.ariaLabel || 'scrollable content';
|
||||
this.contentWrapperEl.setAttribute('tabindex', '0');
|
||||
this.contentWrapperEl.setAttribute('role', 'region');
|
||||
this.contentWrapperEl.setAttribute('aria-label', ariaLabel);
|
||||
};
|
||||
|
||||
_proto.initListeners = function initListeners() {
|
||||
var _this3 = this;
|
||||
|
||||
var elWindow = getElementWindow(this.el); // Event listeners
|
||||
|
||||
if (this.options.autoHide) {
|
||||
this.el.addEventListener('mouseenter', this.onMouseEnter);
|
||||
}
|
||||
|
||||
['mousedown', 'click', 'dblclick'].forEach(function (e) {
|
||||
_this3.el.addEventListener(e, _this3.onPointerEvent, true);
|
||||
});
|
||||
['touchstart', 'touchend', 'touchmove'].forEach(function (e) {
|
||||
_this3.el.addEventListener(e, _this3.onPointerEvent, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
});
|
||||
this.el.addEventListener('mousemove', this.onMouseMove);
|
||||
this.el.addEventListener('mouseleave', this.onMouseLeave);
|
||||
this.contentWrapperEl.addEventListener('scroll', this.onScroll); // Browser zoom triggers a window resize
|
||||
|
||||
elWindow.addEventListener('resize', this.onWindowResize); // Hack for https://github.com/WICG/ResizeObserver/issues/38
|
||||
|
||||
var resizeObserverStarted = false;
|
||||
var resizeObserver = elWindow.ResizeObserver || ResizeObserver;
|
||||
this.resizeObserver = new resizeObserver(function () {
|
||||
if (!resizeObserverStarted) return;
|
||||
|
||||
_this3.recalculate();
|
||||
});
|
||||
this.resizeObserver.observe(this.el);
|
||||
this.resizeObserver.observe(this.contentEl);
|
||||
elWindow.requestAnimationFrame(function () {
|
||||
resizeObserverStarted = true;
|
||||
}); // This is required to detect horizontal scroll. Vertical scroll only needs the resizeObserver.
|
||||
|
||||
this.mutationObserver = new elWindow.MutationObserver(this.recalculate);
|
||||
this.mutationObserver.observe(this.contentEl, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true
|
||||
});
|
||||
};
|
||||
|
||||
_proto.recalculate = function recalculate() {
|
||||
var elWindow = getElementWindow(this.el);
|
||||
this.elStyles = elWindow.getComputedStyle(this.el);
|
||||
this.isRtl = this.elStyles.direction === 'rtl';
|
||||
var isHeightAuto = this.heightAutoObserverEl.offsetHeight <= 1;
|
||||
var isWidthAuto = this.heightAutoObserverEl.offsetWidth <= 1;
|
||||
var contentElOffsetWidth = this.contentEl.offsetWidth;
|
||||
var contentWrapperElOffsetWidth = this.contentWrapperEl.offsetWidth;
|
||||
var elOverflowX = this.elStyles.overflowX;
|
||||
var elOverflowY = this.elStyles.overflowY;
|
||||
this.contentEl.style.padding = this.elStyles.paddingTop + " " + this.elStyles.paddingRight + " " + this.elStyles.paddingBottom + " " + this.elStyles.paddingLeft;
|
||||
this.wrapperEl.style.margin = "-" + this.elStyles.paddingTop + " -" + this.elStyles.paddingRight + " -" + this.elStyles.paddingBottom + " -" + this.elStyles.paddingLeft;
|
||||
var contentElScrollHeight = this.contentEl.scrollHeight;
|
||||
var contentElScrollWidth = this.contentEl.scrollWidth;
|
||||
this.contentWrapperEl.style.height = isHeightAuto ? 'auto' : '100%'; // Determine placeholder size
|
||||
|
||||
this.placeholderEl.style.width = isWidthAuto ? contentElOffsetWidth + "px" : 'auto';
|
||||
this.placeholderEl.style.height = contentElScrollHeight + "px";
|
||||
var contentWrapperElOffsetHeight = this.contentWrapperEl.offsetHeight;
|
||||
this.axis.x.isOverflowing = contentElScrollWidth > contentElOffsetWidth;
|
||||
this.axis.y.isOverflowing = contentElScrollHeight > contentWrapperElOffsetHeight; // Set isOverflowing to false if user explicitely set hidden overflow
|
||||
|
||||
this.axis.x.isOverflowing = elOverflowX === 'hidden' ? false : this.axis.x.isOverflowing;
|
||||
this.axis.y.isOverflowing = elOverflowY === 'hidden' ? false : this.axis.y.isOverflowing;
|
||||
this.axis.x.forceVisible = this.options.forceVisible === 'x' || this.options.forceVisible === true;
|
||||
this.axis.y.forceVisible = this.options.forceVisible === 'y' || this.options.forceVisible === true;
|
||||
this.hideNativeScrollbar(); // Set isOverflowing to false if scrollbar is not necessary (content is shorter than offset)
|
||||
|
||||
var offsetForXScrollbar = this.axis.x.isOverflowing ? this.scrollbarWidth : 0;
|
||||
var offsetForYScrollbar = this.axis.y.isOverflowing ? this.scrollbarWidth : 0;
|
||||
this.axis.x.isOverflowing = this.axis.x.isOverflowing && contentElScrollWidth > contentWrapperElOffsetWidth - offsetForYScrollbar;
|
||||
this.axis.y.isOverflowing = this.axis.y.isOverflowing && contentElScrollHeight > contentWrapperElOffsetHeight - offsetForXScrollbar;
|
||||
this.axis.x.scrollbar.size = this.getScrollbarSize('x');
|
||||
this.axis.y.scrollbar.size = this.getScrollbarSize('y');
|
||||
this.axis.x.scrollbar.el.style.width = this.axis.x.scrollbar.size + "px";
|
||||
this.axis.y.scrollbar.el.style.height = this.axis.y.scrollbar.size + "px";
|
||||
this.positionScrollbar('x');
|
||||
this.positionScrollbar('y');
|
||||
this.toggleTrackVisibility('x');
|
||||
this.toggleTrackVisibility('y');
|
||||
}
|
||||
/**
|
||||
* Calculate scrollbar size
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.getScrollbarSize = function getScrollbarSize(axis) {
|
||||
if (axis === void 0) {
|
||||
axis = 'y';
|
||||
}
|
||||
|
||||
if (!this.axis[axis].isOverflowing) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var contentSize = this.contentEl[this.axis[axis].scrollSizeAttr];
|
||||
var trackSize = this.axis[axis].track.el[this.axis[axis].offsetSizeAttr];
|
||||
var scrollbarSize;
|
||||
var scrollbarRatio = trackSize / contentSize; // Calculate new height/position of drag handle.
|
||||
|
||||
scrollbarSize = Math.max(~~(scrollbarRatio * trackSize), this.options.scrollbarMinSize);
|
||||
|
||||
if (this.options.scrollbarMaxSize) {
|
||||
scrollbarSize = Math.min(scrollbarSize, this.options.scrollbarMaxSize);
|
||||
}
|
||||
|
||||
return scrollbarSize;
|
||||
};
|
||||
|
||||
_proto.positionScrollbar = function positionScrollbar(axis) {
|
||||
if (axis === void 0) {
|
||||
axis = 'y';
|
||||
}
|
||||
|
||||
if (!this.axis[axis].isOverflowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
var contentSize = this.contentWrapperEl[this.axis[axis].scrollSizeAttr];
|
||||
var trackSize = this.axis[axis].track.el[this.axis[axis].offsetSizeAttr];
|
||||
var hostSize = parseInt(this.elStyles[this.axis[axis].sizeAttr], 10);
|
||||
var scrollbar = this.axis[axis].scrollbar;
|
||||
var scrollOffset = this.contentWrapperEl[this.axis[axis].scrollOffsetAttr];
|
||||
scrollOffset = axis === 'x' && this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollingInverted ? -scrollOffset : scrollOffset;
|
||||
var scrollPourcent = scrollOffset / (contentSize - hostSize);
|
||||
var handleOffset = ~~((trackSize - scrollbar.size) * scrollPourcent);
|
||||
handleOffset = axis === 'x' && this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollbarInverted ? handleOffset + (trackSize - scrollbar.size) : handleOffset;
|
||||
scrollbar.el.style.transform = axis === 'x' ? "translate3d(" + handleOffset + "px, 0, 0)" : "translate3d(0, " + handleOffset + "px, 0)";
|
||||
};
|
||||
|
||||
_proto.toggleTrackVisibility = function toggleTrackVisibility(axis) {
|
||||
if (axis === void 0) {
|
||||
axis = 'y';
|
||||
}
|
||||
|
||||
var track = this.axis[axis].track.el;
|
||||
var scrollbar = this.axis[axis].scrollbar.el;
|
||||
|
||||
if (this.axis[axis].isOverflowing || this.axis[axis].forceVisible) {
|
||||
track.style.visibility = 'visible';
|
||||
this.contentWrapperEl.style[this.axis[axis].overflowAttr] = 'scroll';
|
||||
} else {
|
||||
track.style.visibility = 'hidden';
|
||||
this.contentWrapperEl.style[this.axis[axis].overflowAttr] = 'hidden';
|
||||
} // Even if forceVisible is enabled, scrollbar itself should be hidden
|
||||
|
||||
|
||||
if (this.axis[axis].isOverflowing) {
|
||||
scrollbar.style.display = 'block';
|
||||
} else {
|
||||
scrollbar.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
_proto.hideNativeScrollbar = function hideNativeScrollbar() {
|
||||
this.offsetEl.style[this.isRtl ? 'left' : 'right'] = this.axis.y.isOverflowing || this.axis.y.forceVisible ? "-" + this.scrollbarWidth + "px" : 0;
|
||||
this.offsetEl.style.bottom = this.axis.x.isOverflowing || this.axis.x.forceVisible ? "-" + this.scrollbarWidth + "px" : 0;
|
||||
}
|
||||
/**
|
||||
* On scroll event handling
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.onMouseMoveForAxis = function onMouseMoveForAxis(axis) {
|
||||
if (axis === void 0) {
|
||||
axis = 'y';
|
||||
}
|
||||
|
||||
this.axis[axis].track.rect = this.axis[axis].track.el.getBoundingClientRect();
|
||||
this.axis[axis].scrollbar.rect = this.axis[axis].scrollbar.el.getBoundingClientRect();
|
||||
var isWithinScrollbarBoundsX = this.isWithinBounds(this.axis[axis].scrollbar.rect);
|
||||
|
||||
if (isWithinScrollbarBoundsX) {
|
||||
this.axis[axis].scrollbar.el.classList.add(this.classNames.hover);
|
||||
} else {
|
||||
this.axis[axis].scrollbar.el.classList.remove(this.classNames.hover);
|
||||
}
|
||||
|
||||
if (this.isWithinBounds(this.axis[axis].track.rect)) {
|
||||
this.showScrollbar(axis);
|
||||
this.axis[axis].track.el.classList.add(this.classNames.hover);
|
||||
} else {
|
||||
this.axis[axis].track.el.classList.remove(this.classNames.hover);
|
||||
}
|
||||
};
|
||||
|
||||
_proto.onMouseLeaveForAxis = function onMouseLeaveForAxis(axis) {
|
||||
if (axis === void 0) {
|
||||
axis = 'y';
|
||||
}
|
||||
|
||||
this.axis[axis].track.el.classList.remove(this.classNames.hover);
|
||||
this.axis[axis].scrollbar.el.classList.remove(this.classNames.hover);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show scrollbar
|
||||
*/
|
||||
_proto.showScrollbar = function showScrollbar(axis) {
|
||||
if (axis === void 0) {
|
||||
axis = 'y';
|
||||
}
|
||||
|
||||
var scrollbar = this.axis[axis].scrollbar.el;
|
||||
|
||||
if (!this.axis[axis].isVisible) {
|
||||
scrollbar.classList.add(this.classNames.visible);
|
||||
this.axis[axis].isVisible = true;
|
||||
}
|
||||
|
||||
if (this.options.autoHide) {
|
||||
this.hideScrollbars();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Hide Scrollbar
|
||||
*/
|
||||
;
|
||||
|
||||
/**
|
||||
* on scrollbar handle drag movement starts
|
||||
*/
|
||||
_proto.onDragStart = function onDragStart(e, axis) {
|
||||
if (axis === void 0) {
|
||||
axis = 'y';
|
||||
}
|
||||
|
||||
var elDocument = getElementDocument(this.el);
|
||||
var elWindow = getElementWindow(this.el);
|
||||
var scrollbar = this.axis[axis].scrollbar; // Measure how far the user's mouse is from the top of the scrollbar drag handle.
|
||||
|
||||
var eventOffset = axis === 'y' ? e.pageY : e.pageX;
|
||||
this.axis[axis].dragOffset = eventOffset - scrollbar.rect[this.axis[axis].offsetAttr];
|
||||
this.draggedAxis = axis;
|
||||
this.el.classList.add(this.classNames.dragging);
|
||||
elDocument.addEventListener('mousemove', this.drag, true);
|
||||
elDocument.addEventListener('mouseup', this.onEndDrag, true);
|
||||
|
||||
if (this.removePreventClickId === null) {
|
||||
elDocument.addEventListener('click', this.preventClick, true);
|
||||
elDocument.addEventListener('dblclick', this.preventClick, true);
|
||||
} else {
|
||||
elWindow.clearTimeout(this.removePreventClickId);
|
||||
this.removePreventClickId = null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Drag scrollbar handle
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.onTrackClick = function onTrackClick(e, axis) {
|
||||
var _this4 = this;
|
||||
|
||||
if (axis === void 0) {
|
||||
axis = 'y';
|
||||
}
|
||||
|
||||
if (!this.options.clickOnTrack) return;
|
||||
var elWindow = getElementWindow(this.el);
|
||||
this.axis[axis].scrollbar.rect = this.axis[axis].scrollbar.el.getBoundingClientRect();
|
||||
var scrollbar = this.axis[axis].scrollbar;
|
||||
var scrollbarOffset = scrollbar.rect[this.axis[axis].offsetAttr];
|
||||
var hostSize = parseInt(this.elStyles[this.axis[axis].sizeAttr], 10);
|
||||
var scrolled = this.contentWrapperEl[this.axis[axis].scrollOffsetAttr];
|
||||
var t = axis === 'y' ? this.mouseY - scrollbarOffset : this.mouseX - scrollbarOffset;
|
||||
var dir = t < 0 ? -1 : 1;
|
||||
var scrollSize = dir === -1 ? scrolled - hostSize : scrolled + hostSize;
|
||||
|
||||
var scrollTo = function scrollTo() {
|
||||
if (dir === -1) {
|
||||
if (scrolled > scrollSize) {
|
||||
var _this4$contentWrapper;
|
||||
|
||||
scrolled -= _this4.options.clickOnTrackSpeed;
|
||||
|
||||
_this4.contentWrapperEl.scrollTo((_this4$contentWrapper = {}, _this4$contentWrapper[_this4.axis[axis].offsetAttr] = scrolled, _this4$contentWrapper));
|
||||
|
||||
elWindow.requestAnimationFrame(scrollTo);
|
||||
}
|
||||
} else {
|
||||
if (scrolled < scrollSize) {
|
||||
var _this4$contentWrapper2;
|
||||
|
||||
scrolled += _this4.options.clickOnTrackSpeed;
|
||||
|
||||
_this4.contentWrapperEl.scrollTo((_this4$contentWrapper2 = {}, _this4$contentWrapper2[_this4.axis[axis].offsetAttr] = scrolled, _this4$contentWrapper2));
|
||||
|
||||
elWindow.requestAnimationFrame(scrollTo);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scrollTo();
|
||||
}
|
||||
/**
|
||||
* Getter for content element
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.getContentElement = function getContentElement() {
|
||||
return this.contentEl;
|
||||
}
|
||||
/**
|
||||
* Getter for original scrolling element
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.getScrollElement = function getScrollElement() {
|
||||
return this.contentWrapperEl;
|
||||
};
|
||||
|
||||
_proto.getScrollbarWidth = function getScrollbarWidth() {
|
||||
// Try/catch for FF 56 throwing on undefined computedStyles
|
||||
try {
|
||||
// Detect browsers supporting CSS scrollbar styling and do not calculate
|
||||
if (getComputedStyle(this.contentWrapperEl, '::-webkit-scrollbar').display === 'none' || 'scrollbarWidth' in document.documentElement.style || '-ms-overflow-style' in document.documentElement.style) {
|
||||
return 0;
|
||||
} else {
|
||||
return scrollbarWidth(this.el);
|
||||
}
|
||||
} catch (e) {
|
||||
return scrollbarWidth(this.el);
|
||||
}
|
||||
};
|
||||
|
||||
_proto.removeListeners = function removeListeners() {
|
||||
var _this5 = this;
|
||||
|
||||
var elWindow = getElementWindow(this.el); // Event listeners
|
||||
|
||||
if (this.options.autoHide) {
|
||||
this.el.removeEventListener('mouseenter', this.onMouseEnter);
|
||||
}
|
||||
|
||||
['mousedown', 'click', 'dblclick'].forEach(function (e) {
|
||||
_this5.el.removeEventListener(e, _this5.onPointerEvent, true);
|
||||
});
|
||||
['touchstart', 'touchend', 'touchmove'].forEach(function (e) {
|
||||
_this5.el.removeEventListener(e, _this5.onPointerEvent, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
});
|
||||
this.el.removeEventListener('mousemove', this.onMouseMove);
|
||||
this.el.removeEventListener('mouseleave', this.onMouseLeave);
|
||||
|
||||
if (this.contentWrapperEl) {
|
||||
this.contentWrapperEl.removeEventListener('scroll', this.onScroll);
|
||||
}
|
||||
|
||||
elWindow.removeEventListener('resize', this.onWindowResize);
|
||||
|
||||
if (this.mutationObserver) {
|
||||
this.mutationObserver.disconnect();
|
||||
}
|
||||
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
} // Cancel all debounced functions
|
||||
|
||||
|
||||
this.recalculate.cancel();
|
||||
this.onMouseMove.cancel();
|
||||
this.hideScrollbars.cancel();
|
||||
this.onWindowResize.cancel();
|
||||
}
|
||||
/**
|
||||
* UnMount mutation observer and delete SimpleBar instance from DOM element
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.unMount = function unMount() {
|
||||
this.removeListeners();
|
||||
SimpleBar.instances.delete(this.el);
|
||||
}
|
||||
/**
|
||||
* Check if mouse is within bounds
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.isWithinBounds = function isWithinBounds(bbox) {
|
||||
return this.mouseX >= bbox.left && this.mouseX <= bbox.left + bbox.width && this.mouseY >= bbox.top && this.mouseY <= bbox.top + bbox.height;
|
||||
}
|
||||
/**
|
||||
* Find element children matches query
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.findChild = function findChild(el, query) {
|
||||
var matches = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
|
||||
return Array.prototype.filter.call(el.children, function (child) {
|
||||
return matches.call(child, query);
|
||||
})[0];
|
||||
};
|
||||
|
||||
return SimpleBar;
|
||||
}();
|
||||
|
||||
SimpleBar.defaultOptions = {
|
||||
autoHide: true,
|
||||
forceVisible: false,
|
||||
clickOnTrack: true,
|
||||
clickOnTrackSpeed: 40,
|
||||
classNames: {
|
||||
contentEl: 'simplebar-content',
|
||||
contentWrapper: 'simplebar-content-wrapper',
|
||||
offset: 'simplebar-offset',
|
||||
mask: 'simplebar-mask',
|
||||
wrapper: 'simplebar-wrapper',
|
||||
placeholder: 'simplebar-placeholder',
|
||||
scrollbar: 'simplebar-scrollbar',
|
||||
track: 'simplebar-track',
|
||||
heightAutoObserverWrapperEl: 'simplebar-height-auto-observer-wrapper',
|
||||
heightAutoObserverEl: 'simplebar-height-auto-observer',
|
||||
visible: 'simplebar-visible',
|
||||
horizontal: 'simplebar-horizontal',
|
||||
vertical: 'simplebar-vertical',
|
||||
hover: 'simplebar-hover',
|
||||
dragging: 'simplebar-dragging'
|
||||
},
|
||||
scrollbarMinSize: 25,
|
||||
scrollbarMaxSize: 0,
|
||||
timeout: 1000
|
||||
};
|
||||
SimpleBar.instances = new WeakMap();
|
||||
|
||||
export default SimpleBar;
|
||||
//# sourceMappingURL=simplebar-core.esm.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,211 @@
|
||||
[data-simplebar] {
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.simplebar-wrapper {
|
||||
overflow: hidden;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
max-width: inherit;
|
||||
max-height: inherit;
|
||||
}
|
||||
|
||||
.simplebar-mask {
|
||||
direction: inherit;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.simplebar-offset {
|
||||
direction: inherit !important;
|
||||
box-sizing: inherit !important;
|
||||
resize: none !important;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.simplebar-content-wrapper {
|
||||
direction: inherit;
|
||||
box-sizing: border-box !important;
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 100%; /* Required for horizontal native scrollbar to not appear if parent is taller than natural height */
|
||||
width: auto;
|
||||
max-width: 100%; /* Not required for horizontal scroll to trigger */
|
||||
max-height: 100%; /* Needed for vertical scroll to trigger */
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.simplebar-content-wrapper::-webkit-scrollbar,
|
||||
.simplebar-hide-scrollbar::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.simplebar-content:before,
|
||||
.simplebar-content:after {
|
||||
content: ' ';
|
||||
display: table;
|
||||
}
|
||||
|
||||
.simplebar-placeholder {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.simplebar-height-auto-observer-wrapper {
|
||||
box-sizing: inherit !important;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-width: 1px;
|
||||
position: relative;
|
||||
float: left;
|
||||
max-height: 1px;
|
||||
overflow: hidden;
|
||||
z-index: -1;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
pointer-events: none;
|
||||
flex-grow: inherit;
|
||||
flex-shrink: 0;
|
||||
flex-basis: 0;
|
||||
}
|
||||
|
||||
.simplebar-height-auto-observer {
|
||||
box-sizing: inherit;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 1000%;
|
||||
width: 1000%;
|
||||
min-height: 1px;
|
||||
min-width: 1px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.simplebar-track {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[data-simplebar].simplebar-dragging .simplebar-content {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
[data-simplebar].simplebar-dragging .simplebar-track {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.simplebar-scrollbar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
.simplebar-scrollbar:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
background: black;
|
||||
border-radius: 7px;
|
||||
left: 2px;
|
||||
right: 2px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
|
||||
.simplebar-scrollbar.simplebar-visible:before {
|
||||
/* When hovered, remove all transitions from drag handle */
|
||||
opacity: 0.5;
|
||||
transition: opacity 0s linear;
|
||||
}
|
||||
|
||||
.simplebar-track.simplebar-vertical {
|
||||
top: 0;
|
||||
width: 11px;
|
||||
}
|
||||
|
||||
.simplebar-track.simplebar-vertical .simplebar-scrollbar:before {
|
||||
top: 2px;
|
||||
bottom: 2px;
|
||||
}
|
||||
|
||||
.simplebar-track.simplebar-horizontal {
|
||||
left: 0;
|
||||
height: 11px;
|
||||
}
|
||||
|
||||
.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before {
|
||||
height: 100%;
|
||||
left: 2px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.simplebar-track.simplebar-horizontal .simplebar-scrollbar {
|
||||
right: auto;
|
||||
left: 0;
|
||||
top: 2px;
|
||||
height: 7px;
|
||||
min-height: 0;
|
||||
min-width: 10px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Rtl support */
|
||||
[data-simplebar-direction='rtl'] .simplebar-track.simplebar-vertical {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.hs-dummy-scrollbar-size {
|
||||
direction: rtl;
|
||||
position: fixed;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
height: 500px;
|
||||
width: 500px;
|
||||
overflow-y: hidden;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.simplebar-hide-scrollbar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
visibility: hidden;
|
||||
overflow-y: scroll;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
export as namespace SimpleBar;
|
||||
export = SimpleBar;
|
||||
|
||||
declare namespace SimpleBar {
|
||||
interface KnownClassNamesOptions {
|
||||
contentEl?: string;
|
||||
contentWrapper?: string;
|
||||
offset?: string;
|
||||
mask?: string;
|
||||
wrapper?: string;
|
||||
placeholder?: string;
|
||||
scrollbar?: string;
|
||||
track?: string;
|
||||
heightAutoObserverWrapperEl?: string;
|
||||
heightAutoObserverEl?: string;
|
||||
visible?: string;
|
||||
horizontal?: string;
|
||||
vertical?: string;
|
||||
hover?: string;
|
||||
dragging?: string;
|
||||
}
|
||||
|
||||
type ClassNamesOptions = KnownClassNamesOptions & {
|
||||
[className: string]: string;
|
||||
};
|
||||
|
||||
interface Options {
|
||||
autoHide?: boolean;
|
||||
classNames?: ClassNamesOptions;
|
||||
forceVisible?: boolean | 'x' | 'y';
|
||||
direction?: 'rtl' | 'ltr';
|
||||
timeout?: number;
|
||||
clickOnTrack?: boolean;
|
||||
scrollbarMinSize?: number;
|
||||
scrollbarMaxSize?: number;
|
||||
}
|
||||
}
|
||||
|
||||
declare class SimpleBar {
|
||||
static removeObserver(): void;
|
||||
static instances: Pick<WeakMap<HTMLElement, SimpleBar>, 'get' | 'has'>;
|
||||
|
||||
constructor(element: HTMLElement, options?: SimpleBar.Options);
|
||||
|
||||
recalculate(): void;
|
||||
getScrollElement(): HTMLElement;
|
||||
getContentElement(): HTMLElement;
|
||||
unMount(): void;
|
||||
|
||||
el: HTMLElement;
|
||||
}
|
||||
+1002
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+5127
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
[data-simplebar]{position:relative;flex-direction:column;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start;align-items:flex-start}.simplebar-wrapper{overflow:hidden;width:inherit;height:inherit;max-width:inherit;max-height:inherit}.simplebar-mask{direction:inherit;position:absolute;overflow:hidden;padding:0;margin:0;left:0;top:0;bottom:0;right:0;width:auto!important;height:auto!important;z-index:0}.simplebar-offset{direction:inherit!important;box-sizing:inherit!important;resize:none!important;position:absolute;top:0;left:0;bottom:0;right:0;padding:0;margin:0;-webkit-overflow-scrolling:touch}.simplebar-content-wrapper{direction:inherit;box-sizing:border-box!important;position:relative;display:block;height:100%;width:auto;max-width:100%;max-height:100%;scrollbar-width:none;-ms-overflow-style:none}.simplebar-content-wrapper::-webkit-scrollbar,.simplebar-hide-scrollbar::-webkit-scrollbar{width:0;height:0}.simplebar-content:after,.simplebar-content:before{content:' ';display:table}.simplebar-placeholder{max-height:100%;max-width:100%;width:100%;pointer-events:none}.simplebar-height-auto-observer-wrapper{box-sizing:inherit!important;height:100%;width:100%;max-width:1px;position:relative;float:left;max-height:1px;overflow:hidden;z-index:-1;padding:0;margin:0;pointer-events:none;flex-grow:inherit;flex-shrink:0;flex-basis:0}.simplebar-height-auto-observer{box-sizing:inherit;display:block;opacity:0;position:absolute;top:0;left:0;height:1000%;width:1000%;min-height:1px;min-width:1px;overflow:hidden;pointer-events:none;z-index:-1}.simplebar-track{z-index:1;position:absolute;right:0;bottom:0;pointer-events:none;overflow:hidden}[data-simplebar].simplebar-dragging .simplebar-content{pointer-events:none;user-select:none;-webkit-user-select:none}[data-simplebar].simplebar-dragging .simplebar-track{pointer-events:all}.simplebar-scrollbar{position:absolute;left:0;right:0;min-height:10px}.simplebar-scrollbar:before{position:absolute;content:'';background:#000;border-radius:7px;left:2px;right:2px;opacity:0;transition:opacity .2s linear}.simplebar-scrollbar.simplebar-visible:before{opacity:.5;transition:opacity 0s linear}.simplebar-track.simplebar-vertical{top:0;width:11px}.simplebar-track.simplebar-vertical .simplebar-scrollbar:before{top:2px;bottom:2px}.simplebar-track.simplebar-horizontal{left:0;height:11px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before{height:100%;left:2px;right:2px}.simplebar-track.simplebar-horizontal .simplebar-scrollbar{right:auto;left:0;top:2px;height:7px;min-height:0;min-width:10px;width:auto}[data-simplebar-direction=rtl] .simplebar-track.simplebar-vertical{right:auto;left:0}.hs-dummy-scrollbar-size{direction:rtl;position:fixed;opacity:0;visibility:hidden;height:500px;width:500px;overflow-y:hidden;overflow-x:scroll}.simplebar-hide-scrollbar{position:fixed;left:0;visibility:hidden;overflow-y:scroll;scrollbar-width:none;-ms-overflow-style:none}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* simplebar - v6.0.0-beta.10
|
||||
* Scrollbars, simpler.
|
||||
* https://grsmto.github.io/simplebar/
|
||||
*
|
||||
* Made by Adrien Denat from a fork by Jonathan Nicol
|
||||
* Under MIT License
|
||||
*/
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('can-use-dom'), require('simplebar-core')) :
|
||||
typeof define === 'function' && define.amd ? define(['can-use-dom', 'simplebar-core'], factory) :
|
||||
(global = global || self, global.SimpleBar = factory(global.canUseDOM, global.SimpleBar));
|
||||
}(this, (function (canUseDOM, SimpleBar) { 'use strict';
|
||||
|
||||
canUseDOM = canUseDOM && Object.prototype.hasOwnProperty.call(canUseDOM, 'default') ? canUseDOM['default'] : canUseDOM;
|
||||
SimpleBar = SimpleBar && Object.prototype.hasOwnProperty.call(SimpleBar, 'default') ? SimpleBar['default'] : SimpleBar;
|
||||
|
||||
// Helper function to retrieve options from element attributes
|
||||
var getOptions = function getOptions(obj) {
|
||||
var options = Array.prototype.reduce.call(obj, function (acc, attribute) {
|
||||
var option = attribute.name.match(/data-simplebar-(.+)/);
|
||||
|
||||
if (option) {
|
||||
var key = option[1].replace(/\W+(.)/g, function (x, chr) {
|
||||
return chr.toUpperCase();
|
||||
});
|
||||
|
||||
switch (attribute.value) {
|
||||
case 'true':
|
||||
acc[key] = true;
|
||||
break;
|
||||
|
||||
case 'false':
|
||||
acc[key] = false;
|
||||
break;
|
||||
|
||||
case undefined:
|
||||
acc[key] = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
acc[key] = attribute.value;
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
return options;
|
||||
};
|
||||
|
||||
SimpleBar.initDOMLoadedElements = function () {
|
||||
document.removeEventListener('DOMContentLoaded', this.initDOMLoadedElements);
|
||||
window.removeEventListener('load', this.initDOMLoadedElements);
|
||||
Array.prototype.forEach.call(document.querySelectorAll('[data-simplebar]'), function (el) {
|
||||
if (el.getAttribute('data-simplebar') !== 'init' && !SimpleBar.instances.has(el)) new SimpleBar(el, getOptions(el.attributes));
|
||||
});
|
||||
};
|
||||
|
||||
SimpleBar.removeObserver = function () {
|
||||
this.globalObserver.disconnect();
|
||||
};
|
||||
|
||||
SimpleBar.initHtmlApi = function () {
|
||||
this.initDOMLoadedElements = this.initDOMLoadedElements.bind(this); // MutationObserver is IE11+
|
||||
|
||||
if (typeof MutationObserver !== 'undefined') {
|
||||
// Mutation observer to observe dynamically added elements
|
||||
this.globalObserver = new MutationObserver(SimpleBar.handleMutations);
|
||||
this.globalObserver.observe(document, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
} // Taken from jQuery `ready` function
|
||||
// Instantiate elements already present on the page
|
||||
|
||||
|
||||
if (document.readyState === 'complete' || document.readyState !== 'loading' && !document.documentElement.doScroll) {
|
||||
// Handle it asynchronously to allow scripts the opportunity to delay init
|
||||
window.setTimeout(this.initDOMLoadedElements);
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', this.initDOMLoadedElements);
|
||||
window.addEventListener('load', this.initDOMLoadedElements);
|
||||
}
|
||||
};
|
||||
|
||||
SimpleBar.handleMutations = function (mutations) {
|
||||
mutations.forEach(function (mutation) {
|
||||
Array.prototype.forEach.call(mutation.addedNodes, function (addedNode) {
|
||||
if (addedNode.nodeType === 1) {
|
||||
if (addedNode.hasAttribute('data-simplebar')) {
|
||||
!SimpleBar.instances.has(addedNode) && new SimpleBar(addedNode, getOptions(addedNode.attributes));
|
||||
} else {
|
||||
Array.prototype.forEach.call(addedNode.querySelectorAll('[data-simplebar]'), function (el) {
|
||||
if (el.getAttribute('data-simplebar') !== 'init' && !SimpleBar.instances.has(el)) new SimpleBar(el, getOptions(el.attributes));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Array.prototype.forEach.call(mutation.removedNodes, function (removedNode) {
|
||||
if (removedNode.nodeType === 1) {
|
||||
if (removedNode.hasAttribute('data-simplebar')) {
|
||||
SimpleBar.instances.has(removedNode) && SimpleBar.instances.get(removedNode).unMount();
|
||||
} else {
|
||||
Array.prototype.forEach.call(removedNode.querySelectorAll('[data-simplebar="init"]'), function (el) {
|
||||
SimpleBar.instances.has(el) && SimpleBar.instances.get(el).unMount();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
SimpleBar.getOptions = getOptions;
|
||||
SimpleBar.default = SimpleBar;
|
||||
/**
|
||||
* HTML API
|
||||
* Called only in a browser env.
|
||||
*/
|
||||
|
||||
if (canUseDOM) {
|
||||
SimpleBar.initHtmlApi();
|
||||
}
|
||||
|
||||
return SimpleBar;
|
||||
|
||||
})));
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"version": "5.3.6",
|
||||
"name": "simplebar",
|
||||
"title": "SimpleBar.js",
|
||||
"description": "Scrollbars, simpler.",
|
||||
"files": [
|
||||
"dist",
|
||||
"src",
|
||||
"README.md"
|
||||
],
|
||||
"author": "Adrien Denat from a fork by Jonathan Nicol",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/grsmto/simplebar.git",
|
||||
"directory": "packages/simplebar"
|
||||
},
|
||||
"main": "dist/simplebar.min.js",
|
||||
"module": "dist/simplebar.esm.js",
|
||||
"types": "dist/simplebar.d.ts",
|
||||
"style": "dist/simplebar.min.css",
|
||||
"homepage": "https://grsmto.github.io/simplebar/",
|
||||
"bugs": "https://github.com/grsmto/simplebar/issues",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --mode=development",
|
||||
"build": "rollup -c && cp src/simplebar.css dist/simplebar.css && cp simplebar.d.ts dist/simplebar.d.ts && minify dist/simplebar.css > dist/simplebar.min.css && webpack --mode=production",
|
||||
"dev": "rollup -c -w",
|
||||
"test": "yarn test:unit && yarn test:e2e",
|
||||
"test:unit": "jest -c jest-unit.config.js",
|
||||
"test:e2e": "env-cmd intern",
|
||||
"version": "yarn build",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"dependencies": {
|
||||
"@juggle/resize-observer": "^3.3.1",
|
||||
"can-use-dom": "^0.1.0",
|
||||
"core-js": "^3.0.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.memoize": "^4.1.2",
|
||||
"lodash.throttle": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"css-loader": "^0.28.11",
|
||||
"intern": "^4.4.2",
|
||||
"minify": "^3.0.5",
|
||||
"promise": "^8.0.2",
|
||||
"react-select": "^2.4.3",
|
||||
"react-window": "^1.8.1",
|
||||
"style-loader": "^0.21.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,json}": [
|
||||
"prettier-eslint --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"gitHead": "40b847916bfdd96a46dce6e9af0128b239dc70d5"
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Helper function to retrieve options from element attributes
|
||||
export const getOptions = function(obj) {
|
||||
const options = Array.prototype.reduce.call(
|
||||
obj,
|
||||
(acc, attribute) => {
|
||||
const option = attribute.name.match(/data-simplebar-(.+)/);
|
||||
if (option) {
|
||||
const key = option[1].replace(/\W+(.)/g, (x, chr) => chr.toUpperCase());
|
||||
switch (attribute.value) {
|
||||
case 'true':
|
||||
acc[key] = true;
|
||||
break;
|
||||
case 'false':
|
||||
acc[key] = false;
|
||||
break;
|
||||
case undefined:
|
||||
acc[key] = true;
|
||||
break;
|
||||
default:
|
||||
acc[key] = attribute.value;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
return options;
|
||||
};
|
||||
|
||||
export function getElementWindow(element) {
|
||||
if (
|
||||
!element ||
|
||||
!element.ownerDocument ||
|
||||
!element.ownerDocument.defaultView
|
||||
) {
|
||||
return window;
|
||||
}
|
||||
return element.ownerDocument.defaultView;
|
||||
}
|
||||
|
||||
export function getElementDocument(element) {
|
||||
if (!element || !element.ownerDocument) {
|
||||
return document;
|
||||
}
|
||||
return element.ownerDocument;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import canUseDOM from 'can-use-dom';
|
||||
|
||||
import SimpleBar from './simplebar';
|
||||
import { getOptions } from './helpers';
|
||||
|
||||
SimpleBar.initDOMLoadedElements = function() {
|
||||
document.removeEventListener('DOMContentLoaded', this.initDOMLoadedElements);
|
||||
window.removeEventListener('load', this.initDOMLoadedElements);
|
||||
|
||||
Array.prototype.forEach.call(
|
||||
document.querySelectorAll('[data-simplebar]'),
|
||||
el => {
|
||||
if (
|
||||
el.getAttribute('data-simplebar') !== 'init' &&
|
||||
!SimpleBar.instances.has(el)
|
||||
)
|
||||
new SimpleBar(el, getOptions(el.attributes));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
SimpleBar.removeObserver = function() {
|
||||
this.globalObserver.disconnect();
|
||||
};
|
||||
|
||||
SimpleBar.initHtmlApi = function() {
|
||||
this.initDOMLoadedElements = this.initDOMLoadedElements.bind(this);
|
||||
|
||||
// MutationObserver is IE11+
|
||||
if (typeof MutationObserver !== 'undefined') {
|
||||
// Mutation observer to observe dynamically added elements
|
||||
this.globalObserver = new MutationObserver(SimpleBar.handleMutations);
|
||||
|
||||
this.globalObserver.observe(document, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
// Taken from jQuery `ready` function
|
||||
// Instantiate elements already present on the page
|
||||
if (
|
||||
document.readyState === 'complete' ||
|
||||
(document.readyState !== 'loading' && !document.documentElement.doScroll)
|
||||
) {
|
||||
// Handle it asynchronously to allow scripts the opportunity to delay init
|
||||
window.setTimeout(this.initDOMLoadedElements);
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', this.initDOMLoadedElements);
|
||||
window.addEventListener('load', this.initDOMLoadedElements);
|
||||
}
|
||||
};
|
||||
|
||||
SimpleBar.handleMutations = mutations => {
|
||||
mutations.forEach(mutation => {
|
||||
Array.prototype.forEach.call(mutation.addedNodes, addedNode => {
|
||||
if (addedNode.nodeType === 1) {
|
||||
if (addedNode.hasAttribute('data-simplebar')) {
|
||||
!SimpleBar.instances.has(addedNode) &&
|
||||
document.documentElement.contains(addedNode) &&
|
||||
new SimpleBar(addedNode, getOptions(addedNode.attributes));
|
||||
} else {
|
||||
Array.prototype.forEach.call(
|
||||
addedNode.querySelectorAll('[data-simplebar]'),
|
||||
function(el) {
|
||||
if (
|
||||
el.getAttribute('data-simplebar') !== 'init' &&
|
||||
!SimpleBar.instances.has(el) &&
|
||||
document.documentElement.contains(el)
|
||||
)
|
||||
new SimpleBar(el, getOptions(el.attributes));
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Array.prototype.forEach.call(mutation.removedNodes, removedNode => {
|
||||
if (removedNode.nodeType === 1) {
|
||||
if (removedNode.getAttribute('data-simplebar') === 'init') {
|
||||
SimpleBar.instances.has(removedNode) &&
|
||||
!document.documentElement.contains(removedNode) &&
|
||||
SimpleBar.instances.get(removedNode).unMount();
|
||||
} else {
|
||||
Array.prototype.forEach.call(
|
||||
removedNode.querySelectorAll('[data-simplebar="init"]'),
|
||||
el => {
|
||||
SimpleBar.instances.has(el) &&
|
||||
!document.documentElement.contains(el) &&
|
||||
SimpleBar.instances.get(el).unMount();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
SimpleBar.getOptions = getOptions;
|
||||
|
||||
/**
|
||||
* HTML API
|
||||
* Called only in a browser env.
|
||||
*/
|
||||
if (canUseDOM) {
|
||||
SimpleBar.initHtmlApi();
|
||||
}
|
||||
|
||||
export default SimpleBar;
|
||||
@@ -0,0 +1,40 @@
|
||||
import canUseDOM from 'can-use-dom';
|
||||
import { getElementDocument } from "./helpers";
|
||||
|
||||
let cachedScrollbarWidth = null;
|
||||
let cachedDevicePixelRatio = null;
|
||||
|
||||
if (canUseDOM) {
|
||||
window.addEventListener('resize', () => {
|
||||
if (cachedDevicePixelRatio !== window.devicePixelRatio) {
|
||||
cachedDevicePixelRatio = window.devicePixelRatio;
|
||||
cachedScrollbarWidth = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default function scrollbarWidth(el) {
|
||||
if (cachedScrollbarWidth === null) {
|
||||
|
||||
const document = getElementDocument(el);
|
||||
|
||||
if (typeof document === 'undefined') {
|
||||
cachedScrollbarWidth = 0;
|
||||
return cachedScrollbarWidth;
|
||||
}
|
||||
const body = document.body;
|
||||
const box = document.createElement('div');
|
||||
|
||||
box.classList.add('simplebar-hide-scrollbar');
|
||||
|
||||
body.appendChild(box);
|
||||
|
||||
const width = box.getBoundingClientRect().right;
|
||||
|
||||
body.removeChild(box);
|
||||
|
||||
cachedScrollbarWidth = width;
|
||||
}
|
||||
|
||||
return cachedScrollbarWidth;
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
[data-simplebar] {
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.simplebar-wrapper {
|
||||
overflow: hidden;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
max-width: inherit;
|
||||
max-height: inherit;
|
||||
}
|
||||
|
||||
.simplebar-mask {
|
||||
direction: inherit;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.simplebar-offset {
|
||||
direction: inherit !important;
|
||||
box-sizing: inherit !important;
|
||||
resize: none !important;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.simplebar-content-wrapper {
|
||||
direction: inherit;
|
||||
box-sizing: border-box !important;
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 100%; /* Required for horizontal native scrollbar to not appear if parent is taller than natural height */
|
||||
width: auto;
|
||||
max-width: 100%; /* Not required for horizontal scroll to trigger */
|
||||
max-height: 100%; /* Needed for vertical scroll to trigger */
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.simplebar-content-wrapper::-webkit-scrollbar,
|
||||
.simplebar-hide-scrollbar::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.simplebar-content:before,
|
||||
.simplebar-content:after {
|
||||
content: ' ';
|
||||
display: table;
|
||||
}
|
||||
|
||||
.simplebar-placeholder {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.simplebar-height-auto-observer-wrapper {
|
||||
box-sizing: inherit !important;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-width: 1px;
|
||||
position: relative;
|
||||
float: left;
|
||||
max-height: 1px;
|
||||
overflow: hidden;
|
||||
z-index: -1;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
pointer-events: none;
|
||||
flex-grow: inherit;
|
||||
flex-shrink: 0;
|
||||
flex-basis: 0;
|
||||
}
|
||||
|
||||
.simplebar-height-auto-observer {
|
||||
box-sizing: inherit;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 1000%;
|
||||
width: 1000%;
|
||||
min-height: 1px;
|
||||
min-width: 1px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.simplebar-track {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[data-simplebar].simplebar-dragging .simplebar-content {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
[data-simplebar].simplebar-dragging .simplebar-track {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.simplebar-scrollbar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
.simplebar-scrollbar:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
background: black;
|
||||
border-radius: 7px;
|
||||
left: 2px;
|
||||
right: 2px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
|
||||
.simplebar-scrollbar.simplebar-visible:before {
|
||||
/* When hovered, remove all transitions from drag handle */
|
||||
opacity: 0.5;
|
||||
transition: opacity 0s linear;
|
||||
}
|
||||
|
||||
.simplebar-track.simplebar-vertical {
|
||||
top: 0;
|
||||
width: 11px;
|
||||
}
|
||||
|
||||
.simplebar-track.simplebar-vertical .simplebar-scrollbar:before {
|
||||
top: 2px;
|
||||
bottom: 2px;
|
||||
}
|
||||
|
||||
.simplebar-track.simplebar-horizontal {
|
||||
left: 0;
|
||||
height: 11px;
|
||||
}
|
||||
|
||||
.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before {
|
||||
height: 100%;
|
||||
left: 2px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.simplebar-track.simplebar-horizontal .simplebar-scrollbar {
|
||||
right: auto;
|
||||
left: 0;
|
||||
top: 2px;
|
||||
height: 7px;
|
||||
min-height: 0;
|
||||
min-width: 10px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Rtl support */
|
||||
[data-simplebar-direction='rtl'] .simplebar-track.simplebar-vertical {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.hs-dummy-scrollbar-size {
|
||||
direction: rtl;
|
||||
position: fixed;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
height: 500px;
|
||||
width: 500px;
|
||||
overflow-y: hidden;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.simplebar-hide-scrollbar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
visibility: hidden;
|
||||
overflow-y: scroll;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
@@ -0,0 +1,949 @@
|
||||
import throttle from 'lodash.throttle';
|
||||
import debounce from 'lodash.debounce';
|
||||
import memoize from 'lodash.memoize';
|
||||
import { ResizeObserver } from '@juggle/resize-observer';
|
||||
import canUseDOM from 'can-use-dom';
|
||||
import scrollbarWidth from './scrollbar-width';
|
||||
import { getElementWindow, getElementDocument } from './helpers';
|
||||
|
||||
export default class SimpleBar {
|
||||
constructor(element, options) {
|
||||
this.el = element;
|
||||
this.minScrollbarWidth = 20;
|
||||
this.options = { ...SimpleBar.defaultOptions, ...options };
|
||||
this.classNames = {
|
||||
...SimpleBar.defaultOptions.classNames,
|
||||
...this.options.classNames
|
||||
};
|
||||
this.axis = {
|
||||
x: {
|
||||
scrollOffsetAttr: 'scrollLeft',
|
||||
sizeAttr: 'width',
|
||||
scrollSizeAttr: 'scrollWidth',
|
||||
offsetSizeAttr: 'offsetWidth',
|
||||
offsetAttr: 'left',
|
||||
overflowAttr: 'overflowX',
|
||||
dragOffset: 0,
|
||||
isOverflowing: true,
|
||||
isVisible: false,
|
||||
forceVisible: false,
|
||||
track: {},
|
||||
scrollbar: {}
|
||||
},
|
||||
y: {
|
||||
scrollOffsetAttr: 'scrollTop',
|
||||
sizeAttr: 'height',
|
||||
scrollSizeAttr: 'scrollHeight',
|
||||
offsetSizeAttr: 'offsetHeight',
|
||||
offsetAttr: 'top',
|
||||
overflowAttr: 'overflowY',
|
||||
dragOffset: 0,
|
||||
isOverflowing: true,
|
||||
isVisible: false,
|
||||
forceVisible: false,
|
||||
track: {},
|
||||
scrollbar: {}
|
||||
}
|
||||
};
|
||||
this.removePreventClickId = null;
|
||||
|
||||
// Don't re-instantiate over an existing one
|
||||
if (SimpleBar.instances.has(this.el)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.recalculate = throttle(this.recalculate.bind(this), 64);
|
||||
this.onMouseMove = throttle(this.onMouseMove.bind(this), 64);
|
||||
this.hideScrollbars = debounce(
|
||||
this.hideScrollbars.bind(this),
|
||||
this.options.timeout
|
||||
);
|
||||
this.onWindowResize = debounce(this.onWindowResize.bind(this), 64, {
|
||||
leading: true
|
||||
});
|
||||
|
||||
SimpleBar.getRtlHelpers = memoize(SimpleBar.getRtlHelpers);
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Static properties
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper to fix browsers inconsistency on RTL:
|
||||
* - Firefox inverts the scrollbar initial position
|
||||
* - IE11 inverts both scrollbar position and scrolling offset
|
||||
* Directly inspired by @KingSora's OverlayScrollbars https://github.com/KingSora/OverlayScrollbars/blob/master/js/OverlayScrollbars.js#L1634
|
||||
*/
|
||||
static getRtlHelpers() {
|
||||
const dummyDiv = document.createElement('div');
|
||||
dummyDiv.innerHTML =
|
||||
'<div class="hs-dummy-scrollbar-size"><div style="height: 200%; width: 200%; margin: 10px 0;"></div></div>';
|
||||
const scrollbarDummyEl = dummyDiv.firstElementChild;
|
||||
document.body.appendChild(scrollbarDummyEl);
|
||||
const dummyContainerChild = scrollbarDummyEl.firstElementChild;
|
||||
scrollbarDummyEl.scrollLeft = 0;
|
||||
const dummyContainerOffset = SimpleBar.getOffset(scrollbarDummyEl);
|
||||
const dummyContainerChildOffset = SimpleBar.getOffset(dummyContainerChild);
|
||||
scrollbarDummyEl.scrollLeft = 999;
|
||||
const dummyContainerScrollOffsetAfterScroll = SimpleBar.getOffset(
|
||||
dummyContainerChild
|
||||
);
|
||||
|
||||
return {
|
||||
// determines if the scrolling is responding with negative values
|
||||
isRtlScrollingInverted:
|
||||
dummyContainerOffset.left !== dummyContainerChildOffset.left &&
|
||||
dummyContainerChildOffset.left -
|
||||
dummyContainerScrollOffsetAfterScroll.left !==
|
||||
0,
|
||||
// determines if the origin scrollbar position is inverted or not (positioned on left or right)
|
||||
isRtlScrollbarInverted:
|
||||
dummyContainerOffset.left !== dummyContainerChildOffset.left
|
||||
};
|
||||
}
|
||||
|
||||
static defaultOptions = {
|
||||
autoHide: true,
|
||||
forceVisible: false,
|
||||
clickOnTrack: true,
|
||||
clickOnTrackSpeed: 40,
|
||||
classNames: {
|
||||
contentEl: 'simplebar-content',
|
||||
contentWrapper: 'simplebar-content-wrapper',
|
||||
offset: 'simplebar-offset',
|
||||
mask: 'simplebar-mask',
|
||||
wrapper: 'simplebar-wrapper',
|
||||
placeholder: 'simplebar-placeholder',
|
||||
scrollbar: 'simplebar-scrollbar',
|
||||
track: 'simplebar-track',
|
||||
heightAutoObserverWrapperEl: 'simplebar-height-auto-observer-wrapper',
|
||||
heightAutoObserverEl: 'simplebar-height-auto-observer',
|
||||
visible: 'simplebar-visible',
|
||||
horizontal: 'simplebar-horizontal',
|
||||
vertical: 'simplebar-vertical',
|
||||
hover: 'simplebar-hover',
|
||||
dragging: 'simplebar-dragging'
|
||||
},
|
||||
scrollbarMinSize: 25,
|
||||
scrollbarMaxSize: 0,
|
||||
timeout: 1000
|
||||
};
|
||||
|
||||
static getOffset(el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const elDocument = getElementDocument(el);
|
||||
const elWindow = getElementWindow(el);
|
||||
|
||||
return {
|
||||
top:
|
||||
rect.top +
|
||||
(elWindow.pageYOffset || elDocument.documentElement.scrollTop),
|
||||
left:
|
||||
rect.left +
|
||||
(elWindow.pageXOffset || elDocument.documentElement.scrollLeft)
|
||||
};
|
||||
}
|
||||
|
||||
static instances = new WeakMap();
|
||||
|
||||
init() {
|
||||
// Save a reference to the instance, so we know this DOM node has already been instancied
|
||||
SimpleBar.instances.set(this.el, this);
|
||||
|
||||
// We stop here on server-side
|
||||
if (canUseDOM) {
|
||||
this.initDOM();
|
||||
|
||||
this.setAccessibilityAttributes();
|
||||
|
||||
this.scrollbarWidth = this.getScrollbarWidth();
|
||||
|
||||
this.recalculate();
|
||||
|
||||
this.initListeners();
|
||||
}
|
||||
}
|
||||
|
||||
initDOM() {
|
||||
// make sure this element doesn't have the elements yet
|
||||
if (
|
||||
Array.prototype.filter.call(this.el.children, child =>
|
||||
child.classList.contains(this.classNames.wrapper)
|
||||
).length
|
||||
) {
|
||||
// assume that element has his DOM already initiated
|
||||
this.wrapperEl = this.el.querySelector(`.${this.classNames.wrapper}`);
|
||||
this.contentWrapperEl =
|
||||
this.options.scrollableNode ||
|
||||
this.el.querySelector(`.${this.classNames.contentWrapper}`);
|
||||
this.contentEl =
|
||||
this.options.contentNode ||
|
||||
this.el.querySelector(`.${this.classNames.contentEl}`);
|
||||
|
||||
this.offsetEl = this.el.querySelector(`.${this.classNames.offset}`);
|
||||
this.maskEl = this.el.querySelector(`.${this.classNames.mask}`);
|
||||
|
||||
this.placeholderEl = this.findChild(
|
||||
this.wrapperEl,
|
||||
`.${this.classNames.placeholder}`
|
||||
);
|
||||
this.heightAutoObserverWrapperEl = this.el.querySelector(
|
||||
`.${this.classNames.heightAutoObserverWrapperEl}`
|
||||
);
|
||||
this.heightAutoObserverEl = this.el.querySelector(
|
||||
`.${this.classNames.heightAutoObserverEl}`
|
||||
);
|
||||
this.axis.x.track.el = this.findChild(
|
||||
this.el,
|
||||
`.${this.classNames.track}.${this.classNames.horizontal}`
|
||||
);
|
||||
this.axis.y.track.el = this.findChild(
|
||||
this.el,
|
||||
`.${this.classNames.track}.${this.classNames.vertical}`
|
||||
);
|
||||
} else {
|
||||
// Prepare DOM
|
||||
this.wrapperEl = document.createElement('div');
|
||||
this.contentWrapperEl = document.createElement('div');
|
||||
this.offsetEl = document.createElement('div');
|
||||
this.maskEl = document.createElement('div');
|
||||
this.contentEl = document.createElement('div');
|
||||
this.placeholderEl = document.createElement('div');
|
||||
this.heightAutoObserverWrapperEl = document.createElement('div');
|
||||
this.heightAutoObserverEl = document.createElement('div');
|
||||
|
||||
this.wrapperEl.classList.add(this.classNames.wrapper);
|
||||
this.contentWrapperEl.classList.add(this.classNames.contentWrapper);
|
||||
this.offsetEl.classList.add(this.classNames.offset);
|
||||
this.maskEl.classList.add(this.classNames.mask);
|
||||
this.contentEl.classList.add(this.classNames.contentEl);
|
||||
this.placeholderEl.classList.add(this.classNames.placeholder);
|
||||
this.heightAutoObserverWrapperEl.classList.add(
|
||||
this.classNames.heightAutoObserverWrapperEl
|
||||
);
|
||||
this.heightAutoObserverEl.classList.add(
|
||||
this.classNames.heightAutoObserverEl
|
||||
);
|
||||
|
||||
while (this.el.firstChild) {
|
||||
this.contentEl.appendChild(this.el.firstChild);
|
||||
}
|
||||
|
||||
this.contentWrapperEl.appendChild(this.contentEl);
|
||||
this.offsetEl.appendChild(this.contentWrapperEl);
|
||||
this.maskEl.appendChild(this.offsetEl);
|
||||
this.heightAutoObserverWrapperEl.appendChild(this.heightAutoObserverEl);
|
||||
this.wrapperEl.appendChild(this.heightAutoObserverWrapperEl);
|
||||
this.wrapperEl.appendChild(this.maskEl);
|
||||
this.wrapperEl.appendChild(this.placeholderEl);
|
||||
this.el.appendChild(this.wrapperEl);
|
||||
}
|
||||
|
||||
if (!this.axis.x.track.el || !this.axis.y.track.el) {
|
||||
const track = document.createElement('div');
|
||||
const scrollbar = document.createElement('div');
|
||||
|
||||
track.classList.add(this.classNames.track);
|
||||
scrollbar.classList.add(this.classNames.scrollbar);
|
||||
|
||||
track.appendChild(scrollbar);
|
||||
|
||||
this.axis.x.track.el = track.cloneNode(true);
|
||||
this.axis.x.track.el.classList.add(this.classNames.horizontal);
|
||||
|
||||
this.axis.y.track.el = track.cloneNode(true);
|
||||
this.axis.y.track.el.classList.add(this.classNames.vertical);
|
||||
|
||||
this.el.appendChild(this.axis.x.track.el);
|
||||
this.el.appendChild(this.axis.y.track.el);
|
||||
}
|
||||
|
||||
this.axis.x.scrollbar.el = this.axis.x.track.el.querySelector(
|
||||
`.${this.classNames.scrollbar}`
|
||||
);
|
||||
this.axis.y.scrollbar.el = this.axis.y.track.el.querySelector(
|
||||
`.${this.classNames.scrollbar}`
|
||||
);
|
||||
|
||||
if (!this.options.autoHide) {
|
||||
this.axis.x.scrollbar.el.classList.add(this.classNames.visible);
|
||||
this.axis.y.scrollbar.el.classList.add(this.classNames.visible);
|
||||
}
|
||||
|
||||
this.el.setAttribute('data-simplebar', 'init');
|
||||
}
|
||||
|
||||
setAccessibilityAttributes() {
|
||||
const ariaLabel = this.options.ariaLabel || 'scrollable content';
|
||||
|
||||
this.contentWrapperEl.setAttribute('tabindex', '0');
|
||||
this.contentWrapperEl.setAttribute('role', 'region');
|
||||
this.contentWrapperEl.setAttribute('aria-label', ariaLabel);
|
||||
}
|
||||
|
||||
initListeners() {
|
||||
const elWindow = getElementWindow(this.el);
|
||||
// Event listeners
|
||||
if (this.options.autoHide) {
|
||||
this.el.addEventListener('mouseenter', this.onMouseEnter);
|
||||
}
|
||||
|
||||
['mousedown', 'click', 'dblclick'].forEach(e => {
|
||||
this.el.addEventListener(e, this.onPointerEvent, true);
|
||||
});
|
||||
|
||||
['touchstart', 'touchend', 'touchmove'].forEach(e => {
|
||||
this.el.addEventListener(e, this.onPointerEvent, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
});
|
||||
|
||||
this.el.addEventListener('mousemove', this.onMouseMove);
|
||||
this.el.addEventListener('mouseleave', this.onMouseLeave);
|
||||
|
||||
this.contentWrapperEl.addEventListener('scroll', this.onScroll);
|
||||
|
||||
// Browser zoom triggers a window resize
|
||||
elWindow.addEventListener('resize', this.onWindowResize);
|
||||
|
||||
// Hack for https://github.com/WICG/ResizeObserver/issues/38
|
||||
let resizeObserverStarted = false;
|
||||
const resizeObserver = elWindow.ResizeObserver || ResizeObserver;
|
||||
this.resizeObserver = new resizeObserver(() => {
|
||||
if (!resizeObserverStarted) return;
|
||||
this.recalculate();
|
||||
});
|
||||
|
||||
this.resizeObserver.observe(this.el);
|
||||
this.resizeObserver.observe(this.contentEl);
|
||||
|
||||
elWindow.requestAnimationFrame(() => {
|
||||
resizeObserverStarted = true;
|
||||
});
|
||||
|
||||
// This is required to detect horizontal scroll. Vertical scroll only needs the resizeObserver.
|
||||
this.mutationObserver = new elWindow.MutationObserver(this.recalculate);
|
||||
|
||||
this.mutationObserver.observe(this.contentEl, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true
|
||||
});
|
||||
}
|
||||
|
||||
recalculate() {
|
||||
const elWindow = getElementWindow(this.el);
|
||||
this.elStyles = elWindow.getComputedStyle(this.el);
|
||||
this.isRtl = this.elStyles.direction === 'rtl';
|
||||
|
||||
const isHeightAuto = this.heightAutoObserverEl.offsetHeight <= 1;
|
||||
const isWidthAuto = this.heightAutoObserverEl.offsetWidth <= 1;
|
||||
const contentElOffsetWidth = this.contentEl.offsetWidth;
|
||||
|
||||
const contentWrapperElOffsetWidth = this.contentWrapperEl.offsetWidth;
|
||||
|
||||
const elOverflowX = this.elStyles.overflowX;
|
||||
const elOverflowY = this.elStyles.overflowY;
|
||||
|
||||
this.contentEl.style.padding = `${this.elStyles.paddingTop} ${this.elStyles.paddingRight} ${this.elStyles.paddingBottom} ${this.elStyles.paddingLeft}`;
|
||||
this.wrapperEl.style.margin = `-${this.elStyles.paddingTop} -${this.elStyles.paddingRight} -${this.elStyles.paddingBottom} -${this.elStyles.paddingLeft}`;
|
||||
|
||||
const contentElScrollHeight = this.contentEl.scrollHeight;
|
||||
const contentElScrollWidth = this.contentEl.scrollWidth;
|
||||
|
||||
this.contentWrapperEl.style.height = isHeightAuto ? 'auto' : '100%';
|
||||
|
||||
// Determine placeholder size
|
||||
this.placeholderEl.style.width = isWidthAuto
|
||||
? `${contentElOffsetWidth}px`
|
||||
: 'auto';
|
||||
this.placeholderEl.style.height = `${contentElScrollHeight}px`;
|
||||
|
||||
const contentWrapperElOffsetHeight = this.contentWrapperEl.offsetHeight;
|
||||
|
||||
this.axis.x.isOverflowing = contentElScrollWidth > contentElOffsetWidth;
|
||||
this.axis.y.isOverflowing =
|
||||
contentElScrollHeight > contentWrapperElOffsetHeight;
|
||||
|
||||
// Set isOverflowing to false if user explicitely set hidden overflow
|
||||
this.axis.x.isOverflowing =
|
||||
elOverflowX === 'hidden' ? false : this.axis.x.isOverflowing;
|
||||
this.axis.y.isOverflowing =
|
||||
elOverflowY === 'hidden' ? false : this.axis.y.isOverflowing;
|
||||
|
||||
this.axis.x.forceVisible =
|
||||
this.options.forceVisible === 'x' || this.options.forceVisible === true;
|
||||
this.axis.y.forceVisible =
|
||||
this.options.forceVisible === 'y' || this.options.forceVisible === true;
|
||||
|
||||
this.hideNativeScrollbar();
|
||||
|
||||
// Set isOverflowing to false if scrollbar is not necessary (content is shorter than offset)
|
||||
let offsetForXScrollbar = this.axis.x.isOverflowing
|
||||
? this.scrollbarWidth
|
||||
: 0;
|
||||
let offsetForYScrollbar = this.axis.y.isOverflowing
|
||||
? this.scrollbarWidth
|
||||
: 0;
|
||||
|
||||
this.axis.x.isOverflowing =
|
||||
this.axis.x.isOverflowing &&
|
||||
contentElScrollWidth > contentWrapperElOffsetWidth - offsetForYScrollbar;
|
||||
this.axis.y.isOverflowing =
|
||||
this.axis.y.isOverflowing &&
|
||||
contentElScrollHeight >
|
||||
contentWrapperElOffsetHeight - offsetForXScrollbar;
|
||||
|
||||
this.axis.x.scrollbar.size = this.getScrollbarSize('x');
|
||||
this.axis.y.scrollbar.size = this.getScrollbarSize('y');
|
||||
|
||||
this.axis.x.scrollbar.el.style.width = `${this.axis.x.scrollbar.size}px`;
|
||||
this.axis.y.scrollbar.el.style.height = `${this.axis.y.scrollbar.size}px`;
|
||||
|
||||
this.positionScrollbar('x');
|
||||
this.positionScrollbar('y');
|
||||
|
||||
this.toggleTrackVisibility('x');
|
||||
this.toggleTrackVisibility('y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate scrollbar size
|
||||
*/
|
||||
getScrollbarSize(axis = 'y') {
|
||||
if (!this.axis[axis].isOverflowing) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const contentSize = this.contentEl[this.axis[axis].scrollSizeAttr];
|
||||
const trackSize = this.axis[axis].track.el[this.axis[axis].offsetSizeAttr];
|
||||
let scrollbarSize;
|
||||
|
||||
let scrollbarRatio = trackSize / contentSize;
|
||||
|
||||
// Calculate new height/position of drag handle.
|
||||
scrollbarSize = Math.max(
|
||||
~~(scrollbarRatio * trackSize),
|
||||
this.options.scrollbarMinSize
|
||||
);
|
||||
|
||||
if (this.options.scrollbarMaxSize) {
|
||||
scrollbarSize = Math.min(scrollbarSize, this.options.scrollbarMaxSize);
|
||||
}
|
||||
|
||||
return scrollbarSize;
|
||||
}
|
||||
|
||||
positionScrollbar(axis = 'y') {
|
||||
if (!this.axis[axis].isOverflowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentSize = this.contentWrapperEl[this.axis[axis].scrollSizeAttr];
|
||||
const trackSize = this.axis[axis].track.el[this.axis[axis].offsetSizeAttr];
|
||||
const hostSize = parseInt(this.elStyles[this.axis[axis].sizeAttr], 10);
|
||||
const scrollbar = this.axis[axis].scrollbar;
|
||||
|
||||
let scrollOffset = this.contentWrapperEl[this.axis[axis].scrollOffsetAttr];
|
||||
scrollOffset =
|
||||
axis === 'x' &&
|
||||
this.isRtl &&
|
||||
SimpleBar.getRtlHelpers().isRtlScrollingInverted
|
||||
? -scrollOffset
|
||||
: scrollOffset;
|
||||
let scrollPourcent = scrollOffset / (contentSize - hostSize);
|
||||
|
||||
let handleOffset = ~~((trackSize - scrollbar.size) * scrollPourcent);
|
||||
handleOffset =
|
||||
axis === 'x' &&
|
||||
this.isRtl &&
|
||||
SimpleBar.getRtlHelpers().isRtlScrollbarInverted
|
||||
? handleOffset + (trackSize - scrollbar.size)
|
||||
: handleOffset;
|
||||
|
||||
scrollbar.el.style.transform =
|
||||
axis === 'x'
|
||||
? `translate3d(${handleOffset}px, 0, 0)`
|
||||
: `translate3d(0, ${handleOffset}px, 0)`;
|
||||
}
|
||||
|
||||
toggleTrackVisibility(axis = 'y') {
|
||||
const track = this.axis[axis].track.el;
|
||||
const scrollbar = this.axis[axis].scrollbar.el;
|
||||
|
||||
if (this.axis[axis].isOverflowing || this.axis[axis].forceVisible) {
|
||||
track.style.visibility = 'visible';
|
||||
this.contentWrapperEl.style[this.axis[axis].overflowAttr] = 'scroll';
|
||||
} else {
|
||||
track.style.visibility = 'hidden';
|
||||
this.contentWrapperEl.style[this.axis[axis].overflowAttr] = 'hidden';
|
||||
}
|
||||
|
||||
// Even if forceVisible is enabled, scrollbar itself should be hidden
|
||||
if (this.axis[axis].isOverflowing) {
|
||||
scrollbar.style.display = 'block';
|
||||
} else {
|
||||
scrollbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
hideNativeScrollbar() {
|
||||
this.offsetEl.style[this.isRtl ? 'left' : 'right'] =
|
||||
this.axis.y.isOverflowing || this.axis.y.forceVisible
|
||||
? `-${this.scrollbarWidth}px`
|
||||
: 0;
|
||||
this.offsetEl.style.bottom =
|
||||
this.axis.x.isOverflowing || this.axis.x.forceVisible
|
||||
? `-${this.scrollbarWidth}px`
|
||||
: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* On scroll event handling
|
||||
*/
|
||||
onScroll = () => {
|
||||
const elWindow = getElementWindow(this.el);
|
||||
if (!this.scrollXTicking) {
|
||||
elWindow.requestAnimationFrame(this.scrollX);
|
||||
this.scrollXTicking = true;
|
||||
}
|
||||
|
||||
if (!this.scrollYTicking) {
|
||||
elWindow.requestAnimationFrame(this.scrollY);
|
||||
this.scrollYTicking = true;
|
||||
}
|
||||
};
|
||||
|
||||
scrollX = () => {
|
||||
if (this.axis.x.isOverflowing) {
|
||||
this.showScrollbar('x');
|
||||
this.positionScrollbar('x');
|
||||
}
|
||||
|
||||
this.scrollXTicking = false;
|
||||
};
|
||||
|
||||
scrollY = () => {
|
||||
if (this.axis.y.isOverflowing) {
|
||||
this.showScrollbar('y');
|
||||
this.positionScrollbar('y');
|
||||
}
|
||||
|
||||
this.scrollYTicking = false;
|
||||
};
|
||||
|
||||
onMouseEnter = () => {
|
||||
this.showScrollbar('x');
|
||||
this.showScrollbar('y');
|
||||
};
|
||||
|
||||
onMouseMove = e => {
|
||||
this.mouseX = e.clientX;
|
||||
this.mouseY = e.clientY;
|
||||
|
||||
if (this.axis.x.isOverflowing || this.axis.x.forceVisible) {
|
||||
this.onMouseMoveForAxis('x');
|
||||
}
|
||||
|
||||
if (this.axis.y.isOverflowing || this.axis.y.forceVisible) {
|
||||
this.onMouseMoveForAxis('y');
|
||||
}
|
||||
};
|
||||
|
||||
onMouseMoveForAxis(axis = 'y') {
|
||||
this.axis[axis].track.rect = this.axis[
|
||||
axis
|
||||
].track.el.getBoundingClientRect();
|
||||
this.axis[axis].scrollbar.rect = this.axis[
|
||||
axis
|
||||
].scrollbar.el.getBoundingClientRect();
|
||||
|
||||
const isWithinScrollbarBoundsX = this.isWithinBounds(
|
||||
this.axis[axis].scrollbar.rect
|
||||
);
|
||||
|
||||
if (isWithinScrollbarBoundsX) {
|
||||
this.axis[axis].scrollbar.el.classList.add(this.classNames.hover);
|
||||
} else {
|
||||
this.axis[axis].scrollbar.el.classList.remove(this.classNames.hover);
|
||||
}
|
||||
|
||||
if (this.isWithinBounds(this.axis[axis].track.rect)) {
|
||||
this.showScrollbar(axis);
|
||||
this.axis[axis].track.el.classList.add(this.classNames.hover);
|
||||
} else {
|
||||
this.axis[axis].track.el.classList.remove(this.classNames.hover);
|
||||
}
|
||||
}
|
||||
|
||||
onMouseLeave = () => {
|
||||
this.onMouseMove.cancel();
|
||||
|
||||
if (this.axis.x.isOverflowing || this.axis.x.forceVisible) {
|
||||
this.onMouseLeaveForAxis('x');
|
||||
}
|
||||
|
||||
if (this.axis.y.isOverflowing || this.axis.y.forceVisible) {
|
||||
this.onMouseLeaveForAxis('y');
|
||||
}
|
||||
|
||||
this.mouseX = -1;
|
||||
this.mouseY = -1;
|
||||
};
|
||||
|
||||
onMouseLeaveForAxis(axis = 'y') {
|
||||
this.axis[axis].track.el.classList.remove(this.classNames.hover);
|
||||
this.axis[axis].scrollbar.el.classList.remove(this.classNames.hover);
|
||||
}
|
||||
|
||||
onWindowResize = () => {
|
||||
// Recalculate scrollbarWidth in case it's a zoom
|
||||
this.scrollbarWidth = this.getScrollbarWidth();
|
||||
|
||||
this.hideNativeScrollbar();
|
||||
};
|
||||
|
||||
/**
|
||||
* Show scrollbar
|
||||
*/
|
||||
showScrollbar(axis = 'y') {
|
||||
let scrollbar = this.axis[axis].scrollbar.el;
|
||||
|
||||
if (!this.axis[axis].isVisible) {
|
||||
scrollbar.classList.add(this.classNames.visible);
|
||||
this.axis[axis].isVisible = true;
|
||||
}
|
||||
|
||||
if (this.options.autoHide) {
|
||||
this.hideScrollbars();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide Scrollbar
|
||||
*/
|
||||
hideScrollbars = () => {
|
||||
this.axis.x.track.rect = this.axis.x.track.el.getBoundingClientRect();
|
||||
this.axis.y.track.rect = this.axis.y.track.el.getBoundingClientRect();
|
||||
|
||||
if (!this.isWithinBounds(this.axis.y.track.rect)) {
|
||||
this.axis.y.scrollbar.el.classList.remove(this.classNames.visible);
|
||||
this.axis.y.isVisible = false;
|
||||
}
|
||||
|
||||
if (!this.isWithinBounds(this.axis.x.track.rect)) {
|
||||
this.axis.x.scrollbar.el.classList.remove(this.classNames.visible);
|
||||
this.axis.x.isVisible = false;
|
||||
}
|
||||
};
|
||||
|
||||
onPointerEvent = e => {
|
||||
let isWithinTrackXBounds, isWithinTrackYBounds;
|
||||
|
||||
this.axis.x.track.rect = this.axis.x.track.el.getBoundingClientRect();
|
||||
this.axis.y.track.rect = this.axis.y.track.el.getBoundingClientRect();
|
||||
|
||||
if (this.axis.x.isOverflowing || this.axis.x.forceVisible) {
|
||||
isWithinTrackXBounds = this.isWithinBounds(this.axis.x.track.rect);
|
||||
}
|
||||
|
||||
if (this.axis.y.isOverflowing || this.axis.y.forceVisible) {
|
||||
isWithinTrackYBounds = this.isWithinBounds(this.axis.y.track.rect);
|
||||
}
|
||||
|
||||
// If any pointer event is called on the scrollbar
|
||||
if (isWithinTrackXBounds || isWithinTrackYBounds) {
|
||||
// Preventing the event's default action stops text being
|
||||
// selectable during the drag.
|
||||
e.preventDefault();
|
||||
// Prevent event leaking
|
||||
e.stopPropagation();
|
||||
|
||||
if (e.type === 'mousedown') {
|
||||
if (isWithinTrackXBounds) {
|
||||
this.axis.x.scrollbar.rect = this.axis.x.scrollbar.el.getBoundingClientRect();
|
||||
|
||||
if (this.isWithinBounds(this.axis.x.scrollbar.rect)) {
|
||||
this.onDragStart(e, 'x');
|
||||
} else {
|
||||
this.onTrackClick(e, 'x');
|
||||
}
|
||||
}
|
||||
|
||||
if (isWithinTrackYBounds) {
|
||||
this.axis.y.scrollbar.rect = this.axis.y.scrollbar.el.getBoundingClientRect();
|
||||
|
||||
if (this.isWithinBounds(this.axis.y.scrollbar.rect)) {
|
||||
this.onDragStart(e, 'y');
|
||||
} else {
|
||||
this.onTrackClick(e, 'y');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* on scrollbar handle drag movement starts
|
||||
*/
|
||||
onDragStart(e, axis = 'y') {
|
||||
const elDocument = getElementDocument(this.el);
|
||||
const elWindow = getElementWindow(this.el);
|
||||
const scrollbar = this.axis[axis].scrollbar;
|
||||
|
||||
// Measure how far the user's mouse is from the top of the scrollbar drag handle.
|
||||
const eventOffset = axis === 'y' ? e.pageY : e.pageX;
|
||||
this.axis[axis].dragOffset =
|
||||
eventOffset - scrollbar.rect[this.axis[axis].offsetAttr];
|
||||
this.draggedAxis = axis;
|
||||
|
||||
this.el.classList.add(this.classNames.dragging);
|
||||
|
||||
elDocument.addEventListener('mousemove', this.drag, true);
|
||||
elDocument.addEventListener('mouseup', this.onEndDrag, true);
|
||||
if (this.removePreventClickId === null) {
|
||||
elDocument.addEventListener('click', this.preventClick, true);
|
||||
elDocument.addEventListener('dblclick', this.preventClick, true);
|
||||
} else {
|
||||
elWindow.clearTimeout(this.removePreventClickId);
|
||||
this.removePreventClickId = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag scrollbar handle
|
||||
*/
|
||||
drag = e => {
|
||||
let eventOffset;
|
||||
const track = this.axis[this.draggedAxis].track;
|
||||
const trackSize = track.rect[this.axis[this.draggedAxis].sizeAttr];
|
||||
const scrollbar = this.axis[this.draggedAxis].scrollbar;
|
||||
const contentSize = this.contentWrapperEl[
|
||||
this.axis[this.draggedAxis].scrollSizeAttr
|
||||
];
|
||||
const hostSize = parseInt(
|
||||
this.elStyles[this.axis[this.draggedAxis].sizeAttr],
|
||||
10
|
||||
);
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (this.draggedAxis === 'y') {
|
||||
eventOffset = e.pageY;
|
||||
} else {
|
||||
eventOffset = e.pageX;
|
||||
}
|
||||
|
||||
// Calculate how far the user's mouse is from the top/left of the scrollbar (minus the dragOffset).
|
||||
let 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.
|
||||
let dragPerc = dragPos / (trackSize - scrollbar.size);
|
||||
|
||||
// Scroll the content by the same percentage.
|
||||
let scrollPos = dragPerc * (contentSize - hostSize);
|
||||
|
||||
// Fix browsers inconsistency on RTL
|
||||
if (this.draggedAxis === 'x') {
|
||||
scrollPos =
|
||||
this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollbarInverted
|
||||
? scrollPos - (trackSize + scrollbar.size)
|
||||
: scrollPos;
|
||||
scrollPos =
|
||||
this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollingInverted
|
||||
? -scrollPos
|
||||
: scrollPos;
|
||||
}
|
||||
|
||||
this.contentWrapperEl[
|
||||
this.axis[this.draggedAxis].scrollOffsetAttr
|
||||
] = scrollPos;
|
||||
};
|
||||
|
||||
/**
|
||||
* End scroll handle drag
|
||||
*/
|
||||
onEndDrag = e => {
|
||||
const elDocument = getElementDocument(this.el);
|
||||
const elWindow = getElementWindow(this.el);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.el.classList.remove(this.classNames.dragging);
|
||||
|
||||
elDocument.removeEventListener('mousemove', this.drag, true);
|
||||
elDocument.removeEventListener('mouseup', this.onEndDrag, true);
|
||||
this.removePreventClickId = elWindow.setTimeout(() => {
|
||||
// Remove these asynchronously so we still suppress click events
|
||||
// generated simultaneously with mouseup.
|
||||
elDocument.removeEventListener('click', this.preventClick, true);
|
||||
elDocument.removeEventListener('dblclick', this.preventClick, true);
|
||||
this.removePreventClickId = null;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler to ignore click events during drag
|
||||
*/
|
||||
preventClick = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
onTrackClick(e, axis = 'y') {
|
||||
if (!this.options.clickOnTrack) return;
|
||||
|
||||
const elWindow = getElementWindow(this.el);
|
||||
this.axis[axis].scrollbar.rect = this.axis[
|
||||
axis
|
||||
].scrollbar.el.getBoundingClientRect();
|
||||
const scrollbar = this.axis[axis].scrollbar;
|
||||
const scrollbarOffset = scrollbar.rect[this.axis[axis].offsetAttr];
|
||||
const hostSize = parseInt(this.elStyles[this.axis[axis].sizeAttr], 10);
|
||||
let scrolled = this.contentWrapperEl[this.axis[axis].scrollOffsetAttr];
|
||||
const t =
|
||||
axis === 'y'
|
||||
? this.mouseY - scrollbarOffset
|
||||
: this.mouseX - scrollbarOffset;
|
||||
const dir = t < 0 ? -1 : 1;
|
||||
const scrollSize = dir === -1 ? scrolled - hostSize : scrolled + hostSize;
|
||||
|
||||
const scrollTo = () => {
|
||||
if (dir === -1) {
|
||||
if (scrolled > scrollSize) {
|
||||
scrolled -= this.options.clickOnTrackSpeed;
|
||||
this.contentWrapperEl.scrollTo({
|
||||
[this.axis[axis].offsetAttr]: scrolled
|
||||
});
|
||||
elWindow.requestAnimationFrame(scrollTo);
|
||||
}
|
||||
} else {
|
||||
if (scrolled < scrollSize) {
|
||||
scrolled += this.options.clickOnTrackSpeed;
|
||||
this.contentWrapperEl.scrollTo({
|
||||
[this.axis[axis].offsetAttr]: scrolled
|
||||
});
|
||||
elWindow.requestAnimationFrame(scrollTo);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scrollTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for content element
|
||||
*/
|
||||
getContentElement() {
|
||||
return this.contentEl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for original scrolling element
|
||||
*/
|
||||
getScrollElement() {
|
||||
return this.contentWrapperEl;
|
||||
}
|
||||
|
||||
getScrollbarWidth() {
|
||||
// Try/catch for FF 56 throwing on undefined computedStyles
|
||||
try {
|
||||
// Detect browsers supporting CSS scrollbar styling and do not calculate
|
||||
if (
|
||||
getComputedStyle(this.contentWrapperEl, '::-webkit-scrollbar')
|
||||
.display === 'none' ||
|
||||
'scrollbarWidth' in document.documentElement.style ||
|
||||
'-ms-overflow-style' in document.documentElement.style
|
||||
) {
|
||||
return 0;
|
||||
} else {
|
||||
return scrollbarWidth(this.el);
|
||||
}
|
||||
} catch (e) {
|
||||
return scrollbarWidth(this.el);
|
||||
}
|
||||
}
|
||||
|
||||
removeListeners() {
|
||||
const elWindow = getElementWindow(this.el);
|
||||
// Event listeners
|
||||
if (this.options.autoHide) {
|
||||
this.el.removeEventListener('mouseenter', this.onMouseEnter);
|
||||
}
|
||||
|
||||
['mousedown', 'click', 'dblclick'].forEach(e => {
|
||||
this.el.removeEventListener(e, this.onPointerEvent, true);
|
||||
});
|
||||
|
||||
['touchstart', 'touchend', 'touchmove'].forEach(e => {
|
||||
this.el.removeEventListener(e, this.onPointerEvent, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
});
|
||||
|
||||
this.el.removeEventListener('mousemove', this.onMouseMove);
|
||||
this.el.removeEventListener('mouseleave', this.onMouseLeave);
|
||||
|
||||
if (this.contentWrapperEl) {
|
||||
this.contentWrapperEl.removeEventListener('scroll', this.onScroll);
|
||||
}
|
||||
|
||||
elWindow.removeEventListener('resize', this.onWindowResize);
|
||||
|
||||
if (this.mutationObserver) {
|
||||
this.mutationObserver.disconnect();
|
||||
}
|
||||
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
}
|
||||
|
||||
// Cancel all debounced functions
|
||||
this.recalculate.cancel();
|
||||
this.onMouseMove.cancel();
|
||||
this.hideScrollbars.cancel();
|
||||
this.onWindowResize.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* UnMount mutation observer and delete SimpleBar instance from DOM element
|
||||
*/
|
||||
unMount() {
|
||||
this.removeListeners();
|
||||
SimpleBar.instances.delete(this.el);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if mouse is within bounds
|
||||
*/
|
||||
isWithinBounds(bbox) {
|
||||
return (
|
||||
this.mouseX >= bbox.left &&
|
||||
this.mouseX <= bbox.left + bbox.width &&
|
||||
this.mouseY >= bbox.top &&
|
||||
this.mouseY <= bbox.top + bbox.height
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find element children matches query
|
||||
*/
|
||||
findChild(el, query) {
|
||||
const matches =
|
||||
el.matches ||
|
||||
el.webkitMatchesSelector ||
|
||||
el.mozMatchesSelector ||
|
||||
el.msMatchesSelector;
|
||||
return Array.prototype.filter.call(el.children, child =>
|
||||
matches.call(child, query)
|
||||
)[0];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user