import Vue from 'vue' import filter from 'lodash/filter' import isEmpty from 'lodash/isEmpty' import './with_load_more.scss' const withLoadMore = ({ fetch, // function to fetch entries and return a promise select, // function to select data from store childPropName = 'entries' // name of the prop to be passed into the wrapped component }) => (WrappedComponent) => { const originalProps = WrappedComponent.props || [] const props = filter(originalProps, v => v !== 'entries') return Vue.component('withLoadMore', { render (createElement) { const props = { props: { ...this.$props, [childPropName]: this.entries }, on: this.$listeners } return (
) }, props, data () { return { loading: false, bottomedOut: false, error: false } }, computed: { entries () { return select(this.$props, this.$store) || [] } }, created () { window.addEventListener('scroll', this.scrollLoad) if (this.entries.length === 0) { this.fetchEntries() } }, destroyed () { window.removeEventListener('scroll', this.scrollLoad) }, methods: { fetchEntries () { if (!this.loading) { this.loading = true this.error = false fetch(this.$props, this.$store) .then((newEntries) => { this.loading = false this.bottomedOut = isEmpty(newEntries) }) .catch(() => { this.loading = false this.error = true }) } }, scrollLoad (e) { const bodyBRect = document.body.getBoundingClientRect() const height = Math.max(bodyBRect.height, -(bodyBRect.y)) if (this.loading === false && this.bottomedOut === false && this.$el.offsetHeight > 0 && (window.innerHeight + window.pageYOffset) >= (height - 750) ) { this.fetchEntries() } } } }) } export default withLoadMore