React Material UI открыть модал из автозаполнения потерять фокус - PullRequest
6 голосов
/ 07 апреля 2020

Я использую материал-ui lib, и мне нужно иметь автозаполнение, где каждый элемент внутри этого автозаполнения кликабелен и открывает модал.

Структура в целом:

const ModalBtn = () => {
    ...
    return (
        <>
            <button ... (on click set modal to open)
            <Modal ...
        </>

    );
}

const AutoCompleteWithBtns = () => {
    return (
        <Autocomplete
            renderTags={(value, getTagProps) =>
                value.map((option, index) => <ModalBtn />)
            }
            ...
        />
    );

}

Обратите внимание, что ModalBtn - это компонент, который нельзя разделить на два компонента: Button и Modal.

Проблема заключается в том, что при нажатии кнопки внутри модального окна фокус хранится внутри автозаполнения, и модал никогда не получит фокус (если у меня есть вход внутри модала - я ничего не могу написать внутри).

Пробовал все стандартные реквизиты, связанные с автозаполнением / модальным фокусом (disableEnforceFocus, disableEnforceFocus, et c ...), но ничего не работает.

Вот пример рабочих кодов и коробки . Как вы можете видеть - если вы нажмете на кнопку, которая не находится внутри компонента автозаполнения - все работает (вы можете добавить текст внутри поля ввода). Если вы нажмете кнопку внутри автозаполнения - поле ввода внутри модального окна не будет редактироваться (вы потеряете фокус).

Вот пример проблемы:
enter image description here

Ответы [ 2 ]

5 голосов
/ 25 апреля 2020

Проблема с отображением Modal из Autocomplete заключается в том, что события распространяются от Modal до Autocomplete. В частности, события click и mouse-down обрабатываются Autocomplete таким образом, что вызывает проблемы в вашем случае. Это в первую очередь логика c, предназначенная для удержания фокуса в нужном месте при взаимодействии с различными частями Autocomplete.

Ниже (от https://github.com/mui-org/material-ui/blob/v4.9.11/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js#L842) находится часть код Autocomplete, который вам мешает:

  // Prevent input blur when interacting with the combobox
  const handleMouseDown = (event) => {
    if (event.target.getAttribute('id') !== id) {
      event.preventDefault();
    }
  };

  // Focus the input when interacting with the combobox
  const handleClick = () => {
    inputRef.current.focus();

    if (
      selectOnFocus &&
      firstFocus.current &&
      inputRef.current.selectionEnd - inputRef.current.selectionStart === 0
    ) {
      inputRef.current.select();
    }

    firstFocus.current = false;
  };

Поведение браузера по умолчанию при возникновении события нажатия мыши на фокусируемом элементе заключается в том, что этот элемент получает фокус, а обработчик нажатия мыши для Autocomplete вызывает event.preventDefault(), что предотвращает это поведение по умолчанию и, таким образом, предотвращает изменение фокуса от события нажатия мыши (таким образом, фокус остается на самом Modal, как указано его синей рамкой фокуса). Однако вы можете успешно переместить фокус на TextField модала с помощью клавиши табуляции, поскольку ничто не мешает этому механизму изменения фокуса.

Обработчик щелчков Autocomplete сохраняет фокус на вводе Autocomplete, даже если Вы нажимаете на другую часть Autocomplete. Когда ваш Modal открыт, эффект этого состоит в том, что когда вы щелкаете в Modal, фокус ненадолго перемещается на элемент ввода Autocomplete, но фокус сразу возвращается на Modal из-за его " навязывать фокус "функциональности. Если вы добавите свойство disableEnforceFocus к Modal, , вы увидите , что при нажатии на Modal (например, на TextField) курсор остается на входе Autocomplete .

Исправление заключается в том, чтобы эти два события НЕ распространялись за пределы Modal. Вызывая event.stopPropagation() для событий click и mouse-down на Modal, он предотвращает выполнение функциональности Autocomplete для этих двух событий, когда эти события происходят в Modal.

      <Modal
        onClick={event => event.stopPropagation()}
        onMouseDown={event => event.stopPropagation()}
        ...

Edit Modal in Autocomplete

Связанный ответ: Как создать первый интерактивный параметр в автозаполнении лабораторных интерфейсов материалов

3 голосов
/ 07 апреля 2020

Проблема в вашем коде заключалась в том, что модал отображался из тега компонента AutoComplete, что было не в порядке из-за видимости компонентов, проблема была в иерархии компонентов.

Исправление: переместить Modal в компоненте FixedTags и передать открытый обработчик в ModalBtn в renderTags prop;

Я обновил вашу песочницу с рабочим вариантом ЗДЕСЬ

Изменения приведены ниже

const ModalBtn = ({ handleOpen }) => (
  <button type="button" onClick={handleOpen}>
    Open Modal (not working)
  </button>
);

const FixedTags = function() {
  const classes = useStyles();
  const [modalStyle] = React.useState(getModalStyle);
  const [open, setOpen] = React.useState(false);

  const handleOpen = () => {
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
  };

  return (
    <>
      <Autocomplete
        multiple
        options={autoCompleteItems}
        getOptionLabel={option => option.title}
        defaultValue={[autoCompleteItems[1], autoCompleteItems[2]]}
        renderTags={(value, getTagProps) =>
          value.map((option, index) => <ModalBtn handleOpen={handleOpen} />)
        }
        style={{ width: 500 }}
        renderInput={params => (
          <TextField
            {...params}
            label="Fixed tag"
            variant="outlined"
            placeholder="items..."
          />
        )}
      />
      <Modal open={open} onClose={handleClose}>
        <div style={modalStyle} className={classes.paper}>
          <h2 style={{ color: "red" }}>This one doesn't work</h2>
          <p>Text field is not available</p>
          <TextField label="Filled" variant="filled" /> <br />
          <br />
          <br />
          <FixedTags label="Standard" />
        </div>
      </Modal>
    </>
  );
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...