Skip to content

Commit

Permalink
feat(Carousel): expose methods to allow autoplay
Browse files Browse the repository at this point in the history
Resolves #1300
  • Loading branch information
benjamincanac committed Feb 1, 2024
1 parent f361581 commit 41ecd2a
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 13 deletions.
37 changes: 37 additions & 0 deletions docs/components/content/examples/CarouselExampleAutoplay.vue
Original file line number Diff line number Diff line change
@@ -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>
30 changes: 29 additions & 1 deletion docs/content/2.components/carousel.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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"}

Expand All @@ -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
32 changes: 20 additions & 12 deletions src/runtime/components/elements/Carousel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>()
Expand All @@ -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
}
Expand All @@ -140,19 +140,27 @@ 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,
attrs,
isFirst,
isLast,
carouselRef,
indicatorsCount,
currentIndex,
pages,
currentPage,
onClickNext,
onClickPrev,
onClick,
Expand Down

0 comments on commit 41ecd2a

Please sign in to comment.