Как стать автором
Обновить

Комментарии 4

По-моему, было бы гораздо проще реализовать модальное окно на основе элемента dialog, потому что он поддерживает модальность (захват фокуса) и закрытие по Эскейпу "из коробки". Достаточно добавить кнопку X и немного стилей.

Для того, чтобы фокус работал правильно вне зависимости от наличия или отсуствия элементов ввода в теле диалога, кнопку X можно расположить "внизу", переместив её "наверх" с помощю стилей.

Например:

import type { PropsWithChildren, ReactNode } from 'react';

import { forwardRef, useRef, useImperativeHandle } from 'react';

export interface IDialog {
    get isOpen(): boolean;

    show(): void;
    hide(): void;
}

export type DialogProps = PropsWithChildren<{
    label?: string;
    footer?: ReactNode;
}>;

const Dialog = forwardRef<IDialog, DialogProps>(function Dialog(
    { label, footer, children },
    ref
) {
    const dialogRef = useRef<HTMLDialogElement>(null);

    useImperativeHandle(
        ref,
        () => {
            return {
                get isOpen() {
                    return dialogRef?.current?.open;
                },
                show() {
                    dialogRef?.current?.showModal();
                },
                hide() {
                    dialogRef?.current?.close();
                },
            } as IDialog;
        },
        []
    );

    const handleCloseButtonClick = () => {
        dialogRef?.current?.close();
    };

    return (
        <dialog
            ref={dialogRef}
            className="overflow-visible bg-transparent p-0 outline-none backdrop:animate-reveal-100 backdrop:bg-slate-100 backdrop:bg-opacity-50 backdrop:backdrop-blur-xs"
        >
            <div className="flex flex-col-reverse">
                <article className="relative flex h-screen-50 max-h-100 min-h-60 w-screen-40 min-w-xl max-w-2xl flex-col overflow-hidden rounded-md border border-slate-300 bg-white drop-shadow-md">
                    {label && (
                        <header className="flex items-center justify-between border-b border-b-slate-300 bg-slate-100 p-4">
                            <h2 className="text-lg font-semibold">{label}</h2>
                        </header>
                    )}
                    <div className="flex-1 overflow-hidden overflow-y-auto">
                        {children}
                    </div>
                    {footer && (
                        <footer className="flex justify-end border-t border-t-slate-300 p-4">
                            {footer}
                        </footer>
                    )}
                </article>
                <div className="flex w-full justify-end py-2 align-middle">
                    <button
                        onClick={handleCloseButtonClick}
                        className="h-8 w-8 rounded-md bg-transparent hover:bg-slate-200 focus:bg-slate-200"
                    >
                        <span className="leading-none text-slate-800">✕</span>
                    </button>
                </div>
            </div>
        </dialog>
    );
});

export default Dialog;

А как правильно обработать ситуацию, когда одно модальное окно порождает другое? В данной реализации вроде это никак не обрабатывается?

Для такого сценария достаточно будет менять контент внутри модалки по каким-то условиям. Т.е. не модалка пораждает другую модалку, а просто идёт смена контента внутри модального окна.

Например можно попробовать прикинуть на примере пошаговой формы (скажем 3 шага), с сабмитом на последнем шаге.
Контент внутри modal под условным рендерингом будет находится. Каждый "шаг" отображается по набору условий (допустим стейт в компоненте с номером шага). Далее схема будет следующая:
- заполнили 1й шаг, нажали кнопку "дальше"
- по клику на кнопку стейт поменялся ->
- затем сработали условия для скрытия 1го шага и показа 2го шага
- заполнили 2й шаг -> клик-> стейт -> смена условий и показ 3го шага
- сделали сабмит, перестали рендерить модалку

Это, мягко говоря, не совсем то, о чём спрашивал автор комментария.

Дочерний диалог и замена содержимого уже открытого диалога — это весьма разные понятия хотя бы с точки зрения усложнения самих компонентов, не говоря уже об их тестах.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории