feat(Carousel): expose methods to allow autoplay

Resolves #1300
This commit is contained in:
Benjamin Canac
2024-02-01 18:07:39 +01:00
parent f36158133e
commit 41ecd2a3d5
3 changed files with 86 additions and 13 deletions

View File

@@ -0,0 +1,37 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/1920/1080?random=1',
'https://picsum.photos/1920/1080?random=2',
'https://picsum.photos/1920/1080?random=3',
'https://picsum.photos/1920/1080?random=4',
'https://picsum.photos/1920/1080?random=5',
'https://picsum.photos/1920/1080?random=6'
]
const carouselRef = ref()
onMounted(() => {
setInterval(() => {
if (!carouselRef.value) return
if (carouselRef.value.page === carouselRef.value.pages) {
return carouselRef.value.select(0)
}
carouselRef.value.next()
}, 3000)
})
</script>
<template>
<UCarousel
ref="carouselRef"
v-slot="{ item }"
:items="items"
:ui="{ item: 'basis-full' }"
class="rounded-lg overflow-hidden"
indicators
>
<img :src="item" class="w-full" draggable="false">
</UCarousel>
</template>

View File

@@ -100,6 +100,12 @@ The number of indicators will be automatically generated based on the number of
:component-example{component="carousel-example-indicators-size"}
## Autoplay
You can easily implement an autoplay behavior using the exposed [API](#api) through a template ref.
:component-example{component="carousel-example-autoplay"}
## Slots
### `default`
@@ -120,7 +126,7 @@ You can customize the position of the buttons through `ui.arrows.wrapper`.
### `indicator`
With the `indicators` prop enabled, use the `#indicator` slot to set the content of the indicators. You will have access to the `active`, `index` properties and `on-click` method in the slot scope.
With the `indicators` prop enabled, use the `#indicator` slot to set the content of the indicators. You will have access to the `active`, `page` properties and `on-click` method in the slot scope.
:component-example{component="carousel-example-slots-indicator"}
@@ -132,6 +138,28 @@ You can customize the position of the buttons through `ui.indicators.wrapper`.
:component-props
## API
When accessing the component via a template ref, you can use the following:
::field-group
::field{name="page" type="number"}
The current page.
::
::field{name="pages" type="number"}
The total number of pages.
::
::field{name="select (page)"}
Go to a specific page.
::
::field{name="next ()"}
Go to the next page.
::
::field{name="prev ()"}
Go to the previous page.
::
::
## Config
:component-preset

View File

@@ -35,16 +35,16 @@
</div>
<div v-if="indicators" :class="ui.indicators.wrapper">
<template v-for="index in indicatorsCount" :key="index">
<slot name="indicator" :on-click="onClick" :active="index === currentIndex" :index="index">
<template v-for="page in pages" :key="page">
<slot name="indicator" :on-click="onClick" :active="page === currentPage" :page="page">
<button
type="button"
:class="[
ui.indicators.base,
index === currentIndex ? ui.indicators.active : ui.indicators.inactive
page === currentPage ? ui.indicators.active : ui.indicators.inactive
]"
:aria-label="`set slide ${index}`"
@click="onClick(index)"
:aria-label="`set slide ${page}`"
@click="onClick(page)"
/>
</slot>
</template>
@@ -103,7 +103,7 @@ export default defineComponent({
default: undefined
}
},
setup (props) {
setup (props, { expose }) {
const { ui, attrs } = useUI('carousel', toRef(props, 'ui'), config, toRef(props, 'class'))
const carouselRef = ref<HTMLElement>()
@@ -122,9 +122,9 @@ export default defineComponent({
itemWidth.value = entry?.target?.firstElementChild?.clientWidth || 0
})
const currentIndex = computed(() => Math.round(x.value / itemWidth.value) + 1)
const currentPage = computed(() => Math.round(x.value / itemWidth.value) + 1)
const indicatorsCount = computed(() => {
const pages = computed(() => {
if (!itemWidth.value) {
return 0
}
@@ -140,10 +140,18 @@ export default defineComponent({
x.value -= itemWidth.value
}
function onClick (index: number) {
x.value = (index - 1) * itemWidth.value
function onClick (page: number) {
x.value = (page - 1) * itemWidth.value
}
expose({
pages,
page: currentPage,
prev: onClickPrev,
next: onClickNext,
select: onClick
})
return {
// eslint-disable-next-line vue/no-dupe-keys
ui,
@@ -151,8 +159,8 @@ export default defineComponent({
isFirst,
isLast,
carouselRef,
indicatorsCount,
currentIndex,
pages,
currentPage,
onClickNext,
onClickPrev,
onClick,