@empathyco/x-components
Version:
Empathy X Components
106 lines (103 loc) • 4.43 kB
JavaScript
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