UNPKG

@adoratorio/hades

Version:

A smooth scrollbar based on Hermes, scroll down 'till hell

194 lines 8.56 kB
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