refactor(components): refactor affix (#3368)

This commit is contained in:
三咲智子 2021-09-13 12:02:18 +08:00 committed by GitHub
parent 0eca6446e4
commit 7a4bd0f236
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 62 deletions

View File

@ -1,8 +1,9 @@
import { mount } from '@vue/test-utils'
import { defineGetter, makeScroll } from '@element-plus/test-utils'
import Affix from '../src/index.vue'
import Affix from '../src/affix.vue'
import { nextTick } from 'vue'
let clientHeightRestore = null
let clientHeightRestore: () => void
const _mount = (template: string) =>
mount(
@ -35,6 +36,7 @@ describe('Affix.vue', () => {
const wrapper = _mount(`
<el-affix>${AXIOM}</el-affix>
`)
await nextTick()
expect(wrapper.text()).toEqual(AXIOM)
const mockAffixRect = jest
.spyOn(wrapper.find('.el-affix').element, 'getBoundingClientRect')
@ -63,6 +65,7 @@ describe('Affix.vue', () => {
const wrapper = _mount(`
<el-affix :offset="30">${AXIOM}</el-affix>
`)
await nextTick()
const mockAffixRect = jest
.spyOn(wrapper.find('.el-affix').element, 'getBoundingClientRect')
.mockReturnValue({
@ -92,6 +95,7 @@ describe('Affix.vue', () => {
const wrapper = _mount(`
<el-affix position="bottom" :offset="20">${AXIOM}</el-affix>
`)
await nextTick()
const mockAffixRect = jest
.spyOn(wrapper.find('.el-affix').element, 'getBoundingClientRect')
@ -125,6 +129,7 @@ describe('Affix.vue', () => {
</div>
<div style="height: 1000px"></div>
`)
await nextTick()
const mockAffixRect = jest
.spyOn(wrapper.find('.el-affix').element, 'getBoundingClientRect')
@ -166,6 +171,7 @@ describe('Affix.vue', () => {
const wrapper = _mount(`
<el-affix :z-index="1000">${AXIOM}</el-affix>
`)
await nextTick()
const mockAffixRect = jest
.spyOn(wrapper.find('.el-affix').element, 'getBoundingClientRect')
.mockReturnValue({

View File

@ -1,13 +1,8 @@
import Affix from './src/index.vue'
import { withInstall } from '@element-plus/utils/with-install'
import type { App } from 'vue'
import type { SFCWithInstall } from '@element-plus/utils/types'
import Affix from './src/affix.vue'
Affix.install = (app: App): void => {
app.component(Affix.name, Affix)
}
export const ElAffix = withInstall(Affix)
export default ElAffix
const _Affix = Affix as SFCWithInstall<typeof Affix>
export default _Affix
export const ElAffix = _Affix
export * from './src/affix'

View File

@ -0,0 +1,32 @@
import { buildProp } from '@element-plus/utils/props'
import type { ExtractPropTypes } from 'vue'
import type { ZIndexProperty } from 'csstype'
export const affixProps = {
zIndex: buildProp<ZIndexProperty>({
type: [Number, String],
default: 100,
}),
target: {
type: String,
default: '',
},
offset: {
type: Number,
default: 0,
},
position: buildProp({
type: String,
values: ['top', 'bottom'],
default: 'top',
} as const),
} as const
export type AffixProps = ExtractPropTypes<typeof affixProps>
export const affixEmits = {
scroll: ({ scrollTop, fixed }: { scrollTop: number; fixed: boolean }) =>
typeof scrollTop === 'number' && typeof fixed === 'boolean',
change: (fixed: boolean) => typeof fixed === 'boolean',
}
export type AffixEmits = typeof affixEmits

View File

@ -9,47 +9,27 @@
import {
computed,
defineComponent,
onBeforeUnmount,
onMounted,
reactive,
ref,
shallowRef,
watch,
} from 'vue'
import { getScrollContainer, off, on } from '@element-plus/utils/dom'
import {
addResizeListener,
removeResizeListener,
} from '@element-plus/utils/resize-event'
import { useEventListener, useResizeObserver } from '@vueuse/core'
import { getScrollContainer } from '@element-plus/utils/dom'
import { affixEmits, affixProps } from './affix'
import type { PropType } from 'vue'
type Position = 'top' | 'bottom'
import type { CSSProperties } from 'vue'
export default defineComponent({
name: 'ElAffix',
props: {
zIndex: {
type: Number,
default: 100,
},
target: {
type: String,
default: '',
},
offset: {
type: Number,
default: 0,
},
position: {
type: String as PropType<Position>,
default: 'top',
},
},
emits: ['scroll', 'change'],
props: affixProps,
emits: affixEmits,
setup(props, { emit }) {
const target = ref(null)
const root = ref(null)
const scrollContainer = ref(null)
const target = shallowRef<HTMLElement>()
const root = shallowRef<HTMLDivElement>()
const scrollContainer = shallowRef<HTMLElement | Window>()
const state = reactive({
fixed: false,
@ -60,17 +40,16 @@ export default defineComponent({
transform: 0,
})
const rootStyle = computed(() => {
const rootStyle = computed<CSSProperties>(() => {
return {
height: state.fixed ? `${state.height}px` : '',
width: state.fixed ? `${state.width}px` : '',
}
})
const affixStyle = computed(() => {
if (!state.fixed) {
return
}
const affixStyle = computed<CSSProperties | undefined>(() => {
if (!state.fixed) return
const offset = props.offset ? `${props.offset}px` : 0
const transform = state.transform
? `translateY(${state.transform}px)`
@ -87,12 +66,14 @@ export default defineComponent({
})
const update = () => {
if (!root.value || !target.value || !scrollContainer.value) return
const rootRect = root.value.getBoundingClientRect()
const targetRect = target.value.getBoundingClientRect()
state.height = rootRect.height
state.width = rootRect.width
state.scrollTop =
scrollContainer.value === window
scrollContainer.value instanceof Window
? document.documentElement.scrollTop
: scrollContainer.value.scrollTop
state.clientHeight = document.documentElement.clientHeight
@ -137,29 +118,25 @@ export default defineComponent({
onMounted(() => {
if (props.target) {
target.value = document.querySelector(props.target)
target.value =
document.querySelector<HTMLElement>(props.target) ?? undefined
if (!target.value) {
throw new Error(`target is not existed: ${props.target}`)
}
} else {
target.value = document.documentElement
}
scrollContainer.value = getScrollContainer(root.value)
on(scrollContainer.value, 'scroll', onScroll)
addResizeListener(root.value, update)
scrollContainer.value = getScrollContainer(root.value!)
})
onBeforeUnmount(() => {
off(scrollContainer.value, 'scroll', onScroll)
removeResizeListener(root.value, update)
})
useEventListener(scrollContainer, 'scroll', onScroll)
useResizeObserver(root, () => update())
return {
root,
state,
rootStyle,
affixStyle,
update,
}
},
})

View File

@ -4,8 +4,8 @@ import isServer from './isServer'
import type { CustomizedHTMLElement } from './types'
export type ResizableElement = CustomizedHTMLElement<{
__resizeListeners__: Array<(...args: unknown[]) => unknown>
__ro__: ResizeObserver
__resizeListeners__?: Array<(...args: unknown[]) => unknown>
__ro__?: ResizeObserver
}>
/* istanbul ignore next */
@ -43,6 +43,6 @@ export const removeResizeListener = function (
if (!element || !element.__resizeListeners__) return
element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1)
if (!element.__resizeListeners__.length) {
element.__ro__.disconnect()
element.__ro__?.disconnect()
}
}