How to prevent interactions with the main content when a modal is opened in React using Mantine?

39 Views Asked by At

I'm currently working on a project where I'm using Mantine to handle modals in my React application. However, I'm facing an issue where interactions with the main content are still possible when a modal is opened.

Specifically, I want to prevent any interactions with the main content when a modal is opened. This includes blocking scrolling and disabling mouse clicks and keyboard inputs.

I've tried various approaches, including setting overflow: hidden and pointer-events: none on the main content when the modal is opened. While this partially solves the issue by preventing scrolling and mouse clicks, it still allows keyboard inputs to interact with elements behind the modal.

import { Button, Divider, Flex, Group, Modal, TextInput, Box, Text, Image } from "@mantine/core";
import { SaveButton } from "@refinedev/mantine";

import { useDisclosure } from "@mantine/hooks";
import { useState } from "react";
import { useTask } from "hooks/game/useTasks";
import { useEffect } from "react";

import { IOptions, ITaskItem as ITask } from "context/GameContext";

// Component to render a horizontal line with an optional label
export const Line = (props: any) => <Divider w="500px" my="xs" label={`${props.title}`} labelPosition="center" />;

// Component to render an input field with specific props
export const Input = ({ field, handler }: any) => {
  return <TextInput mb={10} w={500} ta="center" variant="filled" value={field} onChange={handler} />;
};

const EditTaskModal = ({ ...task }: ITask) => {
  // Define a state variable to track whether a component is opened or closed, along with functions to open and close it
  const [opened, { open, close }] = useDisclosure(false);

  // Define state variables for task's fields, initializing them with values from the task object
  const [title, setTitle] = useState(task.title);
  const [subtitle, setSubtitle] = useState(task.subtitle);
  const [texts, setTexts]: any = useState(task.texts);
  const [options, setOptions]: any = useState(task.options);

  // Get the update function from a custom hook called useTask
  const { update } = useTask();

  // useEffect hook to update state variables when the 'opened' state changes
  useEffect(() => {
    // If the component is closed, reset the state variables to their original values from the task object
    if (!opened) {
      setTitle(task.title);
      setSubtitle(task.subtitle);
      setTexts(task.texts);
      setOptions(task.options);
    }
  }, [opened]);

  // Function to handle form submission
  const handlder = async (event: any) => {
    event.preventDefault();

    // Initialize a new FormData object to collect form data
    const materials: any = new FormData();

    title && materials.append("title", title);
    subtitle && materials.append("subtitle", subtitle);

    // If texts array exists and is not empty, append each text to form data
    texts &&
      texts.length !== 0 &&
      texts.forEach((text: string, index: number) => {
        materials.append(`texts[${index}]`, text);
      });

    // If options array exists and is not empty, append each option to form data
    options &&
      options.length !== 0 &&
      options.forEach((option: any, index: number) => {
        materials.append(`options[${index}]`, option);
      });

    for (const [key, value] of materials.entries()) {
      console.log(key + ": " + value);
    }

    console.log(materials);

    // Send form data to update function
    await update(materials);
  };

  // Function to handle changes in form fields for OnChange property:
  const Fields = (index: any, state: any, setState: any) => (event: any) => {
    // Create a copy of the state array to avoid directly mutating the state
    let fields = [...state];
    // Update the value at the specified index with the new value from the event target
    fields[index] = event.target.value;
    // Update the state with the modified fields array
    setState(fields);
  };

  return (
    <>
      <Modal
        size="xl"
        radius="md"
        opened={opened}
        onClose={close}
        centered
        shadow="sm"
        overlayProps={{
          backgroundOpacity: 0.4,
          blur: 2
        }}
        closeOnClickOutside={false}
      >
        <Flex justify="center" align="center" direction="column" wrap="nowrap">
          <form onSubmit={handlder}>
            {task.title && (
              <>
                <Line title="Title" />
                <TextInput
                  w={500}
                  ta="center"
                  variant="filled"
                  value={title}
                  onChange={(event) => setTitle(event.currentTarget.value)}
                />
              </>
            )}

            {task.subtitle && (
              <>
                <Line title="Subtitle" />
                <TextInput
                  w={500}
                  ta="center"
                  variant="filled"
                  value={subtitle}
                  onChange={(event) => setSubtitle(event.currentTarget.value)}
                />
              </>
            )}

            {task.texts && task.texts.length !== 0 && (
              <>
                <Line title="Content" />
                {texts.map((text: string, index: number) => {
                  return <Input field={text} handler={Fields(index, texts, setTexts)} />;
                })}
              </>
            )}

            {task.options && (
              <>
                <Line title="Content" />
                {options.map((option: IOptions, index: number) => {
                  return (
                    <Box>
                      {option.emojy_index ? (
                        <Box style={{ display: "flex", alignItems: "center" }}>
                          <Text p={0}>{option.emojy_index}</Text>
                          <Image p={0} w={"48px"} src={`../emoji-${option.emojy_index}.png`} />
                        </Box>
                      ) : (
                        <>
                          {option.icon && <Image w={"24px"} src={option.icon} />}
                          <Input field={option.text} handler={Fields(index, options, setOptions)} />
                        </>
                      )}
                    </Box>
                  );
                })}
              </>
            )}

            <Group justify="center" mt="md">
              <SaveButton type="submit" />
            </Group>
          </form>
        </Flex>
      </Modal>
      <Button mt={10} variant="gradient" gradient={{ from: "violet", to: "blue", deg: 177 }} onClick={open}>
        Open update modal
      </Button>
    </>
  );
};

export default EditTaskModal;

0

There are 0 best solutions below