diff --git a/docs/examples/multiple-Portal.tsx b/docs/examples/multiple-Portal.tsx index 3ae1c6ea..91246734 100644 --- a/docs/examples/multiple-Portal.tsx +++ b/docs/examples/multiple-Portal.tsx @@ -45,6 +45,17 @@ const Demo: React.FC = () => { + {dialog} {drawer} diff --git a/package.json b/package.json index 35225444..32cba92b 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "lint:tsc": "tsc -p tsconfig.json --noEmit", "now-build": "npm run docs:build", "prepare": "husky install", - "prepublishOnly": "npm run compile && np --yolo --no-publish", + "prepublishOnly": "npm run compile && rc-np", "prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"", "start": "dumi dev", "test": "rc-test" @@ -49,14 +49,14 @@ ] }, "dependencies": { - "@babel/runtime": "^7.10.1", "@rc-component/portal": "^1.0.0-8", "@rc-component/util": "^1.0.1", "classnames": "^2.2.6", - "rc-motion": "^2.3.0" + "@rc-component/motion": "^1.1.3" }, "devDependencies": { - "@rc-component/father-plugin": "^2.0.1", + "@rc-component/father-plugin": "^2.0.2", + "@rc-component/np": "^1.0.3", "@testing-library/jest-dom": "^6.1.6", "@testing-library/react": "^13.0.0", "@types/jest": "^29.4.0", @@ -77,7 +77,6 @@ "husky": "^8.0.3", "less": "^4.1.3", "lint-staged": "^15.2.0", - "np": "^10.0.5", "prettier": "^3.2.1", "rc-drawer": "^7.0.0", "rc-select": "^14.11.0", diff --git a/src/Dialog/Content/Panel.tsx b/src/Dialog/Content/Panel.tsx index 8e2cfb1f..2439fe7f 100644 --- a/src/Dialog/Content/Panel.tsx +++ b/src/Dialog/Content/Panel.tsx @@ -17,12 +17,12 @@ export interface PanelProps extends Omit { holderRef?: React.Ref; } -export type ContentRef = { +export type PanelRef = { focus: () => void; changeActive: (next: boolean) => void; }; -const Panel = React.forwardRef((props, ref) => { +const Panel = React.forwardRef((props, ref) => { const { prefixCls, className, diff --git a/src/Dialog/Content/index.tsx b/src/Dialog/Content/index.tsx index af966a6e..4985a194 100644 --- a/src/Dialog/Content/index.tsx +++ b/src/Dialog/Content/index.tsx @@ -1,10 +1,15 @@ import * as React from 'react'; import { useRef } from 'react'; import classNames from 'classnames'; -import CSSMotion from 'rc-motion'; +import CSSMotion from '@rc-component/motion'; import { offset } from '../../util'; -import type { PanelProps, ContentRef } from './Panel'; +import type { PanelProps, PanelRef } from './Panel'; import Panel from './Panel'; +import type { CSSMotionRef } from '@rc-component/motion/es/CSSMotion'; + +export type CSSMotionStateRef = Pick; + +export type ContentRef = PanelRef & CSSMotionStateRef; export type ContentProps = { motionName: string; @@ -27,7 +32,20 @@ const Content = React.forwardRef((props, ref) => { mousePosition, } = props; - const dialogRef = useRef(); + const dialogRef = useRef< + { + nativeElement: HTMLElement; + } & CSSMotionStateRef + >(); + + const panelRef = useRef(); + + // ============================== Refs ============================== + React.useImperativeHandle(ref, () => ({ + ...panelRef.current, + inMotion: dialogRef.current.inMotion, + enableMotion: dialogRef.current.enableMotion, + })); // ============================= Style ============================== const [transformOrigin, setTransformOrigin] = React.useState(); @@ -38,7 +56,7 @@ const Content = React.forwardRef((props, ref) => { } function onPrepare() { - const elementOffset = offset(dialogRef.current); + const elementOffset = offset(dialogRef.current.nativeElement); setTransformOrigin( mousePosition && (mousePosition.x || mousePosition.y) @@ -62,7 +80,7 @@ const Content = React.forwardRef((props, ref) => { {({ className: motionClassName, style: motionStyle }, motionRef) => ( = (props) => { } // ========================= Events ========================= + // Close action will trigger by: + // 1. When hide motion end + // 2. Controlled `open` to `false` immediately after set to `true` which will not trigger motion + function doClose() { + // Clean up scroll bar & focus back + setAnimatedVisible(false); + + if (mask && lastOutSideActiveElementRef.current && focusTriggerAfterClose) { + try { + lastOutSideActiveElementRef.current.focus({ preventScroll: true }); + } catch (e) { + // Do nothing + } + lastOutSideActiveElementRef.current = null; + } + + // Trigger afterClose only when change visible from true to false + if (animatedVisible) { + afterClose?.(); + } + } + function onDialogVisibleChanged(newVisible: boolean) { // Try to focus if (newVisible) { focusDialogContent(); } else { - // Clean up scroll bar & focus back - setAnimatedVisible(false); - - if (mask && lastOutSideActiveElementRef.current && focusTriggerAfterClose) { - try { - lastOutSideActiveElementRef.current.focus({ preventScroll: true }); - } catch (e) { - // Do nothing - } - lastOutSideActiveElementRef.current = null; - } - - // Trigger afterClose only when change visible from true to false - if (animatedVisible) { - afterClose?.(); - } + doClose(); } afterOpenChange?.(newVisible); } @@ -154,6 +160,12 @@ const Dialog: React.FC = (props) => { if (visible) { setAnimatedVisible(true); saveLastOutSideActiveElementRef(); + } else if ( + animatedVisible && + contentRef.current.enableMotion() && + !contentRef.current.inMotion() + ) { + doClose(); } }, [visible]); diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index f160d3ac..e54f0def 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -1,14 +1,14 @@ /* eslint-disable react/no-render-return-value, max-classes-per-file, func-names, no-console */ import { fireEvent, render, act } from '@testing-library/react'; -import { Provider } from 'rc-motion'; +import { Provider } from '@rc-component/motion'; import KeyCode from '@rc-component/util/lib/KeyCode'; import React, { cloneElement, useEffect } from 'react'; import type { DialogProps } from '../src'; import Dialog from '../src'; -jest.mock('rc-motion', () => { +jest.mock('@rc-component/motion', () => { const OriReact = jest.requireActual('react'); - const origin = jest.requireActual('rc-motion'); + const origin = jest.requireActual('@rc-component/motion'); const OriCSSMotion = origin.default; const ProxyCSSMotion = OriReact.forwardRef((props: any, ref: any) => { @@ -515,10 +515,14 @@ describe('dialog', () => { const afterClose = jest.fn(); const { rerender } = render(); - jest.runAllTimers(); + act(() => { + jest.runAllTimers(); + }); rerender(); - jest.runAllTimers(); + act(() => { + jest.runAllTimers(); + }); expect(afterClose).toHaveBeenCalledTimes(1); }); diff --git a/tests/ref.spec.tsx b/tests/ref.spec.tsx index d8e37e8c..368846f6 100644 --- a/tests/ref.spec.tsx +++ b/tests/ref.spec.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/no-render-return-value, max-classes-per-file, func-names, no-console */ import { render } from '@testing-library/react'; -import { Provider } from 'rc-motion'; +import { Provider } from '@rc-component/motion'; import React from 'react'; import Dialog from '../src';