-
Notifications
You must be signed in to change notification settings - Fork 360
feat(popup): add multiple instance methods to popup component #3925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 元素 | ||
|
|
@@ -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'); | ||
| } | ||
|
|
||
|
|
@@ -242,12 +245,56 @@ const Popup = forwardRef<PopupRef, PopupProps>((originalProps, ref) => { | |
| </CSSTransition> | ||
| ); | ||
|
|
||
| // 处理 shadow root(web component)和 trigger 隐藏的情况 | ||
| function updatePopper() { | ||
| const popper = popperRef.current; | ||
| const triggerEl = getRefDom(triggerRef); | ||
| // 如果没有渲染弹层或不可见则不触发更新 | ||
| if (!popupRef.current || !visible) return; | ||
| if (!popper) 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?.(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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, () => ({ | ||
| /** 获取 popper 实例 */ | ||
| getPopper: () => popperRef.current, | ||
| getPopupElement: () => popupRef.current, | ||
| getPortalElement: () => portalRef.current, | ||
| getPopupContentElement: () => contentRef.current, | ||
| setVisible: (visible: boolean) => onVisibleChange(visible, { trigger: 'document' }), | ||
| /** 获取浮层元素 */ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里把文档提供的和内部方法/非文档提供的分开吧 不要交叉 未公开的注释说明下 |
||
| getOverlay: () => portalRef.current, | ||
| /** 获取浮层悬浮状态 */ | ||
| getOverlayState: () => ({ hover: isOverlayHover }), | ||
| /** 更新浮层内容 */ | ||
| update: () => updatePopper(), | ||
| })); | ||
|
|
||
| return ( | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 同上 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,66 +1,76 @@ | ||
| :: BASE_DOC :: | ||
|
|
||
| ### Called Popup via plugin | ||
|
|
||
| Calling Popup through the plug-in method is used to render Popup in a scene with existing nodes. At the same time, no matter how it is called, it will only be mounted on one node, which is used to reduce the number of Popup rendering nodes on the page. | ||
|
|
||
| Support functional calls `PopupPlugin` 。 | ||
| - `PopupPlugin(triggerElement, content, popupProps)` | ||
|
|
||
| {{ plugin }} | ||
|
|
||
| ### Dynamic Adaptation | ||
|
|
||
| When the trigger or popup display content changes dynamically, the position is adjusted adaptively | ||
|
|
||
| {{ dynamic }} | ||
|
|
||
| ### popperOptions usage | ||
|
|
||
| https://popper.js.org/docs/v2/constructors/#types | ||
|
|
||
| - `popperOptions` = `Options` | ||
|
|
||
| {{ popper-options }} | ||
|
|
||
| ## FAQ | ||
|
|
||
| ### How to solve the problem of position offset when nesting `Popup` components? | ||
|
|
||
| Currently, this can be solved by `Fragment` or other `HTML` elements | ||
|
|
||
| ```js | ||
| <Popup content="Popup Content"> | ||
| <> | ||
| {children} | ||
| </> | ||
| </Popup> | ||
| ``` | ||
|
|
||
| ## API | ||
| ### Popup Props | ||
|
|
||
| name | type | default | description | required | ||
| -- | -- | -- | -- | -- | ||
| attach | String / Function | 'body' | Typescript:`AttachNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| children | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| content | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| delay | Number / Array | - | delay to show or hide popover。Typescript:`number \| Array<number>` | N | ||
| destroyOnClose | Boolean | false | \- | N | ||
| disabled | Boolean | - | \- | N | ||
| hideEmptyPopup | Boolean | false | \- | N | ||
| overlayClassName | String / Object / Array | - | Typescript:`ClassName`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| overlayInnerClassName | String / Object / Array | - | Typescript:`ClassName`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| overlayInnerStyle | Boolean / Object / Function | - | Typescript:`Styles \| ((triggerElement: HTMLElement, popupElement: HTMLElement) => Styles)`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| overlayStyle | Boolean / Object / Function | - | Typescript:`Styles \| ((triggerElement: HTMLElement, popupElement: HTMLElement) => Styles)`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| placement | String | top | Typescript:`PopupPlacement` `type PopupPlacement = 'top'\|'left'\|'right'\|'bottom'\|'top-left'\|'top-right'\|'bottom-left'\|'bottom-right'\|'left-top'\|'left-bottom'\|'right-top'\|'right-bottom'`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/popup/type.ts) | N | ||
| popperOptions | Object | - | popper initial options,details refer to https://popper.js.org/docs | N | ||
| showArrow | Boolean | false | \- | N | ||
| trigger | String | hover | options:hover/click/focus/mousedown/context-menu | N | ||
| triggerElement | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| visible | Boolean | - | Typescript:`boolean` | N | ||
| defaultVisible | Boolean | - | uncontrolled property。Typescript:`boolean` | N | ||
| 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 | ||
| :: BASE_DOC :: | ||
|
|
||
| ### Called Popup via plugin | ||
|
|
||
| Calling Popup through the plug-in method is used to render Popup in a scene with existing nodes. At the same time, no matter how it is called, it will only be mounted on one node, which is used to reduce the number of Popup rendering nodes on the page. | ||
|
|
||
| Support functional calls `PopupPlugin` 。 | ||
| - `PopupPlugin(triggerElement, content, popupProps)` | ||
|
|
||
| {{ plugin }} | ||
|
|
||
| ### Dynamic Adaptation | ||
|
|
||
| When the trigger or popup display content changes dynamically, the position is adjusted adaptively | ||
|
|
||
| {{ dynamic }} | ||
|
|
||
| ### popperOptions usage | ||
|
|
||
| https://popper.js.org/docs/v2/constructors/#types | ||
|
|
||
| - `popperOptions` = `Options` | ||
|
|
||
| {{ popper-options }} | ||
|
|
||
| ## FAQ | ||
|
|
||
| ### How to solve the problem of position offset when nesting `Popup` components? | ||
|
|
||
| Currently, this can be solved by `Fragment` or other `HTML` elements | ||
|
|
||
| ```js | ||
| <Popup content="Popup Content"> | ||
| <> | ||
| {children} | ||
| </> | ||
| </Popup> | ||
| ``` | ||
|
|
||
| ## API | ||
|
|
||
| ### Popup Props | ||
|
|
||
| name | type | default | description | required | ||
| -- | -- | -- | -- | -- | ||
| attach | String / Function | 'body' | Typescript:`AttachNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| children | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| content | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| delay | Number / Array | - | delay to show or hide popover。Typescript:`number \| Array<number>` | N | ||
| destroyOnClose | Boolean | false | \- | N | ||
| disabled | Boolean | - | \- | N | ||
| hideEmptyPopup | Boolean | false | \- | N | ||
| overlayClassName | String / Object / Array | - | Typescript:`ClassName`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| overlayInnerClassName | String / Object / Array | - | Typescript:`ClassName`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| overlayInnerStyle | Boolean / Object / Function | - | Typescript:`Styles \| ((triggerElement: HTMLElement, popupElement: HTMLElement) => Styles)`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| overlayStyle | Boolean / Object / Function | - | Typescript:`Styles \| ((triggerElement: HTMLElement, popupElement: HTMLElement) => Styles)`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| placement | String | top | Typescript:`PopupPlacement` `type PopupPlacement = 'top'\|'left'\|'right'\|'bottom'\|'top-left'\|'top-right'\|'bottom-left'\|'bottom-right'\|'left-top'\|'left-bottom'\|'right-top'\|'right-bottom'`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/popup/type.ts) | N | ||
| popperOptions | Object | - | popper initial options,details refer to https://popper.js.org/docs | N | ||
| showArrow | Boolean | false | \- | N | ||
| trigger | String | hover | options:hover/click/focus/mousedown/context-menu | N | ||
| triggerElement | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/packages/components/common.ts) | N | ||
| visible | Boolean | - | Typescript:`boolean` | N | ||
| defaultVisible | Boolean | - | uncontrolled property。Typescript:`boolean` | N | ||
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为什么还得分两个if?