Focus management

Atomico предоставляет мощные средства для управления фокусом внутри веб-компонентов, сохраняя декларативный и реактивный подход. В этой библиотеке фокус рассматривается как часть состояния компонента, позволяя создавать доступные и интерактивные интерфейсы без прямой манипуляции DOM.


Использование useFocus

Хук useFocus служит для отслеживания и управления состоянием фокуса элемента. Он возвращает объект с двумя основными свойствами:

  • focused — булево значение, указывающее, находится ли элемент в фокусе.
  • ref — ссылку на DOM-элемент, к которому применяется управление фокусом.

Пример применения:

import { component, useFocus, html } from "atomico";

function InputField() {
    const { focused, ref } = useFocus();

    return html`
        <input
            ref=${ref}
            class=${focused ? "focused" : ""}
            placeholder="Введите текст"
        />
    `;
}

export const inputField = component(InputField);

В этом примере класс focused автоматически применяется к <input> при получении фокуса. Это упрощает стилизацию и визуальные эффекты без необходимости слушать события focus и blur.


Фокус и управление клавиатурой

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

import { component, useFocus, html } from "atomico";

function MenuItem({ label }) {
    const { focused, ref } = useFocus();

    const handleKeyDown = (event) => {
        if (event.key === "Enter" && focused) {
            console.log(`Выбран пункт: ${label}`);
        }
    };

    return html`
        <div
            ref=${ref}
            class=${focused ? "focused" : ""}
            onkeyd own=${handleKeyDown}
            tabindex="0"
        >
            ${label}
        </div>
    `;
}

export const menuItem = component(MenuItem);

Ключевой момент здесь — использование атрибута tabindex="0". Без него элемент не будет доступен для фокусировки с клавиатуры.


Управление фокусом через события

Atomico позволяет отслеживать изменения фокуса через стандартные события focus и blur, но также предоставляет реактивный подход через хуки. Для сложных интерфейсов с множеством элементов можно использовать централизованное управление фокусом:

import { component, useState, useFocus, html } from "atomico";

function Form() {
    const [currentFocus, setCurrentFocus] = useState(null);

    const fields = ["name", "email", "password"];
    const focusRefs = fields.map(() => useFocus());

    const handleKeyDown = (index, event) => {
        if (event.key === "ArrowDown") {
            const next = (index + 1) % fields.length;
            focusRefs[next].ref.current.focus();
            setCurrentFocus(next);
        } else if (event.key === "ArrowUp") {
            const prev = (index - 1 + fields.length) % fields.length;
            focusRefs[prev].ref.current.focus();
            setCurrentFocus(prev);
        }
    };

    return html`
        <form>
            ${fields.map(
                (name, i) => html`
                    <input
                        ref=${focusRefs[i].ref}
                        class=${currentFocus === i ? "focused" : ""}
                        placeholder=${name}
                        onkeyd own=${(e) => handleKeyDown(i, e)}
                    />
                `
            )}
        </form>
    `;
}

export const form = component(Form);

В этом примере реализована циклическая навигация по полям формы с помощью стрелок, при этом useFocus позволяет отслеживать активное состояние каждого поля.


Программная установка фокуса

Фокус можно задавать не только пользователем через клавиатуру, но и программно, используя ref.current.focus(). Atomico полностью поддерживает этот подход, сохраняя синхронизацию состояния:

const { ref, focused } = useFocus();

const focusInput = () => {
    ref.current.focus();
};

return html`
    <input ref=${ref} placeholder="Автофокус"/>
    <button oncl ick=${focusInput}>Сфокусировать</button>
`;

Фокусировка через метод focus() автоматически обновляет реактивное состояние focused, что позволяет использовать его для условного рендеринга или стилизации.


Адаптивная стилизация состояния фокуса

Использование focused открывает возможности для динамического применения CSS-классов и анимаций. Пример:

input {
    border: 1px solid #ccc;
    transition: border-color 0.2s;
}

input.focused {
    border-color: #007aff;
}

В сочетании с Atomico это позволяет создавать интерактивные и доступные компоненты без ручного управления классами через DOM.


Управление вложенными фокусируемыми элементами

Для сложных компонентов, таких как модальные окна или дропдауны, важно контролировать фокус внутри контейнера. Atomico поддерживает концепцию focus trap:

import { component, useFocus, html, useEffect } from "atomico";

function Modal() {
    const { ref } = useFocus();

    useEffect(() => {
        const firstInput = ref.current.querySelector("input");
        firstInput.focus();
    }, []);

    return html`
        <div ref=${ref} class="modal">
            <input placeholder="Введите имя"/>
            <input placeholder="Введите email"/>
        </div>
    `;
}

export const modal = component(Modal);

Фокус автоматически устанавливается на первый интерактивный элемент, а последующая навигация может быть ограничена рамками модального окна, обеспечивая доступность.


Atomico обеспечивает полноценное управление фокусом через реактивные хуки, ссылочные объекты и интеграцию с событиями клавиатуры, что позволяет создавать сложные интерфейсы с минимальной логикой управления DOM. Основные принципы — декларативность, реактивность и доступность, которые делают библиотеку удобной для построения интерактивных компонентов.