Move scrolling logic into list component

This commit is contained in:
Josh Johnson 2018-05-28 14:09:11 +01:00
parent f9455b1a25
commit 804a4a442b
3 changed files with 70 additions and 101 deletions

View file

@ -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

View file

@ -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);
});
}
}
}

View file

@ -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');