ldc-legaulois/resources/ts/fo/components/RangesSliderComponent.vue
2024-04-18 13:00:19 +02:00

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>