addsearch-search-ui
Version:
JavaScript library to develop Search UIs for the web
107 lines (84 loc) • 3.43 kB
JavaScript
import './loadmore.scss';
import handlebars from 'handlebars';
import { LOAD_MORE_TEMPLATE } from './templates';
import { LOAD_MORE_TYPE } from './index';
import { setPage } from '../../actions/pagination';
import { search } from '../../actions/search';
import { observeStoreByKey } from '../../store';
import { validateContainer } from '../../util/dom';
export default class LoadMore {
constructor(client, reduxStore, conf) {
this.client = client;
this.reduxStore = reduxStore;
this.conf = conf;
if (validateContainer(conf.containerId)) {
observeStoreByKey(this.reduxStore, 'search', (searchState) => this.render(searchState));
}
if (conf.type === LOAD_MORE_TYPE.INFINITE_SCROLL) {
this.conf.infiniteScrollElement.addEventListener('scroll', () => this.onScroll());
}
}
render(searchState) {
const currentPage = searchState.results.page || 1;
const pageSize = this.client.getSettings().paging.pageSize;
const totalHits = searchState.results.total_hits || 0;
const totalPages = Math.ceil(totalHits / pageSize);
const data = {
type: this.conf.type,
hasMorePages: (currentPage < totalPages),
isLoading: searchState.loading,
totalHits
};
// Compile HTML and inject to element if changed
const html = handlebars.compile(this.conf.template || LOAD_MORE_TEMPLATE)(data);
if (this.renderedHtml === html) {
return;
}
const container = document.getElementById(this.conf.containerId);
container.innerHTML = html;
this.renderedHtml = html;
// Attach click event
if (this.conf.type === LOAD_MORE_TYPE.BUTTON) {
const button = container.querySelector('button');
if (button) {
button.onclick = (e) => this.loadMore();
}
}
// If infinite scroll in a scrollable HTML element, scroll top when keyword changes
else if (this.conf.type === LOAD_MORE_TYPE.INFINITE_SCROLL &&
this.conf.infiniteScrollElement.tagName &&
searchState.results.page === 1 && !searchState.loading) {
this.conf.infiniteScrollElement.scrollTop = 0;
}
}
loadMore() {
const currentPage = this.reduxStore.getState().pagination.page || 1;
const pageToDispatch = currentPage + 1;
// Dispatch the new page number
this.reduxStore.dispatch(setPage(this.client, pageToDispatch, false, this.reduxStore));
// Fetch more results
const keyword = this.reduxStore.getState().keyword.value;
this.reduxStore.dispatch(search(this.client, keyword, null, true, null, this.reduxStore, null, 'component.loadMore'));
}
onScroll() {
const isLoading = this.reduxStore.getState().search.loading;
const scrollElement = document.querySelector('#' + this.conf.containerId + ' .loadmore-infinite-scroll');
if (!isLoading && scrollElement) {
// Scrollable HTML element
if (this.conf.infiniteScrollElement.tagName) {
const scrollable = this.conf.infiniteScrollElement;
if (Math.ceil(scrollable.offsetHeight + scrollable.scrollTop) >= scrollable.scrollHeight) {
this.loadMore();
}
}
// Window onscroll
else {
const viewportHeight = window.innerHeight;
const infiniteScrollTop = scrollElement.getBoundingClientRect().top;
if (infiniteScrollTop > 0 && infiniteScrollTop < viewportHeight) {
this.loadMore();
}
}
}
}
}