393 lines
11 KiB
Vue
393 lines
11 KiB
Vue
<template>
|
|
<div
|
|
class="ranges-slider position-relative"
|
|
ref="rangeSlider"
|
|
>
|
|
<!-- RANGE MAIN CONTENT -->
|
|
<div class="row w-100 mx-auto">
|
|
<div class="d-none d-lg-block col-3">
|
|
<!-- SUZETTE -->
|
|
<img
|
|
src="@/images/homepage/suzette-empty-hand.png"
|
|
alt="Suzette tenant un rozell dans sa main"
|
|
title="Suzette tenant un rozell"
|
|
class="suzette-picture z-1 w-100 position-relative"
|
|
>
|
|
</div>
|
|
<!-- RANGES -->
|
|
<template
|
|
v-for="(range, rangeIndex) in rangeModels"
|
|
:key="rangeIndex"
|
|
>
|
|
<div :class="`${rangeIndex === 0 ? 'col-12 col-lg-5 active-range position-relative text-center' : 'col-2 d-none d-lg-flex justify-content-center'}`">
|
|
<button
|
|
v-if="rangeIndex === 0"
|
|
class="bootstrap-btn-play btn btn-primary position-absolute bottom-0 start-0 z-1"
|
|
:title="'Modifier le statut de défilement du carousel'"
|
|
type="button"
|
|
data-bs-toggle="tooltip"
|
|
>
|
|
<FontAwesomeIcon
|
|
icon="fa-solid fa-play"
|
|
:class="(sliderPlay) ? 'd-none' : ''"
|
|
/>
|
|
<FontAwesomeIcon
|
|
icon="fa-solid fa-pause"
|
|
:class="(!sliderPlay) ? 'd-none' : ''"
|
|
/>
|
|
</button>
|
|
<img
|
|
:role="`${rangeIndex !== 0 ? 'button': ''}`"
|
|
:class="`btn btn-link rounded-circle range-picture range-picture-${rangeIndex +1} ${rangeIndex === 0 ? 'position-relative z-2' : ''}`"
|
|
:src="String(range.picture)"
|
|
:alt="String(range.picture_alt)"
|
|
:title="String(range.picture_title)"
|
|
@click.prevent="setSliderRotate(Number.parseInt(String(range.id)))"
|
|
:tabindex="`${rangeIndex !== 0 ? '0': '-1'}`"
|
|
ref="rangePicture"
|
|
>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
<!-- RANGES PICTOGRAMS -->
|
|
<div class="f-intro-head-b-base lh-1 pictos">
|
|
<div
|
|
class="d-flex rotate7 mb-sm-1 mb-lg-3"
|
|
v-if="currentRange.picto1_picture"
|
|
>
|
|
<img
|
|
class="picto-picture my-auto me-2"
|
|
:src="String(currentRange.picto1_picture)"
|
|
:title="String(currentRange.picto1_picture_title)"
|
|
:alt="String(currentRange.picto1_picture_alt)"
|
|
>
|
|
<div
|
|
class="d-flex flex-column align-items-center justify-content-center picto-text text-center"
|
|
>
|
|
<p class="text-uppercase mb-0">
|
|
{{ currentRange.picto1_line1 }}
|
|
</p>
|
|
<p class="text-uppercase fw-bold mb-0">
|
|
{{ currentRange.picto1_line2 }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="d-flex rotate7-lg-reverse second-picto"
|
|
v-if="currentRange.picto2_picture"
|
|
>
|
|
<img
|
|
class="picto-picture my-auto me-2"
|
|
:src="String(currentRange.picto2_picture)"
|
|
:title="String(currentRange.picto2_picture_title)"
|
|
:alt="String(currentRange.picto2_picture_alt)"
|
|
>
|
|
<div
|
|
class="d-flex flex-column align-items-center justify-content-center picto-text text-center"
|
|
>
|
|
<p class="text-uppercase mb-0">
|
|
{{ currentRange.picto2_line1 }}
|
|
</p>
|
|
<p class="text-uppercase fw-bold mb-0">
|
|
{{ currentRange.picto2_line2 }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- PAGINATION -->
|
|
<div class="row">
|
|
<div
|
|
class="pagination d-flex justify-content-center align-items-center position-relative mt-4 p-0"
|
|
ref="pagination"
|
|
>
|
|
<template
|
|
v-for="(range, rangeIndex) in staticRangeModels"
|
|
:key="rangeIndex"
|
|
>
|
|
<div
|
|
:class="`btn btn-link range-pagination m-0 mx-lg-2 ${range.id === currentRange.id ? 'range-pagination-active' : ''}`"
|
|
role="button"
|
|
tabindex="0"
|
|
@click.prevent="setSliderRotate(Number.parseInt(String(range.id)))"
|
|
/>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<!-- RANGE LABEL -->
|
|
<div class="row">
|
|
<a
|
|
class="btn btn-tertiary btn-rotate-left mx-auto mt-4 mb-2 w-fit fs-2 px-4 range-label z-2"
|
|
:href="getRangeShowRoute(currentRange.slug as string)"
|
|
>
|
|
{{ currentRange.label }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
|
import { defineComponent } from "vue";
|
|
import route from "../../modules/route";
|
|
import bsTooltips from "./../../modules/bsTooltips";
|
|
|
|
export default defineComponent({
|
|
name: "RangeSliderComponent",
|
|
mixins: [route, bsTooltips],
|
|
components: {
|
|
FontAwesomeIcon,
|
|
},
|
|
data(): {
|
|
rangeModels: LaravelModelList;
|
|
staticRangeModels: LaravelModelList;
|
|
currentRange: LaravelModel;
|
|
slider: HTMLElement;
|
|
sliderPlay: boolean;
|
|
sliderInterval: number;
|
|
sliderIntervalFunction: ReturnType<typeof setInterval>;
|
|
} {
|
|
return {
|
|
rangeModels: [],
|
|
staticRangeModels: [],
|
|
currentRange: {
|
|
id: 0
|
|
},
|
|
slider: document.createElement("div"),
|
|
sliderPlay: true,
|
|
sliderInterval: 3000,
|
|
sliderIntervalFunction: 0,
|
|
};
|
|
},
|
|
computed: {},
|
|
mounted() {
|
|
const json = String(this.$attrs.json ?? "{}"),
|
|
data = JSON.parse(json);
|
|
this.rangeModels = data.rangeModels;
|
|
this.staticRangeModels = [...this.rangeModels.values()];
|
|
this.currentRange = this.rangeModels[0];
|
|
this.slider = this.$refs.rangeSlider as HTMLElement;
|
|
|
|
this.$nextTick(() => {
|
|
this.initSlider();
|
|
this.initTooltips();
|
|
});
|
|
},
|
|
methods: {
|
|
/** Init pagination for Desktop */
|
|
initSlider() {
|
|
// Pagination dots
|
|
const paginationElements = (this.$refs.pagination as HTMLElement).children as HTMLCollectionOf<HTMLElement>;
|
|
for (const paginationElement of paginationElements) {
|
|
paginationElement.setAttribute("tabindex", "0");
|
|
paginationElement.setAttribute("role", "button");
|
|
paginationElement.addEventListener("keydown", (e: KeyboardEvent) => {
|
|
if (e.code === "Space") {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
paginationElement.click();
|
|
}
|
|
});
|
|
}
|
|
// Range pictures
|
|
const rangePictureElements = this.$refs.rangePicture as Array<HTMLElement>;
|
|
for (const rangePictureElement of rangePictureElements) {
|
|
if (rangePictureElements.indexOf(rangePictureElement) !== 0) {
|
|
rangePictureElement.setAttribute("tabindex", "0");
|
|
rangePictureElement.setAttribute("role", "button");
|
|
rangePictureElement.addEventListener("keydown", (e: KeyboardEvent) => {
|
|
if (e.code === "Space") {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
rangePictureElement.click();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
/** Set autoplay functionnality */
|
|
document.querySelector(".bootstrap-btn-play")?.addEventListener("click", () => {
|
|
this.sliderPlay = (this.sliderPlay) ? false : true;
|
|
this.updateSliderInterval();
|
|
});
|
|
if (this.sliderPlay) {
|
|
this.setSliderInterval();
|
|
}
|
|
},
|
|
/** Rotate ranges */
|
|
setSliderRotate(toId: number):void {
|
|
if(!this.rangeModels.length) {
|
|
return;
|
|
}
|
|
while (this.rangeModels.at(0)?.id !== toId) {
|
|
const tmp = this.rangeModels.pop();
|
|
if (!tmp) {
|
|
throw new Error("Cannot rotate products with empty array");
|
|
}
|
|
this.rangeModels.unshift(tmp);
|
|
}
|
|
this.currentRange = this.rangeModels.at(0) as LaravelModel;
|
|
if (!this.sliderPlay) this.sliderPlay = true;
|
|
this.updateSliderInterval();
|
|
},
|
|
/** Get fo.ranges.show route */
|
|
getRangeShowRoute(slug: string): string {
|
|
const route = this.route("fo.ranges.show", {
|
|
SLUG: slug,
|
|
});
|
|
if (!route) {
|
|
throw new Error("Undefined route fo.ranges.show");
|
|
}
|
|
return route;
|
|
},
|
|
/** Update slider interval */
|
|
updateSliderInterval() {
|
|
clearInterval(this.sliderIntervalFunction);
|
|
this.setSliderInterval();
|
|
},
|
|
/** Set new slider interval */
|
|
setSliderInterval() {
|
|
this.sliderIntervalFunction = setInterval(() => {
|
|
if (this.sliderPlay) this.setSliderRotate(Number(this.rangeModels.at(1)?.id));
|
|
}, this.sliderInterval);
|
|
}
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
// Bootstrap reimport to use breakpoints
|
|
@import "bootstrap/scss/functions";
|
|
@import "bootstrap/scss/variables";
|
|
@import "bootstrap/scss/mixins";
|
|
|
|
.ranges-slider {
|
|
.active-range {
|
|
background-image: url("@/images/global/plate.png");
|
|
background-position-x: center;
|
|
background-repeat: no-repeat;
|
|
background-size: contain;
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.range-picture {
|
|
width: 75%;
|
|
}
|
|
|
|
.pagination {
|
|
.range-pagination {
|
|
background-image: url("@/images/global/slider-nav.png");
|
|
background-position-x: center;
|
|
background-repeat: no-repeat;
|
|
background-size: contain;
|
|
width: 1rem;
|
|
height: 1rem;
|
|
|
|
&:hover {
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
|
|
.range-pagination-active {
|
|
background-image: url("@/images/global/slider-nav-active.png");
|
|
width: 1.3rem;
|
|
height: 1.3rem;
|
|
}
|
|
}
|
|
|
|
.pictos {
|
|
position: relative;
|
|
|
|
.d-flex {
|
|
margin-top: 1rem;
|
|
justify-content: center;
|
|
}
|
|
|
|
@media screen and (min-width: 380px) {
|
|
position: absolute;
|
|
right: -2rem;
|
|
bottom: 5.5rem;
|
|
|
|
.d-flex {
|
|
margin-top: 0;
|
|
justify-content: left;
|
|
}
|
|
}
|
|
|
|
.picto-picture {
|
|
max-width: 25%;
|
|
}
|
|
|
|
.picto-text {
|
|
font-size: 0.9rem;
|
|
}
|
|
}
|
|
}
|
|
|
|
@include media-breakpoint-up(sm) {
|
|
.ranges-slider .pictos {
|
|
bottom: 5.5rem;
|
|
}
|
|
}
|
|
|
|
@include media-breakpoint-up(lg) {
|
|
.ranges-slider {
|
|
.range-picture {
|
|
width: 100%;
|
|
|
|
&-1 {
|
|
transition: transform 330ms ease-in-out;
|
|
&:hover {
|
|
transform: scale(1.1);
|
|
}
|
|
}
|
|
|
|
&-2, &-3 {
|
|
&:hover {
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
|
|
&-2 {
|
|
position: relative;
|
|
top: -20%;
|
|
height: fit-content;
|
|
}
|
|
|
|
&-3 {
|
|
height: fit-content;
|
|
}
|
|
}
|
|
|
|
.pagination {
|
|
left: -2.5rem;
|
|
}
|
|
|
|
.suzette-picture {
|
|
left: 35%;
|
|
}
|
|
|
|
.range-label {
|
|
position: absolute;
|
|
top: 60%;
|
|
left: 54%;
|
|
transform: rotate(2.159deg)
|
|
}
|
|
|
|
.pictos {
|
|
right: 15% !important;
|
|
bottom: 50% !important;
|
|
|
|
.picto-picture {
|
|
max-width: 30% !important;
|
|
}
|
|
|
|
.picto-text {
|
|
font-size: 1.3rem !important;
|
|
}
|
|
}
|
|
|
|
.second-picto {
|
|
transform: rotate(2.159deg);
|
|
}
|
|
}
|
|
}
|
|
</style>
|