feat(components): add carousel dot buttons
This commit is contained in:
parent
9dfb0b86c0
commit
8ef4f200ac
|
|
@ -1,11 +1,8 @@
|
||||||
---
|
---
|
||||||
title: Carousel
|
|
||||||
description: A carousel with motion and swipe built using Embla.
|
|
||||||
source: apps/www/src/lib/registry/default/ui/carousel
|
|
||||||
primitive: https://www.embla-carousel.com/api
|
|
||||||
---
|
|
||||||
|
|
||||||
<ComponentPreview name="CarouselDemo" />
|
## title: Carousel description: A carousel with motion and swipe built using Embla. source: apps/www/src/lib/registry/default/ui/carousel primitive: <https://www.embla-carousel.com/api>
|
||||||
|
|
||||||
|
<ComponentPreview name="CarouselDemo" />
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
|
|
@ -32,6 +29,7 @@ import {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Carousel>
|
<Carousel>
|
||||||
|
<CarouselDotButtons />
|
||||||
<CarouselContent>
|
<CarouselContent>
|
||||||
<CarouselItem>...</CarouselItem>
|
<CarouselItem>...</CarouselItem>
|
||||||
<CarouselItem>...</CarouselItem>
|
<CarouselItem>...</CarouselItem>
|
||||||
|
|
@ -49,11 +47,11 @@ import {
|
||||||
|
|
||||||
To set the size of the items, you can use the `basis` utility class on the `<CarouselItem />`.
|
To set the size of the items, you can use the `basis` utility class on the `<CarouselItem />`.
|
||||||
|
|
||||||
<ComponentPreview name="CarouselSize" />
|
<ComponentPreview name="CarouselSize" />
|
||||||
|
|
||||||
Example
|
Example
|
||||||
|
|
||||||
```vue:line-numbers title="Example" {4-6}
|
```vue:line-numbers
|
||||||
// 33% of the carousel width.
|
// 33% of the carousel width.
|
||||||
<Carousel>
|
<Carousel>
|
||||||
<CarouselContent>
|
<CarouselContent>
|
||||||
|
|
@ -66,7 +64,7 @@ Example
|
||||||
|
|
||||||
Responsive
|
Responsive
|
||||||
|
|
||||||
```vue:line-numbers title="Responsive" {4-6}
|
```vue:line-numbers
|
||||||
// 50% on small screens and 33% on larger screens.
|
// 50% on small screens and 33% on larger screens.
|
||||||
<Carousel>
|
<Carousel>
|
||||||
<CarouselContent>
|
<CarouselContent>
|
||||||
|
|
@ -81,22 +79,17 @@ Responsive
|
||||||
|
|
||||||
To set the spacing between the items, we use a `pl-[VALUE]` utility on the `<CarouselItem />` and a negative `-ml-[VALUE]` on the `<CarouselContent />`.
|
To set the spacing between the items, we use a `pl-[VALUE]` utility on the `<CarouselItem />` and a negative `-ml-[VALUE]` on the `<CarouselContent />`.
|
||||||
|
|
||||||
<Callout class="mt-6">
|
<Callout class="mt-6">
|
||||||
|
|
||||||
**Why:** I tried to use the `gap` property or a `grid` layout on the `
|
**Why:** I tried to use the `gap` property or a `grid` layout on the `CarouselContent` but it required a lot of math and mental effort to get the spacing right. I found `pl-[VALUE]` and `-ml-[VALUE]` utilities much easier to use. <br/><br/> You can always adjust this in your own project if you need to.
|
||||||
CarouselContent` but it required a lot of math and mental effort to get the
|
|
||||||
spacing right. I found `pl-[VALUE]` and `-ml-[VALUE]` utilities much easier to
|
|
||||||
use.
|
|
||||||
<br/><br/>
|
|
||||||
You can always adjust this in your own project if you need to.
|
|
||||||
|
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
<ComponentPreview name="CarouselSpacing" />
|
<ComponentPreview name="CarouselSpacing" />
|
||||||
|
|
||||||
Example
|
Example
|
||||||
|
|
||||||
```vue:line-numbers /-ml-4/ /pl-4/
|
```vue:line-numbers
|
||||||
<template>
|
<template>
|
||||||
<Carousel>
|
<Carousel>
|
||||||
<CarouselContent class="-ml-4">
|
<CarouselContent class="-ml-4">
|
||||||
|
|
@ -116,7 +109,7 @@ Example
|
||||||
|
|
||||||
Responsive
|
Responsive
|
||||||
|
|
||||||
```vue:line-numbers /-ml-2/ /pl-2/ /md:-ml-4/ /md:pl-4/
|
```vue:line-numbers
|
||||||
<template>
|
<template>
|
||||||
<Carousel>
|
<Carousel>
|
||||||
<CarouselContent class="-ml-2 md:-ml-4">
|
<CarouselContent class="-ml-2 md:-ml-4">
|
||||||
|
|
@ -138,7 +131,7 @@ Responsive
|
||||||
|
|
||||||
Use the `orientation` prop to set the orientation of the carousel.
|
Use the `orientation` prop to set the orientation of the carousel.
|
||||||
|
|
||||||
<ComponentPreview name="CarouselOrientation" />
|
<ComponentPreview name="CarouselOrientation" />
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<Carousel orientation="vertical | horizontal">
|
<Carousel orientation="vertical | horizontal">
|
||||||
|
|
@ -148,13 +141,15 @@ Use the `orientation` prop to set the orientation of the carousel.
|
||||||
|
|
||||||
### Thumbnails
|
### Thumbnails
|
||||||
|
|
||||||
<ComponentPreview name="CarouselThumbnails" />
|
<ComponentPreview name="CarouselThumbnails" />
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
You can pass options to the carousel using the `opts` prop. See the [Embla Carousel docs](https://www.embla-carousel.com/api/options/) for more information.
|
You can pass options to the carousel using the `opts` prop. See the [Embla Carousel docs](https://www.embla-carousel.com/api/options/) for more information.
|
||||||
|
|
||||||
```vue:line-numbers {3-6}
|
```vue:line-numbers
|
||||||
<template>
|
<template>
|
||||||
<Carousel
|
<Carousel
|
||||||
:opts="{
|
:opts="{
|
||||||
|
|
@ -177,13 +172,13 @@ You can pass options to the carousel using the `opts` prop. See the [Embla Carou
|
||||||
|
|
||||||
Use the `@init-api` emit method on `<Carousel />` component to set the instance of the API.
|
Use the `@init-api` emit method on `<Carousel />` component to set the instance of the API.
|
||||||
|
|
||||||
<ComponentPreview name="CarouselApi" />
|
<ComponentPreview name="CarouselApi" />
|
||||||
|
|
||||||
### Method 2
|
### Method 2
|
||||||
|
|
||||||
You can access it through setting a template ref on the `<Carousel />` component.
|
You can access it through setting a template ref on the `<Carousel />` component.
|
||||||
|
|
||||||
```vue:line-numbers {2,5,9}
|
```vue:line-numbers
|
||||||
<script setup>
|
<script setup>
|
||||||
const carouselContainerRef = ref<InstanceType<typeof Carousel> | null>(null)
|
const carouselContainerRef = ref<InstanceType<typeof Carousel> | null>(null)
|
||||||
|
|
||||||
|
|
@ -203,7 +198,7 @@ function accessApi() {
|
||||||
|
|
||||||
You can listen to events using the API. To get the API instance use the `@init-api` emit method on the `<Carousel />` component
|
You can listen to events using the API. To get the API instance use the `@init-api` emit method on the `<Carousel />` component
|
||||||
|
|
||||||
```vue:line-numbers {5,7-9,25}
|
```vue:line-numbers
|
||||||
<script setup>
|
<script setup>
|
||||||
import { nextTick, ref, watch } from 'vue'
|
import { nextTick, ref, watch } from 'vue'
|
||||||
import { useCarousel } from '@/components/ui/carousel'
|
import { useCarousel } from '@/components/ui/carousel'
|
||||||
|
|
@ -240,7 +235,7 @@ See the [Embla Carousel docs](https://www.embla-carousel.com/api/events/) for mo
|
||||||
|
|
||||||
You can get the reactive slot props like `carouselRef, canScrollNext..Prev, scrollNext..Prev` using the `v-slot` directive in the `<Carousel v-slot="slotProps" />` component to extend the functionality.
|
You can get the reactive slot props like `carouselRef, canScrollNext..Prev, scrollNext..Prev` using the `v-slot` directive in the `<Carousel v-slot="slotProps" />` component to extend the functionality.
|
||||||
|
|
||||||
```vue:line-numbers {2}
|
```vue:line-numbers
|
||||||
<template>
|
<template>
|
||||||
<Carousel v-slot="{ canScrollNext, canScrollPrev }">
|
<Carousel v-slot="{ canScrollNext, canScrollPrev }">
|
||||||
...
|
...
|
||||||
|
|
@ -258,7 +253,7 @@ You can use the `plugins` prop to add plugins to the carousel.
|
||||||
npm i embla-carousel-autoplay
|
npm i embla-carousel-autoplay
|
||||||
```
|
```
|
||||||
|
|
||||||
```vue:line-numbers {2,8-10}
|
```vue:line-numbers
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Autoplay from 'embla-carousel-autoplay'
|
import Autoplay from 'embla-carousel-autoplay'
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -275,6 +270,6 @@ import Autoplay from 'embla-carousel-autoplay'
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
<ComponentPreview name="CarouselPlugin" />
|
<ComponentPreview name="CarouselPlugin" />
|
||||||
|
|
||||||
See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on using plugins.
|
See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on using plugins.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useCarousel } from './useCarousel'
|
||||||
|
import type { WithClassAsProps } from './interface'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<WithClassAsProps>()
|
||||||
|
const { scrollTo, selectedIndex, scrollSnaps } = useCarousel()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-for="(_, index) in scrollSnaps"
|
||||||
|
:key="index"
|
||||||
|
class="sm:mt-0 first:ml-0 border-1 w-2 h-2 mt-2 ml-2 border-gray-200 border-solid rounded-full"
|
||||||
|
:class="[cn(props.class), index === selectedIndex ? 'border-transparent bg-blue-400' : 'bg-transparent']"
|
||||||
|
@click="scrollTo(index)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
@ -23,13 +23,20 @@ const [useProvideCarousel, useInjectCarousel] = createInjectionState(
|
||||||
function scrollNext() {
|
function scrollNext() {
|
||||||
emblaApi.value?.scrollNext()
|
emblaApi.value?.scrollNext()
|
||||||
}
|
}
|
||||||
|
function scrollTo(index: number) {
|
||||||
|
emblaApi.value?.scrollTo(index)
|
||||||
|
}
|
||||||
|
|
||||||
const canScrollNext = ref(true)
|
const canScrollNext = ref(true)
|
||||||
const canScrollPrev = ref(true)
|
const canScrollPrev = ref(true)
|
||||||
|
const selectedIndex = ref(0)
|
||||||
|
const scrollSnaps = ref([])
|
||||||
|
|
||||||
function onSelect(api: CarouselApi) {
|
function onSelect(api: CarouselApi) {
|
||||||
canScrollNext.value = api.canScrollNext()
|
canScrollNext.value = api.canScrollNext()
|
||||||
canScrollPrev.value = api.canScrollPrev()
|
canScrollPrev.value = api.canScrollPrev()
|
||||||
|
selectedIndex.value = api.selectedScrollSnap()
|
||||||
|
scrollSnaps.value = api.scrollSnapList()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
@ -43,7 +50,7 @@ const [useProvideCarousel, useInjectCarousel] = createInjectionState(
|
||||||
emits('init-api', emblaApi.value)
|
emits('init-api', emblaApi.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
return { carouselRef: emblaNode, carouselApi: emblaApi, canScrollPrev, canScrollNext, scrollPrev, scrollNext, orientation }
|
return { carouselRef: emblaNode, carouselApi: emblaApi, canScrollPrev, canScrollNext, scrollPrev, scrollNext, scrollTo, scrollSnaps, selectedIndex, orientation }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useCarousel } from './useCarousel'
|
||||||
|
import type { WithClassAsProps } from './interface'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<WithClassAsProps>()
|
||||||
|
const { scrollTo, selectedIndex, scrollSnaps } = useCarousel()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in scrollSnaps"
|
||||||
|
:key="index"
|
||||||
|
class="sm:mt-0 first:ml-0 border-1 w-2 h-2 mt-2 ml-2 border-gray-200 border-solid rounded-full"
|
||||||
|
:class="[cn(props.class), index === selectedIndex ? 'border-transparent bg-blue-400' : 'bg-transparent']"
|
||||||
|
@click="scrollTo(index)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
@ -23,13 +23,20 @@ const [useProvideCarousel, useInjectCarousel] = createInjectionState(
|
||||||
function scrollNext() {
|
function scrollNext() {
|
||||||
emblaApi.value?.scrollNext()
|
emblaApi.value?.scrollNext()
|
||||||
}
|
}
|
||||||
|
function scrollTo(index: number) {
|
||||||
|
emblaApi.value?.scrollTo(index)
|
||||||
|
}
|
||||||
|
|
||||||
const canScrollNext = ref(true)
|
const canScrollNext = ref(true)
|
||||||
const canScrollPrev = ref(true)
|
const canScrollPrev = ref(true)
|
||||||
|
const selectedIndex = ref(0)
|
||||||
|
const scrollSnaps = ref([])
|
||||||
|
|
||||||
function onSelect(api: CarouselApi) {
|
function onSelect(api: CarouselApi) {
|
||||||
canScrollNext.value = api.canScrollNext()
|
canScrollNext.value = api.canScrollNext()
|
||||||
canScrollPrev.value = api.canScrollPrev()
|
canScrollPrev.value = api.canScrollPrev()
|
||||||
|
selectedIndex.value = api.selectedScrollSnap()
|
||||||
|
scrollSnaps.value = api.scrollSnapList()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
@ -43,7 +50,7 @@ const [useProvideCarousel, useInjectCarousel] = createInjectionState(
|
||||||
emits('init-api', emblaApi.value)
|
emits('init-api', emblaApi.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
return { carouselRef: emblaNode, carouselApi: emblaApi, canScrollPrev, canScrollNext, scrollPrev, scrollNext, orientation }
|
return { carouselRef: emblaNode, carouselApi: emblaApi, canScrollPrev, canScrollNext, scrollPrev, scrollNext, scrollTo, scrollSnaps, selectedIndex, orientation }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user