diff options
Diffstat (limited to 'src/hocs/with_load_more')
| -rw-r--r-- | src/hocs/with_load_more/with_load_more.js | 91 | ||||
| -rw-r--r-- | src/hocs/with_load_more/with_load_more.scss | 10 |
2 files changed, 101 insertions, 0 deletions
diff --git a/src/hocs/with_load_more/with_load_more.js b/src/hocs/with_load_more/with_load_more.js new file mode 100644 index 00000000..e862a39b --- /dev/null +++ b/src/hocs/with_load_more/with_load_more.js @@ -0,0 +1,91 @@ +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, + scopedSlots: this.$scopedSlots + } + const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value)) + return ( + <div class="with-load-more"> + <WrappedComponent {...props}> + {children} + </WrappedComponent> + <div class="with-load-more-footer"> + {this.error && <a onClick={this.fetchEntries} class="alert error">{this.$t('general.generic_error')}</a>} + {!this.error && this.loading && <i class="icon-spin3 animate-spin"/>} + {!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries}>{this.$t('general.more')}</a>} + </div> + </div> + ) + }, + 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 diff --git a/src/hocs/with_load_more/with_load_more.scss b/src/hocs/with_load_more/with_load_more.scss new file mode 100644 index 00000000..1a0a9c40 --- /dev/null +++ b/src/hocs/with_load_more/with_load_more.scss @@ -0,0 +1,10 @@ +.with-load-more { + &-footer { + padding: 10px; + text-align: center; + + .error { + font-size: 14px; + } + } +}
\ No newline at end of file |
