Skip to content

Commit b64f27a

Browse files
authored
Merge pull request #487 from Tofandel/feat/carousel-ref
Allow carousel ref to be passed to Pagination and Navigation
2 parents c4f6234 + 630ddde commit b64f27a

19 files changed

+141
-52
lines changed

docs/api/data.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,26 @@ import { ref } from 'vue';
1313
const myCarousel = ref(null);
1414

1515
// Data can be accessed under data property
16-
if (myCarousel.data.currentSlide === 10) {
16+
if (myCarousel.currentSlide === 10) {
1717
// Do your magic here
1818
}
1919
....
2020
```
2121

22-
## Available Data
22+
## Available Exposed data
2323

24-
| Data | Description |
25-
| -------------- | ---------------------------------- |
26-
| `config` | the current carousel configuration |
27-
| `currentSlide` | current slide index |
28-
| `maxSlide` | maximum slide index |
29-
| `middleSlide` | middle slide index |
30-
| `minSlide` | minimum slide index |
31-
| `slideSize` | single slide width or height |
32-
| `slidesCount` | slides total count |
24+
| Data | Description |
25+
|----------------|---------------------------------------------------------------------------------------------------|
26+
| `currentSlide` | current slide index |
27+
| `activeSlide` | current slide index even while dragging |
28+
| `isSliding` | if the slider is dragging |
29+
| `isVertical` | if the slider is vertical |
30+
| `nav` | An object of navigation methods |
31+
| `config` | the current carousel configuration |
32+
| `maxSlide` | maximum slide index |
33+
| `minSlide` | minimum slide index |
34+
| `slideSize` | single slide width or height |
35+
| `slidesCount` | slides total count |
36+
| `slides` | an array of Slides component |
37+
| `viewport` | the viewport element |
38+
| `visibleRange` | an object with {min, max} properties min being the lowest visible slide index and max the highest |

docs/config.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## Available Props
44

55
| Prop | Type | Default | Description |
6-
| -------------------------- | ------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------ |
6+
|----------------------------|---------------------------------------------|----------------------------------|--------------------------------------------------------------------------------------------------------|
77
| `autoplay` | `number` | 0 | Time interval (in milliseconds) between auto-advancing slides. Set to 0 to disable autoplay. |
88
| `breakpointMode` | 'viewport', 'carousel' | 'viewport' | Defines whether breakpoints are calculated based on viewport width or carousel container width. |
99
| `breakpoints` | `object` | null | Responsive breakpoint configurations. Each breakpoint can override any carousel prop. |
@@ -15,6 +15,7 @@
1515
| `ignoreAnimations` | `boolean` \| `string` \| `array` | false | Specifies which CSS animations should be excluded from slide size calculations. <Badge text="0.10.0"/> |
1616
| `itemsToScroll` | `number` | 1 | Number of slides to move when navigating. Useful for creating slide groups. |
1717
| `itemsToShow` | `number` \| 'auto' | 1 | Number of slides visible simultaneously. Use 'auto' for variable width slides. |
18+
| `clamp` | `boolean` | false | If true will clamp itemsToShow to the number of available slides |
1819
| `modelValue` | `number` | 0 | Controls the active slide index. Can be used with v-model for two-way binding. |
1920
| `mouseDrag` | `boolean` | true | Enables/disables mouse drag navigation. |
2021
| `pauseAutoplayOnHover` | `boolean` | false | When true, autoplay pauses while the mouse cursor is over the carousel. |
@@ -24,7 +25,7 @@
2425
| `transition` | `number` | 300 | Duration of the slide transition animation in milliseconds. |
2526
| `wrapAround` | `boolean` | false | When true, creates an infinite loop effect by connecting the last slide to the first. |
2627

27-
> **itemsToShow**: Controls the number of visible slides. Values between 1 and the total slide count are valid. Values outside this range are automatically clamped. Using 'auto' allows slides to determine their own width based on content.
28+
> **itemsToShow**: Controls the number of visible slides. Values higher than 1 and decimals are valid. Using 'auto' allows slides to determine their own width based on content.
2829
2930
> **Direction Settings**: For vertical orientations ('ttb'/'top-to-bottom', 'btt'/'bottom-to-top'), the carousel requires a fixed height setting. Direction can be specified using either short ('ltr', 'rtl', 'ttb', 'btt') or verbose ('left-to-right', 'right-to-left', 'top-to-bottom', 'bottom-to-top') formats.
3031
@@ -88,6 +89,19 @@ Used to add display carousel addons components.
8889
</template>
8990
```
9091

92+
#### Using addons outside of the carousel
93+
94+
```vue {7,8,9}
95+
<template>
96+
<Carousel ref="carousel">
97+
<Slide v-for="slide in slides" :key="slide">
98+
<div class="carousel__item">{{ slide }}</div>
99+
</Slide>
100+
</Carousel>
101+
<Navigation :carousel="carousel" v-if="carousel && carousel.slidesCount > 1" />
102+
</template>
103+
```
104+
91105
### I18n
92106

93107
Available keys:

eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default tseslint.config(
2121
},
2222
},
2323
rules: {
24-
'no-console': 'off',
24+
'no-console': ["error", { allow: ["warn", "error"] }],
2525
'@typescript-eslint/no-unused-vars': 'error',
2626
'@typescript-eslint/no-empty-function': 'off',
2727
'@typescript-eslint/no-non-null-assertion': 'off',

rollup.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ export default [
6565
{
6666
input: 'src/index.ts',
6767
output: [{ file: 'dist/carousel.d.ts', format: 'es' }],
68-
external: [/\.css$/],
68+
external: ['vue', /\.css$/],
6969
plugins: [
70+
typescript(),
7071
dts({
7172
rollupTypes: true,
7273
pathsToAliases: true,

src/components/Carousel/Carousel.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
SetupContext,
1313
shallowReactive,
1414
watch,
15-
watchEffect,
15+
watchEffect, toRefs,
1616
} from 'vue'
1717

1818
import { ARIA as ARIAComponent } from '@/components/ARIA'
@@ -141,6 +141,15 @@ export const Carousel = defineComponent({
141141
})
142142

143143
Object.assign(config, fallbackConfig.value, newConfig)
144+
145+
// Validate itemsToShow
146+
if (!isAuto.value) {
147+
config.itemsToShow = getNumberInRange({
148+
val: Number(config.itemsToShow),
149+
max: props.clamp ? slidesCount.value : Infinity,
150+
min: 1,
151+
});
152+
}
144153
}
145154

146155
const handleResize = throttle(() => {
@@ -209,15 +218,6 @@ export const Carousel = defineComponent({
209218
min: minSlideIndex.value,
210219
})
211220
}
212-
213-
// Validate itemsToShow
214-
if (!isAuto.value) {
215-
config.itemsToShow = getNumberInRange({
216-
val: Number(config.itemsToShow),
217-
max: slidesCount.value,
218-
min: 1,
219-
})
220-
}
221221
}
222222

223223
const ignoreAnimations = computed<false | string[]>(() => {
@@ -852,17 +852,17 @@ export const Carousel = defineComponent({
852852
slidesCount,
853853
})
854854

855-
expose<CarouselExposed>({
855+
expose<CarouselExposed>(reactive({
856856
data,
857-
nav,
858857
next,
859858
prev,
860859
restartCarousel,
861860
slideTo,
862861
updateBreakpointsConfig,
863862
updateSlideSize,
864863
updateSlidesData,
865-
})
864+
...toRefs(provided),
865+
}))
866866

867867
return () => {
868868
const slotSlides = slots.default || slots.slides

src/components/Carousel/Carousel.types.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ export type CarouselData = {
2929
}
3030

3131
export type CarouselExposed = CarouselMethods & {
32-
data: Reactive<CarouselData>
33-
nav: CarouselNav
34-
}
32+
data: Reactive<CarouselData> // TODO deprecate and remove
33+
} & InjectedCarousel
3534

3635
export type CarouselMethods = CarouselNav & {
3736
restartCarousel: () => void

src/components/Carousel/carouselProps.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export const carouselProps = {
9696
validator(value: boolean, props: { wrapAround?: boolean }) {
9797
if (value && props.wrapAround) {
9898
console.warn(
99-
`[vue3-carousel warn]: "preventExcessiveDragging" cannot be used with wrapAround. The setting will be ignored.`
99+
`[vue3-carousel]: "preventExcessiveDragging" cannot be used with wrapAround. The setting will be ignored.`
100100
)
101101
}
102102

@@ -139,7 +139,7 @@ export const carouselProps = {
139139
(!props.height || props.height === 'auto')
140140
) {
141141
console.warn(
142-
`[vue3-carousel warn]: The dir "${value}" is not supported with height "auto".`
142+
`[vue3-carousel]: The dir "${value}" is not supported with height "auto".`
143143
)
144144
}
145145
return true
@@ -150,4 +150,7 @@ export const carouselProps = {
150150
default: DEFAULT_CONFIG.wrapAround,
151151
type: Boolean,
152152
},
153+
clamp: {
154+
type: Boolean,
155+
}
153156
}

src/components/Navigation/Navigation.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { computed, defineComponent, h, inject } from 'vue'
1+
import { computed, defineComponent, h, inject, PropType } from 'vue'
22

33
import { injectCarousel, NormalizedDir } from '@/shared'
44

@@ -9,11 +9,13 @@ import { NavigationProps } from './Navigation.types'
99
export const Navigation = defineComponent<NavigationProps>({
1010
name: 'CarouselNavigation',
1111
inheritAttrs: false,
12+
props: {
13+
carousel: {
14+
type: Object as PropType<NavigationProps['carousel']>,
15+
},
16+
},
1217
setup(props, { slots, attrs }) {
13-
const carousel = inject(injectCarousel)
14-
if (!carousel) {
15-
return () => '' // Don't render, let vue warn about the missing provide
16-
}
18+
let carousel = inject(injectCarousel, null)!
1719
const { next: slotNext, prev: slotPrev } = slots
1820

1921
const getPrevIcon = () => {
@@ -45,6 +47,13 @@ export const Navigation = defineComponent<NavigationProps>({
4547
)
4648

4749
return () => {
50+
if (props.carousel) {
51+
carousel = props.carousel;
52+
}
53+
if (!carousel) {
54+
console.warn('[vue3-carousel]: A carousel component must be provided for the navigation component to display')
55+
return '';
56+
}
4857
const { i18n } = carousel.config
4958
const prevButton = h(
5059
'button',
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
export type NavigationProps = Record<never, never> // No props for now
1+
import { Carousel, CarouselExposed } from '@/components/Carousel'
2+
3+
export type NavigationProps = {
4+
carousel?: InstanceType<typeof Carousel> & CarouselExposed
5+
}

src/components/Pagination/Pagination.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { computed, defineComponent, h, inject, VNode } from 'vue'
1+
import { computed, defineComponent, h, inject, PropType, VNode } from 'vue'
22

33
import { injectCarousel } from '@/shared'
44
import { getSnapAlignOffset, i18nFormatter, mapNumberToRange } from '@/utils'
@@ -14,13 +14,12 @@ export const Pagination = defineComponent<PaginationProps>({
1414
paginateByItemsToShow: {
1515
type: Boolean,
1616
},
17+
carousel: {
18+
type: Object as PropType<PaginationProps['carousel']>,
19+
}
1720
},
1821
setup(props) {
19-
const carousel = inject(injectCarousel)
20-
21-
if (!carousel) {
22-
return () => '' // Don't render, let vue warn about the missing provide
23-
}
22+
let carousel = inject(injectCarousel, null)!
2423

2524
const itemsToShow = computed(() => carousel.config.itemsToShow as number)
2625
const offset = computed(() =>
@@ -53,6 +52,13 @@ export const Pagination = defineComponent<PaginationProps>({
5352
) === slide
5453

5554
return () => {
55+
if (props.carousel) {
56+
carousel = props.carousel;
57+
}
58+
if (!carousel) {
59+
console.warn('[vue3-carousel]: A carousel component must be provided for the pagination component to display')
60+
return '';
61+
}
5662
const children: Array<VNode> = []
5763

5864
for (

0 commit comments

Comments
 (0)