2018-05-28 15:09:11 +02:00
|
|
|
import { SCROLLING_SPEED } from '../constants';
|
|
|
|
|
2019-11-03 14:18:16 +01:00
|
|
|
/**
|
|
|
|
* @typedef {import('../../../types/index').Choices.Choice} Choice
|
|
|
|
*/
|
2017-08-29 13:56:54 +02:00
|
|
|
export default class List {
|
2019-11-03 14:18:16 +01:00
|
|
|
/**
|
|
|
|
* @param {{ element: HTMLElement }} args
|
|
|
|
*/
|
2018-05-21 18:01:03 +02:00
|
|
|
constructor({ element }) {
|
2019-11-03 14:18:16 +01:00
|
|
|
this.element = element;
|
2017-08-29 13:56:54 +02:00
|
|
|
this.scrollPos = this.element.scrollTop;
|
|
|
|
this.height = this.element.offsetHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
clear() {
|
|
|
|
this.element.innerHTML = '';
|
|
|
|
}
|
|
|
|
|
2019-11-03 14:18:16 +01:00
|
|
|
/**
|
2019-11-03 18:45:16 +01:00
|
|
|
* @param {Element | DocumentFragment} node
|
2019-11-03 14:18:16 +01:00
|
|
|
*/
|
2017-08-29 13:56:54 +02:00
|
|
|
append(node) {
|
|
|
|
this.element.appendChild(node);
|
|
|
|
}
|
|
|
|
|
2019-11-03 14:18:16 +01:00
|
|
|
/**
|
|
|
|
* @param {string} selector
|
|
|
|
* @returns {Element | null}
|
|
|
|
*/
|
2017-08-29 13:56:54 +02:00
|
|
|
getChild(selector) {
|
|
|
|
return this.element.querySelector(selector);
|
|
|
|
}
|
2018-05-28 15:09:11 +02:00
|
|
|
|
2019-11-03 14:18:16 +01:00
|
|
|
/**
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
2019-10-29 22:19:56 +01:00
|
|
|
hasChildren() {
|
|
|
|
return this.element.hasChildNodes();
|
|
|
|
}
|
|
|
|
|
2018-05-28 15:09:11 +02:00
|
|
|
scrollToTop() {
|
|
|
|
this.element.scrollTop = 0;
|
|
|
|
}
|
|
|
|
|
2019-11-03 14:18:16 +01:00
|
|
|
/**
|
2019-11-08 10:19:18 +01:00
|
|
|
* @param {Element} element
|
2019-11-03 14:18:16 +01:00
|
|
|
* @param {1 | -1} direction
|
|
|
|
*/
|
|
|
|
scrollToChildElement(element, direction) {
|
|
|
|
if (!element) {
|
2018-05-28 15:09:11 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-03 14:18:16 +01:00
|
|
|
const listHeight = this.element.offsetHeight;
|
2018-05-28 15:09:11 +02:00
|
|
|
// Scroll position of dropdown
|
2019-11-03 14:18:16 +01:00
|
|
|
const listScrollPosition = this.element.scrollTop + listHeight;
|
|
|
|
|
|
|
|
const elementHeight = element.offsetHeight;
|
|
|
|
// Distance from bottom of element to top of parent
|
|
|
|
const elementPos = element.offsetTop + elementHeight;
|
|
|
|
|
|
|
|
// Difference between the element and scroll position
|
2019-10-29 22:19:56 +01:00
|
|
|
const destination =
|
2018-05-28 15:09:11 +02:00
|
|
|
direction > 0
|
2019-11-03 14:18:16 +01:00
|
|
|
? this.element.scrollTop + elementPos - listScrollPosition
|
|
|
|
: element.offsetTop;
|
2018-05-28 15:09:11 +02:00
|
|
|
|
2019-11-03 14:18:16 +01:00
|
|
|
requestAnimationFrame(() => {
|
|
|
|
this._animateScroll(destination, direction);
|
2018-05-28 15:09:11 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-11-03 14:18:16 +01:00
|
|
|
/**
|
|
|
|
* @param {number} scrollPos
|
|
|
|
* @param {number} strength
|
|
|
|
* @param {number} destination
|
|
|
|
*/
|
2019-10-29 22:19:56 +01:00
|
|
|
_scrollDown(scrollPos, strength, destination) {
|
|
|
|
const easing = (destination - scrollPos) / strength;
|
2018-05-28 15:09:11 +02:00
|
|
|
const distance = easing > 1 ? easing : 1;
|
|
|
|
|
|
|
|
this.element.scrollTop = scrollPos + distance;
|
|
|
|
}
|
|
|
|
|
2019-11-03 14:18:16 +01:00
|
|
|
/**
|
|
|
|
* @param {number} scrollPos
|
|
|
|
* @param {number} strength
|
|
|
|
* @param {number} destination
|
|
|
|
*/
|
2019-10-29 22:19:56 +01:00
|
|
|
_scrollUp(scrollPos, strength, destination) {
|
|
|
|
const easing = (scrollPos - destination) / strength;
|
2018-05-28 15:09:11 +02:00
|
|
|
const distance = easing > 1 ? easing : 1;
|
|
|
|
|
|
|
|
this.element.scrollTop = scrollPos - distance;
|
|
|
|
}
|
|
|
|
|
2019-11-03 14:18:16 +01:00
|
|
|
/**
|
|
|
|
* @param {*} destination
|
|
|
|
* @param {*} direction
|
|
|
|
*/
|
|
|
|
_animateScroll(destination, direction) {
|
2018-05-28 15:09:11 +02:00
|
|
|
const strength = SCROLLING_SPEED;
|
|
|
|
const choiceListScrollTop = this.element.scrollTop;
|
|
|
|
let continueAnimation = false;
|
|
|
|
|
|
|
|
if (direction > 0) {
|
2019-10-29 22:19:56 +01:00
|
|
|
this._scrollDown(choiceListScrollTop, strength, destination);
|
2018-05-28 15:09:11 +02:00
|
|
|
|
2019-10-29 22:19:56 +01:00
|
|
|
if (choiceListScrollTop < destination) {
|
2018-05-28 15:09:11 +02:00
|
|
|
continueAnimation = true;
|
|
|
|
}
|
|
|
|
} else {
|
2019-10-29 22:19:56 +01:00
|
|
|
this._scrollUp(choiceListScrollTop, strength, destination);
|
2018-05-28 15:09:11 +02:00
|
|
|
|
2019-10-29 22:19:56 +01:00
|
|
|
if (choiceListScrollTop > destination) {
|
2018-05-28 15:09:11 +02:00
|
|
|
continueAnimation = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (continueAnimation) {
|
2018-05-28 17:22:22 +02:00
|
|
|
requestAnimationFrame(() => {
|
2019-11-03 14:18:16 +01:00
|
|
|
this._animateScroll(destination, direction);
|
2018-05-28 15:09:11 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2017-08-29 13:56:54 +02:00
|
|
|
}
|