Banners
TsBannerHomeHighlights
Component Code
<template>
<component
:is="props.product.enableRedirection || props.landingPageUrl ? NuxtLink : 'div'"
v-bind="nuxt_link_attrs"
:id="props.id || `product-banner-${random_id}`"
:class="twMerge(
'ts-banner h-full relative font-gilroy rounded-lg overflow-hidden w-max bg-[#2EB5CD]',
(props.product?.enableRedirection || props.landingPageUrl) && 'inline-block'
)"
:style="root_styles"
>
<div ref="bannerHomeHighlightRef"
class="w-full h-full pl-4 relative min-w-[320px] max-w-[320px] min-h-[170px]
md:min-w-[408px] md:min-h-[205px] md:max-w-[408px] group/highlight-card">
<TsMedia :append-class="twMerge(
'h-full min-h-[inherit] rounded-lg text-left items-stretch'
)">
<TsMediaContent append-class="pt-4 pb-5">
<button
v-if="props.clickAndCollectButton && props.clickAndCollectButton.label && props.clickAndCollectButton.label.text"
class="flex items-center gap-2 text-white opacity-0 rounded-full group-hover/highlight-card:opacity-100 transition-all duration-300 bg-[#1094AC] border-[#52C9DF] absolute top-2 right-2 p-2 text-xs leading-[10px]"
size="xs" rounded :style="click_and_collect_button_styles">
<NuxtImg src="/images/logos/cursor.svg" width="16" />
<span>
{{ props.clickAndCollectButton.label.text }}
</span>
</button>
<div class="mb-2 ">
<NuxtImg
v-if="props.brandLogo && props.brandLogo.src"
:src="props.brandLogo.src"
width="50"
height="12"
/>
</div>
<div
:class="twMerge('text-[24px] font-semibold text-idle-white leading-tight line-clamp-3 mb-1.5')"
:style="title_styles"
v-html="(props.product && props.product.enableTitle && product_data && product_data?.full_name) ? product_data?.full_name : props.title?.text">
</div>
<div
:class="twMerge('text-[14px] font-semibold text-white leading-none line-clamp-2 mb-1.5')"
:style="description_styles"
v-html="(props.product && props.product.enableDescription && product_data && product_data?.description
) ? product_data?.description
: props.description?.text">
</div>
<div v-if="product_price && props.product?.enablePrice" class="text-white mb-1.5" :style="price_styles">
<span class="text-2xl font-bold">
{{ product_price }}
</span>
<span class="text-normal font-semibold leading-none">
{{ rootStore.isIncVat ? 'Inc.' : 'Excl.' }} VAT
</span>
</div>
<button
v-if="props.button && props.button.label && props.button.label.text"
:class="twMerge('bg-primary text-white text-[14px] leading-[20px] font-semibold max-w-max rounded-md px-3 py-2', !props.product?.enablePrice && 'mt-2')"
:style="button_styles"
>
{{ props.button.label.text }}
</button>
</TsMediaContent>
<TsMediaEnd v-if="props.image && props.image.src" append-class="flex items-end">
<NuxtImg class="mt-auto"
:src="props.image.src"
width="200"
height="170"
/>
</TsMediaEnd>
</TsMedia>
</div>
</component>
</template>
<script setup lang="ts">
import {defineProps, ref, withDefaults} from "vue";
import { twMerge } from "tailwind-merge";
const rootStore = useRootStore();
const productStore = useProductStore();
type Props = {
id?: string;
dataTestid?: string;
landingPageUrl?: string;
product?: object;
image?: object;
brandLogo?: object;
title?: object;
description?: object;
price?: object;
button?: object;
clickAndCollectButton?: object;
backgroundGradientColor?: object;
backgroundColor?: object;
}
const props = withDefaults(defineProps<Props>(), {
dataTestid: 'highlights',
image: () => ({
src: '/images/makita-tool.png'
}),
brandLogo: () => ({
src: '/images/logos/makita.svg'
}),
title: () => ({
text: 'Avail benefits <br /> up to €199.00'
}),
description: () => ({
text: 'Cordless Drill'
}),
button: () => ({
label: 'Know More'
}),
clickAndCollectButton: () => ({
label: {
text: 'CLICK & VIEW'
}
})
})
const random_id = ref('');
const product_id = computed(() => props.product?.id);
const product_data = ref(null);
const product_price = computed(() => {
return rootStore.isIncVat ? product_data.value?.prices?.formatted?.gross : product_data.value?.prices?.formatted?.net;
})
const is_video = computed(() => {
const allowed_videos_extensions = [".mp4", ".webm"];
const allowed_images_extensions = [".jpg", "jpeg", ".png", ".gif", ".svg", ".webp"];
if(props.background?.src && props.background.src) return true;
return false;
})
const localePath=useLocalePath();
const NuxtLink = resolveComponent('NuxtLink');
const nuxt_link_attrs = computed(() => ({
...(props.landingPageUrl && {to: props.landingPageUrl}),
...(props.product?.enableRedirection && product_data.value && {to:localePath(`/product/${product_data.value.slug}-${product_data.value.code}`)}),
}))
const title_styles = computed(() => ({
...(props.title?.color && {color: props.title?.color}),
...(props.title?.fontSize && {fontSize: props.title?.fontSize})
}))
const description_styles = computed(() => ({
...(props.description?.color && {color: props.description?.color}),
...(props.description?.fontSize && {fontSize: props.description?.fontSize}),
}))
const price_styles = computed(() => ({
...(props.price?.color && {color: props.price?.color}),
...(props.price?.custom && {color: props.price?.custom}),
}))
const button_styles = computed(() => ({
...(props.button.label?.color && {color: props.button?.label?.color}),
...(props.button.label?.custom && {color: props.button?.label?.custom}),
...(props.button.label?.fontSize && {fontSize: props.button?.label?.fontSize}),
...(props.button.background?.color && {backgroundColor: props.button?.background?.color}),
...(props.button.background?.custom && {backgroundColor: props.button?.background?.custom}),
...((props.button?.marginTop || props.button?.marginTop === 0) && {marginTop: `${props.button.marginTop}px`}),
}))
const click_and_collect_button_styles = computed(() => ({
...(props.clickAndCollectButton.label?.color && {color: props.clickAndCollectButton?.label?.color}),
...(props.clickAndCollectButton.label?.custom && {color: props.clickAndCollectButton?.label?.custom}),
...(props.clickAndCollectButton.label?.fontSize && {fontSize: props.clickAndCollectButton?.label?.fontSize}),
...(props.clickAndCollectButton.background?.color && {backgroundColor: props.clickAndCollectButton?.background?.color}),
...(props.clickAndCollectButton.background?.custom && {backgroundColor: props.clickAndCollectButton?.background?.custom}),
...(props.clickAndCollectButton.borderColor?.color && {border: `1px solid ${props.clickAndCollectButton?.borderColor?.color}`}),
...(props.clickAndCollectButton.borderColor?.custom && {border: `1px solid ${props.clickAndCollectButton?.borderColor?.custom}`}),
}))
const root_styles = computed(() => ({
...(props.backgroundColor?.color && {backgroundColor: props.backgroundColor.color }),
...(props.backgroundColor?.custom && {backgroundColor: props.backgroundColor.custom }),
}))
const bannerHomeHighlightRef = ref('')
let observer;
const observeElement = (element) => {
observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
fetchProduct()
}
})
observer.observe(element)
};
const fetchProduct = async () => {
// Replace with your actual fetch request
if(props.product?.id) {
product_data.value = await productStore.getProductById(product_id.value);
}
if (observer && bannerHomeHighlightRef.value) {
observer.unobserve(bannerHomeHighlightRef.value)
}
}
onMounted(async () => {
random_id.value = useRandomUUID();
if (bannerHomeHighlightRef.value) {
observeElement(bannerHomeHighlightRef.value)
}
});
onUnmounted(() => {
if (observer && bannerHomeHighlightRef.value) {
observer.unobserve(bannerHomeHighlightRef.value)
}
})
watch(product_id, async () => {
await fetchProduct();
})
</script>
<style scoped>
</style>
Usage in Builder
- Go to the
inserttab. - Search
BannerHomeHighlightsin search bar. - Hold
BannerHomeHighlightscomponent & drag where you want to use.
Props
| Name | Type | Default | Description |
|---|---|---|---|
| id | string | "" | It is used to provide the id to the component. |
| landingPageUrl | string | "" | It is used to add link to the banner. |
| product | object | "" | It has keys which are: id - string, enablePrice - boolean, enableTitle - boolean, enableDescription - boolean, enableRedirection - boolean. id is used to provide the productId, enablePrice is used to show the price of product based on id, enableTitle is used to show the title of the product based on id, enableDescription is used to show the description of the product based on id, enableRedirection is used to add link to the banner based on product slug which is dynamic from product data. |
| background | object | "" | It has keys which are: color - string, customColor - string. color - to select color from atomic design system for background, customColor - to add any color using color code. |
| overlay | string | "" | It is used to add the overlay color of background |
| image | object | "" | It has keys which are: src - string. src is used to add the url of image. |
| brandLogo | object | "" | It has keys which are: src - string. src is used to add the url of logo image. |
| title | object | "" | It has keys which are: text - richText, fontSize - string, color - string. text is used to add title text, fontSize is used to add size of title, color is used to add color to the title |
| description | object | "" | It has keys which are: text - richText, fontSize - string, color - string. text is used to add description text, fontSize is used to add size of description, color is used to add color to the description |
| price | object | "" | It has keys which are: color - string, customColor - string. color - to select color from atomic design system for price, customColor - to add any color using color code. |
| button | object | "" | It has keys which are: label - object, background - object, marginTop - number. label has four keys: 1 - text value of button, 2 - fontSize - to select the predefined font sizes, 3 - color - to select color from atomic design system for text, 4 - customColor - to add any color using color code. background has two keys: 1 - color - to select color from atomic design system for background , customColor - to add any color using color code. marginTop - to add top margin to the button. |
| clickAndCollectButton | object | "" | It has keys which are: label - object, background - object, borderColor - number. label has four keys: 1 - text value of button, 2 - fontSize - to select the predefined font sizes, 3 - color - to select color from atomic design system for text, 4 - customColor - to add any color using color code. background has two keys: 1 - color - to select color from atomic design system for background , customColor - to add any color using color code. borderColor has two keys: 1 - color - to select color from atomic design system for borderColor , customColor - to add any color using color code. |