Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion packages/components/popup/Popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const Popup = forwardRef<PopupRef, PopupProps>((originalProps, ref) => {
const { keepExpand, keepFade } = useAnimation();
const { height: windowHeight, width: windowWidth } = useWindowSize();
const [visible, onVisibleChange] = useControlled(props, 'visible', props.onVisibleChange);
const [isOverlayHover, setIsOverlayHover] = useState(false);

const [popupElement, setPopupElement] = useState(null);
const triggerRef = useRef(null); // 记录 trigger 元素
Expand Down Expand Up @@ -153,9 +154,11 @@ const Popup = forwardRef<PopupRef, PopupProps>((originalProps, ref) => {
}, [visible, updateScrollTop, getTriggerDom]);

function handleExited() {
setIsOverlayHover(false);
!destroyOnClose && popupElement && (popupElement.style.display = 'none');
}
function handleEnter() {
setIsOverlayHover(true);
!destroyOnClose && popupElement && (popupElement.style.display = 'block');
}

Expand Down Expand Up @@ -242,12 +245,59 @@ const Popup = forwardRef<PopupRef, PopupProps>((originalProps, ref) => {
</CSSTransition>
);

// 处理 shadow root(web component)和 trigger 隐藏的情况
function updatePopper() {
const popper = popperRef.current;
const triggerEl = getRefDom(triggerRef);
// 如果没有渲染弹层或不可见则不触发更新
if (!popper || !visible) return;

try {
// web component 的元素可能在 shadow root 内,需要特殊处理
const root = triggerEl?.getRootNode();
if (root && root instanceof ShadowRoot) {
// popper 的实例内部结构可能是 state.elements.reference
// 尝试兼容不同实现,先赋值再更新
if (popper.state) popper.state.elements.reference = triggerEl;
popper.update();
} else {
const rect = triggerEl?.getBoundingClientRect();
let parent = triggerEl as HTMLElement | null;
while (parent && parent !== document.body) {
parent = parent.parentElement;
}
const isHidden = parent !== document.body || (rect && rect.width === 0 && rect.height === 0);
if (!isHidden) {
if (popper.state) popper.state.elements.reference = triggerEl;
popper.update();
} else {
// trigger 不在文档流内或被隐藏,则隐藏浮层
onVisibleChange(false, { trigger: 'document' });
}
}
} catch (e) {
// 直接尝试更新
popper.update();
}
}

useImperativeHandle(ref, () => ({
getPopper: () => popperRef.current,
// 未公开
getPopupElement: () => popupRef.current,
// 未公开
getPortalElement: () => portalRef.current,
// 未公开
getPopupContentElement: () => contentRef.current,
/// 未公开
setVisible: (visible: boolean) => onVisibleChange(visible, { trigger: 'document' }),
/** 获取 popper 实例 */
getPopper: () => popperRef.current,
/** 获取浮层元素 */
getOverlay: () => portalRef.current,
/** 获取浮层悬浮状态 */
getOverlayState: () => ({ hover: isOverlayHover }),
/** 更新浮层内容 */
update: () => updatePopper(),
}));

return (
Expand Down
10 changes: 10 additions & 0 deletions packages/components/popup/popup.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Currently, this can be solved by `Fragment` or other `HTML` elements
```

## API

### Popup Props

name | type | default | description | required
Expand All @@ -64,3 +65,12 @@ zIndex | Number | - | \- | N
onScroll | Function | | Typescript:`(context: { e: WheelEvent }) => void`<br/> | N
onScrollToBottom | Function | | Typescript:`(context: { e: WheelEvent }) => void`<br/> | N
onVisibleChange | Function | | Typescript:`(visible: boolean, context: PopupVisibleChangeContext) => void`<br/>[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/popup/type.ts)。<br/>`interface PopupVisibleChangeContext { e?: PopupTriggerEvent; trigger?: PopupTriggerSource }`<br/><br/>`type PopupTriggerEvent = MouseEvent \| FocusEvent \| KeyboardEvent`<br/><br/>`type PopupTriggerSource = 'document' \| 'trigger-element-click' \| 'trigger-element-hover' \| 'trigger-element-blur' \| 'trigger-element-focus' \| 'trigger-element-mousedown' \| 'context-menu' \| 'keydown-esc'`<br/> | N

### PopupInstanceFunctions 组件实例方法

name | params | return | description
-- | -- | -- | --
getOverlay | \- | `HTMLElement \| null` | used to get overly html element
getOverlayState | \- | `{ hover: boolean }` | get mouseover state of overlay
getPopper | \- | `Instance \| null` | get the popup component popper instance。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/popup/type.ts)。<br/>`import { Instance } from '@popperjs/core'`<br/>
update | \- | \- | used to update overlay content
10 changes: 10 additions & 0 deletions packages/components/popup/popup.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ https://popper.js.org/docs/v2/constructors/#types
```

## API

### Popup Props

名称 | 类型 | 默认值 | 说明 | 必传
Expand All @@ -64,3 +65,12 @@ zIndex | Number | - | 组件层级,Web 侧样式默认为 5500,移动端和
onScroll | Function | | TS 类型:`(context: { e: WheelEvent }) => void`<br/>下拉选项滚动事件 | N
onScrollToBottom | Function | | TS 类型:`(context: { e: WheelEvent }) => void`<br/>下拉滚动触底事件,常用于滚动到底执行具体业务逻辑 | N
onVisibleChange | Function | | TS 类型:`(visible: boolean, context: PopupVisibleChangeContext) => void`<br/>当浮层隐藏或显示时触发,`trigger=document` 表示点击非浮层元素触发;`trigger=context-menu` 表示右击触发。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/popup/type.ts)。<br/>`interface PopupVisibleChangeContext { e?: PopupTriggerEvent; trigger?: PopupTriggerSource }`<br/><br/>`type PopupTriggerEvent = MouseEvent \| FocusEvent \| KeyboardEvent`<br/><br/>`type PopupTriggerSource = 'document' \| 'trigger-element-click' \| 'trigger-element-hover' \| 'trigger-element-blur' \| 'trigger-element-focus' \| 'trigger-element-mousedown' \| 'context-menu' \| 'keydown-esc'`<br/> | N

### PopupInstanceFunctions 组件实例方法

名称 | 参数 | 返回值 | 描述
-- | -- | -- | --
getOverlay | \- | `HTMLElement \| null` | 获取浮层元素
getOverlayState | \- | `{ hover: boolean }` | 获取浮层悬浮状态
getPopper | \- | `Instance \| null` | 获取当前组件 popper 实例。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/popup/type.ts)。<br/>`import { Instance } from '@popperjs/core'`<br/>
update | \- | \- | 更新浮层内容
23 changes: 22 additions & 1 deletion packages/components/popup/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */

import { Instance } from '@popperjs/core';
import { TNode, ClassName, Styles, AttachNode } from '../common';
import { MouseEvent, KeyboardEvent, FocusEvent, WheelEvent } from 'react';

export interface TdPopupProps {
/**
* 制定挂载节点。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body
* 指定挂载节点。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body
* @default 'body'
*/
attach?: AttachNode;
Expand Down Expand Up @@ -104,6 +105,26 @@ export interface TdPopupProps {
onVisibleChange?: (visible: boolean, context: PopupVisibleChangeContext) => void;
}

/** 组件实例方法 */
export interface PopupInstanceFunctions {
/**
* 获取浮层元素
*/
getOverlay?: () => HTMLElement | null;
/**
* 获取浮层悬浮状态
*/
getOverlayState?: () => { hover: boolean };
/**
* 获取当前组件 popper 实例
*/
getPopper?: () => Instance | null;
/**
* 更新浮层内容
*/
update?: () => void;
}

export type PopupPlacement =
| 'top'
| 'left'
Expand Down
Loading