Files
consumocuidado/components/ItemsRow.vue
2025-11-12 14:36:01 +01:00

306 lines
6.5 KiB
Vue

<template>
<div class="row container">
<ClientOnly placerholder="Loading...">
<!-- <div class="arrow-left-wrapper col-2 col-sm-1">
<img
class="arrow"
src="@/assets/img/categories-row-arrow-left.svg"
alt=""
@click="slideCarousel('prev')"
/>
</div> -->
<Carousel
ref="productDetails"
class="carousel"
:items-to-show="itemsToShow"
:breakpoints="config.breakpoints"
:gap="1"
:pagination-enabled="false"
:wrap-around="false"
:mouse-wheel="false"
:breakpoints-enabled="true"
>
<Slide v-for="item in items" :key="item.id" class="slide-container">
<div class="slide">
<div class="image-container">
<img class="image" :src="formattedItem(item).image" alt="" />
</div>
<div class="details-container">
<p>{{ formattedItem(item).name }}</p>
<p class="company">{{ item.company.company_name }}</p>
<p v-if="item.price" class="price">{{ item.price }}</p>
</div>
<div class="links-btns">
<NuxtLink :to="`/productos/${item.id}`" class="div-action show-link">
<img class="div-action-img" src="@/assets/img/eye.svg" />
VER
</NuxtLink>
<NuxtLink v-if="item.url" :to="item.url" class="div-action buy-link">
<img class="div-action-img" src="@/assets/img/shopping-cart.svg" />
COMPRAR
</NuxtLink>
</div>
</div>
</Slide>
<template #addons>
<Navigation />
</template>
</Carousel>
<!-- <div class="arrow-right-wrapper col-2 col-sm-1">
<img
class="arrow"
src="@/assets/img/categories-row-arrow-right.svg"
alt=""
@click="slideCarousel('next')"
/>
</div> -->
</ClientOnly>
</div>
</template>
<script>
import { Carousel, Slide, Navigation } from 'vue3-carousel'
import 'vue3-carousel/dist/carousel.css'
import defaultImage from '@/assets/img/producto-default.png'
export default {
components: {
Carousel,
Slide,
Navigation
},
props: {
type: {
type: String,
required: true,
},
items: {
type: Array,
default: () => [],
},
itemsToShow: {
type: Number,
default: 1,
},
},
data() {
return {
defaultImage,
config: {
height: 200,
breakpointMode: 'carousel', // o 'viewport'
breakpoints: {
768: {
itemsToShow: 3,
snapAlign: 'center',
},
1024: {
itemsToShow: 3,
snapAlign: 'start',
},
1440: {
itemsToShow: 4,
snapAlign: 'start',
},
},
}
}
},
methods: {
formattedItem(item) {
if (this.type === 'product') {
return {
name: item.name,
image:
item.image || this.defaultImage,
url: `/productos/${item.id}`,
}
}
if (this.type === 'company') {
return {
name: item.company_name,
image:
item.logo || this.defaultImage,
url: `/productoras/${item.id}`,
}
}
if (this.type === 'category') {
return {
name: item.name,
image:
item.image || this.defaultImage,
url: `/busqueda?category=${item.name}`,
}
}
},
},
}
</script>
<style lang="scss" scoped>
.row {
padding: 0 4rem;
display: flex;
align-items: center;
justify-content: center
}
.image-container {
width: 100%;
height: 11rem;
flex-shrink: 0;
align-self: stretch;
border-radius: 24px 24px 0 0;
@include mobile {
max-height: 10rem;
max-width: 10rem;
}
.image {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 24px 24px 0 0;
}
}
.slide-container {
padding: 0.5rem;
}
.slide {
width: 237px;
height: 316px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: stretch;
background-color: #FDFCFB;
border: 5px solid white;
border-radius: 24px;
box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 0.15);
text-decoration: none;
position: relative; // necesario para controlar hijos absolutos
overflow: hidden;
p {
text-align: center;
display: -webkit-box;
--webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
width: 100%;
font-weight: $medium;
font-size: $m;
}
// detalles visibles por defecto
.details-container {
opacity: 1;
visibility: visible;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
height: 100%;
p {
margin: 0;
}
.company {
font-weight: $regular;
font-size: $s;
margin: 0;
}
.price {
font-weight: $bold;
font-size: $m;
margin-top: auto;
margin-bottom: 2px;
}
}
// botones ocultos por defecto
.links-btns {
opacity: 0;
visibility: hidden;
position: absolute;
bottom: 1rem;
left: 0;
width: 100%;
display: flex;
gap: 0.5rem;
transition: all 0.3s ease;
}
// en hover se intercambian
&:hover {
.details-container {
opacity: 0;
visibility: hidden;
transform: translateY(10px); // opcional efecto
}
.links-btns {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
}
}
.links-btns {
display: flex;
flex-direction: column;
padding: 0 0.5rem;
.div-action {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
font-weight: $bold;
font-size: $s;
text-decoration: none;
&.show-link {
border: 1px solid $color-button;
padding: 0.25rem 0.5rem;
border-radius: 12px;
background-color: white;
&:hover {
background-color: $color-button;
color: white;
}
}
&.buy-link {
color: white;
border: 1px solid $color-button;
padding: 0.25rem 0.5rem;
border-radius: 12px;
background-color: $color-button;
&:hover {
background-color: white;
color: $color-button;
}
}
.div-action-img {
width: 1rem;
height: 1rem;
}
}
}
// Separar las flechas de las cards
.carousel :deep(.carousel__prev) {
left: -2.5rem;
}
.carousel :deep(.carousel__next) {
right: -2.5rem;
}
</style>