mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-26 01:40:34 +01:00
feat(Carousel): implement component (#2288)
This commit is contained in:
1
docs/app/assets/icons/embla-carousel.svg
Normal file
1
docs/app/assets/icons/embla-carousel.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/640/640?random=1',
|
||||||
|
'https://picsum.photos/640/640?random=2',
|
||||||
|
'https://picsum.photos/640/640?random=3',
|
||||||
|
'https://picsum.photos/640/640?random=4',
|
||||||
|
'https://picsum.photos/640/640?random=5',
|
||||||
|
'https://picsum.photos/640/640?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel v-slot="{ item }" arrows :items="items" class="w-full max-w-xs mx-auto">
|
||||||
|
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/640/640?random=1',
|
||||||
|
'https://picsum.photos/640/320?random=2',
|
||||||
|
'https://picsum.photos/640/640?random=3',
|
||||||
|
'https://picsum.photos/640/320?random=4',
|
||||||
|
'https://picsum.photos/640/640?random=5',
|
||||||
|
'https://picsum.photos/640/320?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel
|
||||||
|
v-slot="{ item }"
|
||||||
|
auto-height
|
||||||
|
arrows
|
||||||
|
dots
|
||||||
|
:items="items"
|
||||||
|
:ui="{
|
||||||
|
container: 'transition-[height]',
|
||||||
|
controls: 'absolute -top-8 inset-x-12',
|
||||||
|
dots: '-top-7',
|
||||||
|
dot: 'w-6 h-1'
|
||||||
|
}"
|
||||||
|
class="w-full max-w-xs mx-auto"
|
||||||
|
>
|
||||||
|
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/468/468?random=1',
|
||||||
|
'https://picsum.photos/468/468?random=2',
|
||||||
|
'https://picsum.photos/468/468?random=3',
|
||||||
|
'https://picsum.photos/468/468?random=4',
|
||||||
|
'https://picsum.photos/468/468?random=5',
|
||||||
|
'https://picsum.photos/468/468?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel
|
||||||
|
v-slot="{ item }"
|
||||||
|
loop
|
||||||
|
dots
|
||||||
|
arrows
|
||||||
|
auto-scroll
|
||||||
|
:items="items"
|
||||||
|
:ui="{ item: 'basis-1/3' }"
|
||||||
|
>
|
||||||
|
<img :src="item" width="234" height="234" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/468/468?random=1',
|
||||||
|
'https://picsum.photos/468/468?random=2',
|
||||||
|
'https://picsum.photos/468/468?random=3',
|
||||||
|
'https://picsum.photos/468/468?random=4',
|
||||||
|
'https://picsum.photos/468/468?random=5',
|
||||||
|
'https://picsum.photos/468/468?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel
|
||||||
|
v-slot="{ item }"
|
||||||
|
loop
|
||||||
|
arrows
|
||||||
|
dots
|
||||||
|
:autoplay="{ delay: 2000 }"
|
||||||
|
:items="items"
|
||||||
|
:ui="{ item: 'basis-1/3' }"
|
||||||
|
>
|
||||||
|
<img :src="item" width="234" height="234" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/528/528?random=1',
|
||||||
|
'https://picsum.photos/528/528?random=2',
|
||||||
|
'https://picsum.photos/528/528?random=3',
|
||||||
|
'https://picsum.photos/528/528?random=4',
|
||||||
|
'https://picsum.photos/528/528?random=5',
|
||||||
|
'https://picsum.photos/528/528?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel
|
||||||
|
v-slot="{ item }"
|
||||||
|
class-names
|
||||||
|
arrows
|
||||||
|
:items="items"
|
||||||
|
:ui="{
|
||||||
|
item: 'basis-[70%] transition-opacity [&:not(.is-snapped)]:opacity-10'
|
||||||
|
}"
|
||||||
|
class="mx-auto max-w-sm"
|
||||||
|
>
|
||||||
|
<img :src="item" width="264" height="264" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/640/640?random=1',
|
||||||
|
'https://picsum.photos/640/640?random=2',
|
||||||
|
'https://picsum.photos/640/640?random=3',
|
||||||
|
'https://picsum.photos/640/640?random=4',
|
||||||
|
'https://picsum.photos/640/640?random=5',
|
||||||
|
'https://picsum.photos/640/640?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel v-slot="{ item }" dots :items="items" class="w-full max-w-xs mx-auto">
|
||||||
|
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/640/640?random=1',
|
||||||
|
'https://picsum.photos/640/640?random=2',
|
||||||
|
'https://picsum.photos/640/640?random=3',
|
||||||
|
'https://picsum.photos/640/640?random=4',
|
||||||
|
'https://picsum.photos/640/640?random=5',
|
||||||
|
'https://picsum.photos/640/640?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel v-slot="{ item }" dots :items="items" :ui="{ item: 'basis-1/3' }">
|
||||||
|
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/640/640?random=1',
|
||||||
|
'https://picsum.photos/640/640?random=2',
|
||||||
|
'https://picsum.photos/640/640?random=3',
|
||||||
|
'https://picsum.photos/640/640?random=4',
|
||||||
|
'https://picsum.photos/640/640?random=5',
|
||||||
|
'https://picsum.photos/640/640?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel
|
||||||
|
v-slot="{ item }"
|
||||||
|
fade
|
||||||
|
arrows
|
||||||
|
dots
|
||||||
|
:items="items"
|
||||||
|
class="w-full max-w-xs mx-auto"
|
||||||
|
>
|
||||||
|
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/640/640?random=1',
|
||||||
|
'https://picsum.photos/640/640?random=2',
|
||||||
|
'https://picsum.photos/640/640?random=3',
|
||||||
|
'https://picsum.photos/640/640?random=4',
|
||||||
|
'https://picsum.photos/640/640?random=5',
|
||||||
|
'https://picsum.photos/640/640?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel v-slot="{ item }" :items="items" class="w-full max-w-xs mx-auto">
|
||||||
|
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/468/468?random=1',
|
||||||
|
'https://picsum.photos/468/468?random=2',
|
||||||
|
'https://picsum.photos/468/468?random=3',
|
||||||
|
'https://picsum.photos/468/468?random=4',
|
||||||
|
'https://picsum.photos/468/468?random=5',
|
||||||
|
'https://picsum.photos/468/468?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-1/3' }">
|
||||||
|
<img :src="item" width="234" height="234" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/640/640?random=1',
|
||||||
|
'https://picsum.photos/640/640?random=2',
|
||||||
|
'https://picsum.photos/640/640?random=3',
|
||||||
|
'https://picsum.photos/640/640?random=4',
|
||||||
|
'https://picsum.photos/640/640?random=5',
|
||||||
|
'https://picsum.photos/640/640?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel
|
||||||
|
v-slot="{ item }"
|
||||||
|
orientation="vertical"
|
||||||
|
:items="items"
|
||||||
|
class="w-full max-w-xs mx-auto"
|
||||||
|
:ui="{ container: 'h-[336px]' }"
|
||||||
|
>
|
||||||
|
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/640/640?random=1',
|
||||||
|
'https://picsum.photos/640/640?random=2',
|
||||||
|
'https://picsum.photos/640/640?random=3',
|
||||||
|
'https://picsum.photos/640/640?random=4',
|
||||||
|
'https://picsum.photos/640/640?random=5',
|
||||||
|
'https://picsum.photos/640/640?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel
|
||||||
|
v-slot="{ item }"
|
||||||
|
arrows
|
||||||
|
:prev="{ color: 'primary' }"
|
||||||
|
:next="{ variant: 'solid' }"
|
||||||
|
:items="items"
|
||||||
|
class="w-full max-w-xs mx-auto"
|
||||||
|
>
|
||||||
|
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
prevIcon?: string
|
||||||
|
nextIcon?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/640/640?random=1',
|
||||||
|
'https://picsum.photos/640/640?random=2',
|
||||||
|
'https://picsum.photos/640/640?random=3',
|
||||||
|
'https://picsum.photos/640/640?random=4',
|
||||||
|
'https://picsum.photos/640/640?random=5',
|
||||||
|
'https://picsum.photos/640/640?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel
|
||||||
|
v-slot="{ item }"
|
||||||
|
arrows
|
||||||
|
:prev-icon="prevIcon"
|
||||||
|
:next-icon="nextIcon"
|
||||||
|
:items="items"
|
||||||
|
class="w-full max-w-xs mx-auto"
|
||||||
|
>
|
||||||
|
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const items = [
|
||||||
|
'https://picsum.photos/468/468?random=1',
|
||||||
|
'https://picsum.photos/468/468?random=2',
|
||||||
|
'https://picsum.photos/468/468?random=3',
|
||||||
|
'https://picsum.photos/468/468?random=4',
|
||||||
|
'https://picsum.photos/468/468?random=5',
|
||||||
|
'https://picsum.photos/468/468?random=6'
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCarousel
|
||||||
|
v-slot="{ item }"
|
||||||
|
loop
|
||||||
|
wheel-gestures
|
||||||
|
:items="items"
|
||||||
|
:ui="{ item: 'basis-1/3' }"
|
||||||
|
>
|
||||||
|
<img :src="item" width="234" height="234" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
@@ -36,7 +36,7 @@ Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your
|
|||||||
</style>
|
</style>
|
||||||
```
|
```
|
||||||
|
|
||||||
The `@theme` directive tells Tailwind to make new utilities and variants available based on those variables. It's the equivalent of the `theme.extend` key in Tailwind CSS v3 `tailwind.config.ts` file.
|
The `@theme` directive tells Tailwind to make new utilities and variants available based on these variables. It's the equivalent of the `theme.extend` key in Tailwind CSS v3 `tailwind.config.ts` file.
|
||||||
|
|
||||||
::note
|
::note
|
||||||
You can learn more about this on [https://tailwindcss.com/blog/tailwindcss-v4-alpha](https://tailwindcss.com/blog/tailwindcss-v4-alpha#css-first-configuration).
|
You can learn more about this on [https://tailwindcss.com/blog/tailwindcss-v4-alpha](https://tailwindcss.com/blog/tailwindcss-v4-alpha#css-first-configuration).
|
||||||
@@ -383,7 +383,7 @@ export default {
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
::note
|
::warning
|
||||||
Components without slots don't have a [`ui` prop](#ui-prop), only the [`class` prop](#class-prop) is available to override styles.
|
Components without slots don't have a [`ui` prop](#ui-prop), only the [`class` prop](#class-prop) is available to override styles.
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -434,11 +434,11 @@ The `defaultVariants` property specifies the default values for each variant. It
|
|||||||
|
|
||||||
You have multiple ways to customize the appearance of Nuxt UI components, you can do it for all components at once or on a per-component basis.
|
You have multiple ways to customize the appearance of Nuxt UI components, you can do it for all components at once or on a per-component basis.
|
||||||
|
|
||||||
::tip
|
::note
|
||||||
Tailwind Variants uses [tailwind-merge](https://github.com/dcastil/tailwind-merge) under the hood to merge classes so you don't have to worry about conflicting classes.
|
Tailwind Variants uses [tailwind-merge](https://github.com/dcastil/tailwind-merge) under the hood to merge classes so you don't have to worry about conflicting classes.
|
||||||
::
|
::
|
||||||
|
|
||||||
::note
|
::tip
|
||||||
You can explore the theme for each component in two ways:
|
You can explore the theme for each component in two ways:
|
||||||
|
|
||||||
- Check the `Theme` section in the documentation of each individual component.
|
- Check the `Theme` section in the documentation of each individual component.
|
||||||
@@ -463,7 +463,9 @@ export default defineAppConfig({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
::note
|
||||||
In this example, the `font-bold` class will override the default `font-medium` class on all buttons.
|
In this example, the `font-bold` class will override the default `font-medium` class on all buttons.
|
||||||
|
::
|
||||||
|
|
||||||
### `ui` prop
|
### `ui` prop
|
||||||
|
|
||||||
@@ -492,7 +494,9 @@ slots:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
In this example, the `trailingIcon` slot is overwritten with `size-3` even though the `md` size variant would apply a `size-5` class to it.
|
In this example, the `trailingIcon` slot is overwritten with `size-3` even though the `md` size variant would apply a `size-5` class to it.
|
||||||
|
::
|
||||||
|
|
||||||
### `class` prop
|
### `class` prop
|
||||||
|
|
||||||
@@ -506,3 +510,7 @@ slots:
|
|||||||
default: Button
|
default: Button
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
In this example, the `font-bold` class will override the default `font-medium` class on this button.
|
||||||
|
::
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ npm install @iconify-json/{collection_name}
|
|||||||
|
|
||||||
For example, to use the `i-uil-github` icon, install it's collection with `@iconify-json/uil`. This way the icons can be served locally or from your serverless functions, which is faster and more reliable on both SSR and client-side.
|
For example, to use the `i-uil-github` icon, install it's collection with `@iconify-json/uil`. This way the icons can be served locally or from your serverless functions, which is faster and more reliable on both SSR and client-side.
|
||||||
|
|
||||||
::tip{to="https://github.com/nuxt/icon?tab=readme-ov-file#iconify-dataset" target="_blank"}
|
::note{to="https://github.com/nuxt/icon?tab=readme-ov-file#iconify-dataset" target="_blank"}
|
||||||
Read more about this in the `@nuxt/icon` documentation.
|
Read more about this in the `@nuxt/icon` documentation.
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ Then you can use the icons like this:
|
|||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
::tip{to="https://github.com/nuxt/icon?tab=readme-ov-file#custom-local-collections" target="_blank"}
|
::note{to="https://github.com/nuxt/icon?tab=readme-ov-file#custom-local-collections" target="_blank"}
|
||||||
Read more about this in the `@nuxt/icon` documentation.
|
Read more about this in the `@nuxt/icon` documentation.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,6 @@ Nuxt UI automatically registers the `@nuxt/fonts` module for you, so there's no
|
|||||||
|
|
||||||
That's it! Nuxt Fonts will detect this and you should immediately see the web font loaded in your browser.
|
That's it! Nuxt Fonts will detect this and you should immediately see the web font loaded in your browser.
|
||||||
|
|
||||||
::tip{to="https://fonts.nuxt.com/advanced" target="_blank"}
|
::note{to="https://fonts.nuxt.com/advanced" target="_blank"}
|
||||||
Read more about how `@nuxt/fonts` work behind the scenes to optimize your fonts.
|
Read more about how `@nuxt/fonts` work behind the scenes to optimize your fonts.
|
||||||
::
|
::
|
||||||
|
|||||||
@@ -278,6 +278,7 @@ ignore:
|
|||||||
- exactQuery
|
- exactQuery
|
||||||
- exactHash
|
- exactHash
|
||||||
- external
|
- external
|
||||||
|
- onClick
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ ignore:
|
|||||||
- exactQuery
|
- exactQuery
|
||||||
- exactHash
|
- exactHash
|
||||||
- external
|
- external
|
||||||
|
- onClick
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -248,6 +248,7 @@ ignore:
|
|||||||
- exactHash
|
- exactHash
|
||||||
- external
|
- external
|
||||||
- active
|
- active
|
||||||
|
- onClick
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +1,419 @@
|
|||||||
---
|
---
|
||||||
description: A carousel with motion and swipe support.
|
description: A carousel with motion and swipe built using Embla.
|
||||||
links:
|
links:
|
||||||
|
- label: Embla
|
||||||
|
to: https://www.embla-carousel.com/api/
|
||||||
|
icon: i-custom-embla-carousel
|
||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Carousel.vue
|
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Carousel.vue
|
||||||
navigation:
|
|
||||||
badge:
|
|
||||||
label: Todo
|
|
||||||
color: neutral
|
|
||||||
disabled: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
## Examples
|
### Items
|
||||||
|
|
||||||
<!-- ## API
|
Use the `items` prop as an array and render each item using the default slot:
|
||||||
|
|
||||||
|
::note
|
||||||
|
Use your mouse to drag the carousel horizontally on desktop.
|
||||||
|
::
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-items-example'
|
||||||
|
class: 'p-8'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
You can control how many items are visible by using the [`basis`](https://tailwindcss.com/docs/flex-basis) / [`width`](https://tailwindcss.com/docs/width) utility classes on the `item`:
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-items-multiple-example'
|
||||||
|
class: 'p-8 px-16'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Orientation
|
||||||
|
|
||||||
|
Use the `orientation` prop to change the orientation of the Progress. Defaults to `horizontal`.
|
||||||
|
|
||||||
|
::note
|
||||||
|
Use your mouse to drag the carousel vertically on desktop.
|
||||||
|
::
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-orientation-example'
|
||||||
|
class: 'p-8'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::caution
|
||||||
|
You need to specify a `height` on the container in vertical orientation.
|
||||||
|
::
|
||||||
|
|
||||||
|
### Arrows
|
||||||
|
|
||||||
|
Use the `arrows` prop to display prev and next buttons.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-arrows-example'
|
||||||
|
class: 'p-8'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Prev / Next
|
||||||
|
|
||||||
|
Use the `prev` and `next` props to customize the prev and next buttons.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-prev-next-example'
|
||||||
|
class: 'p-8'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Prev Icon / Next Icon
|
||||||
|
|
||||||
|
Use the `prev-icon` and `next-icon` props to customize the buttons [Icon](/components/icon). Defaults to `i-heroicons-arrow-left-20-solid` / `i-heroicons-arrow-right-20-solid`.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-prev-next-icon-example'
|
||||||
|
class: 'p-8'
|
||||||
|
options:
|
||||||
|
- name: 'prevIcon'
|
||||||
|
label: 'prevIcon'
|
||||||
|
default: 'i-heroicons-chevron-left'
|
||||||
|
- name: 'nextIcon'
|
||||||
|
label: 'nextIcon'
|
||||||
|
default: 'i-heroicons-chevron-right'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::tip{to="/getting-started/icons#theme"}
|
||||||
|
You can customize these icons globally in your `app.config.ts` under `ui.icons.arrowLeft` / `ui.icons.arrowRight` key.
|
||||||
|
::
|
||||||
|
|
||||||
|
### Dots
|
||||||
|
|
||||||
|
Use the `dots` prop to display a list of dots to scroll to a specific slide.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-dots-example'
|
||||||
|
class: 'p-8 pb-12'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
The number of dots is based on the number of slides displayed in the view:
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-dots-multiple-example'
|
||||||
|
class: 'p-8 px-16 pb-12'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
|
The Carousel component implements the official [Embla Carousel plugins](https://www.embla-carousel.com/plugins/).
|
||||||
|
|
||||||
|
### Autoplay
|
||||||
|
|
||||||
|
This plugin is used to extend Embla Carousel with **autoplay** functionality.
|
||||||
|
|
||||||
|
::warning
|
||||||
|
Install the `embla-carousel-autoplay` package:
|
||||||
|
|
||||||
|
::code-group
|
||||||
|
|
||||||
|
```bash [pnpm]
|
||||||
|
pnpm add embla-carousel-autoplay
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [yarn]
|
||||||
|
yarn add embla-carousel-autoplay
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [npm]
|
||||||
|
npm install embla-carousel-autoplay
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [bun]
|
||||||
|
bun add embla-carousel-autoplay
|
||||||
|
```
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Use the `autoplay` prop as a boolean or an object to configure the [Autoplay plugin](https://www.embla-carousel.com/plugins/autoplay/).
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-autoplay-example'
|
||||||
|
class: 'p-8 px-16 pb-12'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
In this example, we're using the `loop` prop for an infinite carousel.
|
||||||
|
::
|
||||||
|
|
||||||
|
### Auto Scroll
|
||||||
|
|
||||||
|
This plugin is used to extend Embla Carousel with **auto scroll** functionality.
|
||||||
|
|
||||||
|
::warning
|
||||||
|
Install the `embla-carousel-auto-scroll` package:
|
||||||
|
|
||||||
|
::code-group
|
||||||
|
|
||||||
|
```bash [pnpm]
|
||||||
|
pnpm add embla-carousel-auto-scroll
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [yarn]
|
||||||
|
yarn add embla-carousel-auto-scroll
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [npm]
|
||||||
|
npm install embla-carousel-auto-scroll
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [bun]
|
||||||
|
bun add embla-carousel-auto-scroll
|
||||||
|
```
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Use the `auto-scroll` prop as a boolean or an object to configure the [Auto Scroll plugin](https://www.embla-carousel.com/plugins/auto-scroll/).
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-auto-scroll-example'
|
||||||
|
class: 'p-8 px-16 pb-12'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
In this example, we're using the `loop` prop for an infinite carousel.
|
||||||
|
::
|
||||||
|
|
||||||
|
### Auto Height
|
||||||
|
|
||||||
|
This plugin is used to extend Embla Carousel with **auto height** functionality. It changes the height of the carousel container to fit the height of the highest slide in view.
|
||||||
|
|
||||||
|
::warning
|
||||||
|
Install the `embla-carousel-auto-height` package:
|
||||||
|
|
||||||
|
::code-group
|
||||||
|
|
||||||
|
```bash [pnpm]
|
||||||
|
pnpm add embla-carousel-auto-height
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [yarn]
|
||||||
|
yarn add embla-carousel-auto-height
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [npm]
|
||||||
|
npm install embla-carousel-auto-height
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [bun]
|
||||||
|
bun add embla-carousel-auto-height
|
||||||
|
```
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Use the `auto-height` prop as a boolean or an object to configure the [Auto Height plugin](https://www.embla-carousel.com/plugins/auto-height/).
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-auto-height-example'
|
||||||
|
class: 'p-8 pt-16'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
In this example, we add the `transition-[height]` class on the container to animate the height change.
|
||||||
|
::
|
||||||
|
|
||||||
|
### Class Names
|
||||||
|
|
||||||
|
Class Names is a **class name toggle** utility plugin for Embla Carousel that enables you to automate the toggling of class names on your carousel.
|
||||||
|
|
||||||
|
::warning
|
||||||
|
Install the `embla-carousel-class-names` package:
|
||||||
|
|
||||||
|
::code-group
|
||||||
|
|
||||||
|
```bash [pnpm]
|
||||||
|
pnpm add embla-carousel-class-names
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [yarn]
|
||||||
|
yarn add embla-carousel-class-names
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [npm]
|
||||||
|
npm install embla-carousel-class-names
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [bun]
|
||||||
|
bun add embla-carousel-class-names
|
||||||
|
```
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Use the `class-names` prop as a boolean or an object to configure the [Class Names plugin](https://www.embla-carousel.com/plugins/class-names/).
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-class-names-example'
|
||||||
|
class: 'p-8'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
In this example, we add the `transition-opacity [&:not(.is-snapped)]:opacity-10` classes on the `item` to animate the opacity change.
|
||||||
|
::
|
||||||
|
|
||||||
|
### Fade
|
||||||
|
|
||||||
|
This plugin is used to replace the Embla Carousel scroll functionality with **fade transitions**.
|
||||||
|
|
||||||
|
::warning
|
||||||
|
Install the `embla-carousel-fade` package:
|
||||||
|
|
||||||
|
::code-group
|
||||||
|
|
||||||
|
```bash [pnpm]
|
||||||
|
pnpm add embla-carousel-fade
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [yarn]
|
||||||
|
yarn add embla-carousel-fade
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [npm]
|
||||||
|
npm install embla-carousel-fade
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [bun]
|
||||||
|
bun add embla-carousel-fade
|
||||||
|
```
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Use the `fade` prop as a boolean or an object to configure the [Fade plugin](https://www.embla-carousel.com/plugins/fade/).
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-fade-example'
|
||||||
|
class: 'p-8 pb-12'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Wheel Gestures
|
||||||
|
|
||||||
|
This plugin is used to extend Embla Carousel with the ability to **use the mouse/trackpad wheel** to navigate the carousel.
|
||||||
|
|
||||||
|
::warning
|
||||||
|
Install the `embla-carousel-wheel-gestures` package:
|
||||||
|
|
||||||
|
::code-group
|
||||||
|
|
||||||
|
```bash [pnpm]
|
||||||
|
pnpm add embla-carousel-wheel-gestures
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [yarn]
|
||||||
|
yarn add embla-carousel-wheel-gestures
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [npm]
|
||||||
|
npm install embla-carousel-wheel-gestures
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [bun]
|
||||||
|
bun add embla-carousel-wheel-gestures
|
||||||
|
```
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Use the `wheel-gestures` prop as a boolean or an object to configure the [Wheel Gestures plugin](https://www.embla-carousel.com/plugins/wheel-gestures/).
|
||||||
|
|
||||||
|
::note
|
||||||
|
Use your mouse wheel to scroll the carousel.
|
||||||
|
::
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
name: 'carousel-wheel-gestures-example'
|
||||||
|
class: 'p-8 px-16'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
### Props
|
### Props
|
||||||
|
|
||||||
:component-props
|
::component-props
|
||||||
|
---
|
||||||
|
ignore:
|
||||||
|
- as
|
||||||
|
- to
|
||||||
|
- target
|
||||||
|
- active
|
||||||
|
- activeClass
|
||||||
|
- inactiveClass
|
||||||
|
- exactActiveClass
|
||||||
|
- ariaCurrentValue
|
||||||
|
- href
|
||||||
|
- rel
|
||||||
|
- noRel
|
||||||
|
- prefetch
|
||||||
|
- prefetchOn
|
||||||
|
- noPrefetch
|
||||||
|
- prefetchedClass
|
||||||
|
- replace
|
||||||
|
- exact
|
||||||
|
- exactQuery
|
||||||
|
- exactHash
|
||||||
|
- external
|
||||||
|
- onClick
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
### Slots
|
### Slots
|
||||||
|
|
||||||
:component-slots
|
:component-slots
|
||||||
|
|
||||||
### Emits
|
### Expose
|
||||||
|
|
||||||
:component-emits
|
When accessing the component via a template ref, you can use the following:
|
||||||
|
|
||||||
|
| Name | Type |
|
||||||
|
| ---- | ---- |
|
||||||
|
| `emblaRef`{lang="ts-type"} | `Ref<HTMLElement \| null>`{lang="ts-type"} |
|
||||||
|
| `emblaApi`{lang="ts-type"} | [`Ref<EmblaCarouselType \| null>`{lang="ts-type"}](https://www.embla-carousel.com/api/methods/#typescript) |
|
||||||
|
|
||||||
## Theme
|
## Theme
|
||||||
|
|
||||||
:component-theme -->
|
:component-theme
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ ignore:
|
|||||||
- exactQuery
|
- exactQuery
|
||||||
- exactHash
|
- exactHash
|
||||||
- external
|
- external
|
||||||
|
- onClick
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -292,6 +292,7 @@ ignore:
|
|||||||
- exactQuery
|
- exactQuery
|
||||||
- exactHash
|
- exactHash
|
||||||
- external
|
- external
|
||||||
|
- onClick
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -361,6 +361,7 @@ ignore:
|
|||||||
- exactQuery
|
- exactQuery
|
||||||
- exactHash
|
- exactHash
|
||||||
- external
|
- external
|
||||||
|
- onClick
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -381,6 +381,7 @@ ignore:
|
|||||||
- exactQuery
|
- exactQuery
|
||||||
- exactHash
|
- exactHash
|
||||||
- external
|
- external
|
||||||
|
- onClick
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ ignore:
|
|||||||
- exactQuery
|
- exactQuery
|
||||||
- exactHash
|
- exactHash
|
||||||
- external
|
- external
|
||||||
|
- onClick
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ ignore:
|
|||||||
- exactQuery
|
- exactQuery
|
||||||
- exactHash
|
- exactHash
|
||||||
- external
|
- external
|
||||||
|
- onClick
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
"@vueuse/core": "^11.1.0",
|
"@vueuse/core": "^11.1.0",
|
||||||
"@vueuse/integrations": "^11.1.0",
|
"@vueuse/integrations": "^11.1.0",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
|
"embla-carousel-vue": "^8.3.0",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"radix-vue": "^1.9.7",
|
"radix-vue": "^1.9.7",
|
||||||
@@ -70,6 +71,13 @@
|
|||||||
"@release-it/conventional-changelog": "^8.0.2",
|
"@release-it/conventional-changelog": "^8.0.2",
|
||||||
"@standard-schema/spec": "1.0.0-beta.1",
|
"@standard-schema/spec": "1.0.0-beta.1",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
|
"embla-carousel": "^8.3.0",
|
||||||
|
"embla-carousel-auto-height": "^8.3.0",
|
||||||
|
"embla-carousel-auto-scroll": "^8.3.0",
|
||||||
|
"embla-carousel-autoplay": "^8.3.0",
|
||||||
|
"embla-carousel-class-names": "^8.3.0",
|
||||||
|
"embla-carousel-fade": "^8.3.0",
|
||||||
|
"embla-carousel-wheel-gestures": "^8.0.1",
|
||||||
"eslint": "^9.11.1",
|
"eslint": "^9.11.1",
|
||||||
"happy-dom": "^15.7.4",
|
"happy-dom": "^15.7.4",
|
||||||
"joi": "^17.13.3",
|
"joi": "^17.13.3",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const components = [
|
|||||||
'button',
|
'button',
|
||||||
'button-group',
|
'button-group',
|
||||||
'card',
|
'card',
|
||||||
|
'carousel',
|
||||||
'checkbox',
|
'checkbox',
|
||||||
'chip',
|
'chip',
|
||||||
'collapsible',
|
'collapsible',
|
||||||
|
|||||||
84
playground/app/pages/components/carousel.vue
Normal file
84
playground/app/pages/components/carousel.vue
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import theme from '#build/ui/navigation-menu'
|
||||||
|
|
||||||
|
const orientations = Object.keys(theme.variants.orientation)
|
||||||
|
|
||||||
|
const orientation = ref('horizontal' as const)
|
||||||
|
const loop = ref(false)
|
||||||
|
const skipSnaps = ref(false)
|
||||||
|
const autoplay = ref(false)
|
||||||
|
const autoScroll = ref(false)
|
||||||
|
const autoHeight = ref(false)
|
||||||
|
const fade = ref(false)
|
||||||
|
const wheelGestures = ref(false)
|
||||||
|
const classNames = ref(false)
|
||||||
|
const arrows = ref(false)
|
||||||
|
const dots = ref(false)
|
||||||
|
const bind = computed(() => ({
|
||||||
|
loop: loop.value,
|
||||||
|
skipSnaps: skipSnaps.value,
|
||||||
|
orientation: orientation.value,
|
||||||
|
autoplay: autoplay.value,
|
||||||
|
autoScroll: autoScroll.value,
|
||||||
|
autoHeight: autoHeight.value,
|
||||||
|
fade: fade.value,
|
||||||
|
wheelGestures: wheelGestures.value,
|
||||||
|
classNames: classNames.value,
|
||||||
|
arrows: arrows.value,
|
||||||
|
dots: dots.value
|
||||||
|
}))
|
||||||
|
|
||||||
|
const items = Array.from({ length: 6 }).map((_, index) => index)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-11">
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<USelect v-model="orientation" :items="orientations" placeholder="Orientation" />
|
||||||
|
</div>
|
||||||
|
<fieldset class="flex items-center justify-center gap-2">
|
||||||
|
<legend class="text-xs font-mono font-bold text-center mb-2">
|
||||||
|
Options
|
||||||
|
</legend>
|
||||||
|
<USwitch v-model="loop" label="Loop" />
|
||||||
|
<USwitch v-model="skipSnaps" label="Skip Snaps" />
|
||||||
|
<USwitch v-model="arrows" label="Arrows" />
|
||||||
|
<USwitch v-model="dots" label="Dots" />
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="flex items-center justify-center gap-2">
|
||||||
|
<legend class="text-xs font-mono font-bold text-center mb-2">
|
||||||
|
Plugins
|
||||||
|
</legend>
|
||||||
|
<USwitch v-model="autoplay" label="Autoplay" />
|
||||||
|
<USwitch v-model="autoScroll" label="Auto Scroll" />
|
||||||
|
<USwitch v-model="autoHeight" label="Auto Height" :disabled="orientation !== 'horizontal'" />
|
||||||
|
<USwitch v-model="fade" label="Fade" />
|
||||||
|
<USwitch v-model="classNames" label="Class Names" />
|
||||||
|
<USwitch v-model="wheelGestures" label="Wheel Gestures" />
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="classNames">
|
||||||
|
<UCarousel v-slot="{ index }" v-bind="bind" :items="items" :ui="{ item: 'basis-[70%] transition-opacity ease-in-out [&:not(.is-snapped)]:opacity-10', container: 'h-[352px]' }" class="w-full max-w-xl mx-auto">
|
||||||
|
<img :src="`https://picsum.photos/600/350?v=${index}`" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="autoHeight">
|
||||||
|
<UCarousel v-slot="{ index }" v-bind="bind" :items="items" :ui="{ container: 'transition-[height] duration-200' }" class="w-full max-w-md mx-auto">
|
||||||
|
<img :src="`https://picsum.photos/600/${index % 2 === 0 ? 350 : 450}?v=${index}`" :class="index % 2 === 0 ? 'h-[350px]' : 'h-[450px]'" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<UCarousel v-slot="{ index }" v-bind="bind" :items="items" class="w-[320px] mx-auto" :ui="{ container: 'h-[336px]' }">
|
||||||
|
<img :src="`https://picsum.photos/640/640?v=${index}`" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
|
||||||
|
<template v-if="orientation === 'horizontal'">
|
||||||
|
<UCarousel v-slot="{ index }" v-bind="bind" :items="items" :ui="{ item: 'basis-1/3' }" class="w-full max-w-xs mx-auto">
|
||||||
|
<img :src="`https://picsum.photos/320/320?v=${index}`" class="rounded-lg">
|
||||||
|
</UCarousel>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
111
pnpm-lock.yaml
generated
111
pnpm-lock.yaml
generated
@@ -43,6 +43,9 @@ importers:
|
|||||||
defu:
|
defu:
|
||||||
specifier: ^6.1.4
|
specifier: ^6.1.4
|
||||||
version: 6.1.4
|
version: 6.1.4
|
||||||
|
embla-carousel-vue:
|
||||||
|
specifier: ^8.3.0
|
||||||
|
version: 8.3.0(vue@3.5.11(typescript@5.6.2))
|
||||||
fuse.js:
|
fuse.js:
|
||||||
specifier: ^7.0.0
|
specifier: ^7.0.0
|
||||||
version: 7.0.0
|
version: 7.0.0
|
||||||
@@ -86,6 +89,27 @@ importers:
|
|||||||
'@vue/test-utils':
|
'@vue/test-utils':
|
||||||
specifier: ^2.4.6
|
specifier: ^2.4.6
|
||||||
version: 2.4.6
|
version: 2.4.6
|
||||||
|
embla-carousel:
|
||||||
|
specifier: ^8.3.0
|
||||||
|
version: 8.3.0
|
||||||
|
embla-carousel-auto-height:
|
||||||
|
specifier: ^8.3.0
|
||||||
|
version: 8.3.0(embla-carousel@8.3.0)
|
||||||
|
embla-carousel-auto-scroll:
|
||||||
|
specifier: ^8.3.0
|
||||||
|
version: 8.3.0(embla-carousel@8.3.0)
|
||||||
|
embla-carousel-autoplay:
|
||||||
|
specifier: ^8.3.0
|
||||||
|
version: 8.3.0(embla-carousel@8.3.0)
|
||||||
|
embla-carousel-class-names:
|
||||||
|
specifier: ^8.3.0
|
||||||
|
version: 8.3.0(embla-carousel@8.3.0)
|
||||||
|
embla-carousel-fade:
|
||||||
|
specifier: ^8.3.0
|
||||||
|
version: 8.3.0(embla-carousel@8.3.0)
|
||||||
|
embla-carousel-wheel-gestures:
|
||||||
|
specifier: ^8.0.1
|
||||||
|
version: 8.0.1(embla-carousel@8.3.0)
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^9.11.1
|
specifier: ^9.11.1
|
||||||
version: 9.12.0(jiti@2.3.3)
|
version: 9.12.0(jiti@2.3.3)
|
||||||
@@ -3276,6 +3300,50 @@ packages:
|
|||||||
electron-to-chromium@1.5.33:
|
electron-to-chromium@1.5.33:
|
||||||
resolution: {integrity: sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==}
|
resolution: {integrity: sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==}
|
||||||
|
|
||||||
|
embla-carousel-auto-height@8.3.0:
|
||||||
|
resolution: {integrity: sha512-RbRu1AK5eC1ysBYysVKHMAl2PvtvbqE3GDa6mYyfuD5nDrLrRG/UHjElW8Elh/WYeJZ6AGW6Vb5SnWVwPsHnDg==}
|
||||||
|
peerDependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-auto-scroll@8.3.0:
|
||||||
|
resolution: {integrity: sha512-ybXWqCTWvl+DeGwtGw0tUU1bOKglS/Ov8F5fueMkiwySKrSFAHizdqSrlMR1SQEXNZHMPH9LqCz7MajNYkdeAQ==}
|
||||||
|
peerDependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-autoplay@8.3.0:
|
||||||
|
resolution: {integrity: sha512-h7DFJLf9uQD+XDxr1NwA3/oFIjsnj/iED2RjET5u6/svMec46IbF1CYPhmB5Q/1Fc0WkcvhPpsEsrtVXQLxNzA==}
|
||||||
|
peerDependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-class-names@8.3.0:
|
||||||
|
resolution: {integrity: sha512-d78aB1rZyuvsgfyqzZJiNL8dvZsjWlF9IP62S9pxhhTwsh4Ry5AAYhPSWPjBfuFnOtnq72QDI06ziUuU6wdxHQ==}
|
||||||
|
peerDependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-fade@8.3.0:
|
||||||
|
resolution: {integrity: sha512-m0NbkNPTAr6ghINhJrCnI0BRgWWoGRIGUd1tYCxTK00Exm9+kzOVL5KBPkrMVzXRXHe6TRgkmsCkb/7npfwRFQ==}
|
||||||
|
peerDependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-reactive-utils@8.3.0:
|
||||||
|
resolution: {integrity: sha512-EYdhhJ302SC4Lmkx8GRsp0sjUhEN4WyFXPOk0kGu9OXZSRMmcBlRgTvHcq8eKJE1bXWBsOi1T83B+BSSVZSmwQ==}
|
||||||
|
peerDependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-vue@8.3.0:
|
||||||
|
resolution: {integrity: sha512-K8ghzWZ9Th2Czvy9dVaz6ItgvFdkI3wHQe6HgeHmvmOlkvhu/fNz83uHrLWVAsjztu/b+yiFk9zvXb0+IQ2mPw==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.2.37
|
||||||
|
|
||||||
|
embla-carousel-wheel-gestures@8.0.1:
|
||||||
|
resolution: {integrity: sha512-LMAnruDqDmsjL6UoQD65aLotpmfO49Fsr3H0bMi7I+BH6jbv9OJiE61kN56daKsVtCQEt0SU1MrJslbhtgF3yQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
embla-carousel: ^8.0.0 || ~8.0.0-rc03
|
||||||
|
|
||||||
|
embla-carousel@8.3.0:
|
||||||
|
resolution: {integrity: sha512-Ve8dhI4w28qBqR8J+aMtv7rLK89r1ZA5HocwFz6uMB/i5EiC7bGI7y+AM80yAVUJw3qqaZYK7clmZMUR8kM3UA==}
|
||||||
|
|
||||||
emoji-regex@10.4.0:
|
emoji-regex@10.4.0:
|
||||||
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
|
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
|
||||||
|
|
||||||
@@ -6574,6 +6642,10 @@ packages:
|
|||||||
whatwg-url@5.0.0:
|
whatwg-url@5.0.0:
|
||||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||||
|
|
||||||
|
wheel-gestures@2.2.48:
|
||||||
|
resolution: {integrity: sha512-f+Gy33Oa5Z14XY9679Zze+7VFhbsQfBFXodnU2x589l4kxGM9L5Y8zETTmcMR5pWOPQyRv4Z0lNax6xCO0NSlA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -10058,6 +10130,43 @@ snapshots:
|
|||||||
|
|
||||||
electron-to-chromium@1.5.33: {}
|
electron-to-chromium@1.5.33: {}
|
||||||
|
|
||||||
|
embla-carousel-auto-height@8.3.0(embla-carousel@8.3.0):
|
||||||
|
dependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-auto-scroll@8.3.0(embla-carousel@8.3.0):
|
||||||
|
dependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-autoplay@8.3.0(embla-carousel@8.3.0):
|
||||||
|
dependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-class-names@8.3.0(embla-carousel@8.3.0):
|
||||||
|
dependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-fade@8.3.0(embla-carousel@8.3.0):
|
||||||
|
dependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-reactive-utils@8.3.0(embla-carousel@8.3.0):
|
||||||
|
dependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
|
||||||
|
embla-carousel-vue@8.3.0(vue@3.5.11(typescript@5.6.2)):
|
||||||
|
dependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
embla-carousel-reactive-utils: 8.3.0(embla-carousel@8.3.0)
|
||||||
|
vue: 3.5.11(typescript@5.6.2)
|
||||||
|
|
||||||
|
embla-carousel-wheel-gestures@8.0.1(embla-carousel@8.3.0):
|
||||||
|
dependencies:
|
||||||
|
embla-carousel: 8.3.0
|
||||||
|
wheel-gestures: 2.2.48
|
||||||
|
|
||||||
|
embla-carousel@8.3.0: {}
|
||||||
|
|
||||||
emoji-regex@10.4.0: {}
|
emoji-regex@10.4.0: {}
|
||||||
|
|
||||||
emoji-regex@8.0.0: {}
|
emoji-regex@8.0.0: {}
|
||||||
@@ -14283,6 +14392,8 @@ snapshots:
|
|||||||
tr46: 0.0.3
|
tr46: 0.0.3
|
||||||
webidl-conversions: 3.0.1
|
webidl-conversions: 3.0.1
|
||||||
|
|
||||||
|
wheel-gestures@2.2.48: {}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { AppConfig } from '@nuxt/schema'
|
|||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/breadcrumb'
|
import theme from '#build/ui/breadcrumb'
|
||||||
import type { AvatarProps, LinkProps } from '../types'
|
import type { AvatarProps, LinkProps } from '../types'
|
||||||
import type { DynamicSlots } from '../types/utils'
|
import type { DynamicSlots, PartialString } from '../types/utils'
|
||||||
|
|
||||||
const appConfig = _appConfig as AppConfig & { ui: { breadcrumb: Partial<typeof theme> } }
|
const appConfig = _appConfig as AppConfig & { ui: { breadcrumb: Partial<typeof theme> } }
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ export interface BreadcrumbProps<T> {
|
|||||||
*/
|
*/
|
||||||
separatorIcon?: string
|
separatorIcon?: string
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Partial<typeof breadcrumb.slots>
|
ui?: PartialString<typeof breadcrumb.slots>
|
||||||
}
|
}
|
||||||
|
|
||||||
type SlotProps<T> = (props: { item: T, index: number, active?: boolean }) => any
|
type SlotProps<T> = (props: { item: T, index: number, active?: boolean }) => any
|
||||||
|
|||||||
306
src/runtime/components/Carousel.vue
Normal file
306
src/runtime/components/Carousel.vue
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
<!-- eslint-disable vue/block-tag-newline -->
|
||||||
|
<script lang="ts">
|
||||||
|
import { tv, type VariantProps } from 'tailwind-variants'
|
||||||
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
|
import type { EmblaCarouselType, EmblaOptionsType, EmblaPluginType } from 'embla-carousel'
|
||||||
|
import type { AutoplayOptionsType } from 'embla-carousel-autoplay'
|
||||||
|
import type { AutoScrollOptionsType } from 'embla-carousel-auto-scroll'
|
||||||
|
import type { AutoHeightOptionsType } from 'embla-carousel-auto-height'
|
||||||
|
import type { ClassNamesOptionsType } from 'embla-carousel-class-names'
|
||||||
|
import type { FadeOptionsType } from 'embla-carousel-fade'
|
||||||
|
import type { WheelGesturesPluginOptions } from 'embla-carousel-wheel-gestures'
|
||||||
|
import _appConfig from '#build/app.config'
|
||||||
|
import theme from '#build/ui/carousel'
|
||||||
|
import type { ButtonProps } from '../types'
|
||||||
|
import type { AcceptableValue, PartialString } from '../types/utils'
|
||||||
|
|
||||||
|
const appConfig = _appConfig as AppConfig & { ui: { carousel: Partial<typeof theme> } }
|
||||||
|
|
||||||
|
const carousel = tv({ extend: tv(theme), ...(appConfig.ui?.carousel || {}) })
|
||||||
|
|
||||||
|
type CarouselVariants = VariantProps<typeof carousel>
|
||||||
|
|
||||||
|
export interface CarouselProps<T> extends Omit<EmblaOptionsType, 'axis' | 'container' | 'slides' | 'direction'> {
|
||||||
|
/**
|
||||||
|
* Configure the prev button when arrows are enabled.
|
||||||
|
* @defaultValue { size: 'md', color: 'neutral', variant: 'link' }
|
||||||
|
*/
|
||||||
|
prev?: ButtonProps
|
||||||
|
/**
|
||||||
|
* The icon displayed in the prev button.
|
||||||
|
* @defaultValue appConfig.ui.icons.arrowLeft
|
||||||
|
*/
|
||||||
|
prevIcon?: string
|
||||||
|
/**
|
||||||
|
* Configure the next button when arrows are enabled.
|
||||||
|
* @defaultValue { size: 'md', color: 'neutral', variant: 'link' }
|
||||||
|
*/
|
||||||
|
next?: ButtonProps
|
||||||
|
/**
|
||||||
|
* The icon displayed in the next button.
|
||||||
|
* @defaultValue appConfig.ui.icons.arrowRight
|
||||||
|
*/
|
||||||
|
nextIcon?: string
|
||||||
|
/**
|
||||||
|
* Display prev and next buttons to scroll the carousel.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
arrows?: boolean
|
||||||
|
/**
|
||||||
|
* Display dots to scroll to a specific slide.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
dots?: boolean
|
||||||
|
orientation?: CarouselVariants['orientation']
|
||||||
|
items?: T[]
|
||||||
|
/**
|
||||||
|
* Enable Autoplay plugin
|
||||||
|
* @see https://www.embla-carousel.com/plugins/autoplay/
|
||||||
|
*/
|
||||||
|
autoplay?: boolean | AutoplayOptionsType
|
||||||
|
/**
|
||||||
|
* Enable Auto Scroll plugin
|
||||||
|
* @see https://www.embla-carousel.com/plugins/auto-scroll/
|
||||||
|
*/
|
||||||
|
autoScroll?: boolean | AutoScrollOptionsType
|
||||||
|
/**
|
||||||
|
* Enable Auto Height plugin
|
||||||
|
* @see https://www.embla-carousel.com/plugins/auto-height/
|
||||||
|
*/
|
||||||
|
autoHeight?: boolean | AutoHeightOptionsType
|
||||||
|
/**
|
||||||
|
* Enable Class Names plugin
|
||||||
|
* @see https://www.embla-carousel.com/plugins/class-names/
|
||||||
|
*/
|
||||||
|
classNames?: boolean | ClassNamesOptionsType
|
||||||
|
/**
|
||||||
|
* Enable Fade plugin
|
||||||
|
* @see https://www.embla-carousel.com/plugins/fade/
|
||||||
|
*/
|
||||||
|
fade?: boolean | FadeOptionsType
|
||||||
|
/**
|
||||||
|
* Enable Wheel Gestures plugin
|
||||||
|
* @see https://www.embla-carousel.com/plugins/wheel-gestures/
|
||||||
|
*/
|
||||||
|
wheelGestures?: boolean | WheelGesturesPluginOptions
|
||||||
|
class?: any
|
||||||
|
ui?: PartialString<typeof carousel.slots>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CarouselSlots<T> = {
|
||||||
|
default(props: { item: T, index: number }): any
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup lang="ts" generic="T extends AcceptableValue">
|
||||||
|
import { computed, ref, watch, onMounted } from 'vue'
|
||||||
|
import useEmblaCarousel from 'embla-carousel-vue'
|
||||||
|
import { useForwardProps } from 'radix-vue'
|
||||||
|
import { reactivePick, computedAsync } from '@vueuse/core'
|
||||||
|
import { useAppConfig } from '#imports'
|
||||||
|
import UButton from './Button.vue'
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<CarouselProps<T>>(), {
|
||||||
|
orientation: 'horizontal',
|
||||||
|
arrows: false,
|
||||||
|
dots: false,
|
||||||
|
// Embla Options
|
||||||
|
active: true,
|
||||||
|
align: 'center',
|
||||||
|
breakpoints: () => ({}),
|
||||||
|
containScroll: 'trimSnaps',
|
||||||
|
dragFree: false,
|
||||||
|
dragThreshold: 10,
|
||||||
|
duration: 25,
|
||||||
|
inViewThreshold: 0,
|
||||||
|
loop: false,
|
||||||
|
skipSnaps: false,
|
||||||
|
slidesToScroll: 1,
|
||||||
|
startIndex: 0,
|
||||||
|
watchDrag: true,
|
||||||
|
watchResize: true,
|
||||||
|
watchSlides: true,
|
||||||
|
watchFocus: true,
|
||||||
|
// Embla Plugins
|
||||||
|
autoplay: false,
|
||||||
|
autoScroll: false,
|
||||||
|
autoHeight: false,
|
||||||
|
classNames: false,
|
||||||
|
fade: false,
|
||||||
|
wheelGestures: false
|
||||||
|
})
|
||||||
|
defineSlots<CarouselSlots<T>>()
|
||||||
|
|
||||||
|
const appConfig = useAppConfig()
|
||||||
|
const rootProps = useForwardProps(reactivePick(props, 'active', 'align', 'breakpoints', 'containScroll', 'dragFree', 'dragThreshold', 'duration', 'inViewThreshold', 'loop', 'skipSnaps', 'slidesToScroll', 'startIndex', 'watchDrag', 'watchResize', 'watchSlides', 'watchFocus'))
|
||||||
|
|
||||||
|
const ui = computed(() => carousel({
|
||||||
|
orientation: props.orientation
|
||||||
|
}))
|
||||||
|
|
||||||
|
const options = computed<EmblaOptionsType>(() => ({
|
||||||
|
...(props.fade ? { align: 'center', containScroll: false } : {}),
|
||||||
|
...rootProps.value,
|
||||||
|
axis: props.orientation === 'horizontal' ? 'x' : 'y',
|
||||||
|
// TODO: Get from ConfigProvider
|
||||||
|
direction: 'ltr'
|
||||||
|
}))
|
||||||
|
|
||||||
|
const plugins = computedAsync<EmblaPluginType[]>(async () => {
|
||||||
|
const plugins = []
|
||||||
|
|
||||||
|
if (props.autoplay) {
|
||||||
|
const AutoplayPlugin = await import('embla-carousel-autoplay').then(r => r.default)
|
||||||
|
plugins.push(AutoplayPlugin(typeof props.autoplay === 'boolean' ? {} : props.autoplay))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.autoScroll) {
|
||||||
|
const AutoScrollPlugin = await import('embla-carousel-auto-scroll').then(r => r.default)
|
||||||
|
plugins.push(AutoScrollPlugin(typeof props.autoScroll === 'boolean' ? {} : props.autoScroll))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.autoHeight) {
|
||||||
|
const AutoHeightPlugin = await import('embla-carousel-auto-height').then(r => r.default)
|
||||||
|
plugins.push(AutoHeightPlugin(typeof props.autoHeight === 'boolean' ? {} : props.autoHeight))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.classNames) {
|
||||||
|
const ClassNamesPlugin = await import('embla-carousel-class-names').then(r => r.default)
|
||||||
|
plugins.push(ClassNamesPlugin(typeof props.classNames === 'boolean' ? {} : props.classNames))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.fade) {
|
||||||
|
const FadePlugin = await import('embla-carousel-fade').then(r => r.default)
|
||||||
|
plugins.push(FadePlugin(typeof props.fade === 'boolean' ? {} : props.fade))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.wheelGestures) {
|
||||||
|
const { WheelGesturesPlugin } = await import('embla-carousel-wheel-gestures')
|
||||||
|
plugins.push(WheelGesturesPlugin(typeof props.wheelGestures === 'boolean' ? {} : props.wheelGestures))
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugins
|
||||||
|
})
|
||||||
|
|
||||||
|
const [emblaRef, emblaApi] = useEmblaCarousel(options.value, plugins.value)
|
||||||
|
|
||||||
|
watch([options, plugins], () => {
|
||||||
|
emblaApi.value?.reInit(options.value, plugins.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
function scrollPrev() {
|
||||||
|
emblaApi.value?.scrollPrev()
|
||||||
|
}
|
||||||
|
function scrollNext() {
|
||||||
|
emblaApi.value?.scrollNext()
|
||||||
|
}
|
||||||
|
function scrollTo(index: number) {
|
||||||
|
emblaApi.value?.scrollTo(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKeyDown(event: KeyboardEvent) {
|
||||||
|
const prevKey = props.orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft'
|
||||||
|
const nextKey = props.orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight'
|
||||||
|
|
||||||
|
if (event.key === prevKey) {
|
||||||
|
event.preventDefault()
|
||||||
|
scrollPrev()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === nextKey) {
|
||||||
|
event.preventDefault()
|
||||||
|
scrollNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const canScrollNext = ref(false)
|
||||||
|
const canScrollPrev = ref(false)
|
||||||
|
const selectedIndex = ref<number>(0)
|
||||||
|
const scrollSnaps = ref<number[]>([])
|
||||||
|
|
||||||
|
function onInit(api: EmblaCarouselType) {
|
||||||
|
scrollSnaps.value = api?.scrollSnapList() || []
|
||||||
|
}
|
||||||
|
function onSelect(api: EmblaCarouselType) {
|
||||||
|
canScrollNext.value = api?.canScrollNext() || false
|
||||||
|
canScrollPrev.value = api?.canScrollPrev() || false
|
||||||
|
selectedIndex.value = api?.selectedScrollSnap() || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!emblaApi.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emblaApi.value?.on('init', onInit)
|
||||||
|
emblaApi.value?.on('init', onSelect)
|
||||||
|
emblaApi.value?.on('reInit', onInit)
|
||||||
|
emblaApi.value?.on('reInit', onSelect)
|
||||||
|
emblaApi.value?.on('select', onSelect)
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
emblaRef,
|
||||||
|
emblaApi
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
role="region"
|
||||||
|
aria-roledescription="carousel"
|
||||||
|
tabindex="0"
|
||||||
|
:class="ui.root({ class: [props.class, props.ui?.root] })"
|
||||||
|
@keydown="onKeyDown"
|
||||||
|
>
|
||||||
|
<div ref="emblaRef" :class="ui.viewport({ class: props.ui?.viewport })">
|
||||||
|
<div :class="ui.container({ class: props.ui?.container })">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in items"
|
||||||
|
:key="index"
|
||||||
|
role="group"
|
||||||
|
aria-roledescription="slide"
|
||||||
|
:class="ui.item({ class: props.ui?.item })"
|
||||||
|
>
|
||||||
|
<slot :item="item" :index="index" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="arrows || dots" :class="ui.controls({ class: props.ui?.controls })">
|
||||||
|
<div v-if="arrows" :class="ui.arrows({ class: props.ui?.arrows })">
|
||||||
|
<UButton
|
||||||
|
:disabled="!canScrollPrev"
|
||||||
|
:icon="prevIcon || appConfig.ui.icons.arrowLeft"
|
||||||
|
size="md"
|
||||||
|
color="neutral"
|
||||||
|
variant="outline"
|
||||||
|
aria-label="Prev"
|
||||||
|
v-bind="typeof prev === 'object' ? prev : undefined"
|
||||||
|
:class="ui.prev({ class: props.ui?.prev })"
|
||||||
|
@click="scrollPrev"
|
||||||
|
/>
|
||||||
|
<UButton
|
||||||
|
:disabled="!canScrollNext"
|
||||||
|
:icon="nextIcon || appConfig.ui.icons.arrowRight"
|
||||||
|
size="md"
|
||||||
|
color="neutral"
|
||||||
|
variant="outline"
|
||||||
|
aria-label="Next"
|
||||||
|
v-bind="typeof next === 'object' ? next : undefined"
|
||||||
|
:class="ui.next({ class: props.ui?.next })"
|
||||||
|
@click="scrollNext"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="dots" :class="ui.dots({ class: props.ui?.dots })">
|
||||||
|
<template v-for="(_, index) in scrollSnaps" :key="index">
|
||||||
|
<button :class="ui.dot({ class: props.ui?.dot, active: selectedIndex === index })" @click="scrollTo(index)" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -6,7 +6,7 @@ import type { AppConfig } from '@nuxt/schema'
|
|||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/context-menu'
|
import theme from '#build/ui/context-menu'
|
||||||
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
||||||
import type { DynamicSlots } from '../types/utils'
|
import type { DynamicSlots, PartialString } from '../types/utils'
|
||||||
|
|
||||||
const appConfig = _appConfig as AppConfig & { ui: { contextMenu: Partial<typeof theme> } }
|
const appConfig = _appConfig as AppConfig & { ui: { contextMenu: Partial<typeof theme> } }
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'>,
|
|||||||
*/
|
*/
|
||||||
portal?: boolean
|
portal?: boolean
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Partial<typeof contextMenu.slots>
|
ui?: PartialString<typeof contextMenu.slots>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContextMenuEmits extends ContextMenuRootEmits {}
|
export interface ContextMenuEmits extends ContextMenuRootEmits {}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { AppConfig } from '@nuxt/schema'
|
|||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/dropdown-menu'
|
import theme from '#build/ui/dropdown-menu'
|
||||||
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
||||||
import type { DynamicSlots } from '../types/utils'
|
import type { DynamicSlots, PartialString } from '../types/utils'
|
||||||
|
|
||||||
const appConfig = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
|
const appConfig = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
|||||||
*/
|
*/
|
||||||
portal?: boolean
|
portal?: boolean
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Partial<typeof dropdownMenu.slots>
|
ui?: PartialString<typeof dropdownMenu.slots>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
|
export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { AppConfig } from '@nuxt/schema'
|
|||||||
import _appConfig from '#build/app.config'
|
import _appConfig from '#build/app.config'
|
||||||
import theme from '#build/ui/navigation-menu'
|
import theme from '#build/ui/navigation-menu'
|
||||||
import type { AvatarProps, BadgeProps, LinkProps } from '../types'
|
import type { AvatarProps, BadgeProps, LinkProps } from '../types'
|
||||||
import type { DynamicSlots } from '../types/utils'
|
import type { DynamicSlots, PartialString } from '../types/utils'
|
||||||
|
|
||||||
const appConfig = _appConfig as AppConfig & { ui: { navigationMenu: Partial<typeof theme> } }
|
const appConfig = _appConfig as AppConfig & { ui: { navigationMenu: Partial<typeof theme> } }
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ export interface NavigationMenuProps<T> extends Pick<NavigationMenuRootProps, 'd
|
|||||||
*/
|
*/
|
||||||
arrow?: boolean
|
arrow?: boolean
|
||||||
class?: any
|
class?: any
|
||||||
ui?: Partial<typeof navigationMenu.slots>
|
ui?: PartialString<typeof navigationMenu.slots>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NavigationMenuEmits extends NavigationMenuRootEmits {}
|
export interface NavigationMenuEmits extends NavigationMenuRootEmits {}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export * from '../components/Badge.vue'
|
|||||||
export * from '../components/Breadcrumb.vue'
|
export * from '../components/Breadcrumb.vue'
|
||||||
export * from '../components/Button.vue'
|
export * from '../components/Button.vue'
|
||||||
export * from '../components/Card.vue'
|
export * from '../components/Card.vue'
|
||||||
|
export * from '../components/Carousel.vue'
|
||||||
export * from '../components/Checkbox.vue'
|
export * from '../components/Checkbox.vue'
|
||||||
export * from '../components/Chip.vue'
|
export * from '../components/Chip.vue'
|
||||||
export * from '../components/Collapsible.vue'
|
export * from '../components/Collapsible.vue'
|
||||||
|
|||||||
40
src/theme/carousel.ts
Normal file
40
src/theme/carousel.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import type { ModuleOptions } from '../module'
|
||||||
|
|
||||||
|
export default (options: Required<ModuleOptions>) => ({
|
||||||
|
slots: {
|
||||||
|
root: 'relative focus:outline-none',
|
||||||
|
viewport: 'overflow-hidden',
|
||||||
|
container: 'flex items-start',
|
||||||
|
item: 'min-w-0 shrink-0 basis-full',
|
||||||
|
controls: '',
|
||||||
|
arrows: '',
|
||||||
|
prev: 'absolute rounded-full',
|
||||||
|
next: 'absolute rounded-full',
|
||||||
|
dots: 'absolute inset-x-0 -bottom-7 flex flex-wrap items-center justify-center gap-3',
|
||||||
|
dot: ['cursor-pointer size-3 bg-[--ui-border-accented] rounded-full', options.theme.transitions && 'transition']
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
orientation: {
|
||||||
|
vertical: {
|
||||||
|
container: 'flex-col -mt-4',
|
||||||
|
item: 'pt-4',
|
||||||
|
prev: '-top-12 left-1/2 -translate-x-1/2 rotate-90',
|
||||||
|
next: '-bottom-12 left-1/2 -translate-x-1/2 rotate-90'
|
||||||
|
},
|
||||||
|
horizontal: {
|
||||||
|
container: 'flex-row -ml-4',
|
||||||
|
item: 'pl-4',
|
||||||
|
prev: '-left-12 top-1/2 -translate-y-1/2',
|
||||||
|
next: '-right-12 top-1/2 -translate-y-1/2'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
true: {
|
||||||
|
dot: 'bg-[--ui-border-inverted]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
orientation: 'horizontal'
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -4,6 +4,8 @@ export default {
|
|||||||
chevronDown: 'i-heroicons-chevron-down-20-solid',
|
chevronDown: 'i-heroicons-chevron-down-20-solid',
|
||||||
chevronLeft: 'i-heroicons-chevron-left-20-solid',
|
chevronLeft: 'i-heroicons-chevron-left-20-solid',
|
||||||
chevronRight: 'i-heroicons-chevron-right-20-solid',
|
chevronRight: 'i-heroicons-chevron-right-20-solid',
|
||||||
|
arrowLeft: 'i-heroicons-arrow-left-20-solid',
|
||||||
|
arrowRight: 'i-heroicons-arrow-right-20-solid',
|
||||||
check: 'i-heroicons-check-20-solid',
|
check: 'i-heroicons-check-20-solid',
|
||||||
close: 'i-heroicons-x-mark-20-solid',
|
close: 'i-heroicons-x-mark-20-solid',
|
||||||
ellipsis: 'i-heroicons-ellipsis-horizontal-20-solid',
|
ellipsis: 'i-heroicons-ellipsis-horizontal-20-solid',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export { default as breadcrumb } from './breadcrumb'
|
|||||||
export { default as button } from './button'
|
export { default as button } from './button'
|
||||||
export { default as buttonGroup } from './button-group'
|
export { default as buttonGroup } from './button-group'
|
||||||
export { default as card } from './card'
|
export { default as card } from './card'
|
||||||
|
export { default as carousel } from './carousel'
|
||||||
export { default as checkbox } from './checkbox'
|
export { default as checkbox } from './checkbox'
|
||||||
export { default as chip } from './chip'
|
export { default as chip } from './chip'
|
||||||
export { default as collapsible } from './collapsible'
|
export { default as collapsible } from './collapsible'
|
||||||
|
|||||||
44
test/components/Carousel.spec.ts
Normal file
44
test/components/Carousel.spec.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import Carousel, { type CarouselProps, type CarouselSlots } from '../../src/runtime/components/Carousel.vue'
|
||||||
|
import ComponentRender from '../component-render'
|
||||||
|
|
||||||
|
const CarouselWrapper = defineComponent({
|
||||||
|
components: {
|
||||||
|
UCarousel: Carousel as any
|
||||||
|
},
|
||||||
|
template: `<UCarousel v-slot="{ item }">
|
||||||
|
<img :src="item.src" width="300" height="300" class="rounded-lg">
|
||||||
|
</UCarousel>`
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Carousel', () => {
|
||||||
|
const items = [
|
||||||
|
{ src: 'https://picsum.photos/600/600?random=1' },
|
||||||
|
{ src: 'https://picsum.photos/600/600?random=2' },
|
||||||
|
{ src: 'https://picsum.photos/600/600?random=3' },
|
||||||
|
{ src: 'https://picsum.photos/600/600?random=4' },
|
||||||
|
{ src: 'https://picsum.photos/600/600?random=5' },
|
||||||
|
{ src: 'https://picsum.photos/600/600?random=6' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const props = { items }
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
// Props
|
||||||
|
['with items', { props }],
|
||||||
|
['with orientation vertical', { props: { ...props, orientation: 'vertical' as const } }],
|
||||||
|
['with arrows', { props: { ...props, arrows: true } }],
|
||||||
|
['with prev', { props: { ...props, arrows: true, prev: { color: 'primary' as const } } }],
|
||||||
|
['with prevIcon', { props: { ...props, arrows: true, prevIcon: 'i-heroicons-arrow-left' } }],
|
||||||
|
['with next', { props: { ...props, arrows: true, next: { color: 'primary' as const } } }],
|
||||||
|
['with nextIcon', { props: { ...props, arrows: true, nextIcon: 'i-heroicons-arrow-right' } }],
|
||||||
|
['with dots', { props: { ...props, dots: true } }],
|
||||||
|
['with as', { props: { ...props, as: 'nav' } }],
|
||||||
|
['with class', { props: { ...props, class: 'w-full max-w-xs' } }],
|
||||||
|
['with ui', { props: { ...props, ui: { viewport: 'h-[320px]' } } }]
|
||||||
|
])('renders %s correctly', async (nameOrHtml: string, options: { props?: CarouselProps<typeof items[number]>, slots?: Partial<CarouselSlots<typeof items[number]>> }) => {
|
||||||
|
const html = await ComponentRender(nameOrHtml, options, CarouselWrapper)
|
||||||
|
expect(html).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
225
test/components/__snapshots__/Carousel.spec.ts.snap
Normal file
225
test/components/__snapshots__/Carousel.spec.ts.snap
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`Carousel > renders with arrows correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="flex items-start flex-row -ml-4" style="transform: translate3d(NaNpx,0px,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<div class=""><button type="button" disabled="" aria-label="Prev" class="font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 ring ring-inset ring-[--ui-border-accented] text-[--ui-text] bg-[--ui-bg] hover:bg-[--ui-bg-elevated] disabled:bg-[--ui-bg] aria-disabled:bg-[--ui-bg] focus-visible:ring-2 focus-visible:ring-[--ui-border-inverted] p-1.5 absolute rounded-full -left-12 top-1/2 -translate-y-1/2"><span class="iconify i-heroicons:arrow-left-20-solid shrink-0 size-5" aria-hidden="true"></span>
|
||||||
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
</button><button type="button" disabled="" aria-label="Next" class="font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 ring ring-inset ring-[--ui-border-accented] text-[--ui-text] bg-[--ui-bg] hover:bg-[--ui-bg-elevated] disabled:bg-[--ui-bg] aria-disabled:bg-[--ui-bg] focus-visible:ring-2 focus-visible:ring-[--ui-border-inverted] p-1.5 absolute rounded-full -right-12 top-1/2 -translate-y-1/2"><span class="iconify i-heroicons:arrow-right-20-solid shrink-0 size-5" aria-hidden="true"></span>
|
||||||
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
</button></div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Carousel > renders with as correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none" as="nav">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="flex items-start flex-row -ml-4" style="transform: translate3d(NaNpx,0px,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Carousel > renders with class correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none w-full max-w-xs">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="flex items-start flex-row -ml-4" style="transform: translate3d(NaNpx,0px,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Carousel > renders with dots correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="flex items-start flex-row -ml-4" style="transform: translate3d(NaNpx,0px,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<!--v-if-->
|
||||||
|
<div class="absolute inset-x-0 -bottom-7 flex flex-wrap items-center justify-center gap-3"></div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Carousel > renders with items correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="flex items-start flex-row -ml-4" style="transform: translate3d(NaNpx,0px,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Carousel > renders with next correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="flex items-start flex-row -ml-4" style="transform: translate3d(NaNpx,0px,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<div class=""><button type="button" disabled="" aria-label="Prev" class="font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 ring ring-inset ring-[--ui-border-accented] text-[--ui-text] bg-[--ui-bg] hover:bg-[--ui-bg-elevated] disabled:bg-[--ui-bg] aria-disabled:bg-[--ui-bg] focus-visible:ring-2 focus-visible:ring-[--ui-border-inverted] p-1.5 absolute rounded-full -left-12 top-1/2 -translate-y-1/2"><span class="iconify i-heroicons:arrow-left-20-solid shrink-0 size-5" aria-hidden="true"></span>
|
||||||
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
</button><button type="button" disabled="" aria-label="Next" class="font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 ring ring-inset ring-[--ui-primary]/50 text-[--ui-primary] hover:bg-[--ui-primary]/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus-visible:ring-2 focus-visible:ring-[--ui-primary] p-1.5 absolute rounded-full -right-12 top-1/2 -translate-y-1/2"><span class="iconify i-heroicons:arrow-right-20-solid shrink-0 size-5" aria-hidden="true"></span>
|
||||||
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
</button></div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Carousel > renders with nextIcon correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="flex items-start flex-row -ml-4" style="transform: translate3d(NaNpx,0px,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<div class=""><button type="button" disabled="" aria-label="Prev" class="font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 ring ring-inset ring-[--ui-border-accented] text-[--ui-text] bg-[--ui-bg] hover:bg-[--ui-bg-elevated] disabled:bg-[--ui-bg] aria-disabled:bg-[--ui-bg] focus-visible:ring-2 focus-visible:ring-[--ui-border-inverted] p-1.5 absolute rounded-full -left-12 top-1/2 -translate-y-1/2"><span class="iconify i-heroicons:arrow-left-20-solid shrink-0 size-5" aria-hidden="true"></span>
|
||||||
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
</button><button type="button" disabled="" aria-label="Next" class="font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 ring ring-inset ring-[--ui-border-accented] text-[--ui-text] bg-[--ui-bg] hover:bg-[--ui-bg-elevated] disabled:bg-[--ui-bg] aria-disabled:bg-[--ui-bg] focus-visible:ring-2 focus-visible:ring-[--ui-border-inverted] p-1.5 absolute rounded-full -right-12 top-1/2 -translate-y-1/2"><span class="iconify i-heroicons:arrow-right shrink-0 size-5" aria-hidden="true"></span>
|
||||||
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
</button></div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Carousel > renders with orientation vertical correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="flex items-start flex-col -mt-4" style="transform: translate3d(0px,NaNpx,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pt-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pt-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pt-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pt-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pt-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pt-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Carousel > renders with prev correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="flex items-start flex-row -ml-4" style="transform: translate3d(NaNpx,0px,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<div class=""><button type="button" disabled="" aria-label="Prev" class="font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 ring ring-inset ring-[--ui-primary]/50 text-[--ui-primary] hover:bg-[--ui-primary]/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus-visible:ring-2 focus-visible:ring-[--ui-primary] p-1.5 absolute rounded-full -left-12 top-1/2 -translate-y-1/2"><span class="iconify i-heroicons:arrow-left-20-solid shrink-0 size-5" aria-hidden="true"></span>
|
||||||
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
</button><button type="button" disabled="" aria-label="Next" class="font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 ring ring-inset ring-[--ui-border-accented] text-[--ui-text] bg-[--ui-bg] hover:bg-[--ui-bg-elevated] disabled:bg-[--ui-bg] aria-disabled:bg-[--ui-bg] focus-visible:ring-2 focus-visible:ring-[--ui-border-inverted] p-1.5 absolute rounded-full -right-12 top-1/2 -translate-y-1/2"><span class="iconify i-heroicons:arrow-right-20-solid shrink-0 size-5" aria-hidden="true"></span>
|
||||||
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
</button></div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Carousel > renders with prevIcon correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="flex items-start flex-row -ml-4" style="transform: translate3d(NaNpx,0px,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<div class=""><button type="button" disabled="" aria-label="Prev" class="font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 ring ring-inset ring-[--ui-border-accented] text-[--ui-text] bg-[--ui-bg] hover:bg-[--ui-bg-elevated] disabled:bg-[--ui-bg] aria-disabled:bg-[--ui-bg] focus-visible:ring-2 focus-visible:ring-[--ui-border-inverted] p-1.5 absolute rounded-full -left-12 top-1/2 -translate-y-1/2"><span class="iconify i-heroicons:arrow-left shrink-0 size-5" aria-hidden="true"></span>
|
||||||
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
</button><button type="button" disabled="" aria-label="Next" class="font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 ring ring-inset ring-[--ui-border-accented] text-[--ui-text] bg-[--ui-bg] hover:bg-[--ui-bg-elevated] disabled:bg-[--ui-bg] aria-disabled:bg-[--ui-bg] focus-visible:ring-2 focus-visible:ring-[--ui-border-inverted] p-1.5 absolute rounded-full -right-12 top-1/2 -translate-y-1/2"><span class="iconify i-heroicons:arrow-right-20-solid shrink-0 size-5" aria-hidden="true"></span>
|
||||||
|
<!--v-if-->
|
||||||
|
<!--v-if-->
|
||||||
|
</button></div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Carousel > renders with ui correctly 1`] = `
|
||||||
|
"<div role="region" aria-roledescription="carousel" tabindex="0" class="relative focus:outline-none">
|
||||||
|
<div class="overflow-hidden h-[320px]">
|
||||||
|
<div class="flex items-start flex-row -ml-4" style="transform: translate3d(NaNpx,0px,0px);">
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=1" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=2" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=3" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=4" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=5" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
<div role="group" aria-roledescription="slide" class="min-w-0 shrink-0 basis-full pl-4"><img src="https://picsum.photos/600/600?random=6" width="300" height="300" class="rounded-lg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
Reference in New Issue
Block a user