diff --git a/docs/app/assets/icons/embla-carousel.svg b/docs/app/assets/icons/embla-carousel.svg
new file mode 100644
index 00000000..9dbcd64f
--- /dev/null
+++ b/docs/app/assets/icons/embla-carousel.svg
@@ -0,0 +1 @@
+
diff --git a/docs/app/components/content/examples/carousel/CarouselArrowsExample.vue b/docs/app/components/content/examples/carousel/CarouselArrowsExample.vue
new file mode 100644
index 00000000..7f1f258a
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselArrowsExample.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselAutoHeightExample.vue b/docs/app/components/content/examples/carousel/CarouselAutoHeightExample.vue
new file mode 100644
index 00000000..a1886866
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselAutoHeightExample.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselAutoScrollExample.vue b/docs/app/components/content/examples/carousel/CarouselAutoScrollExample.vue
new file mode 100644
index 00000000..e89b24ba
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselAutoScrollExample.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselAutoplayExample.vue b/docs/app/components/content/examples/carousel/CarouselAutoplayExample.vue
new file mode 100644
index 00000000..e7bfa75f
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselAutoplayExample.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselClassNamesExample.vue b/docs/app/components/content/examples/carousel/CarouselClassNamesExample.vue
new file mode 100644
index 00000000..889185fa
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselClassNamesExample.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselDotsExample.vue b/docs/app/components/content/examples/carousel/CarouselDotsExample.vue
new file mode 100644
index 00000000..9c02afa0
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselDotsExample.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselDotsMultipleExample.vue b/docs/app/components/content/examples/carousel/CarouselDotsMultipleExample.vue
new file mode 100644
index 00000000..3037ae17
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselDotsMultipleExample.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselFadeExample.vue b/docs/app/components/content/examples/carousel/CarouselFadeExample.vue
new file mode 100644
index 00000000..dc83f9ba
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselFadeExample.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselItemsExample.vue b/docs/app/components/content/examples/carousel/CarouselItemsExample.vue
new file mode 100644
index 00000000..0559d053
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselItemsExample.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselItemsMultipleExample.vue b/docs/app/components/content/examples/carousel/CarouselItemsMultipleExample.vue
new file mode 100644
index 00000000..6ed826e3
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselItemsMultipleExample.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselOrientationExample.vue b/docs/app/components/content/examples/carousel/CarouselOrientationExample.vue
new file mode 100644
index 00000000..38ae34fc
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselOrientationExample.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselPrevNextExample.vue b/docs/app/components/content/examples/carousel/CarouselPrevNextExample.vue
new file mode 100644
index 00000000..c248c0f8
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselPrevNextExample.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselPrevNextIconExample.vue b/docs/app/components/content/examples/carousel/CarouselPrevNextIconExample.vue
new file mode 100644
index 00000000..6235e5e4
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselPrevNextIconExample.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/carousel/CarouselWheelGesturesExample.vue b/docs/app/components/content/examples/carousel/CarouselWheelGesturesExample.vue
new file mode 100644
index 00000000..0a895634
--- /dev/null
+++ b/docs/app/components/content/examples/carousel/CarouselWheelGesturesExample.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/docs/content/1.getting-started/3.theme.md b/docs/content/1.getting-started/3.theme.md
index 2011afb5..b371ed20 100644
--- a/docs/content/1.getting-started/3.theme.md
+++ b/docs/content/1.getting-started/3.theme.md
@@ -36,7 +36,7 @@ Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your
```
-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
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.
::
@@ -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.
-::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.
::
-::note
+::tip
You can explore the theme for each component in two ways:
- 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.
+::
### `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.
+::
### `class` prop
@@ -506,3 +510,7 @@ slots:
default: Button
---
::
+
+::note
+In this example, the `font-bold` class will override the default `font-medium` class on this button.
+::
diff --git a/docs/content/1.getting-started/4.icons.md b/docs/content/1.getting-started/4.icons.md
index 50d686f7..5eb5bb7b 100644
--- a/docs/content/1.getting-started/4.icons.md
+++ b/docs/content/1.getting-started/4.icons.md
@@ -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.
-::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.
::
@@ -109,7 +109,7 @@ Then you can use the icons like this:
```
-::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.
::
diff --git a/docs/content/1.getting-started/5.fonts.md b/docs/content/1.getting-started/5.fonts.md
index c5984cb9..e6a45479 100644
--- a/docs/content/1.getting-started/5.fonts.md
+++ b/docs/content/1.getting-started/5.fonts.md
@@ -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.
-::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.
::
diff --git a/docs/content/3.components/alert.md b/docs/content/3.components/alert.md
index c7ffbe5d..fcbc5e46 100644
--- a/docs/content/3.components/alert.md
+++ b/docs/content/3.components/alert.md
@@ -278,6 +278,7 @@ ignore:
- exactQuery
- exactHash
- external
+ - onClick
---
::
diff --git a/docs/content/3.components/breadcrumb.md b/docs/content/3.components/breadcrumb.md
index 46980c07..ea36b81b 100644
--- a/docs/content/3.components/breadcrumb.md
+++ b/docs/content/3.components/breadcrumb.md
@@ -123,6 +123,7 @@ ignore:
- exactQuery
- exactHash
- external
+ - onClick
---
::
diff --git a/docs/content/3.components/button.md b/docs/content/3.components/button.md
index 9694467a..ab886986 100644
--- a/docs/content/3.components/button.md
+++ b/docs/content/3.components/button.md
@@ -248,6 +248,7 @@ ignore:
- exactHash
- external
- active
+ - onClick
---
::
diff --git a/docs/content/3.components/carousel.md b/docs/content/3.components/carousel.md
index fe1b7224..08420dff 100644
--- a/docs/content/3.components/carousel.md
+++ b/docs/content/3.components/carousel.md
@@ -1,34 +1,419 @@
---
-description: A carousel with motion and swipe support.
+description: A carousel with motion and swipe built using Embla.
links:
+ - label: Embla
+ to: https://www.embla-carousel.com/api/
+ icon: i-custom-embla-carousel
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Carousel.vue
-navigation:
- badge:
- label: Todo
- color: neutral
- disabled: true
---
## Usage
-## Examples
+### Items
-
+:component-theme
diff --git a/docs/content/3.components/command-palette.md b/docs/content/3.components/command-palette.md
index 83fb82ac..4139e43f 100644
--- a/docs/content/3.components/command-palette.md
+++ b/docs/content/3.components/command-palette.md
@@ -561,6 +561,7 @@ ignore:
- exactQuery
- exactHash
- external
+ - onClick
---
::
diff --git a/docs/content/3.components/context-menu.md b/docs/content/3.components/context-menu.md
index 66952c90..42e48e65 100644
--- a/docs/content/3.components/context-menu.md
+++ b/docs/content/3.components/context-menu.md
@@ -292,6 +292,7 @@ ignore:
- exactQuery
- exactHash
- external
+ - onClick
---
::
diff --git a/docs/content/3.components/dropdown-menu.md b/docs/content/3.components/dropdown-menu.md
index 06a4f16b..9e70153e 100644
--- a/docs/content/3.components/dropdown-menu.md
+++ b/docs/content/3.components/dropdown-menu.md
@@ -361,6 +361,7 @@ ignore:
- exactQuery
- exactHash
- external
+ - onClick
---
::
diff --git a/docs/content/3.components/modal.md b/docs/content/3.components/modal.md
index ab498bcf..f5839715 100644
--- a/docs/content/3.components/modal.md
+++ b/docs/content/3.components/modal.md
@@ -381,6 +381,7 @@ ignore:
- exactQuery
- exactHash
- external
+ - onClick
---
::
diff --git a/docs/content/3.components/slideover.md b/docs/content/3.components/slideover.md
index c8df66f8..90d3581a 100644
--- a/docs/content/3.components/slideover.md
+++ b/docs/content/3.components/slideover.md
@@ -369,6 +369,7 @@ ignore:
- exactQuery
- exactHash
- external
+ - onClick
---
::
diff --git a/docs/content/3.components/toast.md b/docs/content/3.components/toast.md
index a9cb84ca..8f2f9c2b 100644
--- a/docs/content/3.components/toast.md
+++ b/docs/content/3.components/toast.md
@@ -239,6 +239,7 @@ ignore:
- exactQuery
- exactHash
- external
+ - onClick
---
::
diff --git a/package.json b/package.json
index b6ad8fea..92dd4336 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,7 @@
"@vueuse/core": "^11.1.0",
"@vueuse/integrations": "^11.1.0",
"defu": "^6.1.4",
+ "embla-carousel-vue": "^8.3.0",
"fuse.js": "^7.0.0",
"ohash": "^1.1.4",
"radix-vue": "^1.9.7",
@@ -70,6 +71,13 @@
"@release-it/conventional-changelog": "^8.0.2",
"@standard-schema/spec": "1.0.0-beta.1",
"@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",
"happy-dom": "^15.7.4",
"joi": "^17.13.3",
diff --git a/playground/app/app.vue b/playground/app/app.vue
index 7f9aae63..b8f65cbb 100644
--- a/playground/app/app.vue
+++ b/playground/app/app.vue
@@ -13,6 +13,7 @@ const components = [
'button',
'button-group',
'card',
+ 'carousel',
'checkbox',
'chip',
'collapsible',
diff --git a/playground/app/pages/components/carousel.vue b/playground/app/pages/components/carousel.vue
new file mode 100644
index 00000000..a3d17576
--- /dev/null
+++ b/playground/app/pages/components/carousel.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f4ceebd1..c234d89e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -43,6 +43,9 @@ importers:
defu:
specifier: ^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:
specifier: ^7.0.0
version: 7.0.0
@@ -86,6 +89,27 @@ importers:
'@vue/test-utils':
specifier: ^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:
specifier: ^9.11.1
version: 9.12.0(jiti@2.3.3)
@@ -3276,6 +3300,50 @@ packages:
electron-to-chromium@1.5.33:
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:
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
@@ -6574,6 +6642,10 @@ packages:
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+ wheel-gestures@2.2.48:
+ resolution: {integrity: sha512-f+Gy33Oa5Z14XY9679Zze+7VFhbsQfBFXodnU2x589l4kxGM9L5Y8zETTmcMR5pWOPQyRv4Z0lNax6xCO0NSlA==}
+ engines: {node: '>=18'}
+
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -10058,6 +10130,43 @@ snapshots:
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@8.0.0: {}
@@ -14283,6 +14392,8 @@ snapshots:
tr46: 0.0.3
webidl-conversions: 3.0.1
+ wheel-gestures@2.2.48: {}
+
which@2.0.2:
dependencies:
isexe: 2.0.0
diff --git a/src/runtime/components/Breadcrumb.vue b/src/runtime/components/Breadcrumb.vue
index 61215eb6..6c909eb5 100644
--- a/src/runtime/components/Breadcrumb.vue
+++ b/src/runtime/components/Breadcrumb.vue
@@ -5,7 +5,7 @@ import type { AppConfig } from '@nuxt/schema'
import _appConfig from '#build/app.config'
import theme from '#build/ui/breadcrumb'
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 } }
@@ -31,7 +31,7 @@ export interface BreadcrumbProps {
*/
separatorIcon?: string
class?: any
- ui?: Partial
+ ui?: PartialString
}
type SlotProps = (props: { item: T, index: number, active?: boolean }) => any
diff --git a/src/runtime/components/Carousel.vue b/src/runtime/components/Carousel.vue
new file mode 100644
index 00000000..db88d7a2
--- /dev/null
+++ b/src/runtime/components/Carousel.vue
@@ -0,0 +1,306 @@
+
+
+
+
+
+
+
+
diff --git a/src/runtime/components/ContextMenu.vue b/src/runtime/components/ContextMenu.vue
index e2a05a17..9a0c897d 100644
--- a/src/runtime/components/ContextMenu.vue
+++ b/src/runtime/components/ContextMenu.vue
@@ -6,7 +6,7 @@ import type { AppConfig } from '@nuxt/schema'
import _appConfig from '#build/app.config'
import theme from '#build/ui/context-menu'
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 } }
@@ -43,7 +43,7 @@ export interface ContextMenuProps extends Omit,
*/
portal?: boolean
class?: any
- ui?: Partial
+ ui?: PartialString
}
export interface ContextMenuEmits extends ContextMenuRootEmits {}
diff --git a/src/runtime/components/DropdownMenu.vue b/src/runtime/components/DropdownMenu.vue
index 99c697c9..2cf3d559 100644
--- a/src/runtime/components/DropdownMenu.vue
+++ b/src/runtime/components/DropdownMenu.vue
@@ -6,7 +6,7 @@ import type { AppConfig } from '@nuxt/schema'
import _appConfig from '#build/app.config'
import theme from '#build/ui/dropdown-menu'
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 } }
@@ -51,7 +51,7 @@ export interface DropdownMenuProps extends Omit
*/
portal?: boolean
class?: any
- ui?: Partial
+ ui?: PartialString
}
export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
diff --git a/src/runtime/components/NavigationMenu.vue b/src/runtime/components/NavigationMenu.vue
index d20f5a22..f0d49c26 100644
--- a/src/runtime/components/NavigationMenu.vue
+++ b/src/runtime/components/NavigationMenu.vue
@@ -6,7 +6,7 @@ import type { AppConfig } from '@nuxt/schema'
import _appConfig from '#build/app.config'
import theme from '#build/ui/navigation-menu'
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 } }
@@ -62,7 +62,7 @@ export interface NavigationMenuProps extends Pick
+ ui?: PartialString
}
export interface NavigationMenuEmits extends NavigationMenuRootEmits {}
diff --git a/src/runtime/types/index.ts b/src/runtime/types/index.ts
index dc8dc09f..bf0a1ee6 100644
--- a/src/runtime/types/index.ts
+++ b/src/runtime/types/index.ts
@@ -7,6 +7,7 @@ export * from '../components/Badge.vue'
export * from '../components/Breadcrumb.vue'
export * from '../components/Button.vue'
export * from '../components/Card.vue'
+export * from '../components/Carousel.vue'
export * from '../components/Checkbox.vue'
export * from '../components/Chip.vue'
export * from '../components/Collapsible.vue'
diff --git a/src/theme/carousel.ts b/src/theme/carousel.ts
new file mode 100644
index 00000000..e174b66b
--- /dev/null
+++ b/src/theme/carousel.ts
@@ -0,0 +1,40 @@
+import type { ModuleOptions } from '../module'
+
+export default (options: Required) => ({
+ 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'
+ }
+})
diff --git a/src/theme/icons.ts b/src/theme/icons.ts
index 4c002ff8..203b0975 100644
--- a/src/theme/icons.ts
+++ b/src/theme/icons.ts
@@ -4,6 +4,8 @@ export default {
chevronDown: 'i-heroicons-chevron-down-20-solid',
chevronLeft: 'i-heroicons-chevron-left-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',
close: 'i-heroicons-x-mark-20-solid',
ellipsis: 'i-heroicons-ellipsis-horizontal-20-solid',
diff --git a/src/theme/index.ts b/src/theme/index.ts
index 2354152c..8ed54a2e 100644
--- a/src/theme/index.ts
+++ b/src/theme/index.ts
@@ -7,6 +7,7 @@ export { default as breadcrumb } from './breadcrumb'
export { default as button } from './button'
export { default as buttonGroup } from './button-group'
export { default as card } from './card'
+export { default as carousel } from './carousel'
export { default as checkbox } from './checkbox'
export { default as chip } from './chip'
export { default as collapsible } from './collapsible'
diff --git a/test/components/Carousel.spec.ts b/test/components/Carousel.spec.ts
new file mode 100644
index 00000000..b0bf999c
--- /dev/null
+++ b/test/components/Carousel.spec.ts
@@ -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: `
+
+`
+})
+
+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, slots?: Partial> }) => {
+ const html = await ComponentRender(nameOrHtml, options, CarouselWrapper)
+ expect(html).toMatchSnapshot()
+ })
+})
diff --git a/test/components/__snapshots__/Carousel.spec.ts.snap b/test/components/__snapshots__/Carousel.spec.ts.snap
new file mode 100644
index 00000000..4ad42e06
--- /dev/null
+++ b/test/components/__snapshots__/Carousel.spec.ts.snap
@@ -0,0 +1,225 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`Carousel > renders with arrows correctly 1`] = `
+""
+`;
+
+exports[`Carousel > renders with as correctly 1`] = `
+""
+`;
+
+exports[`Carousel > renders with class correctly 1`] = `
+""
+`;
+
+exports[`Carousel > renders with dots correctly 1`] = `
+""
+`;
+
+exports[`Carousel > renders with items correctly 1`] = `
+""
+`;
+
+exports[`Carousel > renders with next correctly 1`] = `
+""
+`;
+
+exports[`Carousel > renders with nextIcon correctly 1`] = `
+""
+`;
+
+exports[`Carousel > renders with orientation vertical correctly 1`] = `
+""
+`;
+
+exports[`Carousel > renders with prev correctly 1`] = `
+""
+`;
+
+exports[`Carousel > renders with prevIcon correctly 1`] = `
+""
+`;
+
+exports[`Carousel > renders with ui correctly 1`] = `
+""
+`;