diff --git a/components/_util/utils.ts b/components/_util/utils.ts index 75c78ed3..49348189 100644 --- a/components/_util/utils.ts +++ b/components/_util/utils.ts @@ -109,3 +109,41 @@ export const pxfy = (value: string | number) => { if (value.endsWith('px')) return value; return `${value}px`; }; + +export function getParentNode(node: Node): Node | null { + // document type + if (node.nodeType === 9) { + return null; + } + return node.parentNode; +} + +export function getScrollParent( + node: Node | null, +): HTMLElement | Document | null { + if (node === null) return null; + + const parentNode = getParentNode(node); + + if (parentNode === null) { + return null; + } + + // Document + if (parentNode.nodeType === 9) { + return document; + } + + // Element + if (parentNode.nodeType === 1) { + // Firefox want us to check `-x` and `-y` variations as well + const { overflow, overflowX, overflowY } = getComputedStyle( + parentNode as HTMLElement, + ); + if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) { + return parentNode as HTMLElement; + } + } + + return getScrollParent(parentNode); +} diff --git a/components/popper/popper.tsx b/components/popper/popper.tsx index 42d520d0..30e2c98b 100644 --- a/components/popper/popper.tsx +++ b/components/popper/popper.tsx @@ -16,6 +16,7 @@ import getElementFromRef from '../_util/getElementFromRef'; import { useTheme } from '../_theme/useTheme'; import useTrigger from './useTrigger'; import usePopper from './usePopper'; +import useScroll from './useScroll'; import { useConfig } from '../config-provider'; @@ -47,6 +48,18 @@ export default defineComponent({ updateVirtualRect, placement, } = usePopper(props, emit); + + useScroll( + computed(() => getElementFromRef(triggerRef.value)), + (e: Event) => { + // 不挂载在container上 + if (!props.appendToContainer) return; + if (!visible.value) return; + if (e.target === getContainer.value?.()) return; + computePopper(); + }, + ); + const disabledWatch = computed(() => props.disabled || !visible.value); useClickOutSide( [triggerRef, popperRef], diff --git a/components/popper/useScroll.ts b/components/popper/useScroll.ts new file mode 100644 index 00000000..faedbc8f --- /dev/null +++ b/components/popper/useScroll.ts @@ -0,0 +1,37 @@ +import { Ref, onMounted, onUnmounted } from 'vue'; +import { getScrollParent } from '../_util/utils'; + +export default function useScroll( + targetRef: Ref, + onScroll: (e: Event) => void, +) { + // scroll related + let scrollableNodes: Array = []; + + const ensureScrollListener = (): void => { + let cursor: Element | Document | null = targetRef.value; + while (true) { + cursor = getScrollParent(cursor); + if (cursor === null) break; + scrollableNodes.push(cursor); + } + for (const el of scrollableNodes) { + el.addEventListener('scroll', onScroll, true); + } + }; + + const removeScrollListeners = (): void => { + for (const el of scrollableNodes) { + el.removeEventListener('scroll', onScroll, true); + } + scrollableNodes = []; + }; + + onMounted(() => { + ensureScrollListener(); + }); + + onUnmounted(() => { + removeScrollListeners(); + }); +}