mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
fix(components): [carousel] improper active item (#8904)
* fix(components): [carousel] none of active item closed #8891 * fix: ts error * fix: guarantee items order when reusing carousel-item component * style: code format * fix: test fail * chore: correct type * fix: sorting failed in some cases * better implementation better implementation * impove code accroding to review comment
This commit is contained in:
@@ -195,4 +195,30 @@ describe('Carousel', () => {
|
||||
await wait(60)
|
||||
expect(items[1].classList.contains('is-active')).toBeTruthy()
|
||||
})
|
||||
it('should guarantee order of indicators', async () => {
|
||||
const data = reactive([1, 2, 3, 4])
|
||||
wrapper = mount({
|
||||
setup() {
|
||||
return () => (
|
||||
<div>
|
||||
<Carousel>
|
||||
{data.map((value) => (
|
||||
<CarouselItem label={value} key={value}>
|
||||
{value}
|
||||
</CarouselItem>
|
||||
))}
|
||||
</Carousel>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
await nextTick()
|
||||
|
||||
data.splice(1, 0, 5)
|
||||
await nextTick()
|
||||
const indicators = wrapper.findAll('.el-carousel__button')
|
||||
data.forEach((value, index) => {
|
||||
expect(indicators[index].element.textContent).toEqual(value.toString())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
computed,
|
||||
nextTick,
|
||||
getCurrentInstance,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
provide,
|
||||
@@ -78,7 +78,7 @@ import { useResizeObserver } from '@vueuse/core'
|
||||
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'
|
||||
import { useNamespace, useOrderedChildren } from '@element-plus/hooks'
|
||||
import { carouselContextKey } from '@element-plus/tokens'
|
||||
import { carouselEmits, carouselProps } from './carousel'
|
||||
import type { CarouselItemContext } from '@element-plus/tokens'
|
||||
@@ -93,12 +93,20 @@ const ns = useNamespace('carousel')
|
||||
const COMPONENT_NAME = 'ElCarousel'
|
||||
const THROTTLE_TIME = 300
|
||||
|
||||
const {
|
||||
children: items,
|
||||
addChild: addItem,
|
||||
removeChild: removeItem,
|
||||
} = useOrderedChildren<CarouselItemContext>(
|
||||
getCurrentInstance()!,
|
||||
'ElCarouselItem'
|
||||
)
|
||||
|
||||
// refs
|
||||
const activeIndex = ref(-1)
|
||||
const timer = ref<ReturnType<typeof setInterval> | null>(null)
|
||||
const hover = ref(false)
|
||||
const root = ref<HTMLDivElement>()
|
||||
const items = ref<Array<CarouselItemContext>>([])
|
||||
|
||||
// computed
|
||||
const arrowDisplay = computed(
|
||||
@@ -199,18 +207,6 @@ function resetItemPosition(oldIndex?: number) {
|
||||
})
|
||||
}
|
||||
|
||||
function addItem(item: CarouselItemContext) {
|
||||
items.value.push(item)
|
||||
}
|
||||
|
||||
function removeItem(uid?: number) {
|
||||
const index = items.value.findIndex((item) => item.uid === uid)
|
||||
if (index !== -1) {
|
||||
items.value.splice(index, 1)
|
||||
if (activeIndex.value === index) next()
|
||||
}
|
||||
}
|
||||
|
||||
function itemInStage(item: CarouselItemContext, index: number) {
|
||||
const _items = unref(items)
|
||||
const itemCount = _items.length
|
||||
@@ -312,17 +308,19 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => items.value,
|
||||
() => {
|
||||
if (items.value.length > 0) setActiveItem(props.initialIndex)
|
||||
}
|
||||
)
|
||||
|
||||
const resizeObserver = shallowRef<ReturnType<typeof useResizeObserver>>()
|
||||
// lifecycle
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
|
||||
onMounted(() => {
|
||||
resizeObserver.value = useResizeObserver(root.value, () => {
|
||||
resetItemPosition()
|
||||
})
|
||||
if (props.initialIndex < items.value.length && props.initialIndex >= 0) {
|
||||
activeIndex.value = props.initialIndex
|
||||
}
|
||||
startTimer()
|
||||
})
|
||||
|
||||
|
||||
@@ -28,3 +28,4 @@ export * from './use-namespace'
|
||||
export * from './use-z-index'
|
||||
export * from './use-floating'
|
||||
export * from './use-cursor'
|
||||
export * from './use-ordered-children'
|
||||
|
||||
44
packages/hooks/use-ordered-children/index.ts
Normal file
44
packages/hooks/use-ordered-children/index.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { shallowRef } from 'vue'
|
||||
import { flattedChildren, isVNode } from '@element-plus/utils'
|
||||
|
||||
import type { ComponentInternalInstance, VNode } from 'vue'
|
||||
|
||||
const getOrderedChildren = <T>(
|
||||
vm: ComponentInternalInstance,
|
||||
childComponentName: string,
|
||||
children: Record<number, T>
|
||||
): T[] => {
|
||||
const nodes = flattedChildren(vm.subTree).filter(
|
||||
(n): n is VNode =>
|
||||
isVNode(n) &&
|
||||
(n.type as any)?.name === childComponentName &&
|
||||
!!n.component
|
||||
)
|
||||
const uids = nodes.map((n) => n.component!.uid)
|
||||
return uids.map((uid) => children[uid]).filter((p) => !!p)
|
||||
}
|
||||
|
||||
export const useOrderedChildren = <T extends { uid: number }>(
|
||||
vm: ComponentInternalInstance,
|
||||
childComponentName: string
|
||||
) => {
|
||||
const children: Record<number, T> = {}
|
||||
const orderedChildren = shallowRef<T[]>([])
|
||||
|
||||
const addChild = (child: T) => {
|
||||
children[child.uid] = child
|
||||
orderedChildren.value = getOrderedChildren(vm, childComponentName, children)
|
||||
}
|
||||
const removeChild = (uid: number) => {
|
||||
delete children[uid]
|
||||
orderedChildren.value = orderedChildren.value.filter(
|
||||
(children) => children.uid !== uid
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
children: orderedChildren,
|
||||
addChild,
|
||||
removeChild,
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ export type CarouselItemStates = {
|
||||
export type CarouselItemContext = {
|
||||
props: CarouselItemProps
|
||||
states: CarouselItemStates
|
||||
uid: number | undefined
|
||||
uid: number
|
||||
translateItem: (index: number, activeIndex: number, oldIndex?: number) => void
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export type CarouselContext = {
|
||||
isVertical: Ref<boolean>
|
||||
loop: boolean
|
||||
addItem: (item: CarouselItemContext) => void
|
||||
removeItem: (uid: number | undefined) => void
|
||||
removeItem: (uid: number) => void
|
||||
setActiveItem: (index: number) => void
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user