Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

[Feature Request] Allow disabling of focus trap in modal #2175

Closed
pkeuter opened this issue Dec 27, 2023 · 7 comments
Closed

[Feature Request] Allow disabling of focus trap in modal #2175

pkeuter opened this issue Dec 27, 2023 · 7 comments
Labels
🔦 Type: Feature New feature or request

Comments

@pkeuter
Copy link

pkeuter commented Dec 27, 2023

Is your feature request related to a problem? Please describe.

We are using Usersnap on our website to have users notify us of issues they have. When a modal is opened, it is not possible click into any of the usersnap input-fields. I believe this is because a focustrap is available on the Modal, because of this rule:

While open, focus is contained within the modal, preventing the user from tabbing outside.

Which is obviously a great feature, but it also would be very useful to have an escape-hatch to disable this functionality when we see that the usersnap-window is open.

Describe the solution you'd like

It would be preferred to have a disableFocusTrap-flag on the Modal or something to be able to opt-out of this functionality.

Describe alternatives you've considered

I've looked for options available in the Modal-component, but there does not seem to be a way to work around this.

Screenshots or Videos

No response

@pkeuter pkeuter added the 🔦 Type: Feature New feature or request label Dec 27, 2023
@Maol-1997
Copy link

Also in Drawer, please 😢 . It's very intrusive when embedding the page in an iframe.

@johannbuscail
Copy link

Have you found any solution for this ?

@Maol-1997
Copy link

Have you found any solution for this ?

no 😔

@ArielBenichou
Copy link

ArielBenichou commented Jul 8, 2024

I have a similar case using Spreedly fields (iframes), while those are inside the Modal we can't tab into them, but we can tab out of them back to the modal.

This feature would help use alot.


I think this is somehow related to #3204 and @wingkwong

@wingkwong
Copy link
Member

@ArielBenichou I can supplement a bit. For the issue you linked, it was just fixing the expected behaviour. By design, focus trap is applied when the modal is open, previously I applied disableFocusManagement for autocomplete component but it turned out affecting some other components so I reverted the changes.

This issue is a bit tricky because the focus trap logic comes from the RA dependencies. We can disable the focus management by passing disableFocusManagement to Overlay but it probably causes more issues, e.g. other focus behaviours of other components inside the modal.

@ArielBenichou
Copy link

@wingkwong I'm not well versed in NextUI code to argue otherwise.
But for my use case I used patch-package to add this prop to the <Modal /> component, marking it as unstable_ (React style).

I don't like to patch-package, but it was that or using a non-NextUI Modal only for one case in my app, losing consistency across my app.


Hacky Solution

If anyone needs it

  1. Install patch-package (follow Set Up).
  2. Clone NextUI locally.
  3. Patch the packages/components/modal/src/modal.tsx file with the following diff:
    diff --git a/packages/components/modal/src/modal.tsx b/packages/components/modal/src/modal.tsx
    index 323aa5308..410c32e70 100644
    --- a/packages/components/modal/src/modal.tsx
    +++ b/packages/components/modal/src/modal.tsx
    @@ -11,13 +11,14 @@ export interface ModalProps extends UseModalProps {
        * The content of the modal. Usually the ModalContent
        */
       children: ReactNode;
    +  unstable_disableFocusManagement?: boolean;
     }
     
     const Modal = forwardRef<"div", ModalProps>((props, ref) => {
    -  const {children, ...otherProps} = props;
    +  const {children, unstable_disableFocusManagement, ...otherProps} = props;
       const context = useModal({...otherProps, ref});
     
    -  const overlay = <Overlay portalContainer={context.portalContainer}>{children}</Overlay>;
    +  const overlay = <Overlay disableFocusManagement={unstable_disableFocusManagement} portalContainer={context.portalContainer}>{children}</Overlay>;
     
       return (
         <ModalProvider value={context}>
  4. Build NextUI with pnpm build.
  5. Copy packages/components/modal/dist to your node_modules/@nextui-org/modal/dist.
  6. Run npx patch-package @nextui-org/modal.
  7. Now you can add the unstable_disableFocusManagement prop to your modal.

Note: I've only taken the minimum changes from the dist and applied it to my node_modules NextUI package, making a smaller patch:

diff --git a/node_modules/@nextui-org/modal/dist/chunk-X4CB5I5S.mjs b/node_modules/@nextui-org/modal/dist/chunk-X4CB5I5S.mjs
index b6a8fb6..4ee537f 100644
--- a/node_modules/@nextui-org/modal/dist/chunk-X4CB5I5S.mjs
+++ b/node_modules/@nextui-org/modal/dist/chunk-X4CB5I5S.mjs
@@ -12,9 +12,9 @@ import { Overlay } from "@react-aria/overlays";
 import { forwardRef } from "@nextui-org/system";
 import { jsx } from "react/jsx-runtime";
 var Modal = forwardRef((props, ref) => {
-  const { children, ...otherProps } = props;
+  const { children, disableFocusManagement, ...otherProps } = props;
   const context = useModal({ ...otherProps, ref });
-  const overlay = /* @__PURE__ */ jsx(Overlay, { portalContainer: context.portalContainer, children });
+  const overlay = /* @__PURE__ */ jsx(Overlay, { disableFocusManagement: Boolean(disableFocusManagement), portalContainer: context.portalContainer, children });
   return /* @__PURE__ */ jsx(ModalProvider, { value: context, children: context.disableAnimation && context.isOpen ? overlay : /* @__PURE__ */ jsx(AnimatePresence, { children: context.isOpen ? overlay : null }) });
 });
 Modal.displayName = "NextUI.Modal";
diff --git a/node_modules/@nextui-org/modal/dist/modal.d.ts b/node_modules/@nextui-org/modal/dist/modal.d.ts
index 7943946..51887b2 100644
--- a/node_modules/@nextui-org/modal/dist/modal.d.ts
+++ b/node_modules/@nextui-org/modal/dist/modal.d.ts
@@ -9,10 +9,11 @@ import '@nextui-org/react-utils';
 import '@react-stately/overlays';

 interface ModalProps extends UseModalProps {
-    /**
-     * The content of the modal. Usually the ModalContent
-     */
-    children: ReactNode;
+  /**
+   * The content of the modal. Usually the ModalContent
+   */
+  children: ReactNode;
+  disableFocusManagement?: boolean;
 }
 declare const Modal: _nextui_org_system.InternalForwardRefRenderFunction<"div", ModalProps, never>;

diff --git a/node_modules/@nextui-org/modal/dist/modal.js b/node_modules/@nextui-org/modal/dist/modal.js
index ecf5967..769c4e8 100644
--- a/node_modules/@nextui-org/modal/dist/modal.js
+++ b/node_modules/@nextui-org/modal/dist/modal.js
@@ -162,9 +164,9 @@ var [ModalProvider, useModalContext] = (0, import_react_utils2.createContext)({
 // src/modal.tsx
 var import_jsx_runtime = require("react/jsx-runtime");
 var Modal = (0, import_system2.forwardRef)((props, ref) => {
-  const { children, ...otherProps } = props;
+  const { children, disableFocusManagement, ...otherProps } = props;
   const context = useModal({ ...otherProps, ref });
-  const overlay = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_overlays2.Overlay, { portalContainer: context.portalContainer, children });
+  const overlay = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_overlays2.Overlay, { disableFocusManagement: Boolean(disableFocusManagement), portalContainer: context.portalContainer, children });
   return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalProvider, { value: context, children: context.disableAnimation && context.isOpen ? overlay : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_framer_motion.AnimatePresence, { children: context.isOpen ? overlay : null }) });
 });
 Modal.displayName = "NextUI.Modal";

@u3u
Copy link
Contributor

u3u commented Sep 13, 2024

Is there a way to globally disable disableFocusManagement? For example, it can be configured in NextUIProvider. I put an input box in the Popover, and once it gains focus, it can never lose focus. Also, when the Popover is not closed, no elements on the page can be clicked (need to click twice, only after the Popover is closed can the click event be triggered or focus gained). This has caused me a lot of trouble in using Popover!

@heroui-inc heroui-inc locked and limited conversation to collaborators Jan 28, 2025
@wingkwong wingkwong converted this issue into discussion #4679 Jan 28, 2025

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
🔦 Type: Feature New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants