1 Востаннє редагувалося flatliner (17.02.2021 18:45:03)

Тема: React/and.design: модальний popover

Виникла така дивна проблемка. Роблю таблицю з клітинкою, що може редагуватися... банальний "rename" у контекстному меню. Клітинка перетворюється на input і коли ти, щось змінюєш всередині, з'являється popover з двома кнопочками 'Ok' чи 'Cancel'.

Чесно кажучи, я зовсім не в темі, як робляться модальні вікна. Спочатку я погодився на те, щоб, будь-який клік поза полем вводу просто спрацьовувало як 'Cancel' і почепив на той input обробник onBlur. Цим самим я вбив можливість клікнути на кнопки в popover`і - бо onBlur спрацьовує раніше і просто ховає popover.

От не знаю, що краще, чи якось намагатися обманути оцю проблему. Чи таки спробувати зробити правильний модал, так щоб усе окрім того input'у і popover'у ставало розмитим і не реагувало на кліки, доки не буде завершено редагування поля. Однак, я зовсім не знаю, як це робиться. Буду вдячний за будь які поради і підказки.

Нижче наведу шматочок коду, що є назараз:

function CellEdit({ initialValue, setEditRowName }) {
    const [value, setValue] = useState(initialValue);
    const [visible, setVisible] = useState(false);

    const onOkClick = e => {
        console.log('here'); // тут має бути відправка на сервер відредагованих данних
        console.log(e);
        e.stopPropagation();
    };

    const onCancelClick = e => {
        setEditRowName(); // скидає обраний для редагування рядок і тим самим ховає компонент
        e.stopPropagation();
    };

    const cellEditClick = e => {
        e.stopPropagation(); // забороняє функціонал, що працює не в режимі редагування
    };

    const cellEditChange = e => {
        setVisible(e.target.value !== initialValue); // поле відредаговано, показати popover
        setValue(e.target.value);
    };

    const cellEditCancel = () => {
        if (!visible) {
            setEditRowName(); // onBlur відміна редагування тільки, якщо поле не змінено
        }
    };

    return (
        <Popover
            content={(
                <>
                    <Button onClick={onOkClick}>Ok</Button>
                    <Button onClick={onCancelClick}>Cancel</Button>
                </>
            )}
            title="Save changed?"
            trigger="focus"
            visible={visible}
        >
            <Input value={value}
                onClick={e => cellEditClick(e)}
                onChange={cellEditChange}
                onBlur={cellEditCancel}
                autoFocus
            />
        </Popover>
    );
}

2

Re: React/and.design: модальний popover

https://uk.reactjs.org/docs/portals.html

Подякували: flatliner, leofun012

3

Re: React/and.design: модальний popover

щодо onBlur... я коли дививсь курс по реакту, то там була та сама проблема.
По-перше, використовуйте оті портали, бо вони дозволять показувати ті віконця не всередині

    <div id="root"></div>

а в іншому елементі, в моєму проєкті це

    <div id="modal"></div>

котрий розташовується відразу після root'а.

Далі, коли ваша модалка починає рендеритись, вона матима якийсь контейнер, котрий має розміри всього віконця, і вже всередині цього контейнера у вас буде сама формочка з кнопочками.
Як розумієте, тепер, аби закрити модалку, коли користувач тицяє поза формою - вам всього лише треба слухати onClick на контейнері модалки, ну а щоб onClick не піднімався до контейнера, коли ви тицяєте щось в формочці - вам треба просто перехоплювати onClick на самій формочці, і викликати stopPropagation.

Ось приклад модалки з курсу.
props.title - заголовок модалки
props.content - власне, контент
props.actions - це вже кнопочки
(тут використовується css ліба semantic, https://cdnjs.com/libraries/semantic-ui)

const Modal = (props) => {
  return ReactDOM.createPortal(
    <div onClick={props.onDismiss} className="ui dimmer modals visible active">
      <div onClick={e => e.stopPropagation()} className="ui standard modal visible active">
        <div className="header">{props.title}</div>
        <div className="content">
          {props.content}
        </div>
        <div className="actions">
          {props.actions}
        </div>
      </div>
    </div>,
    document.getElementById("modal")
  );
};

onDismiss - це коли ви тицяєте десь за формочкою

Подякували: flatliner, leofun012

4

Re: React/and.design: модальний popover

Дякую за поради. Можливо це було б найбільш правильне рішення, і посиланнячко ваше я обов'язково почитаю для загального розвитку, бо якось тема порталів поки що пройшла повз мене.

Однак, як виявилось ця проблема навіть не дуже-то й реактівська і вирішення в неї досить простеньке - замінити onClick отої кнопочки на onMouseDown - виходить воно спрацьовує першим, а потім вже бравзер вирішує, що фокус має зміститися і спрацьовує onBlur. Тож я поки що піду простішим шляхом. Дякую.

5

Re: React/and.design: модальний popover

тоді хоч перевірте то в інших бровзерах, бо теоретично воно може бути реалізовано по-різному

Подякували: flatliner1