Модальное/диалоговое окно всплывающее из кнопки/элемента
Есть задача, где необходимо показать модальное окно, будто всплывающее из кнопки (в конечном результате отображается, по центру экрана), есть ли какие-то готовые реализации данного функционала в MUI или сторонние решение и если нет, то как подобное можно реализовать?
Подойдет анимация появления/закрытия как в MacOS:

Ответы (2 шт):
В MUI есть компонент Modal, который может использоваться для создания модальных окон. Однако, он не имеет встроенной анимации появления/закрытия.
Вы можете использовать сторонние библиотеки анимаций, такие как react-spring или framer-motion, чтобы добавить анимацию к вашему модальному окну.
Если вы хотите реализовать анимацию появления/закрытия как в MacOS, вы можете использовать анимацию "scale" и "opacity". Например, для появления модального окна вы можете использовать следующую анимацию:
- Установите начальное значение для модального окна:
transform: "scale(0.8)",
opacity: 0
- Создайте анимацию для появления модального окна:
transform: "scale(1)",
opacity: 1,
config: { tension: 300, friction: 20 }
- Примените анимацию к модальному окну при его открытии.
Для закрытия модального окна вы можете использовать обратную анимацию:
- Создайте анимацию для закрытия модального окна:
transform: "scale(0.8)",
opacity: 0,
config: { tension: 300, friction: 20 }
- Примените анимацию к модальному окну при его закрытии.
Обратите внимание, что вы можете настроить параметры анимации (например, "tension" и "friction") в зависимости от ваших потребностей.
Ну вот наконец-то и ответ... Мы будем использовать Zoom transition и для его props мы можем передать style и изменить для него transfromOrigin в котором будут содержаться координаты, откуда должна начинаться анимация.
Координаты мы можем получить при клике на кнопку с помощью:
const rect = event.currentTarget.getBoundingClientRect();
В rect.left, rect.top находятся координаты левого верхнего угла элемента. Чтобы получить центр элемента нужно прибавить rect.width / 2 и rect.height / 2.
import { Dialog, Zoom, DialogTitle, DialogContent } from "@mui/material";
import { useState } from "react";
function DialogTest()
{
const [ isDialogOpen, setIsDialogOpen ] = useState<boolean>(false);
const [ clickPosition, setClickPosition ] = useState<{ x: number, y: number; }>({ x: 0, y: 0 });
return (
<>
<button
onClick={(event) =>
{
const rect = event.currentTarget.getBoundingClientRect();
setClickPosition({
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2
});
setIsDialogOpen(true);
}}
>
Test button
</button>
<Dialog
open={isDialogOpen}
onClose={() => setIsDialogOpen(false)}
slots={{ transition: Zoom }}
slotProps={{
transition:
{
style: { transformOrigin: `${ clickPosition.x }px ${ clickPosition.y }px` }
}
}}
>
<DialogTitle>
Modal title
</DialogTitle>
<DialogContent>
Lorem ipsum dolor sit amet consectetur.
</DialogContent>
</Dialog>
</>
);
}
export default DialogTest;
(если кто-то может сделать так, чтобы прямо на SO можно было запустить - отредактируйте и спасибо вам)