Skip to content

Commit f57515c

Browse files
committed
feat: add clamp and dragThreshold options to carousel configuration
1 parent f76cd7f commit f57515c

File tree

5 files changed

+74
-71
lines changed

5 files changed

+74
-71
lines changed

docs/config.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,22 @@
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. |
10+
| `clamp` | `boolean` | false | If true will clamp itemsToShow to the number of available slides |
1011
| `dir` | 'ltr', 'rtl', 'ttb', 'btt' | 'ltr' | Carousel sliding direction. Supports horizontal (ltr/rtl) and vertical (ttb/btt) orientations. |
12+
| `dragThreshold` | `number` | 0.3 | Define a threshold for the drag distance required to trigger a slide transition. |
1113
| `enabled` | `boolean` | true | Controls whether the carousel is interactive. When false, all interactions are disabled. |
1214
| `gap` | `number` | 0 | Space (in pixels) between carousel slides. |
1315
| `height` | `number` \| `string` | 'auto' | Sets the carousel track height. Required for vertical orientation. |
1416
| `i18n` | `object` | [`{ ariaNextSlide: ...}`](#i18n) | Internationalization options for accessibility labels and text content. |
1517
| `ignoreAnimations` | `boolean` \| `string` \| `array` | false | Specifies which CSS animations should be excluded from slide size calculations. <Badge text="0.10.0"/> |
1618
| `itemsToScroll` | `number` | 1 | Number of slides to move when navigating. Useful for creating slide groups. |
1719
| `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 |
1920
| `modelValue` | `number` | 0 | Controls the active slide index. Can be used with v-model for two-way binding. |
2021
| `mouseDrag` | `boolean` | true | Enables/disables mouse drag navigation. |
2122
| `pauseAutoplayOnHover` | `boolean` | false | When true, autoplay pauses while the mouse cursor is over the carousel. |
2223
| `preventExcessiveDragging` | `boolean` | false | Limits dragging behavior at carousel boundaries for better UX. <Badge text="0.13.0" /> |
24+
| `slideEffect` | 'slide', 'fade' | 'slide' | Determines the transition effect between slides. |
2325
| `snapAlign` | 'start', 'end', 'center-odd', 'center-even' | 'center' | Determines how slides are aligned within the viewport. |
24-
| `threshold` | `number` | 0.5 | Define a threshold for the drag distance required to trigger a slide transition. |
2526
| `touchDrag` | `boolean` | true | Enables/disables touch navigation on touch-enabled devices. |
2627
| `transition` | `number` | 300 | Duration of the slide transition animation in milliseconds. |
2728
| `wrapAround` | `boolean` | false | When true, creates an infinite loop effect by connecting the last slide to the first. |
@@ -111,10 +112,10 @@ Available keys:
111112
| --------------------- | -------------------------------------- | -------------------------------------------------------------------------- |
112113
| `ariaGallery` | "Gallery" | Used as the aria-label for the main carousel element, indicating purpose. |
113114
| `ariaNavigateToSlide` | "Navigate to slide {slideNumber}" | Sets title and aria-label for pagination buttons to select a slide. |
114-
| `ariaNextSlide` | "Navigate to next slide" | Sets title and aria-label for the Next navigation button. |
115-
| `ariaPreviousSlide` | "Navigate to previous slide" | Sets title and aria-label for the Previous navigation button. |
115+
| `ariaNextSlide` | "Navigate to next slide" | Sets title and aria-label for the "Next" navigation button. |
116+
| `ariaPreviousSlide` | "Navigate to previous slide" | Sets title and aria-label for the "Previous" navigation button. |
116117
| `iconArrowDown` | "Arrow pointing downwards" | Sets title and aria-label for the downward-pointing arrow SVG icon. |
117118
| `iconArrowLeft` | "Arrow pointing to the left" | Sets title and aria-label for the left-pointing arrow SVG icon. |
118119
| `iconArrowRight` | "Arrow pointing to the right" | Sets title and aria-label for the right-pointing arrow SVG icon. |
119120
| `iconArrowUp` | "Arrow pointing upwards" | Sets title and aria-label for the upward-pointing arrow SVG icon. |
120-
| `itemXofY` | "Item {currentSlide} of {slidesCount}" | Provides screen readers with the current slides position in the sequence. |
121+
| `itemXofY` | "Item {currentSlide} of {slidesCount}" | Provides screen readers with the current slide's position in the sequence. |

src/components/Carousel/Carousel.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import {
1212
SetupContext,
1313
shallowReactive,
1414
watch,
15-
watchEffect, toRefs,
15+
watchEffect,
16+
toRefs,
1617
} from 'vue'
1718

1819
import { ARIA as ARIAComponent } from '@/components/ARIA'
@@ -148,7 +149,7 @@ export const Carousel = defineComponent({
148149
val: Number(config.itemsToShow),
149150
max: props.clamp ? slidesCount.value : Infinity,
150151
min: 1,
151-
});
152+
})
152153
}
153154
}
154155

@@ -422,7 +423,7 @@ export const Carousel = defineComponent({
422423
isReversed: isReversed.value,
423424
dragged,
424425
effectiveSlideSize: effectiveSlideSize.value,
425-
threshold: config.threshold,
426+
threshold: config.dragThreshold,
426427
})
427428

428429
activeSlideIndex.value = config.wrapAround
@@ -853,17 +854,19 @@ export const Carousel = defineComponent({
853854
slidesCount,
854855
})
855856

856-
expose<CarouselExposed>(reactive({
857-
data,
858-
next,
859-
prev,
860-
restartCarousel,
861-
slideTo,
862-
updateBreakpointsConfig,
863-
updateSlideSize,
864-
updateSlidesData,
865-
...toRefs(provided),
866-
}))
857+
expose<CarouselExposed>(
858+
reactive({
859+
data,
860+
next,
861+
prev,
862+
restartCarousel,
863+
slideTo,
864+
updateBreakpointsConfig,
865+
updateSlideSize,
866+
updateSlidesData,
867+
...toRefs(provided),
868+
})
869+
)
867870

868871
return () => {
869872
const slotSlides = slots.default || slots.slides

src/components/Carousel/carouselProps.ts

Lines changed: 48 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,37 @@ export const carouselProps = {
3737
return BREAKPOINT_MODE_OPTIONS.includes(value)
3838
},
3939
},
40+
clamp: {
41+
type: Boolean,
42+
},
43+
// control the gap between slides
44+
dir: {
45+
type: String as PropType<Dir>,
46+
default: DEFAULT_CONFIG.dir,
47+
validator(value: Dir, props: { height?: string }) {
48+
// The value must match one of these strings
49+
if (!DIR_OPTIONS.includes(value)) {
50+
return false
51+
}
52+
53+
const normalizedDir =
54+
value in DIR_MAP ? DIR_MAP[value as NonNormalizedDir] : (value as NormalizedDir)
55+
if (
56+
['ttb', 'btt'].includes(normalizedDir) &&
57+
(!props.height || props.height === 'auto')
58+
) {
59+
console.warn(
60+
`[vue3-carousel]: The dir "${value}" is not supported with height "auto".`
61+
)
62+
}
63+
return true
64+
},
65+
},
66+
// control the threshold to trigger slide change
67+
dragThreshold: {
68+
default: DEFAULT_CONFIG.dragThreshold,
69+
type: Number,
70+
},
4071
// enable/disable the carousel component
4172
enabled: {
4273
default: DEFAULT_CONFIG.enabled,
@@ -52,6 +83,11 @@ export const carouselProps = {
5283
default: DEFAULT_CONFIG.height,
5384
type: [Number, String],
5485
},
86+
// aria-labels and additional text labels
87+
i18n: {
88+
default: DEFAULT_CONFIG.i18n,
89+
type: Object as PropType<typeof DEFAULT_CONFIG.i18n>,
90+
},
5591
ignoreAnimations: {
5692
default: false,
5793
type: [Array, Boolean, String] as PropType<CarouselConfig['ignoreAnimations']>,
@@ -66,26 +102,16 @@ export const carouselProps = {
66102
default: DEFAULT_CONFIG.itemsToShow,
67103
type: [Number, String],
68104
},
69-
// aria-labels and additional text labels
70-
i18n: {
71-
default: DEFAULT_CONFIG.i18n,
72-
type: Object as PropType<typeof DEFAULT_CONFIG.i18n>,
73-
},
74105
// slide number number of initial slide
75106
modelValue: {
76107
default: undefined,
77108
type: Number,
78109
},
79-
// toggle mouse dragging.
110+
// toggle mouse dragging
80111
mouseDrag: {
81112
default: DEFAULT_CONFIG.mouseDrag,
82113
type: Boolean,
83114
},
84-
// toggle mouse dragging.
85-
touchDrag: {
86-
default: DEFAULT_CONFIG.touchDrag,
87-
type: Boolean,
88-
},
89115
pauseAutoplayOnHover: {
90116
default: DEFAULT_CONFIG.pauseAutoplayOnHover,
91117
type: Boolean,
@@ -99,63 +125,36 @@ export const carouselProps = {
99125
`[vue3-carousel]: "preventExcessiveDragging" cannot be used with wrapAround. The setting will be ignored.`
100126
)
101127
}
102-
103128
return true
104129
},
105130
},
106-
// control snap position alignment
107-
snapAlign: {
108-
default: DEFAULT_CONFIG.snapAlign,
109-
validator(value: SnapAlign) {
110-
return SNAP_ALIGN_OPTIONS.includes(value)
111-
},
112-
},
113131
slideEffect: {
114132
type: String as PropType<SlideEffect>,
115133
default: DEFAULT_CONFIG.slideEffect,
116134
validator(value: SlideEffect) {
117135
return SLIDE_EFFECTS.includes(value)
118136
},
119137
},
120-
// control the threshold to trigger slide change
121-
threshold: {
122-
default: DEFAULT_CONFIG.threshold,
123-
type: Number
138+
// control snap position alignment
139+
snapAlign: {
140+
default: DEFAULT_CONFIG.snapAlign,
141+
validator(value: SnapAlign) {
142+
return SNAP_ALIGN_OPTIONS.includes(value)
143+
},
144+
},
145+
// toggle touch dragging
146+
touchDrag: {
147+
default: DEFAULT_CONFIG.touchDrag,
148+
type: Boolean,
124149
},
125150
// sliding transition time in ms
126151
transition: {
127152
default: DEFAULT_CONFIG.transition,
128153
type: Number,
129154
},
130-
// control the gap between slides
131-
dir: {
132-
type: String as PropType<Dir>,
133-
default: DEFAULT_CONFIG.dir,
134-
validator(value: Dir, props: { height?: string }) {
135-
// The value must match one of these strings
136-
if (!DIR_OPTIONS.includes(value)) {
137-
return false
138-
}
139-
140-
const normalizedDir =
141-
value in DIR_MAP ? DIR_MAP[value as NonNormalizedDir] : (value as NormalizedDir)
142-
if (
143-
['ttb', 'btt'].includes(normalizedDir) &&
144-
(!props.height || props.height === 'auto')
145-
) {
146-
console.warn(
147-
`[vue3-carousel]: The dir "${value}" is not supported with height "auto".`
148-
)
149-
}
150-
return true
151-
},
152-
},
153155
// control infinite scrolling mode
154156
wrapAround: {
155157
default: DEFAULT_CONFIG.wrapAround,
156158
type: Boolean,
157159
},
158-
clamp: {
159-
type: Boolean,
160-
}
161160
}

src/shared/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export const DEFAULT_CONFIG: CarouselConfig = {
5050
breakpointMode: BREAKPOINT_MODE_OPTIONS[0],
5151
breakpoints: undefined,
5252
dir: DIR_OPTIONS[0],
53+
dragThreshold: 0.3,
5354
enabled: true,
5455
gap: 0,
5556
height: 'auto',
@@ -63,7 +64,6 @@ export const DEFAULT_CONFIG: CarouselConfig = {
6364
preventExcessiveDragging: false,
6465
slideEffect: SLIDE_EFFECTS[0],
6566
snapAlign: SNAP_ALIGN_OPTIONS[0],
66-
threshold: 0.5,
6767
touchDrag: true,
6868
transition: 300,
6969
wrapAround: false,

src/shared/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ export type CarouselConfig = {
3232
autoplay?: number
3333
breakpointMode?: BreakpointMode
3434
breakpoints?: Breakpoints
35+
clamp?: boolean
3536
dir?: Dir
37+
dragThreshold: number
3638
enabled: boolean
3739
gap: number
3840
height: string | number
@@ -46,11 +48,9 @@ export type CarouselConfig = {
4648
preventExcessiveDragging: boolean
4749
slideEffect: SlideEffect
4850
snapAlign: SnapAlign
49-
threshold: number
5051
touchDrag?: boolean
5152
transition?: number
5253
wrapAround?: boolean
53-
clamp?: boolean
5454
}
5555

5656
export type VueClass = string | Record<string, boolean> | VueClass[]

0 commit comments

Comments
 (0)