diff --git a/src/scripts/choices.js b/src/scripts/choices.js index b1c2bd7..50d37a6 100644 --- a/src/scripts/choices.js +++ b/src/scripts/choices.js @@ -15,7 +15,6 @@ import { DEFAULT_CLASSNAMES, EVENTS, KEY_CODES, - SCROLLING_SPEED, } from './constants'; import { TEMPLATES } from './templates'; import { @@ -1116,7 +1115,7 @@ class Choices { if ( !isScrolledIntoView(nextEl, this.choiceList.element, directionInt) ) { - this._scrollToChoice(nextEl, directionInt); + this.choiceList.scrollToChoice(nextEl, directionInt); } this._highlightChoice(nextEl); } @@ -1392,69 +1391,6 @@ class Choices { } } - _scrollToChoice(choice, direction) { - if (!choice) { - return; - } - - const dropdownHeight = this.choiceList.element.offsetHeight; - const choiceHeight = choice.offsetHeight; - // Distance from bottom of element to top of parent - const choicePos = choice.offsetTop + choiceHeight; - // Scroll position of dropdown - const containerScrollPos = - this.choiceList.element.scrollTop + dropdownHeight; - // Difference between the choice and scroll position - const endPoint = - direction > 0 - ? this.choiceList.element.scrollTop + choicePos - containerScrollPos - : choice.offsetTop; - - const scrollDown = (scrollPos, strength) => { - const easing = (endPoint - scrollPos) / strength; - const distance = easing > 1 ? easing : 1; - - this.choiceList.scrollTo(scrollPos + distance); - }; - - const scrollUp = (scrollPos, strength) => { - const easing = (scrollPos - endPoint) / strength; - const distance = easing > 1 ? easing : 1; - - this.choiceList.scrollTo(scrollPos - distance); - }; - - const animateScroll = () => { - const strength = SCROLLING_SPEED; - const choiceListScrollTop = this.choiceList.element.scrollTop; - let continueAnimation = false; - - if (direction > 0) { - scrollDown(choiceListScrollTop, strength); - - if (choiceListScrollTop < endPoint) { - continueAnimation = true; - } - } else { - scrollUp(choiceListScrollTop, strength); - - if (choiceListScrollTop > endPoint) { - continueAnimation = true; - } - } - - if (continueAnimation) { - requestAnimationFrame(time => { - animateScroll(time, endPoint, direction); - }); - } - }; - - requestAnimationFrame(time => { - animateScroll(time, endPoint, direction); - }); - } - _highlightChoice(el = null) { const choices = Array.from( this.dropdown.element.querySelectorAll('[data-choice-selectable]'), @@ -2028,7 +1964,7 @@ class Choices { // Scroll back to top of choices list if (this.config.resetScrollPosition) { - this.choiceList.scrollTo(0); + this.choiceList.scrollToTop(); } // If we have grouped options diff --git a/src/scripts/components/list.js b/src/scripts/components/list.js index 2d66560..1ddeccc 100644 --- a/src/scripts/components/list.js +++ b/src/scripts/components/list.js @@ -1,3 +1,5 @@ +import { SCROLLING_SPEED } from '../constants'; + export default class List { constructor({ element }) { Object.assign(this, { element }); @@ -7,31 +9,81 @@ export default class List { this.hasChildren = !!this.element.children; } - /** - * Clear List contents - */ clear() { this.element.innerHTML = ''; } - /** - * Scroll to passed position on Y axis - */ - scrollTo(scrollPos = 0) { - this.element.scrollTop = scrollPos; - } - /** - * Append node to element - */ append(node) { this.element.appendChild(node); } - /** - * Find element that matches passed selector - * @return {HTMLElement} - */ getChild(selector) { return this.element.querySelector(selector); } + + scrollToTop() { + this.element.scrollTop = 0; + } + + scrollToChoice(choice, direction) { + if (!choice) { + return; + } + + const dropdownHeight = this.element.offsetHeight; + const choiceHeight = choice.offsetHeight; + // Distance from bottom of element to top of parent + const choicePos = choice.offsetTop + choiceHeight; + // Scroll position of dropdown + const containerScrollPos = this.element.scrollTop + dropdownHeight; + // Difference between the choice and scroll position + const endpoint = + direction > 0 + ? this.element.scrollTop + choicePos - containerScrollPos + : choice.offsetTop; + + requestAnimationFrame(time => { + this._animateScroll(time, endpoint, direction); + }); + } + + _scrollDown(scrollPos, strength, endpoint) { + const easing = (endpoint - scrollPos) / strength; + const distance = easing > 1 ? easing : 1; + + this.element.scrollTop = scrollPos + distance; + } + + _scrollUp(scrollPos, strength, endpoint) { + const easing = (scrollPos - endpoint) / strength; + const distance = easing > 1 ? easing : 1; + + this.element.scrollTop = scrollPos - distance; + } + + _animateScroll(time, endpoint, direction) { + const strength = SCROLLING_SPEED; + const choiceListScrollTop = this.element.scrollTop; + let continueAnimation = false; + + if (direction > 0) { + this._scrollDown(choiceListScrollTop, strength, endpoint); + + if (choiceListScrollTop < endpoint) { + continueAnimation = true; + } + } else { + this._scrollUp(choiceListScrollTop, strength, endpoint); + + if (choiceListScrollTop > endpoint) { + continueAnimation = true; + } + } + + if (continueAnimation) { + requestAnimationFrame(time => { + this._animateScroll(time, endpoint, direction); + }); + } + } } diff --git a/src/scripts/components/list.test.js b/src/scripts/components/list.test.js index 858f897..bed1fb1 100644 --- a/src/scripts/components/list.test.js +++ b/src/scripts/components/list.test.js @@ -33,25 +33,6 @@ describe('components/list', () => { }); }); - describe('scrollTo', () => { - describe('passing position', () => { - it('scrolls element to passed position', () => { - const scrollPosition = 20; - expect(instance.element.scrollTop).to.equal(0); - instance.scrollTo(scrollPosition); - expect(instance.element.scrollTop).to.equal(scrollPosition); - }); - }); - - describe('not passing position', () => { - it('scrolls element to default position', () => { - expect(instance.element.scrollTop).to.equal(0); - instance.scrollTo(); - expect(instance.element.scrollTop).to.equal(0); - }); - }); - }); - describe('append', () => { it('appends passed node to element', () => { const elementToAppend = document.createElement('span');