Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/docs/src/examples/v-carousel/prop-mousewheel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<v-carousel
height="300"
mousewheel
>
<v-carousel-item
v-for="(color, i) in colors"
:key="i"
:color="color"
>
<div class="d-flex fill-height align-center justify-center text-h5 text-white">
Slide {{ i + 1 }}
</div>
</v-carousel-item>
</v-carousel>
</template>

<script setup>
const colors = ['primary', 'secondary', 'success', 'info', 'warning', 'error']
</script>
6 changes: 6 additions & 0 deletions packages/docs/src/pages/en/components/carousels.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ You can show a linear progress bar with the **progress** prop. It will indicate

<ExamplesExample file="v-carousel/prop-progress" />

#### Mousewheel

Enable mouse-wheel (and trackpad scroll) navigation with the **mousewheel** prop. Scrolling down or right advances to the next slide; scrolling up or left goes to the previous one. A 300ms cooldown prevents accidental multi-slide jumps per scroll gesture.

<ExamplesExample file="v-carousel/prop-mousewheel" />

#### Model

You can control carousel with **v-model**.
Expand Down
40 changes: 38 additions & 2 deletions packages/vuetify/src/components/VCarousel/VCarousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { useLocale } from '@/composables/locale'
import { useProxiedModel } from '@/composables/proxiedModel'

// Utilities
import { nextTick, onMounted, ref, watch } from 'vue'
import { convertToUnit, genericComponent, propsFactory, useRender } from '@/util'
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { convertToUnit, genericComponent, IN_BROWSER, propsFactory, useRender } from '@/util'

// Types
import type { PropType } from 'vue'
Expand All @@ -40,6 +40,7 @@ export const makeVCarouselProps = propsFactory({
default: 6000,
validator: (value: string | number) => Number(value) > 0,
},
mousewheel: Boolean,
progress: [Boolean, String],
verticalDelimiters: [Boolean, String] as PropType<boolean | 'left' | 'right'>,

Expand Down Expand Up @@ -105,6 +106,41 @@ export const VCarousel = genericComponent<new <T>(
window.requestAnimationFrame(startTimeout)
}

let lastWheelAt = 0
function onWheel (e: WheelEvent) {
e.preventDefault()
const now = Date.now()
if (now - lastWheelAt < 300 || !windowRef.value) return
lastWheelAt = now
if (e.deltaY > 0 || e.deltaX > 0) {
windowRef.value.group.next()
} else {
windowRef.value.group.prev()
}
}

watch(() => props.mousewheel, val => {
if (!IN_BROWSER || !windowRef.value) return
const el: HTMLElement = windowRef.value.$el
if (val) {
el.addEventListener('wheel', onWheel, { passive: false })
} else {
el.removeEventListener('wheel', onWheel)
}
})

onMounted(() => {
if (props.mousewheel && windowRef.value) {
windowRef.value.$el.addEventListener('wheel', onWheel, { passive: false })
}
})

onBeforeUnmount(() => {
if (windowRef.value) {
windowRef.value.$el.removeEventListener('wheel', onWheel)
}
})

function onDelimiterKeyDown (e: KeyboardEvent, group: GroupProvide) {
if (
(props.direction === 'horizontal' && e.key === 'ArrowLeft') ||
Expand Down
Loading