From 1724fb14a5f21422095d2aa0b25f10f19c1ea8b7 Mon Sep 17 00:00:00 2001 From: Kyrielin Date: Mon, 7 Nov 2022 18:46:09 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=8F=AF=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E9=80=89=E6=8B=A9=E5=99=A8=E9=80=89=E4=B8=AD=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E5=A4=B1=E5=8E=BB=E7=84=A6=E7=82=B9=E9=80=89=E4=B8=AD?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98=20(#1675)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 优化 portal 逻辑 * fix: 修复可过滤选择器选中项目失去焦点选中失败问题 * fix: 修复可过滤选择器选中项目失去焦点选中失败问题 * fix: 修复可过滤选择器选中项目失去焦点选中失败问题 --- src/common/Portal.tsx | 36 +++++-------- src/popup/hooks/useTrigger.tsx | 6 +-- src/select/base/Select.tsx | 38 +++++++------ src/tooltip/_example/base.jsx | 71 +++++++++++++++++++++++- src/tooltip/_example/placementStyle.ts | 75 -------------------------- 5 files changed, 103 insertions(+), 123 deletions(-) delete mode 100644 src/tooltip/_example/placementStyle.ts diff --git a/src/common/Portal.tsx b/src/common/Portal.tsx index b04db80f76..472fe410e1 100644 --- a/src/common/Portal.tsx +++ b/src/common/Portal.tsx @@ -16,20 +16,24 @@ export interface PortalProps { children: React.ReactNode; } -export function getAttach(attach: PortalProps['attach']) { +export function getAttach(attach: PortalProps['attach'], triggerNode?: HTMLElement): AttachNodeReturnValue { if (!canUseDocument) return null; - let parent: AttachNodeReturnValue; + let el: AttachNodeReturnValue; if (typeof attach === 'string') { - parent = document.querySelector(attach); + el = document.querySelector(attach); } if (typeof attach === 'function') { - parent = attach(); + el = attach(triggerNode); } if (typeof attach === 'object' && attach instanceof window.HTMLElement) { - parent = attach; + el = attach; } - return parent || document.body; + + // fix el in iframe + if (el && el.nodeType === 1) return el; + + return document.body; } const Portal = forwardRef((props: PortalProps, ref) => { @@ -44,25 +48,11 @@ const Portal = forwardRef((props: PortalProps, ref) => { }, [classPrefix]); useEffect(() => { - let parentElement = document.body; - let el = null; - - // 处理 attach - if (typeof attach === 'function') { - el = attach(triggerNode); - } else if (typeof attach === 'string') { - el = document.querySelector(attach); - } - - // fix el in iframe - if (el && el.nodeType === 1) { - parentElement = el; - } - - parentElement.appendChild(container); + const parentElement = getAttach(attach, triggerNode); + parentElement?.appendChild?.(container); return () => { - parentElement?.removeChild(container); + parentElement?.removeChild?.(container); }; }, [container, attach, triggerNode]); diff --git a/src/popup/hooks/useTrigger.tsx b/src/popup/hooks/useTrigger.tsx index 341cb51a27..9ce142fb88 100644 --- a/src/popup/hooks/useTrigger.tsx +++ b/src/popup/hooks/useTrigger.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useEffect, isValidElement } from 'react'; +import React, { useRef, useEffect, isValidElement, useCallback } from 'react'; import { isFragment } from 'react-is'; import classNames from 'classnames'; import useConfig from '../../hooks/useConfig'; @@ -147,9 +147,7 @@ export default function useTrigger({ content, disabled, trigger, visible, onVisi } // ref 透传失败时使用 dom 查找 - function getTriggerDom() { - return document.querySelector(`[data-popup="${triggerDataKey.current}"]`); - } + const getTriggerDom = useCallback(() => document.querySelector(`[data-popup="${triggerDataKey.current}"]`), []); return { getTriggerNode, diff --git a/src/select/base/Select.tsx b/src/select/base/Select.tsx index 452df85de8..1afe4744ac 100644 --- a/src/select/base/Select.tsx +++ b/src/select/base/Select.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, Ref, useMemo, useCallback, ReactElement } from 'react'; +import React, { useState, useEffect, Ref, useMemo, ReactElement } from 'react'; import classNames from 'classnames'; import isFunction from 'lodash/isFunction'; import get from 'lodash/get'; @@ -378,25 +378,23 @@ const Select = forwardRefWithStatics( ); // 将第一个选中的 option 置于列表可见范围的最后一位 - const updateScrollTop = useCallback( - (content: HTMLDivElement) => { - const firstSelectedNode: HTMLDivElement = content.querySelector(`.${classPrefix}-is-selected`); - if (firstSelectedNode && content) { - const { paddingBottom } = getComputedStyle(firstSelectedNode); - const { marginBottom } = getComputedStyle(content); - const elementBottomHeight = parseInt(paddingBottom, 10) + parseInt(marginBottom, 10); - // 小于0时不需要特殊处理,会被设为0 - const updateValue = - firstSelectedNode.offsetTop - - content.offsetTop - - (content.clientHeight - firstSelectedNode.clientHeight) + - elementBottomHeight; - // eslint-disable-next-line no-param-reassign - content.scrollTop = updateValue; - } - }, - [classPrefix], - ); + const updateScrollTop = (content: HTMLDivElement) => { + if (!content) return; + const firstSelectedNode: HTMLDivElement = content.querySelector(`.${classPrefix}-is-selected`); + if (firstSelectedNode) { + const { paddingBottom } = getComputedStyle(firstSelectedNode); + const { marginBottom } = getComputedStyle(content); + const elementBottomHeight = parseInt(paddingBottom, 10) + parseInt(marginBottom, 10); + // 小于0时不需要特殊处理,会被设为0 + const updateValue = + firstSelectedNode.offsetTop - + content.offsetTop - + (content.clientHeight - firstSelectedNode.clientHeight) + + elementBottomHeight; + // eslint-disable-next-line no-param-reassign + content.scrollTop = updateValue; + } + }; const { onMouseEnter, onMouseLeave } = props; diff --git a/src/tooltip/_example/base.jsx b/src/tooltip/_example/base.jsx index e1228035e6..a64247d4a5 100644 --- a/src/tooltip/_example/base.jsx +++ b/src/tooltip/_example/base.jsx @@ -1,6 +1,75 @@ import React from 'react'; import { Button, Tooltip } from 'tdesign-react'; -import styles from './placementStyle'; + +const styles = { + container: { + margin: '0 auto', + width: '500px', + height: '260px', + position: 'relative', + }, + placementTop: { + position: 'absolute', + top: '0', + left: '42%', + }, + placementTopLeft: { + position: 'absolute', + top: '0', + left: '70px', + }, + placementTopRight: { + position: 'absolute', + top: '0', + right: '70px', + }, + placementBottom: { + position: 'absolute', + bottom: '0', + left: '42%', + }, + placementBottomLeft: { + position: 'absolute', + bottom: '0', + left: '70px', + width: '120px', + }, + placementBottomRight: { + position: 'absolute', + bottom: '0', + right: '70px', + }, + placementLeft: { + position: 'absolute', + left: '0', + top: '42%', + }, + placementLeftTop: { + position: 'absolute', + left: '0', + top: '50px', + }, + placementLeftBottom: { + position: 'absolute', + left: '0', + bottom: '50px', + }, + placementRight: { + position: 'absolute', + right: '0', + top: '42%', + }, + placementRightTop: { + position: 'absolute', + right: '0', + top: '50px', + }, + placementRightBottom: { + position: 'absolute', + right: '0', + bottom: '50px', + }, +}; export default function Placements() { return ( diff --git a/src/tooltip/_example/placementStyle.ts b/src/tooltip/_example/placementStyle.ts deleted file mode 100644 index 4747f37912..0000000000 --- a/src/tooltip/_example/placementStyle.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { CSSProperties } from 'react'; - -interface StylesDictionary { - [Key: string]: CSSProperties; -} -const styles: StylesDictionary = { - container: { - margin: '0 auto', - width: '500px', - height: '260px', - position: 'relative', - }, - placementTop: { - position: 'absolute', - top: '0', - left: '42%', - }, - placementTopLeft: { - position: 'absolute', - top: '0', - left: '70px', - }, - placementTopRight: { - position: 'absolute', - top: '0', - right: '70px', - }, - placementBottom: { - position: 'absolute', - bottom: '0', - left: '42%', - }, - placementBottomLeft: { - position: 'absolute', - bottom: '0', - left: '70px', - width: '120px', - }, - placementBottomRight: { - position: 'absolute', - bottom: '0', - right: '70px', - }, - placementLeft: { - position: 'absolute', - left: '0', - top: '42%', - }, - placementLeftTop: { - position: 'absolute', - left: '0', - top: '50px', - }, - placementLeftBottom: { - position: 'absolute', - left: '0', - bottom: '50px', - }, - placementRight: { - position: 'absolute', - right: '0', - top: '42%', - }, - placementRightTop: { - position: 'absolute', - right: '0', - top: '50px', - }, - placementRightBottom: { - position: 'absolute', - right: '0', - bottom: '50px', - }, -}; -export default styles;