UNPKG

@empathyco/x-components

Version:
106 lines (103 loc) 4.43 kB
import { defineComponent, inject, isRef, ref, computed, provide, h } from 'vue'; import { LIST_ITEMS_KEY } from '../../../components/decorators/injection.consts.js'; import ItemsList from '../../../components/items-list.vue.js'; import { use$x } from '../../../composables/use-_x.js'; import { useState } from '../../../composables/use-state.js'; import { AnimationProp } from '../../../types/animation-prop.js'; import { searchXModule } from '../x-module.js'; /** * It renders a {@link ItemsList} list of banners from {@link SearchState.banners}. * * The component provides a default slot which wraps the whole component with the `banners` * plus the `injectedListItems` which also contains the injected list items from * the ancestor. * * It also provides the parent slots to customize the items. * * @public */ var _sfc_main = defineComponent({ name: 'BannersList', xModule: searchXModule.name, props: { /** Animation component that will be used to animate the banners. */ animation: { type: AnimationProp, default: 'ul', }, }, setup(props, { slots }) { const $x = use$x(); /** The banners to render from the state. */ const stateItems = useState('search').banners; /** The provided {@link FeatureLocation} for the component. */ const injectedLocation = inject('location'); const location = isRef(injectedLocation) ? injectedLocation.value : injectedLocation; /** Number of columns the grid is being divided into. */ const columnsNumber = ref(0); /** * Handler to update the number of columns when it changes. * * @param newColumnsNumber - The new columns value. * @param metadata - The {@link @empathyco/x-bus#SubjectPayload.metadata}. */ $x.on('RenderedColumnsNumberChanged', true).subscribe(({ eventPayload, metadata }) => { if (metadata.location === location) { columnsNumber.value = eventPayload; } }); /** It injects {@link ListItem} provided by an ancestor as injectedListItems. */ const injectedListItems = inject(LIST_ITEMS_KEY); /** * The `stateItems` concatenated with the `injectedListItems` if there are. * * @remarks This computed defines the merging strategy of the `stateItems` and the * `injectedListItems`. * * @returns List of {@link ListItem}. */ const items = computed(() => { if (!injectedListItems?.value.length) { return stateItems.value; } const items = [...injectedListItems.value]; let index = 0; let previousBannerRow = -1; for (const item of stateItems.value) { const position = item.position ?? 1; let row = position - 1; if (row <= previousBannerRow) { row = previousBannerRow + 1; } const rowsDiff = row - previousBannerRow; if (rowsDiff > 1) { index += (rowsDiff - 1) * columnsNumber.value; } const isIndexInLoadedPages = index <= items.length; const areAllPagesLoaded = $x.results.length === $x.totalResults; if (!isIndexInLoadedPages && !areAllPagesLoaded) { break; } items.splice(index, 0, item); index++; previousBannerRow = row; } return items; }); /** * The computed list items of the entity that uses the mixin. * * @remarks It should be overridden in the component that uses the mixin and it's intended to be * filled with items from the state. Vue doesn't allow mixins as abstract classes. * @returns An empty array as fallback in case it is not overridden. */ provide(LIST_ITEMS_KEY, items); return () => { const innerProps = { items: items.value, animation: props.animation }; // https://vue-land.github.io/faq/forwarding-slots#passing-all-slots return slots.default?.(innerProps)[0] ?? h(ItemsList, innerProps, slots); }; }, }); export { _sfc_main as default }; //# sourceMappingURL=banners-list.vue.js.map