@adoratorio/hades
Version:
A smooth scrollbar based on Hermes, scroll down 'till hell
194 lines • 8.56 kB
JavaScript
import style from './style';
import { TRACK } from './declarations';
class Scrollbars {
options;
context = null;
virtual = undefined;
wrapper = null;
style = style;
trackX = { wrapper: null, thumb: null, thumbSize: 0, ratio: 0, drag: false, };
trackY = { wrapper: null, thumb: null, thumbSize: 0, ratio: 0, drag: false, };
drag = false;
detectPositionHandler;
dragStartHandler;
dragEndHandler;
name = 'Scrollbars';
constructor(options) {
const defaults = {
viewport: document.body,
tracks: [TRACK.Y],
};
this.options = { ...defaults, ...options };
this.detectPositionHandler = (event) => this.detectPosition(event);
this.dragStartHandler = (event) => this.dragStart(event);
this.dragEndHandler = (event) => this.dragEnd(event);
}
register(context) {
this.virtual = context.getPlugin('VirtualRender');
if (!this.virtual) {
throw new Error('Cannot initialize scrollbar without Virtual Render Plugin');
}
this.context = context;
this.appendStyle();
this.appendDom();
if (!window.matchMedia('(pointer: coarse) and (hover: none)').matches) {
this.attachEvents();
}
}
;
appendDom() {
const scrollbar = document.createElement('div');
scrollbar.classList.add('scrollbar__wrapper');
this.options.viewport.append(scrollbar);
this.options.tracks.forEach((track) => {
const wrapper = document.createElement('div');
wrapper.setAttribute('data-scrollbar', `track-${track}`);
const thumb = document.createElement('div');
thumb.classList.add('scrollbar__thumb');
wrapper.append(thumb);
scrollbar.append(wrapper);
this.wrapper = scrollbar;
if (track === 'x') {
const thumbSize = thumb.getBoundingClientRect().width;
this.trackX = {
wrapper,
thumb,
thumbSize,
ratio: 0,
drag: false,
};
}
if (track === 'y') {
const thumbSize = thumb.getBoundingClientRect().height;
this.trackY = {
wrapper,
thumb,
thumbSize,
ratio: 0,
drag: false,
};
}
});
}
;
appendStyle() {
const style = document.createElement('style');
style.id = 'hades-style';
style.textContent = this.style;
if (document.head)
document.head.appendChild(style);
}
;
attachEvents() {
if (this.trackX.wrapper !== null && this.trackX.thumb !== null) {
this.trackX.wrapper.addEventListener('click', this.detectPositionHandler);
this.trackX.wrapper.addEventListener('mousedown', this.dragStartHandler);
}
if (this.trackY.wrapper !== null && this.trackY.thumb !== null) {
this.trackY.wrapper.addEventListener('click', this.detectPositionHandler);
this.trackY.wrapper.addEventListener('mousedown', this.dragStartHandler);
}
}
render() {
if (this.context && this.virtual && (this.trackX.wrapper !== null && this.trackX.thumb !== null)) {
const ratio = this.context.amount.x / this.virtual.boundaries.max.x;
const { width } = this.trackX.wrapper.getBoundingClientRect();
const translate = (width - this.trackX.thumbSize) * ratio;
this.trackX.thumb.style.transform = `translate3d(${translate}px, 0px, 0px)`;
if (ratio === this.trackX.ratio) {
this.trackX.wrapper.classList.remove('show');
}
else {
this.trackX.wrapper.classList.add('show');
}
this.trackX.ratio = ratio;
}
if (this.context && this.virtual && (this.trackY.wrapper !== null && this.trackY.thumb !== null)) {
const ratio = this.context.amount.y / this.virtual.boundaries.max.y;
const { height } = this.trackY.wrapper.getBoundingClientRect();
const translate = (height - this.trackY.thumbSize) * ratio;
this.trackY.thumb.style.transform = `translate3d(0px, ${translate}px, 0px)`;
if (ratio === this.trackY.ratio) {
this.trackY.wrapper.classList.remove('show');
}
else {
this.trackY.wrapper.classList.add('show');
}
this.trackY.ratio = ratio;
}
}
;
detectPosition(event) {
const duration = event.type === 'click' ? 400 : 200;
if (this.context && this.virtual && ((event.type === 'click' && event.target.dataset.scrollbar === 'track-y')
|| (event.type === 'mousemove' && this.drag && this.trackY.drag))) {
if (this.trackY.wrapper !== null && this.trackY.thumb !== null) {
const { height } = this.trackY.wrapper.getBoundingClientRect();
this.context.scrollTo({
y: event.clientY / height * this.virtual.boundaries.max.y,
}, duration);
}
}
if (this.context && this.virtual && ((event.type === 'click' && event.target.dataset.scrollbar === 'track-x')
|| (event.type === 'mousemove' && this.drag && this.trackX.drag))) {
if (this.trackX.wrapper !== null && this.trackX.thumb !== null) {
const { width } = this.trackX.wrapper.getBoundingClientRect();
this.context.scrollTo({
x: event.clientX / width * this.virtual.boundaries.max.x,
}, duration);
}
}
}
dragStart(event) {
this.drag = true;
if (this.trackY.wrapper !== null && this.trackY.thumb !== null) {
this.trackY.wrapper.classList.add('show');
this.trackY.drag = event.target.parentNode.dataset.scrollbar === 'track-y';
}
if (this.trackX.wrapper !== null && this.trackX.thumb !== null) {
this.trackX.wrapper.classList.add('show');
this.trackX.drag = event.target.parentNode.dataset.scrollbar === 'track-x';
}
document.body.addEventListener('mousemove', this.detectPositionHandler);
document.body.addEventListener('mouseup', this.dragEndHandler);
document.addEventListener('mouseleave', this.dragEndHandler);
document.body.addEventListener('mouseleave', this.dragEndHandler);
}
dragEnd(event) {
this.drag = false;
if (this.trackY.wrapper !== null && this.trackY.thumb !== null) {
this.trackY.wrapper.classList.remove('show');
this.trackY.drag = false;
}
if (this.trackX.wrapper !== null && this.trackX.thumb !== null) {
this.trackX.wrapper.classList.remove('show');
this.trackX.drag = false;
}
document.body.removeEventListener('mousemove', this.detectPositionHandler);
document.body.removeEventListener('mouseup', this.dragEndHandler);
document.removeEventListener('mouseleave', this.dragEndHandler);
document.body.removeEventListener('mouseleave', this.dragEndHandler);
}
destroy() {
if (this.trackX.wrapper !== null && this.trackX.thumb !== null) {
this.trackX.wrapper.removeEventListener('click', this.detectPositionHandler);
this.trackX.wrapper.removeEventListener('mousedown', this.dragStartHandler);
}
if (this.trackY.wrapper !== null && this.trackY.thumb !== null) {
this.trackY.wrapper.removeEventListener('click', this.detectPositionHandler);
this.trackY.wrapper.removeEventListener('mousedown', this.dragStartHandler);
}
document.body.removeEventListener('mousemove', this.detectPositionHandler);
document.body.removeEventListener('mouseup', this.dragEndHandler);
document.removeEventListener('mouseleave', this.dragEndHandler);
document.body.removeEventListener('mouseleave', this.dragEndHandler);
const style = document.getElementById('hades-style');
if (!style || !style.parentNode)
return;
style.parentNode.removeChild(style);
if (this.wrapper !== null)
this.wrapper.remove();
}
}
export default Scrollbars;
//# sourceMappingURL=index.js.map