Запретить мобильную клавиатуру, но разрешить взаимодействие с курсором

Вопрос или проблема

Этот вопрос, похоже, задавался несколько раз, но я пока не нашел окончательного решения. Я разрабатываю компонент калькулятора, удобного для мобильных устройств, с использованием React и shadcn.

Вот базовая структура

 <div className="w-full max-w-md mx-auto p-4 rounded-lg">
            <Input
                type="text"
                value={input}
                onChange={handleInputChange}
                onKeyDown={handleKeyDown}
                className="w-full mb-4 text-left text-lg font-mono text-black dark:text-white cursor-text"
                aria-label="Ввод калькулятора"
                id='calculator-input'
                inputMode="none"
            />
            {error && (
                <Alert variant="destructive" className="mb-4">
                    <AlertCircle className="h-4 w-4"/>
                    <AlertDescription>{error}</AlertDescription>
                </Alert>
            )}
            <div className="grid grid-cols-4 gap-2">
                {buttons.map((btn) => (
                    <Button
                        key={btn}
                        onClick={() => handleButtonClick(btn)}
                        className="h-12"
                        useGlobalStyles={false}
                        variant={buttonVariants[btn] || 'default'}
                        aria-label={btn}
                    >
                        {btn === 'Backspace' ? <Delete className="h-4 w-4"/> :
                            btn === 'Enter' ? <ArrowRight className="h-4 w-4 text-black dark:text-white"/> :
                                btn}
                    </Button>
                ))}
            </div>
        </div>

Логика кнопок, которая добавляет/удаляет значения в позиции курсора

const handleButtonClick = (value: string) => {
        setError(null)
        if (value === 'Enter') {
            handleSubmit()
        } else if (value === 'Backspace') {
            setInput(prev => {
                const inputElement = document.getElementById('calculator-input') as HTMLInputElement;
                const cursorPosition = inputElement.selectionStart || 0;
                const newValue = (prev || '').toString().slice(0, cursorPosition - 1) + (prev || '').toString().slice(cursorPosition);
                setTimeout(() => {
                    inputElement.setSelectionRange(cursorPosition - 1, cursorPosition - 1);
                }, 0);
                return newValue;
            })
            setIsEvaluated(false)
        } else if (value === '=') {
            if (isEvaluated) {
                handleSubmit()
            } else {
                handleEvaluate()
            }
        } else {
            setInput(prev => {
                const inputElement = document.getElementById('calculator-input') as HTMLInputElement;
                const cursorPosition = inputElement.selectionStart || 0;
                const newValue = (prev || '').toString().slice(0, cursorPosition) + value + (prev || '').toString().slice(cursorPosition);
                setTimeout(() => {
                    inputElement.setSelectionRange(cursorPosition + value.length, cursorPosition + value.length);
                }, 0);
                return newValue;
            });
            setIsEvaluated(false)
        }
    }

Использование inputMode="none" не кажется предотвращающим появление клавиатуры на мобильных устройствах, любые решения по этому поводу будут оценены. Также буду признателен за советы по более элегантной логике взаимодействия с курсором.

Спасибо

Ответ или решение

Чтобы предотвратить появление виртуальной клавиатуры при взаимодействии с полем ввода на мобильных устройствах, но позволить пользователю управлять курсором, есть несколько подходов, которые вы можете рассмотреть. Ваша текущая реализация с использованием inputMode="none" не всегда гарантирует отключение клавиатуры, поэтому важно применить другие методы.

1. Использование contentEditable

Вместо поля ввода <input>, вы можете использовать элемент div с атрибутом contentEditable, что позволит пользователю использовать курсор и редактировать текст, при этом клавиатура не будет автоматически открываться. Вот пример того, как это можно реализовать:

<div
  contentEditable
  role="textbox"
  className="w-full mb-4 text-left text-lg font-mono text-black dark:text-white cursor-text"
  aria-label="Calculator input"
  onInput={handleInputChange}
  onKeyDown={handleKeyDown}
  onFocus={(e) => e.target.setAttribute('tabindex', '0')}
  onBlur={(e) => e.target.removeAttribute('tabindex')}
  id="calculator-input"
></div>

2. Настройка событий для управления курсором

Ваша логика для управления курсором может быть реорганизована, чтобы обеспечить более гибкое изменеие позиции курсора. Вот пример для обработки ввода с помощью contentEditable:

const handleInputChange = (e) => {
  setInput(e.target.innerText);
};

const handleButtonClick = (value) => {
  setError(null);
  const inputElement = document.getElementById('calculator-input');
  const selectionStart = window.getSelection().focusOffset;

  if (value === 'Enter') {
    handleSubmit();
  } else if (value === 'Backspace') {
    setInput((prev) => {
      const newValue = prev.slice(0, selectionStart - 1) + prev.slice(selectionStart);
      setTimeout(() => {
        setCursorPosition(inputElement, selectionStart - 1);
      }, 0);
      return newValue;
    });
  } else {
    setInput((prev) => {
      const newValue = prev.slice(0, selectionStart) + value + prev.slice(selectionStart);
      setTimeout(() => {
        setCursorPosition(inputElement, selectionStart + value.length);
      }, 0);
      return newValue;
    });
  }
};

const setCursorPosition = (element, position) => {
  const range = document.createRange();
  const sel = window.getSelection();
  range.setStart(element.childNodes[0], position);
  range.collapse(true);
  sel.removeAllRanges();
  sel.addRange(range);
};

3. Стиль и доступность

Не забудьте добавить необходимые атрибуты для обеспечения доступности, такие как role="textbox" и aria-label, которые улучшат взаимодействие для пользователей с ограниченными возможностями.

Заключение

Использование contentEditable предоставляет вам необходимую гибкость для управления текстом без открытия клавиатуры. Это также освобождает вас от необходимости работать с inputMode. Обеспечьте контроль за курсором и редактированием текста с помощью обработчиков событий, чтобы создать удобный и функциональный интерфейс калькулятора.

Оцените материал
Добавить комментарий

Капча загружается...