mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
feat(components): [affix] support append-to and teleported (#23053)
* feat(components): [affix] implement append-to-body * feat: change append-to-body to teleported and append-to * test: optimize affix teleport case * Update packages/components/affix/src/affix.ts Co-authored-by: rzzf <cszhjh@gmail.com> * Update packages/components/affix/src/affix.vue Co-authored-by: rzzf <cszhjh@gmail.com> * Update packages/components/affix/src/affix.vue Co-authored-by: rzzf <cszhjh@gmail.com> * Update packages/components/affix/src/affix.vue Co-authored-by: btea <2356281422@qq.com> * Update docs/en-US/component/affix.md Co-authored-by: btea <2356281422@qq.com> * Update docs/en-US/component/affix.md Co-authored-by: btea <2356281422@qq.com> * chore: update the affix.md format --------- Co-authored-by: rzzf <cszhjh@gmail.com> Co-authored-by: btea <2356281422@qq.com>
This commit is contained in:
@@ -41,12 +41,14 @@ affix/fixed
|
||||
|
||||
### Attributes
|
||||
|
||||
| Name | Description | Type | Default |
|
||||
| -------- | ------------------------------- | -------------------------- | ------- |
|
||||
| offset | offset distance | ^[number] | 0 |
|
||||
| position | position of affix | ^[enum]`'top' \| 'bottom'` | top |
|
||||
| target | target container (CSS selector) | ^[string] | — |
|
||||
| z-index | `z-index` of affix | ^[number] | 100 |
|
||||
| Name | Description | Type | Default |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------- | ------- |
|
||||
| offset | offset distance | ^[number] | 0 |
|
||||
| position | position of affix | ^[enum]`'top' \| 'bottom'` | top |
|
||||
| target | target container (CSS selector) | ^[string] | — |
|
||||
| z-index | `z-index` of affix | ^[number] | 100 |
|
||||
| teleported ^(2.13.0) | whether affix element is teleported, if `true` it will be teleported to where `append-to` sets | ^[boolean] | false |
|
||||
| append-to ^(2.13.0) | which element the affix element appends to | ^[CSSSelector] / ^[HTMLElement] | body |
|
||||
|
||||
### Events
|
||||
|
||||
|
||||
@@ -192,4 +192,41 @@ describe('Affix.vue', () => {
|
||||
mockAffixRect.mockRestore()
|
||||
mockDocumentRect.mockRestore()
|
||||
})
|
||||
|
||||
test('should render append-to props', async () => {
|
||||
const wrapper = _mount(() => (
|
||||
<>
|
||||
<div class="teleport-target"></div>
|
||||
<Affix teleported appendTo=".teleport-target">
|
||||
{AXIOM}
|
||||
</Affix>
|
||||
</>
|
||||
))
|
||||
const teleportTarget = wrapper.find('.teleport-target')
|
||||
await nextTick()
|
||||
|
||||
expect(wrapper.text()).toEqual(AXIOM)
|
||||
const mockAffixRect = vi
|
||||
.spyOn(wrapper.find('.el-affix').element, 'getBoundingClientRect')
|
||||
.mockReturnValue({
|
||||
height: 40,
|
||||
width: 1000,
|
||||
top: -100,
|
||||
bottom: -80,
|
||||
} as DOMRect)
|
||||
const mockDocumentRect = vi
|
||||
.spyOn(document.documentElement, 'getBoundingClientRect')
|
||||
.mockReturnValue({
|
||||
height: 200,
|
||||
width: 1000,
|
||||
top: 0,
|
||||
bottom: 200,
|
||||
} as DOMRect)
|
||||
expect(wrapper.find('.el-affix--fixed').exists()).toBe(false)
|
||||
expect(teleportTarget.find('.el-affix--fixed').exists()).toBe(false)
|
||||
await makeScroll(document.documentElement, 'scrollTop', 200)
|
||||
expect(teleportTarget.find('.el-affix--fixed').exists()).toBe(true)
|
||||
mockAffixRect.mockRestore()
|
||||
mockDocumentRect.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
isNumber,
|
||||
} from '@element-plus/utils'
|
||||
import { CHANGE_EVENT } from '@element-plus/constants'
|
||||
import { teleportProps } from '@element-plus/components/teleport'
|
||||
|
||||
import type { ExtractPropTypes, __ExtractPublicPropTypes } from 'vue'
|
||||
import type { ZIndexProperty } from 'csstype'
|
||||
@@ -40,6 +41,17 @@ export const affixProps = buildProps({
|
||||
values: ['top', 'bottom'],
|
||||
default: 'top',
|
||||
},
|
||||
/**
|
||||
* @description whether affix element is teleported, if `true` it will be teleported to where `append-to` sets
|
||||
* */
|
||||
teleported: Boolean,
|
||||
/**
|
||||
* @description which element the affix element appends to
|
||||
* */
|
||||
appendTo: {
|
||||
type: teleportProps.to.type,
|
||||
default: 'body',
|
||||
},
|
||||
} as const)
|
||||
export type AffixProps = ExtractPropTypes<typeof affixProps>
|
||||
export type AffixPropsPublic = __ExtractPublicPropTypes<typeof affixProps>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<template>
|
||||
<div ref="root" :class="ns.b()" :style="rootStyle">
|
||||
<div :class="{ [ns.m('fixed')]: fixed }" :style="affixStyle">
|
||||
<slot />
|
||||
</div>
|
||||
<el-teleport :disabled="teleportDisabled" :to="appendTo">
|
||||
<div :class="{ [ns.m('fixed')]: fixed }" :style="affixStyle">
|
||||
<slot />
|
||||
</div>
|
||||
</el-teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -21,6 +23,7 @@ import {
|
||||
useEventListener,
|
||||
useWindowSize,
|
||||
} from '@vueuse/core'
|
||||
import ElTeleport from '@element-plus/components/teleport'
|
||||
import { addUnit, getScrollContainer, throwError } from '@element-plus/utils'
|
||||
import { useNamespace } from '@element-plus/hooks'
|
||||
import { CHANGE_EVENT } from '@element-plus/constants'
|
||||
@@ -46,6 +49,7 @@ const {
|
||||
width: rootWidth,
|
||||
top: rootTop,
|
||||
bottom: rootBottom,
|
||||
left: rootLeft,
|
||||
update: updateRoot,
|
||||
} = useElementBounding(root, { windowScroll: false })
|
||||
const targetRect = useElementBounding(target)
|
||||
@@ -54,6 +58,10 @@ const fixed = ref(false)
|
||||
const scrollTop = ref(0)
|
||||
const transform = ref(0)
|
||||
|
||||
const teleportDisabled = computed(() => {
|
||||
return !props.teleported || !fixed.value
|
||||
})
|
||||
|
||||
const rootStyle = computed<CSSProperties>(() => {
|
||||
return {
|
||||
height: fixed.value ? `${rootHeight.value}px` : '',
|
||||
@@ -70,6 +78,7 @@ const affixStyle = computed<CSSProperties>(() => {
|
||||
width: `${rootWidth.value}px`,
|
||||
top: props.position === 'top' ? offset : '',
|
||||
bottom: props.position === 'bottom' ? offset : '',
|
||||
left: props.teleported ? `${rootLeft.value}px` : '',
|
||||
transform: transform.value ? `translateY(${transform.value}px)` : '',
|
||||
zIndex: props.zIndex,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user