UNPKG

vue-instantsearch

Version:

👀 Lightning-fast Algolia search for Vue apps

181 lines (165 loc) • 4.63 kB
import { createFeedContainer } from 'instantsearch.js/es/connectors/feeds/FeedContainer'; import { connectFeeds } from 'instantsearch.js/es/connectors/index'; import { createSuitMixin } from '../mixins/suit'; import { createWidgetMixin } from '../mixins/widget'; import { isVue3, renderCompat, getScopedSlot } from '../util/vue-compat'; const AisFeedProvider = { name: 'AisFeedProvider', props: { feedContainer: { type: Object, required: true, }, }, provide() { return { $_ais_getParentIndex: () => this.feedContainer, }; }, render: renderCompat(function(h) { const defaultSlot = getScopedSlot(this, 'default'); return h('div', {}, defaultSlot ? defaultSlot() : []); }), }; export default { name: 'AisFeeds', mixins: [ createWidgetMixin( { connector: connectFeeds, }, { $$widgetType: 'ais.feeds', } ), createSuitMixin({ name: 'Feeds' }), ], props: { isolated: { type: Boolean, required: true, }, transformFeeds: { type: Function, default: undefined, }, }, data() { return { feedContainers: new Map(), removalTimer: null, pendingRemovals: new Map(), }; }, watch: { state(newState) { if (!newState) return; this.reconcileContainers(newState.feedIDs || []); }, }, [isVue3 ? 'beforeUnmount' : 'beforeDestroy']() { if (this.removalTimer !== null) { clearTimeout(this.removalTimer); this.removalTimer = null; } const toRemove = Array.from( new Set([ ...this.feedContainers.values(), ...this.pendingRemovals.values(), ]) ); this.pendingRemovals.clear(); this.feedContainers.clear(); if (toRemove.length > 0) { this.getParentIndex().removeWidgets(toRemove); } }, methods: { reconcileContainers(feedIDs) { const parentIndex = this.getParentIndex(); const activeFeedIDs = new Set(feedIDs); // Remove disappeared feeds (deferred) const toRemove = []; this.feedContainers.forEach((container, id) => { if (!activeFeedIDs.has(id)) { toRemove.push([id, container]); this.feedContainers.delete(id); } }); if (toRemove.length > 0) { toRemove.forEach(([id, container]) => { this.pendingRemovals.set(id, container); }); if (this.removalTimer !== null) { clearTimeout(this.removalTimer); } this.removalTimer = setTimeout(() => { const widgetsToRemove = Array.from(this.pendingRemovals.values()); this.pendingRemovals.clear(); this.removalTimer = null; if (widgetsToRemove.length > 0) { parentIndex.removeWidgets(widgetsToRemove); } }, 0); } // Create new FeedContainers (synchronous — must be registered before render) const toAdd = []; feedIDs.forEach(feedID => { if (!this.feedContainers.has(feedID)) { const pendingContainer = this.pendingRemovals.get(feedID); if (pendingContainer) { this.pendingRemovals.delete(feedID); this.feedContainers.set(feedID, pendingContainer); return; } const container = createFeedContainer( feedID, parentIndex, this.instantSearchInstance ); this.feedContainers.set(feedID, container); toAdd.push(container); } }); if (toAdd.length > 0) { parentIndex.addWidgets(toAdd); } }, }, render: renderCompat(function(h) { if (!this.state) { return h('div', { class: [this.suit()] }); } const feedIDs = this.state.feedIDs || []; const defaultSlot = getScopedSlot(this, 'default'); const children = feedIDs.map(feedID => { const container = this.feedContainers.get(feedID); if (!container || !defaultSlot) { return null; } const slotContent = defaultSlot({ feedID }); return h( AisFeedProvider, isVue3 ? { key: feedID, feedContainer: container, scopedSlots: { default: () => slotContent, }, } : { key: feedID, props: { feedContainer: container } }, slotContent ); }); return h('div', { class: [this.suit()] }, children); }), computed: { widgetParams() { return { isolated: this.isolated, transformFeeds: this.transformFeeds, }; }, }, };