Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(components): [carousel-item] #10219

Merged
merged 1 commit into from Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/components/carousel/index.ts
Expand Up @@ -13,4 +13,4 @@ export const ElCarouselItem = withNoopInstall(CarouselItem)
export * from './src/carousel'
export * from './src/carousel-item'

export type { CarouselInstance } from './src/instance'
export type { CarouselInstance, CarouselItemInstance } from './src/instance'
3 changes: 0 additions & 3 deletions packages/components/carousel/src/carousel-item.ts
@@ -1,6 +1,5 @@
import { buildProps } from '@element-plus/utils'
import type { ExtractPropTypes } from 'vue'
import type CarouselItem from './carousel-item.vue'

export const carouselItemProps = buildProps({
name: { type: String, default: '' },
Expand All @@ -11,5 +10,3 @@ export const carouselItemProps = buildProps({
} as const)

export type CarouselItemProps = ExtractPropTypes<typeof carouselItemProps>

export type CarouselItemInstance = InstanceType<typeof CarouselItem>
163 changes: 14 additions & 149 deletions packages/components/carousel/src/carousel-item.vue
Expand Up @@ -18,20 +18,10 @@
</template>

<script lang="ts" setup>
import {
computed,
getCurrentInstance,
inject,
onMounted,
onUnmounted,
reactive,
ref,
unref,
} from 'vue'
import { debugWarn, isUndefined } from '@element-plus/utils'
import { computed, unref } from 'vue'
import { useNamespace } from '@element-plus/hooks'
import { carouselContextKey } from '@element-plus/tokens'
import { carouselItemProps } from './carousel-item'
import { useCarouselItem } from './use-carousel-item'

import type { CSSProperties } from 'vue'

Expand All @@ -43,35 +33,18 @@ const props = defineProps(carouselItemProps)
const ns = useNamespace('carousel')
const COMPONENT_NAME = 'ElCarouselItem'
// inject
const carouselContext = inject(carouselContextKey)!
// instance
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)
const scale = ref(1)
const active = ref(false)
const ready = ref(false)
const inStage = ref(false)
const animating = ref(false)

// computed
const { isCardType, isVertical } = carouselContext
const {
active,
animating,
hover,
inStage,
isVertical,
translate,
isCardType,
scale,
ready,
handleItemClick,
} = useCarouselItem(props, COMPONENT_NAME)

const itemStyle = computed<CSSProperties>(() => {
const translateType = `translate${unref(isVertical) ? 'Y' : 'X'}`
Expand All @@ -83,112 +56,4 @@ const itemStyle = computed<CSSProperties>(() => {
transform,
}
})

// methods

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 === lastItemIndex && index === 0) {
return length
} else if (index < prevItemIndex && activeIndex - index >= halfItemIndex) {
return length + 1
} else if (index > nextItemIndex && index - activeIndex >= halfItemIndex) {
return -2
}
return index
}

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) {
return (-(1 + CARD_SCALE) * parentWidth) / 4
} else {
return ((3 + CARD_SCALE) * parentWidth) / 4
}
}

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
) => {
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 (!isActive && carouselItemLength > 2 && carouselContext.loop) {
index = processIndex(index, activeIndex, carouselItemLength)
}

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
translate.value = calcCardTranslate(index, activeIndex)
scale.value = unref(active) ? 1 : CARD_SCALE
} else {
translate.value = calcTranslate(index, activeIndex, _isVertical)
}

ready.value = true
}

function handleItemClick() {
if (carouselContext && unref(isCardType)) {
const index = carouselContext.items.value.findIndex(
({ uid }) => uid === instance.uid
)
carouselContext.setActiveItem(index)
}
}

// lifecycle
onMounted(() => {
carouselContext.addItem({
props,
states: reactive({
hover,
translate,
scale,
active,
ready,
inStage,
animating,
}),
uid: instance.uid,
translateItem,
})
})

onUnmounted(() => {
carouselContext.removeItem(instance.uid)
})
</script>
2 changes: 2 additions & 0 deletions packages/components/carousel/src/instance.ts
@@ -1,3 +1,5 @@
import type Carousel from './carousel.vue'
import type CarouselItem from './carousel-item.vue'

export type CarouselInstance = InstanceType<typeof Carousel>
export type CarouselItemInstance = InstanceType<typeof CarouselItem>
173 changes: 173 additions & 0 deletions packages/components/carousel/src/use-carousel-item.ts
@@ -0,0 +1,173 @@
import {
getCurrentInstance,
inject,
onMounted,
onUnmounted,
reactive,
ref,
unref,
} from 'vue'
import { debugWarn, isUndefined } from '@element-plus/utils'
import { carouselContextKey } from '@element-plus/tokens'

import type { CarouselItemProps } from './carousel-item'

export const useCarouselItem = (
props: CarouselItemProps,
componentName: string
) => {
const carouselContext = inject(carouselContextKey)!
// instance
const instance = getCurrentInstance()!
if (!carouselContext) {
debugWarn(
componentName,
'usage: <el-carousel></el-carousel-item></el-carousel>'
)
}

if (!instance) {
debugWarn(
componentName,
'compositional hook can only be invoked inside setups'
)
}

const CARD_SCALE = 0.83

const hover = ref(false)
const translate = ref(0)
const scale = ref(1)
const active = ref(false)
const ready = ref(false)
const inStage = ref(false)
const animating = ref(false)

// computed
const { isCardType, isVertical } = carouselContext

// methods

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 === lastItemIndex && index === 0) {
return length
} else if (index < prevItemIndex && activeIndex - index >= halfItemIndex) {
return length + 1
} else if (index > nextItemIndex && index - activeIndex >= halfItemIndex) {
return -2
}
return index
}

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) {
return (-(1 + CARD_SCALE) * parentWidth) / 4
} else {
return ((3 + CARD_SCALE) * parentWidth) / 4
}
}

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
) => {
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 (!isActive && carouselItemLength > 2 && carouselContext.loop) {
index = processIndex(index, activeIndex, carouselItemLength)
}

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
translate.value = calcCardTranslate(index, activeIndex)
scale.value = unref(active) ? 1 : CARD_SCALE
} else {
translate.value = calcTranslate(index, activeIndex, _isVertical)
}

ready.value = true
}

function handleItemClick() {
if (carouselContext && unref(isCardType)) {
const index = carouselContext.items.value.findIndex(
({ uid }) => uid === instance.uid
)
carouselContext.setActiveItem(index)
}
}

// lifecycle
onMounted(() => {
carouselContext.addItem({
props,
states: reactive({
hover,
translate,
scale,
active,
ready,
inStage,
animating,
}),
uid: instance.uid,
translateItem,
})
})

onUnmounted(() => {
carouselContext.removeItem(instance.uid)
})

return {
active,
animating,
hover,
inStage,
isVertical,
translate,
isCardType,
scale,
ready,
handleItemClick,
}
}