mirror of
https://github.com/element-plus/element-plus.git
synced 2025-01-18 10:59:10 +08:00
chore(components): [el-carousel] code style refactor (#6693)
* chore(components): [el-carousel] code style refactor - Remove duplicated code - Simplified the code for components * Wrap resize handle into shallowRef * Export CourouselItemState type * Enhance itemInStage method * Refine code
This commit is contained in:
parent
176bc652ea
commit
6b74660a20
@ -2,27 +2,49 @@ import { nextTick, reactive } from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Carousel from '../src/carousel.vue'
|
||||
import CarouselItem from '../src/carousel-item.vue'
|
||||
|
||||
import type { VueWrapper } from '@vue/test-utils'
|
||||
import type { CarouselInstance } from '../src/carousel'
|
||||
|
||||
const wait = (ms = 100) =>
|
||||
new Promise((resolve) => setTimeout(() => resolve(0), ms))
|
||||
|
||||
const generateCarouselItems = (count = 3) => {
|
||||
const generateCarouselItems = (count = 3, hasLabel = false) => {
|
||||
const list = Array.from({ length: count }, (_, index) => index + 1)
|
||||
return list.map((i) => <CarouselItem key={i}></CarouselItem>)
|
||||
return list.map((i) =>
|
||||
hasLabel ? <CarouselItem key={i} label={i} /> : <CarouselItem key={i} />
|
||||
)
|
||||
}
|
||||
|
||||
describe('Carousel', () => {
|
||||
it('create', () => {
|
||||
const wrapper = mount({
|
||||
let wrapper: VueWrapper<any>
|
||||
|
||||
const createComponent = (
|
||||
props: any = {},
|
||||
count?: number,
|
||||
hasLabel?: boolean
|
||||
) => {
|
||||
return mount({
|
||||
setup() {
|
||||
return () => (
|
||||
<div>
|
||||
<Carousel ref="carousel">{generateCarouselItems()}</Carousel>
|
||||
<Carousel {...props}>
|
||||
{generateCarouselItems(count, hasLabel)}
|
||||
</Carousel>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('create', () => {
|
||||
wrapper = createComponent({
|
||||
ref: 'carousel',
|
||||
})
|
||||
|
||||
const carousel = wrapper.findComponent({ ref: 'carousel' })
|
||||
.vm as CarouselInstance
|
||||
@ -31,11 +53,9 @@ describe('Carousel', () => {
|
||||
})
|
||||
|
||||
it('auto play', async (done) => {
|
||||
const wrapper = mount(() => (
|
||||
<div>
|
||||
<Carousel interval={50}>{generateCarouselItems()}</Carousel>
|
||||
</div>
|
||||
))
|
||||
wrapper = createComponent({
|
||||
interval: 50,
|
||||
})
|
||||
|
||||
await nextTick()
|
||||
await wait(10)
|
||||
@ -47,13 +67,10 @@ describe('Carousel', () => {
|
||||
})
|
||||
|
||||
it('initial index', async (done) => {
|
||||
const wrapper = mount(() => (
|
||||
<div>
|
||||
<Carousel autoplay={false} initial-index={1}>
|
||||
{generateCarouselItems()}
|
||||
</Carousel>
|
||||
</div>
|
||||
))
|
||||
wrapper = createComponent({
|
||||
autoplay: false,
|
||||
'initial-index': 1,
|
||||
})
|
||||
|
||||
await nextTick()
|
||||
await wait(10)
|
||||
@ -67,11 +84,9 @@ describe('Carousel', () => {
|
||||
})
|
||||
|
||||
it('reset timer', async (done) => {
|
||||
const wrapper = mount(() => (
|
||||
<div>
|
||||
<Carousel interval={500}>{generateCarouselItems()}</Carousel>
|
||||
</div>
|
||||
))
|
||||
wrapper = createComponent({
|
||||
interval: 500,
|
||||
})
|
||||
await nextTick()
|
||||
const items = wrapper.vm.$el.querySelectorAll('.el-carousel__item')
|
||||
await wrapper.trigger('mouseenter')
|
||||
@ -90,21 +105,12 @@ describe('Carousel', () => {
|
||||
oldVal: -1,
|
||||
})
|
||||
|
||||
mount({
|
||||
setup() {
|
||||
const handleChange = (val: number, oldVal: number) => {
|
||||
state.val = val
|
||||
state.oldVal = oldVal
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div>
|
||||
<Carousel interval={50} onChange={handleChange}>
|
||||
{generateCarouselItems()}
|
||||
</Carousel>
|
||||
</div>
|
||||
)
|
||||
wrapper = createComponent({
|
||||
onChange(val: number, prevVal: number) {
|
||||
state.val = val
|
||||
state.oldVal = prevVal
|
||||
},
|
||||
interval: 50,
|
||||
})
|
||||
|
||||
await nextTick()
|
||||
@ -115,15 +121,7 @@ describe('Carousel', () => {
|
||||
})
|
||||
|
||||
it('label', async (done) => {
|
||||
const wrapper = mount(() => (
|
||||
<div>
|
||||
<Carousel>
|
||||
{[1, 2, 3].map((i) => (
|
||||
<CarouselItem key={i} label={i}></CarouselItem>
|
||||
))}
|
||||
</Carousel>
|
||||
</div>
|
||||
))
|
||||
wrapper = createComponent(undefined, 3, true)
|
||||
await nextTick()
|
||||
expect(wrapper.find('.el-carousel__button span').text()).toBe('1')
|
||||
done()
|
||||
@ -131,11 +129,9 @@ describe('Carousel', () => {
|
||||
|
||||
describe('manual control', () => {
|
||||
it('hover', async (done) => {
|
||||
const wrapper = mount(() => (
|
||||
<div>
|
||||
<Carousel autoplay={false}>{generateCarouselItems()}</Carousel>
|
||||
</div>
|
||||
))
|
||||
wrapper = createComponent({
|
||||
autoplay: false,
|
||||
})
|
||||
|
||||
await nextTick()
|
||||
await wait()
|
||||
@ -152,13 +148,14 @@ describe('Carousel', () => {
|
||||
})
|
||||
|
||||
it('card', async (done) => {
|
||||
const wrapper = mount(() => (
|
||||
<div>
|
||||
<Carousel autoplay={false} type="card">
|
||||
{generateCarouselItems(7)}
|
||||
</Carousel>
|
||||
</div>
|
||||
))
|
||||
wrapper = createComponent(
|
||||
{
|
||||
autoplay: false,
|
||||
type: 'card',
|
||||
},
|
||||
7
|
||||
)
|
||||
|
||||
await nextTick()
|
||||
await wait()
|
||||
const items = wrapper.vm.$el.querySelectorAll('.el-carousel__item')
|
||||
@ -178,21 +175,11 @@ describe('Carousel', () => {
|
||||
})
|
||||
|
||||
it('vertical direction', () => {
|
||||
const wrapper = mount({
|
||||
setup() {
|
||||
return () => (
|
||||
<div>
|
||||
<Carousel
|
||||
ref="carousel"
|
||||
autoplay={false}
|
||||
direction="vertical"
|
||||
height="100px"
|
||||
>
|
||||
{generateCarouselItems()}
|
||||
</Carousel>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
wrapper = createComponent({
|
||||
ref: 'carousel',
|
||||
autoplay: false,
|
||||
direction: 'vertical',
|
||||
height: '100px',
|
||||
})
|
||||
const items = wrapper.vm.$el.querySelectorAll('.el-carousel__item')
|
||||
const carousel = wrapper.findComponent({ ref: 'carousel' })
|
||||
@ -202,13 +189,10 @@ describe('Carousel', () => {
|
||||
})
|
||||
|
||||
it('pause auto play on hover', async (done) => {
|
||||
const wrapper = mount(() => (
|
||||
<div>
|
||||
<Carousel interval={50} pause-on-hover={false}>
|
||||
{generateCarouselItems()}
|
||||
</Carousel>
|
||||
</div>
|
||||
))
|
||||
wrapper = createComponent({
|
||||
interval: 50,
|
||||
'pause-on-hover': false,
|
||||
})
|
||||
|
||||
await nextTick()
|
||||
await wrapper.find('.el-carousel').trigger('mouseenter')
|
||||
|
@ -7,12 +7,12 @@
|
||||
ns.is('in-stage', inStage),
|
||||
ns.is('hover', hover),
|
||||
ns.is('animating', animating),
|
||||
{ [ns.em('item', 'card')]: type === 'card' },
|
||||
{ [ns.em('item', 'card')]: isCardType },
|
||||
]"
|
||||
:style="itemStyle"
|
||||
@click="handleItemClick"
|
||||
>
|
||||
<div v-if="type === 'card'" v-show="!active" :class="ns.e('mask')" />
|
||||
<div v-if="isCardType" v-show="!active" :class="ns.e('mask')" />
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@ -25,11 +25,14 @@ import {
|
||||
getCurrentInstance,
|
||||
onUnmounted,
|
||||
ref,
|
||||
reactive,
|
||||
unref,
|
||||
} from 'vue'
|
||||
import { debugWarn } from '@element-plus/utils'
|
||||
import { debugWarn, isUndefined } from '@element-plus/utils'
|
||||
import { useNamespace } from '@element-plus/hooks'
|
||||
import { carouselContextKey } from '@element-plus/tokens'
|
||||
import { carouselItemProps } from './carousel-item'
|
||||
|
||||
import type { CSSProperties } from 'vue'
|
||||
|
||||
defineOptions({
|
||||
@ -37,16 +40,27 @@ defineOptions({
|
||||
})
|
||||
|
||||
const props = defineProps(carouselItemProps)
|
||||
|
||||
// inject
|
||||
const carouselContext = inject(carouselContextKey)
|
||||
|
||||
const ns = useNamespace('carousel')
|
||||
const CARD_SCALE = 0.83
|
||||
const type = carouselContext?.type
|
||||
|
||||
const COMPONENT_NAME = 'ElCarouselItem'
|
||||
// inject
|
||||
const carouselContext = inject(carouselContextKey)!
|
||||
// instance
|
||||
const instance = getCurrentInstance()
|
||||
const instance = getCurrentInstance()!
|
||||
if (!carouselContext) {
|
||||
debugWarn(
|
||||
COMPONENT_NAME,
|
||||
'usage: <el-carousel></el-carousel-item></el-carousel>'
|
||||
)
|
||||
}
|
||||
|
||||
if (!instance) {
|
||||
debugWarn(
|
||||
COMPONENT_NAME,
|
||||
'compositional hook can only be invoked inside setups'
|
||||
)
|
||||
}
|
||||
|
||||
const CARD_SCALE = 0.83
|
||||
|
||||
const hover = ref(false)
|
||||
const translate = ref(0)
|
||||
@ -57,37 +71,41 @@ const inStage = ref(false)
|
||||
const animating = ref(false)
|
||||
|
||||
// computed
|
||||
const parentDirection = computed(() => {
|
||||
return carouselContext?.direction
|
||||
})
|
||||
const { isCardType, isVertical } = carouselContext
|
||||
|
||||
const itemStyle = computed(() => {
|
||||
const translateType =
|
||||
parentDirection.value === 'vertical' ? 'translateY' : 'translateX'
|
||||
const value = `${translateType}(${translate.value}px) scale(${scale.value})`
|
||||
const style: CSSProperties = {
|
||||
transform: value,
|
||||
const itemStyle = computed<CSSProperties>(() => {
|
||||
const translateType = `translate${unref(isVertical) ? 'Y' : 'X'}`
|
||||
const _translate = `${translateType}(${unref(translate)}px)`
|
||||
const _scale = `scale(${unref(scale)})`
|
||||
const transform = [_translate, _scale].join(' ')
|
||||
|
||||
return {
|
||||
transform,
|
||||
}
|
||||
return style
|
||||
})
|
||||
|
||||
// methods
|
||||
|
||||
function processIndex(index, activeIndex, length) {
|
||||
if (activeIndex === 0 && index === length - 1) {
|
||||
function processIndex(index: number, activeIndex: number, length: number) {
|
||||
const lastItemIndex = length - 1
|
||||
const prevItemIndex = activeIndex - 1
|
||||
const nextItemIndex = activeIndex + 1
|
||||
const halfItemIndex = length / 2
|
||||
|
||||
if (activeIndex === 0 && index === lastItemIndex) {
|
||||
return -1
|
||||
} else if (activeIndex === length - 1 && index === 0) {
|
||||
} else if (activeIndex === lastItemIndex && index === 0) {
|
||||
return length
|
||||
} else if (index < activeIndex - 1 && activeIndex - index >= length / 2) {
|
||||
} else if (index < prevItemIndex && activeIndex - index >= halfItemIndex) {
|
||||
return length + 1
|
||||
} else if (index > activeIndex + 1 && index - activeIndex >= length / 2) {
|
||||
} else if (index > nextItemIndex && index - activeIndex >= halfItemIndex) {
|
||||
return -2
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
function calcCardTranslate(index, activeIndex) {
|
||||
const parentWidth = carouselContext?.root.value?.offsetWidth || 0
|
||||
function calcCardTranslate(index: number, activeIndex: number) {
|
||||
const parentWidth = carouselContext.root.value?.offsetWidth || 0
|
||||
if (inStage.value) {
|
||||
return (parentWidth * ((2 - CARD_SCALE) * (index - activeIndex) + 1)) / 4
|
||||
} else if (index < activeIndex) {
|
||||
@ -97,58 +115,66 @@ function calcCardTranslate(index, activeIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
function calcTranslate(index, activeIndex, isVertical) {
|
||||
const distance =
|
||||
(isVertical
|
||||
? carouselContext?.root.value?.offsetHeight
|
||||
: carouselContext?.root.value?.offsetWidth) || 0
|
||||
function calcTranslate(
|
||||
index: number,
|
||||
activeIndex: number,
|
||||
isVertical: boolean
|
||||
) {
|
||||
const rootEl = carouselContext.root.value
|
||||
if (!rootEl) return 0
|
||||
|
||||
const distance = (isVertical ? rootEl.offsetHeight : rootEl.offsetWidth) || 0
|
||||
return distance * (index - activeIndex)
|
||||
}
|
||||
|
||||
const translateItem = (
|
||||
index: number,
|
||||
activeIndex: number,
|
||||
oldIndex: number
|
||||
oldIndex?: number
|
||||
) => {
|
||||
const parentType = carouselContext?.type
|
||||
const length = carouselContext?.items.value.length ?? Number.NaN
|
||||
if (parentType !== 'card' && oldIndex !== undefined) {
|
||||
animating.value = index === activeIndex || index === oldIndex
|
||||
const _isCardType = unref(isCardType)
|
||||
const carouselItemLength = carouselContext.items.value.length ?? Number.NaN
|
||||
|
||||
const isActive = index === activeIndex
|
||||
if (!_isCardType && !isUndefined(oldIndex)) {
|
||||
animating.value = isActive || index === oldIndex
|
||||
}
|
||||
if (index !== activeIndex && length > 2 && carouselContext?.loop) {
|
||||
index = processIndex(index, activeIndex, length)
|
||||
|
||||
if (!isActive && carouselItemLength > 2 && carouselContext.loop) {
|
||||
index = processIndex(index, activeIndex, carouselItemLength)
|
||||
}
|
||||
if (parentType === 'card') {
|
||||
if (parentDirection.value === 'vertical') {
|
||||
debugWarn('Carousel', 'vertical direction is not supported in card mode')
|
||||
|
||||
const _isVertical = unref(isVertical)
|
||||
active.value = isActive
|
||||
|
||||
if (_isCardType) {
|
||||
if (_isVertical) {
|
||||
debugWarn('Carousel', 'vertical direction is not supported for card mode')
|
||||
}
|
||||
inStage.value = Math.round(Math.abs(index - activeIndex)) <= 1
|
||||
active.value = index === activeIndex
|
||||
translate.value = calcCardTranslate(index, activeIndex)
|
||||
scale.value = active.value ? 1 : CARD_SCALE
|
||||
scale.value = unref(active) ? 1 : CARD_SCALE
|
||||
} else {
|
||||
active.value = index === activeIndex
|
||||
const isVertical = parentDirection.value === 'vertical'
|
||||
translate.value = calcTranslate(index, activeIndex, isVertical)
|
||||
translate.value = calcTranslate(index, activeIndex, _isVertical)
|
||||
}
|
||||
|
||||
ready.value = true
|
||||
}
|
||||
|
||||
function handleItemClick() {
|
||||
if (carouselContext && carouselContext?.type === 'card') {
|
||||
const index = carouselContext?.items.value
|
||||
.map((d) => d.uid)
|
||||
.indexOf(instance?.uid)
|
||||
carouselContext?.setActiveItem(index)
|
||||
if (carouselContext && unref(isCardType)) {
|
||||
const index = carouselContext.items.value.findIndex(
|
||||
({ uid }) => uid === instance.uid
|
||||
)
|
||||
carouselContext.setActiveItem(index)
|
||||
}
|
||||
}
|
||||
|
||||
// lifecycle
|
||||
onMounted(() => {
|
||||
if (carouselContext?.addItem) {
|
||||
carouselContext?.addItem({
|
||||
uid: instance?.uid,
|
||||
...props,
|
||||
carouselContext.addItem({
|
||||
props,
|
||||
states: reactive({
|
||||
hover,
|
||||
translate,
|
||||
scale,
|
||||
@ -156,14 +182,13 @@ onMounted(() => {
|
||||
ready,
|
||||
inStage,
|
||||
animating,
|
||||
translateItem,
|
||||
})
|
||||
}
|
||||
}),
|
||||
uid: instance.uid,
|
||||
translateItem,
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (carouselContext?.removeItem) {
|
||||
carouselContext?.removeItem(instance?.uid)
|
||||
}
|
||||
carouselContext.removeItem(instance.uid)
|
||||
})
|
||||
</script>
|
||||
|
@ -54,7 +54,7 @@
|
||||
@click.stop="handleIndicatorClick(index)"
|
||||
>
|
||||
<button :class="ns.e('button')">
|
||||
<span v-if="hasLabel">{{ item.label }}</span>
|
||||
<span v-if="hasLabel">{{ item.props.label }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
@ -70,10 +70,12 @@ import {
|
||||
onBeforeUnmount,
|
||||
watch,
|
||||
nextTick,
|
||||
shallowRef,
|
||||
unref,
|
||||
} from 'vue'
|
||||
import { throttle } from 'lodash-unified'
|
||||
import { useResizeObserver } from '@vueuse/core'
|
||||
import { debugWarn } from '@element-plus/utils'
|
||||
import { debugWarn, isString } from '@element-plus/utils'
|
||||
import { ElIcon } from '@element-plus/components/icon'
|
||||
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
|
||||
import { useNamespace } from '@element-plus/hooks'
|
||||
@ -84,29 +86,32 @@ import type { CarouselItemContext } from '@element-plus/tokens'
|
||||
defineOptions({
|
||||
name: 'ElCarousel',
|
||||
})
|
||||
|
||||
const props = defineProps(carouselProps)
|
||||
const emit = defineEmits(carouselEmits)
|
||||
const ns = useNamespace('carousel')
|
||||
const COMPONENT_NAME = 'ElCarousel'
|
||||
const THROTTLE_TIME = 300
|
||||
|
||||
// refs
|
||||
const activeIndex = ref(-1)
|
||||
const timer = ref<ReturnType<typeof setInterval> | null>(null)
|
||||
const hover = ref(false)
|
||||
const root = ref<HTMLDivElement>()
|
||||
const items = ref<CarouselItemContext[]>([])
|
||||
const items = ref<Array<CarouselItemContext>>([])
|
||||
|
||||
// computed
|
||||
const arrowDisplay = computed(
|
||||
() => props.arrow !== 'never' && props.direction !== 'vertical'
|
||||
() => props.arrow !== 'never' && !unref(isVertical)
|
||||
)
|
||||
|
||||
const hasLabel = computed(() => {
|
||||
return items.value.some((item) => item.label.toString().length > 0)
|
||||
return items.value.some((item) => item.props.label.toString().length > 0)
|
||||
})
|
||||
|
||||
const carouselClasses = computed(() => {
|
||||
const classes = [ns.b(), ns.m(props.direction)]
|
||||
if (props.type === 'card') {
|
||||
if (unref(isCardType)) {
|
||||
classes.push(ns.m('card'))
|
||||
}
|
||||
return classes
|
||||
@ -117,24 +122,27 @@ const indicatorsClasses = computed(() => {
|
||||
if (hasLabel.value) {
|
||||
classes.push(ns.em('indicators', 'labels'))
|
||||
}
|
||||
if (props.indicatorPosition === 'outside' || props.type === 'card') {
|
||||
if (props.indicatorPosition === 'outside' || unref(isCardType)) {
|
||||
classes.push(ns.em('indicators', 'outside'))
|
||||
}
|
||||
return classes
|
||||
})
|
||||
|
||||
const isCardType = computed(() => props.type === 'card')
|
||||
const isVertical = computed(() => props.direction === 'vertical')
|
||||
|
||||
// methods
|
||||
const throttledArrowClick = throttle(
|
||||
(index) => {
|
||||
(index: number) => {
|
||||
setActiveItem(index)
|
||||
},
|
||||
300,
|
||||
THROTTLE_TIME,
|
||||
{ trailing: true }
|
||||
)
|
||||
|
||||
const throttledIndicatorHover = throttle((index) => {
|
||||
const throttledIndicatorHover = throttle((index: number) => {
|
||||
handleIndicatorHover(index)
|
||||
}, 300)
|
||||
}, THROTTLE_TIME)
|
||||
|
||||
function pauseTimer() {
|
||||
if (timer.value) {
|
||||
@ -156,24 +164,26 @@ const playSlides = () => {
|
||||
}
|
||||
}
|
||||
|
||||
function setActiveItem(index) {
|
||||
if (typeof index === 'string') {
|
||||
const filteredItems = items.value.filter((item) => item.name === index)
|
||||
function setActiveItem(index: number | string) {
|
||||
if (isString(index)) {
|
||||
const filteredItems = items.value.filter(
|
||||
(item) => item.props.name === index
|
||||
)
|
||||
if (filteredItems.length > 0) {
|
||||
index = items.value.indexOf(filteredItems[0])
|
||||
}
|
||||
}
|
||||
index = Number(index)
|
||||
if (Number.isNaN(index) || index !== Math.floor(index)) {
|
||||
debugWarn('Carousel', 'index must be an integer.')
|
||||
debugWarn(COMPONENT_NAME, 'index must be integer.')
|
||||
return
|
||||
}
|
||||
const length = items.value.length
|
||||
const itemCount = items.value.length
|
||||
const oldIndex = activeIndex.value
|
||||
if (index < 0) {
|
||||
activeIndex.value = props.loop ? length - 1 : 0
|
||||
} else if (index >= length) {
|
||||
activeIndex.value = props.loop ? 0 : length - 1
|
||||
activeIndex.value = props.loop ? itemCount - 1 : 0
|
||||
} else if (index >= itemCount) {
|
||||
activeIndex.value = props.loop ? 0 : itemCount - 1
|
||||
} else {
|
||||
activeIndex.value = index
|
||||
}
|
||||
@ -182,17 +192,17 @@ function setActiveItem(index) {
|
||||
}
|
||||
}
|
||||
|
||||
function resetItemPosition(oldIndex) {
|
||||
function resetItemPosition(oldIndex?: number) {
|
||||
items.value.forEach((item, index) => {
|
||||
item.translateItem(index, activeIndex.value, oldIndex)
|
||||
})
|
||||
}
|
||||
|
||||
function addItem(item) {
|
||||
function addItem(item: CarouselItemContext) {
|
||||
items.value.push(item)
|
||||
}
|
||||
|
||||
function removeItem(uid) {
|
||||
function removeItem(uid?: number) {
|
||||
const index = items.value.findIndex((item) => item.uid === uid)
|
||||
if (index !== -1) {
|
||||
items.value.splice(index, 1)
|
||||
@ -200,17 +210,21 @@ function removeItem(uid) {
|
||||
}
|
||||
}
|
||||
|
||||
function itemInStage(item, index) {
|
||||
const length = items.value.length
|
||||
if (
|
||||
(index === length - 1 && item.inStage && items.value[0].active) ||
|
||||
(item.inStage && items.value[index + 1] && items.value[index + 1].active)
|
||||
) {
|
||||
function itemInStage(item: CarouselItemContext, index: number) {
|
||||
const _items = unref(items)
|
||||
const itemCount = _items.length
|
||||
if (itemCount === 0 || !item.states.inStage) return false
|
||||
const nextItemIndex = index + 1
|
||||
const prevItemIndex = index - 1
|
||||
const lastItemIndex = itemCount - 1
|
||||
const isLastItemActive = _items[lastItemIndex].states.active
|
||||
const isFirstItemActive = _items[0].states.active
|
||||
const isNextItemActive = _items[nextItemIndex]?.states?.active
|
||||
const isPrevItemActive = _items[prevItemIndex]?.states?.active
|
||||
|
||||
if ((index === lastItemIndex && isFirstItemActive) || isNextItemActive) {
|
||||
return 'left'
|
||||
} else if (
|
||||
(index === 0 && item.inStage && items.value[length - 1].active) ||
|
||||
(item.inStage && items.value[index - 1] && items.value[index - 1].active)
|
||||
) {
|
||||
} else if ((index === 0 && isLastItemActive) || isPrevItemActive) {
|
||||
return 'right'
|
||||
}
|
||||
return false
|
||||
@ -228,27 +242,27 @@ function handleMouseLeave() {
|
||||
startTimer()
|
||||
}
|
||||
|
||||
function handleButtonEnter(arrow) {
|
||||
if (props.direction === 'vertical') return
|
||||
function handleButtonEnter(arrow: 'left' | 'right') {
|
||||
if (unref(isVertical)) return
|
||||
items.value.forEach((item, index) => {
|
||||
if (arrow === itemInStage(item, index)) {
|
||||
item.hover = true
|
||||
item.states.hover = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleButtonLeave() {
|
||||
if (props.direction === 'vertical') return
|
||||
if (unref(isVertical)) return
|
||||
items.value.forEach((item) => {
|
||||
item.hover = false
|
||||
item.states.hover = false
|
||||
})
|
||||
}
|
||||
|
||||
function handleIndicatorClick(index) {
|
||||
function handleIndicatorClick(index: number) {
|
||||
activeIndex.value = index
|
||||
}
|
||||
|
||||
function handleIndicatorHover(index) {
|
||||
function handleIndicatorHover(index: number) {
|
||||
if (props.trigger === 'hover' && index !== activeIndex.value) {
|
||||
activeIndex.value = index
|
||||
}
|
||||
@ -274,8 +288,8 @@ watch(
|
||||
)
|
||||
watch(
|
||||
() => props.autoplay,
|
||||
(current) => {
|
||||
current ? startTimer() : pauseTimer()
|
||||
(autoplay) => {
|
||||
autoplay ? startTimer() : pauseTimer()
|
||||
}
|
||||
)
|
||||
watch(
|
||||
@ -285,27 +299,30 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
const resizeObserver = shallowRef<ReturnType<typeof useResizeObserver>>()
|
||||
// lifecycle
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
const resizeObserver = useResizeObserver(root.value, resetItemPosition)
|
||||
if (props.initialIndex < items.value.length && props.initialIndex >= 0) {
|
||||
activeIndex.value = props.initialIndex
|
||||
}
|
||||
startTimer()
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (root.value) resizeObserver.stop()
|
||||
pauseTimer()
|
||||
})
|
||||
resizeObserver.value = useResizeObserver(root.value, () => {
|
||||
resetItemPosition()
|
||||
})
|
||||
if (props.initialIndex < items.value.length && props.initialIndex >= 0) {
|
||||
activeIndex.value = props.initialIndex
|
||||
}
|
||||
startTimer()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
pauseTimer()
|
||||
if (root.value && resizeObserver.value) resizeObserver.value.stop()
|
||||
})
|
||||
|
||||
// provide
|
||||
provide(carouselContextKey, {
|
||||
root,
|
||||
direction: props.direction,
|
||||
type: props.type,
|
||||
isCardType,
|
||||
isVertical,
|
||||
items,
|
||||
loop: props.loop,
|
||||
addItem,
|
||||
|
@ -1,30 +1,29 @@
|
||||
import type { InjectionKey, Ref, ToRefs, UnwrapRef } from 'vue'
|
||||
import type { InjectionKey, Ref } from 'vue'
|
||||
|
||||
import type { CarouselItemProps } from '@element-plus/components/carousel'
|
||||
|
||||
export type CarouselItemContext = CarouselItemProps &
|
||||
ToRefs<{
|
||||
hover: boolean
|
||||
translate: number
|
||||
scale: number
|
||||
active: boolean
|
||||
ready: boolean
|
||||
inStage: boolean
|
||||
animating: boolean
|
||||
}> & {
|
||||
uid: number | undefined
|
||||
translateItem: (
|
||||
index: number,
|
||||
activeIndex: number,
|
||||
oldIndex: number
|
||||
) => void
|
||||
}
|
||||
export type CarouselItemStates = {
|
||||
hover: boolean
|
||||
translate: number
|
||||
scale: number
|
||||
active: boolean
|
||||
ready: boolean
|
||||
inStage: boolean
|
||||
animating: boolean
|
||||
}
|
||||
|
||||
export type CarouselItemContext = {
|
||||
props: CarouselItemProps
|
||||
states: CarouselItemStates
|
||||
uid: number | undefined
|
||||
translateItem: (index: number, activeIndex: number, oldIndex?: number) => void
|
||||
}
|
||||
|
||||
export type CarouselContext = {
|
||||
root: Ref<HTMLElement | undefined>
|
||||
direction: string
|
||||
type: string
|
||||
items: Ref<UnwrapRef<CarouselItemContext[]>>
|
||||
items: Ref<CarouselItemContext[]>
|
||||
isCardType: Ref<boolean>
|
||||
isVertical: Ref<boolean>
|
||||
loop: boolean
|
||||
addItem: (item: CarouselItemContext) => void
|
||||
removeItem: (uid: number | undefined) => void
|
||||
|
Loading…
Reference in New Issue
Block a user