refactor(components): [affix] use vueuse utils (#6295)

* refactor(components): refactor affix

* refactor: resolve review comments
This commit is contained in:
三咲智子 2022-03-01 23:27:00 +08:00 committed by GitHub
parent b97ac7cfb8
commit bb160c5f9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 53 deletions

View File

@ -55,7 +55,7 @@ describe('Affix.vue', () => {
})
test('should render offset props', async () => {
const wrapper = _mount(() => <Affix offset={30}>AXIOM</Affix>)
const wrapper = _mount(() => <Affix offset={30}>{AXIOM}</Affix>)
await nextTick()
const mockAffixRect = jest
.spyOn(wrapper.find('.el-affix').element, 'getBoundingClientRect')
@ -85,7 +85,7 @@ describe('Affix.vue', () => {
test('should render position props', async () => {
const wrapper = _mount(() => (
<Affix position="bottom" offset={20}>
AXIOM
{AXIOM}
</Affix>
))
await nextTick()
@ -119,7 +119,7 @@ describe('Affix.vue', () => {
const wrapper = _mount(() => (
<>
<div class="target" style="height: 200px">
<Affix target=".target">AXIOM</Affix>
<Affix target=".target">{AXIOM}</Affix>
</div>
<div style="height: 1000px"></div>
</>
@ -163,7 +163,7 @@ describe('Affix.vue', () => {
})
test('should render z-index props', async () => {
const wrapper = _mount(() => <Affix z-index={1000}>AXIOM</Affix>)
const wrapper = _mount(() => <Affix zIndex={1000}>{AXIOM}</Affix>)
await nextTick()
const mockAffixRect = jest
.spyOn(wrapper.find('.el-affix').element, 'getBoundingClientRect')

View File

@ -1,19 +1,24 @@
<template>
<div ref="root" :class="ns.b()" :style="rootStyle">
<div :class="{ [ns.m('fixed')]: fixed }" :style="affixStyle">
<slot></slot>
<slot />
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, shallowRef, watch } from 'vue'
import { useEventListener, useResizeObserver } from '@vueuse/core'
import { getScrollContainer } from '@element-plus/utils'
import { computed, onMounted, ref, shallowRef, watch, watchEffect } from 'vue'
import {
useEventListener,
useElementBounding,
useWindowSize,
} from '@vueuse/core'
import { getScrollContainer, throwError } from '@element-plus/utils'
import { useNamespace } from '@element-plus/hooks'
import { affixEmits, affixProps } from './affix'
import type { CSSProperties } from 'vue'
const COMPONENT_NAME = 'ElAffix'
defineOptions({
name: 'ElAffix',
})
@ -26,101 +31,97 @@ const ns = useNamespace('affix')
const target = shallowRef<HTMLElement>()
const root = shallowRef<HTMLDivElement>()
const scrollContainer = shallowRef<HTMLElement | Window>()
const { height: windowHeight } = useWindowSize()
const {
height: rootHeight,
width: rootWidth,
top: rootTop,
bottom: rootBottom,
update: updateRoot,
} = useElementBounding(root)
const targetRect = useElementBounding(target)
const fixed = ref(false)
const height = ref(0) // height of root
const width = ref(0) // width of root
const scrollTop = ref(0) // scrollTop of documentElement
const clientHeight = ref(0) // clientHeight of documentElement
const scrollTop = ref(0)
const transform = ref(0)
const rootStyle = computed<CSSProperties>(() => {
return {
height: fixed.value ? `${height.value}px` : '',
width: fixed.value ? `${width.value}px` : '',
height: fixed.value ? `${rootHeight.value}px` : '',
width: fixed.value ? `${rootWidth.value}px` : '',
}
})
const affixStyle = computed<CSSProperties | undefined>(() => {
if (!fixed.value) return
const affixStyle = computed<CSSProperties>(() => {
if (!fixed.value) return {}
const offset = props.offset ? `${props.offset}px` : 0
const _transform = transform.value ? `translateY(${transform.value}px)` : ''
return {
height: `${height.value}px`,
width: `${width.value}px`,
height: `${rootHeight.value}px`,
width: `${rootWidth.value}px`,
top: props.position === 'top' ? offset : '',
bottom: props.position === 'bottom' ? offset : '',
transform: _transform,
transform: transform.value ? `translateY(${transform.value}px)` : '',
zIndex: props.zIndex,
}
})
const update = () => {
if (!root.value || !target.value || !scrollContainer.value) return
if (!scrollContainer.value) return
const rootRect = root.value.getBoundingClientRect()
const targetRect = target.value.getBoundingClientRect()
height.value = rootRect.height
width.value = rootRect.width
scrollTop.value =
scrollContainer.value instanceof Window
? document.documentElement.scrollTop
: scrollContainer.value.scrollTop || 0
clientHeight.value = document.documentElement.clientHeight
if (props.position === 'top') {
if (props.target) {
const difference = targetRect.bottom - props.offset - height.value
fixed.value = props.offset > rootRect.top && targetRect.bottom > 0
const difference =
targetRect.bottom.value - props.offset - rootHeight.value
fixed.value = props.offset > rootTop.value && targetRect.bottom.value > 0
transform.value = difference < 0 ? difference : 0
} else {
fixed.value = props.offset > rootRect.top
fixed.value = props.offset > rootTop.value
}
} else if (props.target) {
const difference =
windowHeight.value -
targetRect.top.value -
props.offset -
rootHeight.value
fixed.value =
windowHeight.value - props.offset < rootBottom.value &&
windowHeight.value > targetRect.top.value
transform.value = difference < 0 ? -difference : 0
} else {
if (props.target) {
const difference =
clientHeight.value - targetRect.top - props.offset - height.value
fixed.value =
clientHeight.value - props.offset < rootRect.bottom &&
clientHeight.value > targetRect.top
transform.value = difference < 0 ? -difference : 0
} else {
fixed.value = clientHeight.value - props.offset < rootRect.bottom
}
fixed.value = windowHeight.value - props.offset < rootBottom.value
}
}
const onScroll = () => {
update()
const handleScroll = () => {
emit('scroll', {
scrollTop: scrollTop.value,
fixed: fixed.value,
})
}
watch(fixed, (fixed) => {
emit('change', fixed)
})
watch(fixed, (val) => emit('change', val))
onMounted(() => {
if (props.target) {
target.value =
document.querySelector<HTMLElement>(props.target) ?? undefined
if (!target.value) {
throw new Error(`Target is not existed: ${props.target}`)
}
if (!target.value)
throwError(COMPONENT_NAME, `Target is not existed: ${props.target}`)
} else {
target.value = document.documentElement
}
scrollContainer.value = getScrollContainer(root.value!, true)
updateRoot()
})
useEventListener(scrollContainer, 'scroll', onScroll)
useResizeObserver(root, () => update())
useResizeObserver(target, () => update())
useEventListener(scrollContainer, 'scroll', handleScroll)
watchEffect(update)
defineExpose({
/** @description update affix status */