diff --git a/docs/demo/with-portal.md b/docs/demo/with-portal.md
new file mode 100644
index 00000000..652b8f1f
--- /dev/null
+++ b/docs/demo/with-portal.md
@@ -0,0 +1,8 @@
+---
+title: with-portal
+nav:
+ title: Demo
+ path: /demo
+---
+
+
\ No newline at end of file
diff --git a/docs/examples/with-portal.tsx b/docs/examples/with-portal.tsx
new file mode 100644
index 00000000..39c61cde
--- /dev/null
+++ b/docs/examples/with-portal.tsx
@@ -0,0 +1,53 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Dialog from '@rc-component/dialog';
+
+const DivPortal: React.FC = () => {
+ return ReactDOM.createPortal(
+
+
+
,
+ document.body
+ );
+};
+
+const MyControl: React.FC = () => {
+ const [visible, setVisible] = React.useState(false);
+
+ const onClick = () => {
+ setVisible(true);
+ };
+
+ const onClose = () => {
+ setVisible(false);
+ };
+
+ return (
+
+ );
+};
+
+export default MyControl;
diff --git a/package.json b/package.json
index a26ecbdc..77eda6db 100644
--- a/package.json
+++ b/package.json
@@ -51,7 +51,7 @@
"dependencies": {
"@rc-component/motion": "^1.1.3",
"@rc-component/portal": "^2.1.0",
- "@rc-component/util": "^1.7.0",
+ "@rc-component/util": "^1.9.0",
"clsx": "^2.1.1"
},
"devDependencies": {
@@ -87,4 +87,4 @@
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
-}
+}
\ No newline at end of file
diff --git a/src/Dialog/Content/Panel.tsx b/src/Dialog/Content/Panel.tsx
index 9e1185b7..d8caf667 100644
--- a/src/Dialog/Content/Panel.tsx
+++ b/src/Dialog/Content/Panel.tsx
@@ -54,7 +54,10 @@ const Panel = React.forwardRef((props, ref) => {
const internalRef = useRef(null);
const mergedRef = useComposeRef(holderRef, panelRef, internalRef);
- useLockFocus(visible && isFixedPos && focusTrap !== false, () => internalRef.current);
+ const [ignoreElement] = useLockFocus(
+ visible && isFixedPos && focusTrap !== false,
+ () => internalRef.current,
+ );
React.useImperativeHandle(ref, () => ({
focus: () => {
@@ -152,6 +155,9 @@ const Panel = React.forwardRef((props, ref) => {
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
tabIndex={-1}
+ onFocus={(e) => {
+ ignoreElement(e.target);
+ }}
>
{modalRender ? modalRender(content) : content}
diff --git a/tests/focus.spec.tsx b/tests/focus.spec.tsx
index 4f8dc44b..9e2449de 100644
--- a/tests/focus.spec.tsx
+++ b/tests/focus.spec.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react/no-render-return-value, max-classes-per-file, func-names, no-console */
import React from 'react';
+import ReactDOM from 'react-dom';
import { act, render } from '@testing-library/react';
import Dialog from '../src';
@@ -9,7 +10,12 @@ jest.mock('@rc-component/util/lib/Dom/focus', () => {
const useLockFocus = (visible: boolean, ...rest: any[]) => {
globalThis.__useLockFocusVisible = visible;
- return actual.useLockFocus(visible, ...rest);
+ const hooks = actual.useLockFocus(visible, ...rest);
+ const proxyIgnoreElement = (ele: HTMLElement) => {
+ globalThis.__ignoredElement = ele;
+ hooks[0](ele);
+ };
+ return [proxyIgnoreElement, ...hooks.slice(1)] as ReturnType;
};
return {
@@ -82,4 +88,23 @@ describe('Dialog.Focus', () => {
expect(globalThis.__useLockFocusVisible).toBe(false);
});
+
+ it('should call ignoreElement when input in portal is focused', () => {
+ render(
+ ,
+ );
+
+ act(() => {
+ jest.runAllTimers();
+ });
+
+ const input = document.getElementById('portal-input') as HTMLElement;
+ act(() => {
+ input.focus();
+ });
+
+ expect(globalThis.__ignoredElement).toBe(input);
+ });
});