Spatial navigation
npm install @salutejs/spatialПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.
Есть спецификация для браузеров, которая пока в статусе "draft".
Мы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,
а библиотеку считать устаревшей.
Для мобильных устройств или других устройств с сенсорным экраном это не надо.
Документации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/
- Установка
- " class="text-primary hover:underline" target="_blank" rel="noopener noreferrer">Минимальная настройка приложения для работы с @salutejs/spatial
- Инициализация в родительском компоненте всего приложения
- Добавление секции
- Включение навигации на DOM элементе
- Углубление в @salutejs/spatial
- Варианты инициализации
- Подробнее о секциях
- Настройка параметров секции
- Хуки
- Полезные методы SpatialNavigation
- Оптимизация и ускорение работы
- Intersection и Mutation observer
- Простые секции
- Запуск тестов
- Pitfalls
- Next.js >= 13.0.0
- Next.js < 13.0.0
``sh`
npm install --save @salutejs/spatial
Нужно выполнить три обязательных шага.
`jsx
import { useSpatnavInitialization } from '@salutejs/spatial';
import { Page } from './pages/Page';
const App = () => {
useSpatnavInitialization();
return
};
`
Для навигации @salutejs/spatial использует секции. Секцию можно добавить с помощью хука useSection.
`jsx
import { useSection } from '@salutejs/spatial';
const Page = ({ children }) => {
const [sectionProps] = useSection('sectionName');
return
;export default Page;
`
Для того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex="-1".
Далее для работы внутренних функций @salutejs/spatial необходимо добавить DOM элементу CSS класс "sn-section-item".
`jsx`
<>
навигация работает
навигация работает
>
Если убрать класс sn-section-item, то элемент исключается из навигации.
`jsx`
<>
навигация работает
навигация НЕ работает
>
Добавим элементы в секцию.
`jsx
import { useEffect, useRef } from 'react';
import { useSection } from '@salutejs/spatial';
const Page = ({ children }) => {
// создание секции
const [sectionProps] = useSection('sectionName');
// установка фокуса на элемент
const ref = useRef(null);
useEffect(() => {
const focusable = ref.current;
if (focusable) {
focusable.focus();
}
}, []);
return (
export default Page;
`
Готово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука useDefaultSectionFocus.
Здесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.
Можно инициализировать @salutejs/spatial без использования хука useSpatnavInitialization, если такое требуется.
`js
import { spatnavInstance } from '@salutejs/spatial';
// вызывать только на клиенте
spatnavInstance.init();
`
Аналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.
`js`
spatnavInstance.unInit();
Секция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.
Включение секции в навигацию происходит с помощью хука useSection. У корневого элемента секции должны быть установлены аттрибуты id="имя секции, переданное в useSection" и className="sn-section-root", которые возвращает хук useSection.className="sn-section-item"
Элементы секции должны быть потомками корневого элемента и иметь аттрибут .
`jsx
import { useSection } from '@salutejs/spatial';
const Page = ({ children }) => {
const [section1] = useSection('section1');
const [section2] = useSection('section2');
return (
<>
#### Настройка параметров секции
Также хук
useSection возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.`jsx
import { useSection } from '@salutejs/spatial';const Page = ({ children }) => {
const [section1, customize1] = useSection('section1');
const [section2, customize2] = useSection('section2');
useEffect(() => {
// выключаем навигацию в секции section1 целиком
customize1({
disabled: true,
});
customize2({
simpleSectionOptions: { type: 'row' },
// установка элемента по умолчанию для секции
getDefaultElement: (section2Root) => section2Root.firstElementChild,
enterTo: 'default-element',
});
}, [[customize1, customize2]]);
// установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге
// https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html
useDefaultSectionFocus('section2');
return (
<>
принадлежит секции section1, навигация выключена
принадлежит секции section1, навигация выключена
принадлежит секции section1, навигация выключена
принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент
принадлежит секции section2
>
);
};
`О всех параметрах секции можно почитать в документации к типу
Config. Параметры передаются в функцию customize.Хуки
useSpatnavInitialization - инициализация навигации;
- useSection - создание секции;
- useSelfSection - создание секции, состоящей только из одного элемента;
- useDefaultSectionFocus.Полезные методы SpatialNavigation
У инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:
- Включение и выключение навигации для данной секции или полностью
- Удаление или добавление секции в навигацию
- Изменение глобального конфига или конфига секции
- Фокусирование указанной секции
Например, фокусирование на определенной секции на монтирование компонента.
`jsx
import { useSection, spatnavInstance } from '@salutejs/spatial';const Page = ({ children }) => {
const [sectionProps] = useSection('suggest');
useEffect(() => {
spatnavInstance.focus('suggest');
}, []);
...
};
`Полный список методов и их сигнатуры смотрите в коде SpatialNavigation или на советующей странице документации
Оптимизация и ускорение работы
$3
Для ускорения расчётов в
@salutejs/spatial используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. @salutejs/spatial в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.
Mutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.$3
@salutejs/spatial делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.
Но если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.
Вместо расчётов @salutejs/spatial просто возьмёт нужный элемент из DOM с помощью nextSiblingElement или previousSiblingElement.
Для того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.В обоих примерах ниже режим простых секций будет работать.
`jsx
import { useSection } from '@salutejs/spatial';const Page = ({ children }) => {
const [section1, customize1] = useSection('section1');
const [section2, customize2] = useSection('section2');
useEffect(() => {
customize1({
simpleSectionOptions: { type: 'column' },
});
customize2({
simpleSectionOptions: { type: 'row' },
});
}, [customize1, customize2]);
return (
<>
>
);
};
`Запуск тестов
Для запуска тесов нужно собрать пакет spatial, запустить
test-app и cypress.`sh
npm run buildnpm run test-app:start
npm run cypress:open
`В открывшемся окне Cypress выбрать
E2E Testing, тестировать можно как в Chrome так и в Electron.Pitfalls
@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:
$3
В
next.config.js вам необходимо добавить свойство transpilePackages.`js
const config = {
transpilePackages: ['@salutejs/spatial'],
};
``Документация по transpilePackages.
Воспользуйтесь пакетом next-transpile-modules.