Skip to content

Commit b171632

Browse files
micaiguairzzfbtea
authored
feat(components): [affix] support append-to and teleported (element-plus#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>
1 parent 5862e86 commit b171632

File tree

4 files changed

+69
-9
lines changed

4 files changed

+69
-9
lines changed

docs/en-US/component/affix.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,14 @@ affix/fixed
4141

4242
### Attributes
4343

44-
| Name | Description | Type | Default |
45-
| -------- | ------------------------------- | -------------------------- | ------- |
46-
| offset | offset distance | ^[number] | 0 |
47-
| position | position of affix | ^[enum]`'top' \| 'bottom'` | top |
48-
| target | target container (CSS selector) | ^[string] ||
49-
| z-index | `z-index` of affix | ^[number] | 100 |
44+
| Name | Description | Type | Default |
45+
| -------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------- | ------- |
46+
| offset | offset distance | ^[number] | 0 |
47+
| position | position of affix | ^[enum]`'top' \| 'bottom'` | top |
48+
| target | target container (CSS selector) | ^[string] ||
49+
| z-index | `z-index` of affix | ^[number] | 100 |
50+
| teleported ^(2.13.0) | whether affix element is teleported, if `true` it will be teleported to where `append-to` sets | ^[boolean] | false |
51+
| append-to ^(2.13.0) | which element the affix element appends to | ^[CSSSelector] / ^[HTMLElement] | body |
5052

5153
### Events
5254

packages/components/affix/__tests__/affix.test.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,41 @@ describe('Affix.vue', () => {
192192
mockAffixRect.mockRestore()
193193
mockDocumentRect.mockRestore()
194194
})
195+
196+
test('should render append-to props', async () => {
197+
const wrapper = _mount(() => (
198+
<>
199+
<div class="teleport-target"></div>
200+
<Affix teleported appendTo=".teleport-target">
201+
{AXIOM}
202+
</Affix>
203+
</>
204+
))
205+
const teleportTarget = wrapper.find('.teleport-target')
206+
await nextTick()
207+
208+
expect(wrapper.text()).toEqual(AXIOM)
209+
const mockAffixRect = vi
210+
.spyOn(wrapper.find('.el-affix').element, 'getBoundingClientRect')
211+
.mockReturnValue({
212+
height: 40,
213+
width: 1000,
214+
top: -100,
215+
bottom: -80,
216+
} as DOMRect)
217+
const mockDocumentRect = vi
218+
.spyOn(document.documentElement, 'getBoundingClientRect')
219+
.mockReturnValue({
220+
height: 200,
221+
width: 1000,
222+
top: 0,
223+
bottom: 200,
224+
} as DOMRect)
225+
expect(wrapper.find('.el-affix--fixed').exists()).toBe(false)
226+
expect(teleportTarget.find('.el-affix--fixed').exists()).toBe(false)
227+
await makeScroll(document.documentElement, 'scrollTop', 200)
228+
expect(teleportTarget.find('.el-affix--fixed').exists()).toBe(true)
229+
mockAffixRect.mockRestore()
230+
mockDocumentRect.mockRestore()
231+
})
195232
})

packages/components/affix/src/affix.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
isNumber,
66
} from '@element-plus/utils'
77
import { CHANGE_EVENT } from '@element-plus/constants'
8+
import { teleportProps } from '@element-plus/components/teleport'
89

910
import type { ExtractPropTypes, __ExtractPublicPropTypes } from 'vue'
1011
import type { ZIndexProperty } from 'csstype'
@@ -40,6 +41,17 @@ export const affixProps = buildProps({
4041
values: ['top', 'bottom'],
4142
default: 'top',
4243
},
44+
/**
45+
* @description whether affix element is teleported, if `true` it will be teleported to where `append-to` sets
46+
* */
47+
teleported: Boolean,
48+
/**
49+
* @description which element the affix element appends to
50+
* */
51+
appendTo: {
52+
type: teleportProps.to.type,
53+
default: 'body',
54+
},
4355
} as const)
4456
export type AffixProps = ExtractPropTypes<typeof affixProps>
4557
export type AffixPropsPublic = __ExtractPublicPropTypes<typeof affixProps>

packages/components/affix/src/affix.vue

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<template>
22
<div ref="root" :class="ns.b()" :style="rootStyle">
3-
<div :class="{ [ns.m('fixed')]: fixed }" :style="affixStyle">
4-
<slot />
5-
</div>
3+
<el-teleport :disabled="teleportDisabled" :to="appendTo">
4+
<div :class="{ [ns.m('fixed')]: fixed }" :style="affixStyle">
5+
<slot />
6+
</div>
7+
</el-teleport>
68
</div>
79
</template>
810

@@ -21,6 +23,7 @@ import {
2123
useEventListener,
2224
useWindowSize,
2325
} from '@vueuse/core'
26+
import ElTeleport from '@element-plus/components/teleport'
2427
import { addUnit, getScrollContainer, throwError } from '@element-plus/utils'
2528
import { useNamespace } from '@element-plus/hooks'
2629
import { CHANGE_EVENT } from '@element-plus/constants'
@@ -46,6 +49,7 @@ const {
4649
width: rootWidth,
4750
top: rootTop,
4851
bottom: rootBottom,
52+
left: rootLeft,
4953
update: updateRoot,
5054
} = useElementBounding(root, { windowScroll: false })
5155
const targetRect = useElementBounding(target)
@@ -54,6 +58,10 @@ const fixed = ref(false)
5458
const scrollTop = ref(0)
5559
const transform = ref(0)
5660
61+
const teleportDisabled = computed(() => {
62+
return !props.teleported || !fixed.value
63+
})
64+
5765
const rootStyle = computed<CSSProperties>(() => {
5866
return {
5967
height: fixed.value ? `${rootHeight.value}px` : '',
@@ -70,6 +78,7 @@ const affixStyle = computed<CSSProperties>(() => {
7078
width: `${rootWidth.value}px`,
7179
top: props.position === 'top' ? offset : '',
7280
bottom: props.position === 'bottom' ? offset : '',
81+
left: props.teleported ? `${rootLeft.value}px` : '',
7382
transform: transform.value ? `translateY(${transform.value}px)` : '',
7483
zIndex: props.zIndex,
7584
}

0 commit comments

Comments
 (0)