<section class="relative flex flex-col gap-4 adjusted-marg-y-xs" x-data="initSlider" x-init="calcPageSize(); $nextTick(() => calcActive())" @resize.window.debounce="calcPageSize(); $nextTick(() => calcActive())" role="group" aria-roledescription="Carousel" aria-label="échantillons offerts">
<h3 class="text-body-base font-bold">Je souhaite recevoir 3 échantillons offerts: <span class="text-brand-700">3</span> restants</h3>
<div class="relative w-full flex flex-row justify-between items-center gap-4 px-1 overflow-x-hidden">
<div class="flex flex-row gap-4">
<button type="button" :class="{ 'opacity-25 pointer-events-none' : active === 0 }" @click="scrollPrevious" :disabled="active === 0" aria-label="Voir le slide précédent" class=" btn btn-dark btn-subtle btn-border btn-size-sm btn-only-icon">
<svg class=" shrink-0" width="24" height="24" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M10.5 19.5L3 12M3 12L10.5 4.5M3 12H21" stroke="currentColor" stroke-width="1.5" fill="none" />
</svg>
</button>
</div>
<div class="relative flex flex-nowrap w-full overflow-auto js_slides snap snap-x gap-1.5" @scroll.debounce="calcActive" aria-label="Échantillons">
<label class="js_slide selection-control-label selection-control-checkbox
w-[268px] sm:w-[218px] md:w-[248px] 3xl:w-[328px]
flex flex-row gap-4 flex-shrink-0 px-2 py-4 snap-start cursor-pointer transition-all duration-200
bg-white border-dashed border border-black/16 hover:!border-brand-500
has-[input:checked]:border-solid has-[input:checked]:border-black" role="tabpanel" :aria-hidden="active !== 0">
<input type="checkbox" name="La crème riche" value="La crème riche" class="peer">
<div>
<p class="text-body-sm font-bold">La crème riche - <span class="text-black/64">(2ml)</span></p>
<a href="#" class="
underline
link-medium
link-dark
">
En savoir plus
</a>
</div>
</label>
<label class="js_slide selection-control-label selection-control-checkbox
w-[268px] sm:w-[218px] md:w-[248px] 3xl:w-[328px]
flex flex-row gap-4 flex-shrink-0 px-2 py-4 snap-start cursor-pointer transition-all duration-200
bg-white border-dashed border border-black/16 hover:!border-brand-500
has-[input:checked]:border-solid has-[input:checked]:border-black" role="tabpanel" :aria-hidden="active !== 1">
<input type="checkbox" name="La crème légère" value="La crème légère" class="peer">
<div>
<p class="text-body-sm font-bold">La crème légère - <span class="text-black/64">(2ml)</span></p>
<a href="#" class="
underline
link-medium
link-dark
">
En savoir plus
</a>
</div>
</label>
<label class="js_slide selection-control-label selection-control-checkbox
w-[268px] sm:w-[218px] md:w-[248px] 3xl:w-[328px]
flex flex-row gap-4 flex-shrink-0 px-2 py-4 snap-start cursor-pointer transition-all duration-200
bg-white border-dashed border border-black/16 hover:!border-brand-500
has-[input:checked]:border-solid has-[input:checked]:border-black" role="tabpanel" :aria-hidden="active !== 2">
<input type="checkbox" name="Niacinamide 5%" value="Niacinamide 5%" class="peer">
<div>
<p class="text-body-sm font-bold">Niacinamide 5% - <span class="text-black/64">(1ml)</span></p>
<a href="#" class="
underline
link-medium
link-dark
">
En savoir plus
</a>
</div>
</label>
<label class="js_slide selection-control-label selection-control-checkbox
w-[268px] sm:w-[218px] md:w-[248px] 3xl:w-[328px]
flex flex-row gap-4 flex-shrink-0 px-2 py-4 snap-start cursor-pointer transition-all duration-200
bg-white border-dashed border border-black/16 hover:!border-brand-500
has-[input:checked]:border-solid has-[input:checked]:border-black" role="tabpanel" :aria-hidden="active !== 3">
<input type="checkbox" name="La crème riche" value="La crème riche" class="peer">
<div>
<p class="text-body-sm font-bold">La crème riche - <span class="text-black/64">(2ml)</span></p>
<a href="#" class="
underline
link-medium
link-dark
">
En savoir plus
</a>
</div>
</label>
<label class="js_slide selection-control-label selection-control-checkbox
w-[268px] sm:w-[218px] md:w-[248px] 3xl:w-[328px]
flex flex-row gap-4 flex-shrink-0 px-2 py-4 snap-start cursor-pointer transition-all duration-200
bg-white border-dashed border border-black/16 hover:!border-brand-500
has-[input:checked]:border-solid has-[input:checked]:border-black" role="tabpanel" :aria-hidden="active !== 4">
<input type="checkbox" name="La crème légère" value="La crème légère" class="peer">
<div>
<p class="text-body-sm font-bold">La crème légère - <span class="text-black/64">(2ml)</span></p>
<a href="#" class="
underline
link-medium
link-dark
">
En savoir plus
</a>
</div>
</label>
<label class="js_slide selection-control-label selection-control-checkbox
w-[268px] sm:w-[218px] md:w-[248px] 3xl:w-[328px]
flex flex-row gap-4 flex-shrink-0 px-2 py-4 snap-start cursor-pointer transition-all duration-200
bg-white border-dashed border border-black/16 hover:!border-brand-500
has-[input:checked]:border-solid has-[input:checked]:border-black" role="tabpanel" :aria-hidden="active !== 5">
<input type="checkbox" name="Niacinamide 5%" value="Niacinamide 5%" class="peer">
<div>
<p class="text-body-sm font-bold">Niacinamide 5% - <span class="text-black/64">(1ml)</span></p>
<a href="#" class="
underline
link-medium
link-dark
">
En savoir plus
</a>
</div>
</label>
</div>
<div class="flex flex-row gap-4">
<button type="button" :class="{ 'opacity-25 pointer-events-none' : active >= itemCount - pageSize }" @click="scrollNext" :disabled="active >= itemCount - pageSize" aria-label="Voir le slide suivant" class=" btn btn-dark btn-subtle btn-border btn-size-sm btn-only-icon">
<svg class=" shrink-0" width="24" height="24" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" stroke="currentColor" stroke-width="1.5" fill="none" />
</svg>
</button>
</div>
</div>
</section>
<script>
function initSlider() {
return {
active: 0,
itemCount: 0,
getSlider() {
return this.$root.querySelector('.js_slides');
},
pageSize: 3,
pageFillers: 0,
calcPageSize() {
const slider = this.getSlider();
if (slider) {
this.itemCount = slider.querySelectorAll('.js_slide').length;
this.pageSize = Math.round(slider.clientWidth / slider.querySelector('.js_slide').clientWidth);
this.pageFillers = (this.pageSize * Math.ceil(this.itemCount / this.pageSize)) - this.itemCount;
}
},
calcActive() {
const slider = this.getSlider();
if (slider) {
const sliderItems = this.itemCount + this.pageFillers;
const calculatedActiveSlide = slider.scrollLeft / (slider.scrollWidth / sliderItems);
this.active = Math.round(calculatedActiveSlide / this.pageSize) * this.pageSize;
}
},
scrollPrevious() {
this.scrollTo(this.active - this.pageSize);
},
scrollNext() {
this.scrollTo(this.active + this.pageSize);
},
scrollTo(idx) {
const slider = this.getSlider();
if (slider) {
const slideWidth = slider.scrollWidth / (this.itemCount + this.pageFillers);
slider.scrollLeft = Math.floor(slideWidth) * idx;
this.active = idx;
}
},
}
}
</script>
<section class="relative flex flex-col gap-4 adjusted-marg-y-xs"
x-data="initSlider"
x-init="calcPageSize(); $nextTick(() => calcActive())"
@resize.window.debounce="calcPageSize(); $nextTick(() => calcActive())"
role="group"
aria-roledescription="Carousel"
aria-label="échantillons offerts"
>
<h3 class="text-body-base font-bold">Je souhaite recevoir 3 échantillons offerts: <span class="text-brand-700">3</span> restants</h3>
<div class="relative w-full flex flex-row justify-between items-center gap-4 px-1 overflow-x-hidden">
{% if samples|length > 1 %}
<div class="flex flex-row gap-4">
{% render "@template-button" with {
color: 'dark',
type: 'subtle',
icon_type: "only-icon",
size: 'sm',
border: true,
label: 'Voir le slide précédent',
icon: {
name: "heroicons--arrow-left-outline",
},
button_attribute: (":class=\"{ 'opacity-25 pointer-events-none' : active === 0 }\" @click=\"scrollPrevious\" :disabled=\"active === 0\"")|replace({'\"': '"'})
} %}
</div>
{% endif %}
<div class="relative flex flex-nowrap w-full overflow-auto js_slides snap snap-x gap-1.5"
@scroll.debounce="calcActive"
aria-label="Échantillons"
>
{% for sample in samples %}
<label class="js_slide selection-control-label selection-control-checkbox
w-[268px] sm:w-[218px] md:w-[248px] 3xl:w-[328px]
flex flex-row gap-4 flex-shrink-0 px-2 py-4 snap-start cursor-pointer transition-all duration-200
bg-white border-dashed border border-black/16 hover:!border-brand-500
has-[input:checked]:border-solid has-[input:checked]:border-black"
role="tabpanel"
:aria-hidden="active !== {{ loop.index0 }}"
>
<input
type="checkbox"
name="{{ sample.name }}"
value="{{ sample.name }}"
class="peer"
>
<div>
<p class="text-body-sm font-bold">{{ sample.name }} - <span
class="text-black/64">({{ sample.size }})</span></p>
{% render "@template-link" with {
size: 'medium',
type: 'dark',
underlined: true,
href: '#',
label: "En savoir plus"
} %}
</div>
</label>
{% endfor %}
</div>
{% if samples|length > 1 %}
<div class="flex flex-row gap-4">
{% render "@template-button" with {
color: 'dark',
type: 'subtle',
icon_type: "only-icon",
size: 'sm',
border: true,
label: 'Voir le slide suivant',
icon: {
name: "heroicons--arrow-right-outline",
},
button_attribute: (":class=\"{ 'opacity-25 pointer-events-none' : active >= itemCount - pageSize }\" @click=\"scrollNext\" :disabled=\"active >= itemCount - pageSize\"")|replace({'\"': '"'})
} %}
</div>
{% endif %}
</div>
</section>
<script>
function initSlider() {
return {
active: 0,
itemCount: 0,
getSlider() {
return this.$root.querySelector('.js_slides');
},
pageSize: 3,
pageFillers: 0,
calcPageSize() {
const slider = this.getSlider();
if (slider) {
this.itemCount = slider.querySelectorAll('.js_slide').length;
this.pageSize = Math.round(slider.clientWidth / slider.querySelector('.js_slide').clientWidth);
this.pageFillers = (this.pageSize * Math.ceil(this.itemCount / this.pageSize)) - this.itemCount;
}
},
calcActive() {
const slider = this.getSlider();
if (slider) {
const sliderItems = this.itemCount + this.pageFillers;
const calculatedActiveSlide = slider.scrollLeft / (slider.scrollWidth / sliderItems);
this.active = Math.round(calculatedActiveSlide / this.pageSize) * this.pageSize;
}
},
scrollPrevious() {
this.scrollTo(this.active - this.pageSize);
},
scrollNext() {
this.scrollTo(this.active + this.pageSize);
},
scrollTo(idx) {
const slider = this.getSlider();
if (slider) {
const slideWidth = slider.scrollWidth / (this.itemCount + this.pageFillers);
slider.scrollLeft = Math.floor(slideWidth) * idx;
this.active = idx;
}
},
}
}
</script>
{
"title": "Hero Product Slider",
"maxVisibleSlides": 1,
"showNavigation": true,
"showPagination": true,
"samples": [
{
"name": "La crème riche",
"size": "2ml",
"cta": "#"
},
{
"name": "La crème légère",
"size": "2ml",
"cta": "#"
},
{
"name": "Niacinamide 5%",
"size": "1ml",
"cta": "#"
},
{
"name": "La crème riche",
"size": "2ml",
"cta": "#"
},
{
"name": "La crème légère",
"size": "2ml",
"cta": "#"
},
{
"name": "Niacinamide 5%",
"size": "1ml",
"cta": "#"
}
]
}
No notes defined.