nextui icon indicating copy to clipboard operation
nextui copied to clipboard

[BUG] - Modal does not adjust position when keyboard appears on iOS

Open jalvarezz13 opened this issue 10 months ago • 30 comments

NextUI Version

2.3.5

Describe the bug

After updating from version 2.2.X to 2.3.X, there is an issue observed on iOS devices where the modal dialog does not adjust its position when the keyboard appears on the screen. As a result, the modal remains in its original position, obscuring the view of the input fields and preventing users from seeing what they are typing. This behavior affects the usability of the application, especially in forms and dialogs requiring user input.

Your Example Website or App

No response

Steps to Reproduce the Bug or Issue

  1. Create a webpage that includes a modal dialog.
  2. Within the modal, include a form with several input fields.
  3. Open the webpage on an iOS device using Safari.
  4. Tap on the input field within the modal to bring up the iOS keyboard.

Notice that when the keyboard appears, the modal remains in its fixed position at the bottom of the screen. The keyboard covers the modal, obscuring the input field and preventing the user from seeing what is being typed.

Expected behavior

The modal should adjust its position when the keyboard appears, moving up to ensure that the input field is visible and the user can see what they are typing.

Screenshots or Videos

No response

Operating System Version

iOS 17.4.1

Browser

Safari

jalvarezz13 avatar Apr 22 '24 06:04 jalvarezz13

@jalvarezz13 can you attach a screenshot and some code? Also can you share how it looked in 2.2.x?

wingkwong avatar Apr 22 '24 07:04 wingkwong

The code could be this modal https://nextui.org/docs/components/modal#with-form without "top-center" placement. I attach a simple demo here:

https://github.com/nextui-org/nextui/assets/64201633/49faa35e-df48-4239-8329-9544d008db1d

jalvarezz13 avatar Apr 22 '24 08:04 jalvarezz13

Facing the same issue after bumping to 2.3.5. Seems like a regression, everything worked just fine before update.

vtsybulin avatar Apr 26 '24 20:04 vtsybulin

Im facing the same issue

daniel-esserweb avatar May 02 '24 16:05 daniel-esserweb

Can also reproduce

mashnoon33 avatar May 06 '24 03:05 mashnoon33

Looks like it's somehow related to the internal implementation of the scroll-blocking behavior within Modal shouldBlockScroll. Unfortunately, I'm short on time to dig into it more, what I ended up doing was disabling shouldBlockScroll completely, and relying on custom implementation, using body-scroll-lock, which is fine for my case, since it's already used in the project. Tho I had to rely on useEffect to make it work, which is unfortunate and I had to mimic <ModalBody /> classnames explicitly as I needed to get a scrollable modal content ref.

const ModalTemplate = <ContentWrapperProps extends TContentWrapperProps, ModalFooterProps extends TModalFooterProps>({ title, children, ModalContentWrapper = Fragment, modalContentWrapperProps, ModalFooter, getModalFooterProps, isLoading, ...modalProps }: IModalTemplateProps<ContentWrapperProps, ModalFooterProps>): ReactElement => {
  const scrollableTargetRef = useRef<HTMLDivElement>(null);

  useEffect(
    (): void => {
      if (modalProps.isOpen && scrollableTargetRef.current) {
        disableBodyScroll(scrollableTargetRef.current);

        return;
      }

      clearAllBodyScrollLocks();
    },
    [modalProps.isOpen],
  );

  return (
    <Modal
      size="2xl"
      backdrop="blur"
      scrollBehavior="inside"
      hideCloseButton
      shouldBlockScroll={false}
      classNames={{
        backdrop: 'top-0 left-0',
      }}
      {...modalProps}
    >
      <ModalContent>
        {(close: () => void): ReactElement => (
          <ModalContentWrapper
            {...modalContentWrapperProps as ContentWrapperProps}
            className="[display:contents]"
          >

            <ModalHeader className="flex flex-col gap-1">
              <div className="flex w-full items-center justify-between gap-2">
                {title}

                <Button
                  variant="light"
                  isIconOnly
                  onPress={close}
                >
                  <CloseIcon />
                </Button>
              </div>
            </ModalHeader>

            /** Explicit <ModalBody /> implementation. class names list was taken from nextui.Modal.ModalBody as is **/
            <div
              className="flex flex-1 flex-col gap-3 overflow-y-auto px-6 py-2"
              ref={scrollableTargetRef}
            >
              {children}
            </div>

            {ModalFooter && (
              <ModalFooterBase>
                <ModalFooter
                  {...(typeof getModalFooterProps === 'function' && getModalFooterProps({
                    close,
                    isLoading,
                  })) as ModalFooterProps}
                />
              </ModalFooterBase>
            )}
          </ModalContentWrapper>
        )}
      </ModalContent>
    </Modal>
  );
};

export default ModalTemplate;


vtsybulin avatar May 06 '24 07:05 vtsybulin

same issue with 2.3.6
2.2.10 is working fine

HastaCs avatar May 06 '24 13:05 HastaCs

Any update? Thanks!

jalvarezz13 avatar May 13 '24 14:05 jalvarezz13

In case it helps... I'm temporarily using @nextui-org/modal in version 2.0.29 until this bug is fixed.

jalvarezz13 avatar May 15 '24 15:05 jalvarezz13

@jrgarciadev The error persists in version 2.4.0

jalvarezz13 avatar Jun 03 '24 17:06 jalvarezz13

same issue is here https://github.com/nextui-org/nextui/issues/3241

#3241 anybody know how to fix this issue?

dharmveer97 avatar Jun 12 '24 18:06 dharmveer97

i'm having the same problem, does anyone have an idea on how to fix it?

0xAskar avatar Jun 14 '24 03:06 0xAskar

i'm having the same problem, does anyone have an idea on how to fix it?

Just use the version of the modal from further up where it works. Install just the individual modal package version above and import the modal from that package instead of the central package

theonlyway avatar Jun 14 '24 11:06 theonlyway

Same issue with version 2.0.36 of modal.

kamami avatar Jun 17 '24 21:06 kamami

@kamami If you want to fix this issue, you can install the individual modal box using version 2.0.0. It is a little bit buggy too, but the keyboard issue does not occur in this version. However, in other versions, the keyboard hides the inputs in all versions.

https://www.npmjs.com/package/@nextui-org/modal

dharmveer97 avatar Jun 18 '24 11:06 dharmveer97

@theonlyway thanks! I also just kept the latest version and on mobile screens i adjusted the absolute position as others suggested here.

0xAskar avatar Jun 19 '24 19:06 0xAskar

same issue here, installing @nextui-org/[email protected] works for now

erodriguezvesti avatar Jun 21 '24 13:06 erodriguezvesti

Oof. 2.0.0 fixes this, but scrolling is broken even with scrollBehavior outside if the modal is too big... ruh roh.

EDIT: For scrollBehavior: outside to work you also need placement set to top.... hmm

daveycodez avatar Jun 22 '24 19:06 daveycodez

TEMP SOLUTION

'use client';

import React, { useEffect, useRef } from 'react';
import {
  Button,
  useDisclosure,
  Modal,
  ModalContent,
  ModalBody,
} from '@nextui-org/react';

import CreateLotForm from '../../components/forms/CreateLotForm';

const AddLotModal = () => {
  const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure();
  const modalBodyRef = useRef(null);

  const handleOpen = () => {
    onOpen();
  };

  useEffect(() => {
    if (modalBodyRef.current) {
      const inputs = modalBodyRef.current.querySelectorAll(
        'input, textarea, select',
      );
      inputs.forEach((input) => {
        input.addEventListener('focus', () => {
          setTimeout(() => {
            input.scrollIntoView({ behavior: 'smooth', block: 'center' });
          }, 300);
        });
      });
    }
  }, [isOpen]);

  return (
    <div>
      <div className="flex flex-wrap gap-3">
        <Button
          variant="flat"
          color="warning"
          onClick={handleOpen}
          className="capitalize"
        >
          Add New
        </Button>
      </div>

      <Modal
        isOpen={isOpen}
        onOpenChange={onOpenChange}
        isDismissable={false}
        scrollBehavior="outside"
        size="5xl"
      >
        <ModalContent>
          {() => (
            <>
              {/* empty div for more space or height */}
              <div className="pt-12 block sm:hidden" />
              <ModalBody ref={modalBodyRef}>
                <CreateLotForm
                  enableReinitialize
                  close={onClose}
                  onSubmit={() => {}}
                />
                {/* empty div for more space or height */}
                <div className="pb-44 block sm:hidden" />
              </ModalBody>
            </>
          )}
        </ModalContent>
      </Modal>
    </div>
  );
};

export default AddLotModal;

dharmveer97 avatar Jun 24 '24 13:06 dharmveer97

any update? :(

HastaCs avatar Jul 19 '24 10:07 HastaCs

+1

ArielBenichou avatar Jul 21 '24 13:07 ArielBenichou

any fixes, updates?

n-lavrenko avatar Jul 30 '24 12:07 n-lavrenko

It's been almost 4 months since the bug appeared, and no one from the NextUI organization seems to have addressed it in this thread.

For those who cannot do the same, I recommend installing the NextUI modal component in isolation from the rest. Also, remember to install the following dependencies to avoid potential issues:

  • @nextui-org/modal: ^2.0.29
  • @nextui-org/system: ^2.1.0
  • @nextui-org/theme: ^2.0.0

You can install them with: yarn add @nextui-org/modal@^2.0.29 @nextui-org/system@^2.1.0 @nextui-org/theme@^2.0.0

jalvarezz13 avatar Aug 05 '24 13:08 jalvarezz13

I found a solution with css, tested it and it seems to work reasonably, hope it helps too.

BaseModal.tsx

import { Modal } from "@nextui-org/modal";

type BaseModalProps = {} & React.ComponentProps<typeof Modal>;
export function BaseModal({ children, backdrop = "opaque", ...props }: BaseModalProps) {
  return (
    <Modal
      backdrop={backdrop}
      classNames={{
        wrapper: "pb-[65%] landscape:pb-[50vh] landscape:xl:pb-0",
      }}
      isDismissable={false}
      placement="top"
      scrollBehavior="outside"
      {...props}
    >
      {children}
    </Modal>
  );
}

tailwind.config.ts Add to your theme extension

portrait: {
  raw: "(orientation: portrait)",
},
landscape: {
  raw: "(orientation: landscape)",
},

Usage example

<BaseModal isOpen={isOpen} onOpenChange={onOpenChange}>
  <ModalContent>
    {(onClose) => (
      <>
        <ModalHeader className="flex flex-col gap-1">Title</ModalHeader>
        <ModalBody>
          <span>Your modal body</span>
        </ModalBody>
        <ModalFooter>
          <Button color="danger" variant="light" onPress={onClose}>
            Close
          </Button>
          <Button color="primary" type="submit">
            Save
          </Button>
        </ModalFooter>
      </>
    )}
  </ModalContent>
</BaseModal>

cristyz avatar Aug 15 '24 12:08 cristyz

I had to use “top-center” placement as a temporary solution since the modal does not behave consistently on mobile screens. We are really looking forward to fixing this issue.

ETOPS7 avatar Aug 18 '24 06:08 ETOPS7

@wingkwong I see you have self-assigned this issue, are you working on a specific branch you can share? Thanks

jalvarezz13 avatar Aug 19 '24 16:08 jalvarezz13

@cristyz Thank you for sharing the temporal solution, it's not working for my specific use case but it's a good solution for other cases :)

jalvarezz13 avatar Aug 19 '24 16:08 jalvarezz13

I have managed to avoid this by using an external library, which is not ideal, but will do for now.

https://www.npmjs.com/package/use-detect-keyboard-open

const isKeyboardOpen = useDetectKeyboardOpen();
const placement = isKeyboardOpen ? "top" : "auto"

return <MyCustomModal  isOpen={isOpen} placement={placement} onClose={onClose}/>

Vorashil avatar Aug 22 '24 21:08 Vorashil

Any update or release plz?

hoanmq avatar Aug 28 '24 11:08 hoanmq