Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
21 changes: 14 additions & 7 deletions packages/components/common/Portal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { forwardRef, useMemo, useImperativeHandle } from 'react';
import React, { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { AttachNode, AttachNodeReturnValue } from '../common';
import { canUseDocument } from '../_util/dom';
Expand Down Expand Up @@ -40,26 +40,33 @@ export function getAttach(attach: PortalProps['attach'], triggerNode?: HTMLEleme
const Portal = forwardRef((props: PortalProps, ref) => {
const { attach, children, triggerNode } = props;
const { classPrefix } = useConfig();

const container = useMemo(() => {
const [container] = useState(() => {
if (!canUseDocument) return null;
const el = document.createElement('div');
el.className = `${classPrefix}-portal-wrapper`;
return el;
}, [classPrefix]);
});
const [mounted, setMounted] = useState(false);

useIsomorphicLayoutEffect(() => {
if (!mounted) return;

const parentElement = getAttach(attach, triggerNode);
parentElement?.appendChild?.(container);

return () => {
parentElement?.removeChild?.(container);
};
}, [container, attach, triggerNode]);
}, [container, attach, triggerNode, mounted]);

useEffect(() => {
if (!mounted) {
setMounted(true);
}
}, [mounted]);

useImperativeHandle(ref, () => container);

return canUseDocument ? createPortal(children, container) : null;
return canUseDocument && mounted ? createPortal(children, container) : null;
});

Portal.displayName = 'Portal';
Expand Down
65 changes: 53 additions & 12 deletions packages/components/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { forwardRef, useEffect, useRef, useImperativeHandle } from 'react';
import React, { forwardRef, useEffect, useRef, useImperativeHandle, useState } from 'react';
import { CSSTransition } from 'react-transition-group';
import classNames from 'classnames';
import log from '@tdesign/common-js/log/index';
Expand All @@ -13,11 +13,33 @@ import { dialogDefaultProps } from './defaultProps';
import DialogCard from './DialogCard';
import useDialogEsc from './hooks/useDialogEsc';
import useLockStyle from './hooks/useLockStyle';
import useDialogPosition from './hooks/useDialogPosition';
import useDialogDrag from './hooks/useDialogDrag';
import { parseValueToPx } from './utils';
import useDefaultProps from '../hooks/useDefaultProps';
import useAttach from '../hooks/useAttach';
import { canUseDocument } from '../_util/dom';

type MousePosition = { x: number; y: number } | null;

let mousePosition: MousePosition;

const getClickPosition = (e: MouseEvent) => {
mousePosition = {
x: e.pageX,
y: e.pageY,
};
// 100ms 内发生过点击事件,则从点击位置动画展示
// 否则直接 zoom 展示
// 这样可以兼容非点击方式展开
setTimeout(() => {
mousePosition = null;
}, 100);
};

// 只有点击事件支持从鼠标位置动画展开
if (canUseDocument) {
document.documentElement.addEventListener('click', getClickPosition, true);
}

export interface DialogProps extends TdDialogProps, StyledProps {
isPlugin?: boolean; // 是否以插件形式调用
Expand Down Expand Up @@ -72,10 +94,12 @@ const Dialog = forwardRef<DialogInstance, DialogProps>((originalProps, ref) => {
} = state;

const dialogAttach = useAttach('dialog', attach);
const [animationVisible, setAnimationVisible] = useState(visible);
const [DialogAnimationVisible, setDialogAnimationVisible] = useState(visible);

useLockStyle({ preventScrollThrough, visible, mode, showInAttachedElement });
useDialogEsc(visible, wrapRef);
useDialogPosition(visible, dialogCardRef);

const { onDialogMoveStart } = useDialogDrag({
dialogCardRef,
contentClickRef,
Expand All @@ -90,6 +114,17 @@ const Dialog = forwardRef<DialogInstance, DialogProps>((originalProps, ref) => {
setState((prevState) => ({ ...prevState, ...props }));
}, [props, setState, isPlugin]);

useEffect(() => {
if (DialogAnimationVisible) {
if (mousePosition && dialogCardRef.current) {
const offsetX = mousePosition.x - dialogCardRef.current.offsetLeft;
const offsetY = mousePosition.y - dialogCardRef.current.offsetTop;

dialogCardRef.current.style.transformOrigin = `${offsetX}px ${offsetY}px`;
}
}
}, [DialogAnimationVisible]);

useImperativeHandle(ref, () => ({
show() {
setState({ visible: true });
Expand Down Expand Up @@ -151,25 +186,30 @@ const Dialog = forwardRef<DialogInstance, DialogProps>((originalProps, ref) => {
}
};

const onAnimateLeave = () => {
onClosed?.();

// Portal Animation
const onAnimateStart = () => {
onBeforeOpen?.();
setAnimationVisible(true);
if (!wrapRef.current) return;
wrapRef.current.style.display = 'none';
wrapRef.current.style.display = 'block';
};

const onAnimateStart = () => {
const onAnimateLeave = () => {
onClosed?.();
setAnimationVisible(false);
if (!wrapRef.current) return;
onBeforeOpen?.();
wrapRef.current.style.display = 'block';
wrapRef.current.style.display = 'none';
};

// Dialog Animation
const onInnerAnimateStart = () => {
setDialogAnimationVisible(true);
if (!dialogCardRef.current) return;
dialogCardRef.current.style.display = 'block';
};

const onInnerAnimateLeave = () => {
setDialogAnimationVisible(false);
if (!dialogCardRef.current) return;
dialogCardRef.current.style.display = 'none';
};
Expand All @@ -191,6 +231,7 @@ const Dialog = forwardRef<DialogInstance, DialogProps>((originalProps, ref) => {
</CSSTransition>
) : null;
};

return (
<CSSTransition
in={visible}
Expand All @@ -201,7 +242,7 @@ const Dialog = forwardRef<DialogInstance, DialogProps>((originalProps, ref) => {
nodeRef={portalRef}
onEnter={onAnimateStart}
onEntered={onOpened}
onExit={() => onBeforeClose?.()}
onExit={onBeforeClose}
onExited={onAnimateLeave}
>
<Portal attach={dialogAttach} ref={portalRef}>
Expand All @@ -211,7 +252,7 @@ const Dialog = forwardRef<DialogInstance, DialogProps>((originalProps, ref) => {
[`${componentCls}__ctx--fixed`]: !showInAttachedElement,
[`${componentCls}__ctx--absolute`]: showInAttachedElement,
})}
style={{ zIndex, display: 'none' }}
style={{ zIndex, display: animationVisible ? null : 'none' }}
onKeyDown={handleKeyDown}
tabIndex={0}
>
Expand Down
1 change: 0 additions & 1 deletion packages/components/textarea/Textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ const Textarea = forwardRef<TextareaRefInterface, TextareaProps>((originalProps,
}, [value, autosize, adjustTextareaHeight]);

useEffect(() => {
handleAutoFocus();
adjustTextareaHeight();
}, [handleAutoFocus, adjustTextareaHeight]);

Expand Down
Loading