simplebar
Version:
Scrollbars, simpler.
203 lines (174 loc) • 6.92 kB
text/typescript
import SimpleBarCore from 'simplebar-core';
const { getOptions, addClasses, canUseDOM } = SimpleBarCore.helpers;
export default class SimpleBar extends SimpleBarCore {
static globalObserver: MutationObserver;
static instances = new WeakMap<Node, SimpleBar>();
constructor(...args: ConstructorParameters<typeof SimpleBarCore>) {
super(...args);
// // Save a reference to the instance, so we know this DOM node has already been instancied
SimpleBar.instances.set(args[0], this);
}
static initDOMLoadedElements() {
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));
}
);
}
static removeObserver() {
SimpleBar.globalObserver?.disconnect();
}
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
) {
// 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');
addClasses(this.wrapperEl, this.classNames.wrapper);
addClasses(this.contentWrapperEl, this.classNames.contentWrapper);
addClasses(this.offsetEl, this.classNames.offset);
addClasses(this.maskEl, this.classNames.mask);
addClasses(this.contentEl, this.classNames.contentEl);
addClasses(this.placeholderEl, this.classNames.placeholder);
addClasses(
this.heightAutoObserverWrapperEl,
this.classNames.heightAutoObserverWrapperEl
);
addClasses(
this.heightAutoObserverEl,
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);
this.contentWrapperEl?.setAttribute(
'tabindex',
this.options.tabIndex.toString()
);
this.contentWrapperEl?.setAttribute('role', 'region');
this.contentWrapperEl?.setAttribute('aria-label', this.options.ariaLabel);
}
if (!this.axis.x.track.el || !this.axis.y.track.el) {
const track = document.createElement('div');
const scrollbar = document.createElement('div');
addClasses(track, this.classNames.track);
addClasses(scrollbar, this.classNames.scrollbar);
track.appendChild(scrollbar);
this.axis.x.track.el = track.cloneNode(true) as HTMLElement;
addClasses(this.axis.x.track.el, this.classNames.horizontal);
this.axis.y.track.el = track.cloneNode(true) as HTMLElement;
addClasses(this.axis.y.track.el, this.classNames.vertical);
this.el.appendChild(this.axis.x.track.el);
this.el.appendChild(this.axis.y.track.el);
}
SimpleBarCore.prototype.initDOM.call(this);
this.el.setAttribute('data-simplebar', 'init');
}
unMount() {
SimpleBarCore.prototype.unMount.call(this);
SimpleBar.instances.delete(this.el);
}
static initHtmlApi() {
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' || // @ts-ignore: IE specific
(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);
}
}
static handleMutations(mutations: MutationRecord[]) {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((addedNode) => {
if (addedNode.nodeType === 1) {
if ((addedNode as Element).hasAttribute('data-simplebar')) {
!SimpleBar.instances.has(addedNode) &&
document.documentElement.contains(addedNode) &&
new SimpleBar(
addedNode as HTMLElement,
getOptions((addedNode as Element).attributes)
);
} else {
(addedNode as Element)
.querySelectorAll('[data-simplebar]')
.forEach((el) => {
if (
el.getAttribute('data-simplebar') !== 'init' &&
!SimpleBar.instances.has(el) &&
document.documentElement.contains(el)
)
new SimpleBar(el as HTMLElement, getOptions(el.attributes));
});
}
}
});
mutation.removedNodes.forEach((removedNode) => {
if (removedNode.nodeType === 1) {
if (
(removedNode as Element).getAttribute('data-simplebar') === 'init'
) {
!document.documentElement.contains(removedNode) &&
SimpleBar.instances.get(removedNode)?.unMount();
} else {
Array.prototype.forEach.call(
(removedNode as Element).querySelectorAll(
'[data-simplebar="init"]'
),
(el) => {
!document.documentElement.contains(el) &&
SimpleBar.instances.get(el)?.unMount();
}
);
}
}
});
});
}
}
/**
* HTML API
* Called only in a browser env.
*/
if (canUseDOM) {
SimpleBar.initHtmlApi();
}