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;