338 lines
11 KiB
Vue
338 lines
11 KiB
Vue
<template>
|
|
<div
|
|
class="row"
|
|
:id="intId"
|
|
>
|
|
<!-- RANGES SLIDER -->
|
|
<div class="col-12 col-xxl-8 mx-auto p-0 my-4">
|
|
<div
|
|
class="swiper swiper-ranges overflow-hidden position-relative px-md-4"
|
|
:inert="intLoading"
|
|
>
|
|
<div class="swiper-wrapper w-fit">
|
|
<div
|
|
class="swiper-slide p-md-4 swiper-slide-content"
|
|
v-for="(rangeModel, id) in intRangeModels"
|
|
:key="id"
|
|
>
|
|
<button
|
|
class="card justify-content-center align-items-center bg-transparent position-relative border-0 w-fit mx-auto"
|
|
@click="setSelectedRange(rangeModel)"
|
|
>
|
|
<div
|
|
class="card-background line-border-small position-absolute top-0 start-50 translate-middle-x w-75 z-n1"
|
|
:style="(intRangeModelSelected?.id === rangeModel.id) ? 'background-color:' + rangeModel?.color : ''"
|
|
/>
|
|
<div class="card-img-top text-center">
|
|
<img
|
|
class="img-fluid"
|
|
:src="String(rangeModel.picture)"
|
|
:alt="String(rangeModel.picture_alt)"
|
|
:title="String(rangeModel.picture_title)"
|
|
>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<p class="h6 m-0">
|
|
{{ rangeModel.name }}
|
|
</p>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<button class="swiper-btns swiper-btn-prev border-0 d-flex justify-content-center align-items-center position-absolute top-50 z-1 bg-white rounded-pill">
|
|
<img
|
|
src="@/images/common/arrow.svg"
|
|
:title="trans.methods.__('Navigation du slider (gauche)')"
|
|
:alt="trans.methods.__('flèche vers la gauche')"
|
|
role="button"
|
|
>
|
|
</button>
|
|
<button class="swiper-btns swiper-btn-next border-0 d-flex justify-content-center align-items-center position-absolute top-50 translate-middle-y z-1 bg-white rounded-pill">
|
|
<img
|
|
src="@/images/common/arrow.svg"
|
|
:title="trans.methods.__('Navigation du slider (droite)')"
|
|
:alt="trans.methods.__('flèche vers la droite')"
|
|
role="button"
|
|
>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- PRODUCTS SLIDER -->
|
|
<div class="col-12 col-xxl-10 mx-auto px-0">
|
|
<div class="swiper swiper-products swiper-placeholder overflow-hidden position-relative px-md-4">
|
|
<div
|
|
class="swiper-wrapper"
|
|
:class="[intProductViews.length ? 'w-fit' : 'w-100']"
|
|
>
|
|
<template v-if="!intLoading">
|
|
<template v-if="intProductViews.length">
|
|
<div
|
|
class="swiper-slide p-md-4 swiper-slide-content"
|
|
v-for="(intProductView, id) in intProductViews"
|
|
:key="id"
|
|
v-html="intProductView"
|
|
/>
|
|
</template>
|
|
<template v-else>
|
|
<p class="h6 text-center mx-auto mb-4 mb-md-5">
|
|
{{ trans.methods.__('Aucun produits disponibles dans cette gamme') }}
|
|
</p>
|
|
</template>
|
|
</template>
|
|
<div
|
|
class="swiper-slide p-md-4 swiper-slide-content"
|
|
v-if="intLoading"
|
|
>
|
|
<div class="position-relative product-card bottom-left-shadow mt-3 py-3">
|
|
<div class="line-border-medium bg-tertiary position-absolute w-100 h-75 left-0 start-0" />
|
|
<div class="position-relative d-flex justify-content-center pb-4">
|
|
<div class="w-50 rotate-right product-card-packaging placeholder-glow">
|
|
<span class="placeholder col-12" />
|
|
</div>
|
|
<div class="product-card-fav position-absolute z-1 bottom-0 placeholder-glow">
|
|
<span class="placeholder h-100 col-12 rounded-circle" />
|
|
</div>
|
|
</div>
|
|
<div class="position-relative">
|
|
<div class="line-border-medium position-relative d-flex flex-column align-items-center product-card-bottom-part bg-white pb-3">
|
|
<div class="w-100 placeholder-glow my-4 px-4">
|
|
<span class="placeholder col-12" />
|
|
<span class="placeholder col-12 mt-2" />
|
|
</div>
|
|
</div>
|
|
<div class="position-absolute w-100 d-flex justify-content-center bottom-0">
|
|
<div class="product-card-view-more w-100 text-center placeholder-glow">
|
|
<span class="placeholder col-7" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button class="swiper-btns swiper-btn-prev border-0 d-flex justify-content-center align-items-center position-absolute top-50 z-1 bg-white rounded-pill">
|
|
<img
|
|
src="@/images/common/arrow.svg"
|
|
:title="trans.methods.__('Navigation du slider (gauche)')"
|
|
:alt="trans.methods.__('flèche vers la gauche')"
|
|
role="button"
|
|
>
|
|
</button>
|
|
<button class="swiper-btns swiper-btn-next border-0 d-flex justify-content-center align-items-center position-absolute top-50 translate-middle-y z-1 bg-white rounded-pill">
|
|
<img
|
|
src="@/images/common/arrow.svg"
|
|
:title="trans.methods.__('Navigation du slider (droite)')"
|
|
:alt="trans.methods.__('flèche vers la droite')"
|
|
role="button"
|
|
>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- BUTTONS -->
|
|
<div class="col-12 d-flex flex-column flex-md-row justify-content-center align-items-center w-100">
|
|
<a
|
|
class="range-btns btn btn-primary border-0 my-1 my-md-0 mx-md-1"
|
|
:href="'#FIXME: décommenter cette ligne et aussi l\'équivalent dans script-routes' /* getShowRangeRoute(rangeModel.slug as string) */"
|
|
title="Accéder à la page de tous les produits de la gamme"
|
|
:style="'background-color:' + intRangeModelSelected?.color"
|
|
>
|
|
{{ (intRangeModelSelected) ? intBtn1Label + " " + intRangeModelSelected?.name : intBtn1Label }}
|
|
</a>
|
|
<a
|
|
class="range-btns btn btn-secondary border-0 my-1 my-md-0 mx-md-1"
|
|
:href="'#FIXME: décommenter cette ligne et aussi l\'équivalent dans script-routes' /* getIndexRangeRoute() */"
|
|
title="Accéder à la page de tous les produits"
|
|
>
|
|
{{ intBtn2Label }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import Swiper from "swiper";
|
|
import { Navigation } from "swiper/modules";
|
|
import { defineOptions, onMounted, ref, useAttrs } from "vue";
|
|
import route from "../../modules/route";
|
|
import trans from "../../modules/trans";
|
|
|
|
defineOptions({
|
|
name: "RangesSlideComponent",
|
|
inheritAttrs: true,
|
|
});
|
|
|
|
// * ATTRIBUTES
|
|
const attrs = useAttrs();
|
|
|
|
// * DATA
|
|
let intId = ref<string>("");
|
|
const intRangeModels = ref<LaravelModelList>([]);
|
|
const intRangeModelSelected = ref<LaravelModel|null>(null);
|
|
const intSwiper = ref<Swiper|null>(null);
|
|
const intRouteRangeIndex = ref<string>("");
|
|
const intRouteRangeShow = ref<string>("");
|
|
const intRouteProductShow = ref<string>("");
|
|
const intBtn1Label = ref<string>("");
|
|
const intBtn2Label = ref<string>("");
|
|
const intProductViews = ref<string[]>([]);
|
|
const intLoading = ref<boolean>(false);
|
|
|
|
// * LIFECYCLE
|
|
onMounted(() => {
|
|
const json = String(attrs.json ?? "{}"),
|
|
data = JSON.parse(json);
|
|
intId.value = data.id;
|
|
intRangeModels.value = data.ranges;
|
|
intRouteRangeIndex.value = data.routeRangeIndex;
|
|
intRouteRangeShow.value = data.routeRangeShow;
|
|
intRouteProductShow.value = data.routeProductShow;
|
|
intBtn1Label.value = data.btn1Label;
|
|
intBtn2Label.value = data.btn2Label;
|
|
intRangeModelSelected.value = intRangeModels.value[0];
|
|
intProductViews.value = data.productViews ?? [];
|
|
initRangesSwiper();
|
|
initProductsSwiper();
|
|
});
|
|
|
|
// * METHODS
|
|
|
|
/** Init ranges slider */
|
|
function initRangesSwiper(): void {
|
|
intSwiper.value = new Swiper(".swiper-ranges", swiperOptions(".swiper-ranges"));
|
|
}
|
|
|
|
/** Init products slider */
|
|
function initProductsSwiper(): void {
|
|
const customOptions = {
|
|
spaceBetween: 40,
|
|
breakpoints: {
|
|
992: {
|
|
slidesPerView: 3,
|
|
centeredSlides: false,
|
|
}
|
|
}
|
|
};
|
|
intSwiper.value = new Swiper(".swiper-products", Object.assign(swiperOptions(".swiper-products"), customOptions));
|
|
}
|
|
|
|
function swiperOptions(classParent: string): object {
|
|
return {
|
|
modules: [Navigation],
|
|
grabCursor: true,
|
|
slidesPerView: 1.5,
|
|
centeredSlides: true,
|
|
spaceBetween: 20,
|
|
navigation: {
|
|
nextEl: classParent + " .swiper-btn-next",
|
|
prevEl: classParent + " .swiper-btn-prev",
|
|
},
|
|
breakpoints: {
|
|
768: {
|
|
slidesPerView: 2,
|
|
},
|
|
992: {
|
|
slidesPerView: 4,
|
|
centeredSlides: false,
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/** Get index item route */
|
|
function getIndexRangeRoute(): string {
|
|
const routeRangeIndex = route.methods.route(String(intRouteRangeIndex));
|
|
if (!routeRangeIndex) {
|
|
throw new Error("Undefined route " + intRouteRangeIndex.value);
|
|
}
|
|
return routeRangeIndex;
|
|
}
|
|
|
|
/** Get show item route */
|
|
function getShowRangeRoute(slug: string): string {
|
|
const routeRangeShow = route.methods.route(String(intRouteRangeShow), {
|
|
SLUG: slug,
|
|
});
|
|
if (!routeRangeShow) {
|
|
throw new Error("Undefined route " + intRouteRangeShow.value);
|
|
}
|
|
return routeRangeShow;
|
|
}
|
|
|
|
/** Get show item route */
|
|
function setSelectedRange(rangeModel: LaravelModel): void {
|
|
intLoading.value = true;
|
|
intRangeModelSelected.value = rangeModel;
|
|
const routeHomeProducts = route.methods.route("fo.home.products");
|
|
if (!routeHomeProducts) {
|
|
throw new Error("Undefined route fo.home.products");
|
|
}
|
|
window.axios
|
|
.post(routeHomeProducts, { RANGESLUG: String(intRangeModelSelected.value.slug) })
|
|
.then((reponse: { data: any; }) => {
|
|
intProductViews.value = reponse.data;
|
|
intLoading.value = false;
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "bootstrap/scss/functions";
|
|
@import "bootstrap/scss/variables";
|
|
@import "bootstrap/scss/mixins";
|
|
@import 'swiper/css';
|
|
@import 'swiper/css/navigation';
|
|
|
|
.card-background, .range-btns {
|
|
transition: .3s;
|
|
}
|
|
.card-background {
|
|
height: 60%;
|
|
}
|
|
.swiper-btns {
|
|
width: 2rem;
|
|
height: 2rem;
|
|
}
|
|
.swiper-btn-prev {
|
|
left: 1.5rem;
|
|
transform: translateY(-50%) rotate(180deg);
|
|
@include media-breakpoint-up(xxl) {
|
|
left: 0;
|
|
}
|
|
}
|
|
.swiper-btn-next {
|
|
right: 1.5rem;
|
|
@include media-breakpoint-up(xxl) {
|
|
right: 0;
|
|
}
|
|
}
|
|
.swiper-products .swiper-slide {
|
|
opacity: 1;
|
|
animation: fade-in 0.3s;
|
|
}
|
|
@keyframes fade-in {
|
|
from {opacity: 0;}
|
|
to {opacity: 1;}
|
|
}
|
|
.swiper-placeholder {
|
|
.product-card-packaging .placeholder {
|
|
height: 46vw;
|
|
@include media-breakpoint-up(md) {
|
|
height: 30vw;
|
|
}
|
|
@include media-breakpoint-up(lg) {
|
|
height: 15vw;
|
|
min-height: 8rem;
|
|
}
|
|
}
|
|
.product-card-fav {
|
|
right: 1rem;
|
|
width: 2rem;
|
|
height: 2rem;
|
|
}
|
|
.product-card-bottom-part .placeholder {
|
|
height: 1rem;
|
|
}
|
|
.product-card-view-more .placeholder {
|
|
height: 2rem;
|
|
}
|
|
}
|
|
</style>
|