{"_id":"@salutejs/spatial","_rev":"58-cba9f77a73297ccec863dc3adea6b647","name":"@salutejs/spatial","dist-tags":{"canary":"3.0.14--canary.17.6693235005.0","latest":"3.0.14"},"versions":{"3.0.1":{"name":"@salutejs/spatial","version":"3.0.1","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.1","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"33cca53c589994b2a30aa0b7c7758b905d615791","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.1.tgz","fileCount":98,"integrity":"sha512-NI1Jt0REHcJ8IG1T45u+vSkDSPrHLVT3K20TqkETGEegqJoPght36zT8bezlXOb/qTG/n3c8hJoqTX4g+s/KKQ==","signatures":[{"sig":"MEUCIBd5lToWQU/fEotWh94DwCF9Wr387Grn2BL7YIV9tShKAiEA5+4Oe8CdWOeanX0YnpF97Q7jPMU2rAILrBQ+xkS79mU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":95431,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjqYdrACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrCMhAAmuJbZBkILF7/+mYCremGpi0zULg/Cj05TGGdN8vhJILBPYNJ\r\n/9xIRLMfhpDqr8JbOwJSecfebyWf70PFbtSpn9kgrO4bHIrSNFmFrG96QmAz\r\nvUQNjkp4S1JD6DWCkFt2zDolSNDfiHtLdJ7CruO3Zm/mKrnbG6ot+ibfecWr\r\nJTZ8CHOLs0gT0zajD9627TA7jvWQQHg/Sr8uS7knDabwCjUFxpfEpqmgVx0p\r\n7lSABV+MRL2WBVSAGRcrsuaVk5hxlQaa25GcbGKrTcrlkgZUQ2TVqsQoA805\r\nrZ/vXRpoUzrLt6hPisYhPHAO6lTVJCYQPrFfsNNP95s/Nt7X2hy18gf8aEtA\r\nfQt8CeoA3cq63VLz5kGpdbPx1khn2EJxSkkKaRQ2qsDPgzqQh4D8Rj1H1GcD\r\nn33FDnPbzLFgJp+vQBbJGPuKHVQ4NNhQfxvtq3gWABuwKisQT/A831uVxhD+\r\njFd95knoqvjSs+GVQJLqRwryy9n+i8/l2Bp9c6OaIOZmDHoncFdPJyjle6AK\r\nmnMF9Wi7kkae6Xl5vYWE/Crx5m3NRisUwm4Jo1++31PNikisdWV8b0NuMPqt\r\n36iPpk/XfK39V9+9fx6YeFWM9q8fpuF9/REUJPXCVx5YkL+a/rW8gZ18mjqZ\r\n5ZNgRL/po2ILEumfvsAE08nGxg9QHOdOflo=\r\n=pR+O\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=16.18.0"},"gitHead":"c9f9ae2a0348680f71cd9c536969f1bb9c8624f4","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"16.18.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.1_1672054634792_0.38919839380661836","host":"s3://npm-registry-packages"}},"3.0.2--canary.1.4027832972.0":{"name":"@salutejs/spatial","version":"3.0.2--canary.1.4027832972.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.2--canary.1.4027832972.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"d2e5f112fe0cb41396ba7254c5537194bed530fe","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.2--canary.1.4027832972.0.tgz","fileCount":98,"integrity":"sha512-+W6l4JopIzMigTsoJge3NHyEnsOjbKCvJN4hJ9HnVKex7mxP4plU65DrcjDnR/7aGKmS6TFZLyGTgWOppklzow==","signatures":[{"sig":"MEYCIQD++d+qan0O2/u1jAZIS9hdrmZ71yJf9wP69QsIQR9R1wIhAJgICjgQiH3ywfcDj0hPUDKl1pS8ERhGuKryKSV/JFVy","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":95393,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj1C/lACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrQSw/7BGajunXPL+cWtoNV/YprHD69wHbK2reHL/ugTTvh2831J89i\r\noGIHFBaQB+zJpPVQh43i48ee8fzuYc+6W0fYy3SBNc26tSGJcRwZBw7r69cJ\r\ndFnkAKxXlNQfUH7cMiDvDTyvpYEFjTsjiUkrtUgN8tKJ0pRm8LqT+uLiyRGp\r\nGlduGiW94bBtF28Y9LkCdd0onQwDIw+fLfmnk5uEGAYkhPJkAklk+zGqXHjl\r\n/tHIHaXHxs/4XRh4HjzO38tHOKM4AkD36xG2c2HRGfw4G8iX+jZWrsSxPVIJ\r\noToF/cIgk/WNZW6DL9hXBgiPWxxUv7Ph+ZXKxqoIA9xE3qf69i7usWtMymCY\r\nBwdAvV5Wu29Vx4eIQGzhxyU5Kvn1BlDvfmf/0N21ZeJO005NL8XJJupfPJcW\r\nG4bVmGOLKIRZS30A80hFwmia4dkuR0cTCgOyYSVz9Ei8pj/Uz1+Y3EYQRJK0\r\nRx3u/D8vncWAI32HGMlYKGtqRg7PL5BPSgMD6+lDNMUO2XzZwL/la6UbiHTL\r\nkqIRm+MIwbp0gO0DNA9MRkze+VYfH2XXO3tEngCIqX27izWv3ha7z6vraUJP\r\nrWDV9TTZ/CHP1mx/sUcw3hzOKS8ADcgp3n+dHtATOwkIcLCsVm5c3PyXzitt\r\njslvBq2cUbpybNQTFUW2gadgd5FDI8PP5j8=\r\n=727i\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"c8fe690e1d86cd164604e15e0a01912a66332851","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.0","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.2--canary.1.4027832972.0_1674850277538_0.7866968587369936","host":"s3://npm-registry-packages"}},"3.0.2--canary.2.4028016401.0":{"name":"@salutejs/spatial","version":"3.0.2--canary.2.4028016401.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.2--canary.2.4028016401.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"6e4ed747be8fed0a2a83dfc196a8bf8c7a0f6051","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.2--canary.2.4028016401.0.tgz","fileCount":98,"integrity":"sha512-rzD20BlqptOPYb57T8fqx9UaBwg6N+y77p4JBMv11g7lNOXAlAI3U0q9jOEQ1pFg4Ph5mMv0vXE1ljFS+nMwxw==","signatures":[{"sig":"MEQCIC6ufQS+8YOsxINrWU0gW9y2eRBKEtvn6U4MPIlm70GjAiAbYyMaT6p9Hu2zetxmPPpYPPHQO/HOK6yfbHczBMkT9g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":95454,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj1DaUACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrnPQ/9HKd1G9PCL8pSwE7MWeu/Pfggd0rzuxUBBWpT2yqukeB2LVR9\r\nPseRd874OJwnkuayFHXgwLJUQ1F+oca7KpJAkf8FiyOK7PPaENRRBpLuqC5o\r\n0kB7Krr4H7yVT3GgSOElOXLIyr+NYidcknEJnL81cIBs+yU6C8Us1O75EssD\r\nYFiG2WFi/HsC9VvyAsK5WnQmfDmpCSweUVD9gRbLYpi/uxd8Ywqo9nJMaDQP\r\n0L8cAUHIx7q8zHnwXq+gnpyu7Yib8CsGwtn7c8ABKSUcmjkA3Fld/4glvpgW\r\n3SrWb/uV/3N8XdzSIvdVqn8fMGcuQkeVNie7ySnq27dizMMd2bB3n6wt+269\r\n0afGi9h3RA9notQKf8SvZRLzqh3s8rFY7jfFHq+pCt/sg/igedfjyPfNo+Mn\r\nPox0rGjPo3q/fDgvJbv84QfHcuUd0jAnYiupb4UfI2c7LITcZ5cJdUfNBxeK\r\no486HTKmqpOG2Uutscql4TmsDvimsg4x5ZX22FtI45vEirfRz4Dl4EwrPi9G\r\n3sFvnO1ro29fIzatWoFoxWi4wBHKiLAo7/EfcXZp6Gx3EVXfWuyPsyuSwbqV\r\noWRpj24t0cAIq2S5uiucLMca4usnf+1bxt/ZTWK0pnkHX32SZO5B4/bN+DfN\r\nWMv4HkStxPdA9cg+k/XL65zcCc0g/IA5T6Q=\r\n=8aqw\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](###Подробнееосекциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"ef546369cca403583e6fc97a951d2292ddb76da4","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.0","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.2--canary.2.4028016401.0_1674851987979_0.5711310491898405","host":"s3://npm-registry-packages"}},"3.0.2":{"name":"@salutejs/spatial","version":"3.0.2","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.2","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"12105352512cfdca3c4600903e5c6b05a256d9f2","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.2.tgz","fileCount":98,"integrity":"sha512-U+b5PPHDNe8UcRriDlr6pDx0myQOQdUira+5zAvdf893smC8Otd5GpdeOUi0HzSsTouCCJ2vuM6BmoqMeR9kkA==","signatures":[{"sig":"MEQCIEwn3t9+xI3NELEgirERWkU0eKts7OrDsVVR6zuVzwR+AiAYeL8+uNsVEaWgnNAwFu0ROrWNh45RPkHr3rJkx0dxQQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":95370,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj18IAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpuZQ//Y3gmayWoUaxxAge3BCKOgSvuGm+omY6fZSI512m+Qn+tMNza\r\ncFA/8LUmQxzRqla+9WmuDnK42xdoSnm84ck0r4wm4z2+0XuryRPZRL9QbFVZ\r\nObe6WafoTqX21VnV7MRPn4tqmohbXEsLCiKDnCqDkGPpdLHIT2dqGK9gRfH/\r\nbdqzr45RHwfy0pekmyBfmgPGjeaK7jnsOpJfLkhQmiNAUuclqa6NPfe4yoaF\r\nx1gDH2IghyTTpucnGvfmK5hj48XEF3UgF0iFwIsXJTL6Udj7Wr4TkoTS8MwJ\r\nWhE/M4IaV2YqdERy1BBNrt9jDafM6xnmHpxWxBrVXr2ckOmCXyYREnngnvzT\r\nQgxkO1jWppgFBRRT/i2QvdqZONV1FqiN1vcf1t2xzSwrja10BCrcmMZmUN4B\r\nnfQckzk2qc05G9n8ehMx3wXErQglL89cypjOLaN/LwKOxLe7g7OGXGek6qNw\r\nuQ0g6V5w1BCmnU+XJBdaycRG4wGJy+6qB+1KtlrDAk5pW6yzY3MFep1IhTV4\r\nPpieTpRxcvXhRVf52Ix52ek8gxcKXG2JR3JHtQ2IsVmGUzwlgCCQgtsVT3RY\r\nfYBP9GCla3oSK0R7yxd/ibDzGuI3VPEJtpHvcjjAbmSq9vXTqce7c+9vSRTc\r\noA9rxmTaw+DepaM4NYPYoJp5IATz/D8kghs=\r\n=Dd3b\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=16.18.0"},"gitHead":"5cf4455386deaaec8bbeefd3e41e6d56c6cee023","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.0","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.2_1675084287926_0.6713451382757052","host":"s3://npm-registry-packages"}},"3.0.3--canary.3.4078167671.0":{"name":"@salutejs/spatial","version":"3.0.3--canary.3.4078167671.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.3--canary.3.4078167671.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"c74676872c544b6edc8117b3eaca149bbd32eafb","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.3--canary.3.4078167671.0.tgz","fileCount":98,"integrity":"sha512-vGQRYDIRkfTEA5TjGtxu8N0w4AWuj7R57ruWP2nTuiRac5HjGLIgIdE8CavnoqPTtMchoTTWX5XIJfDj0y3L3w==","signatures":[{"sig":"MEQCIF8k1hsNVheitMaHcUf0PNgaYDaXI+weJoOmyCe2qD7tAiBlIz9MX9flQxmgk3I8VIkGzI9aGKOryCqarBVo6Q5dJA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96579,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj3BX5ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo1vQ//WncOPH1shTuleQSgwz4YSMbfOKPSMPwVsIxK5cgAz2Swl35p\r\nijxL+nYMdwmJP3qwP6FqHma8n+ZnVSWBWynWNvDZvNT1RNUc5rZDMZ5vHLpu\r\nfv65Uh6RclWkSs0jbTBtohjDpbblotfbRURMhb/NMwBia4OkmGZ26OcABfZJ\r\njQ6zO6OXcwHa8FfQJ4VooldPz9sHIW7HZ/i789P1fRkPy7tZPkMWHPErEid8\r\nxHXhQZvu2JqP1Tuey/NyDiG96vyHc73tLKhdt6DUrL/gEiwb5YZQOPVYKX9y\r\nOFeUGkb1u8EHQNFq+hs083BUyTcI63fkqDB1HRYZTnOjZImk188XiaZ/SmWU\r\nUvYr97WotG344q6sg/abpaIXaCzxXrMzd9wcAI6Ewm3lSGGGNs9lSVO8Cp1n\r\nIuMaTtmsQklpepy0gN9N7QbPdwwyC5MQAhGywEVebHP074V+bWQK7vB1SSlL\r\nR3IwpHVIt8DmTqs2llxsFhljVMpY37dd09PvfCoRdpm/1HYK9aPXJyAMDSzN\r\nfdHyjzqvtGGxPYaZTimmc5bnxF8tMKIrBovd3qc0DHddlxD3ZadEw+GzEajr\r\njhSpStbAdQwMa9D0rM8bbgVxKj2PpeKSdSNyIOnTe7U8E3n5x3b4NSdEfqNO\r\nZwGzW7h8Eess+A5P5nczx4UNmGnd2TCzqe8=\r\n=Pulc\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование а указанной секции\n\n Например фокусирование на определенной секции на маунт компонента будет выглядеть так\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        () => spatnavInstance.focus('@suggest');\n    }, []);\n\n    <Suggest>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"958045853c8f8f9d2edd6ba476a33d972314fc1b","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.0","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.3--canary.3.4078167671.0_1675367929077_0.07554358761979918","host":"s3://npm-registry-packages"}},"3.0.3--canary.3.4087646755.0":{"name":"@salutejs/spatial","version":"3.0.3--canary.3.4087646755.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.3--canary.3.4087646755.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"63e07fca420e163ea8ca6cfbcc7b5f4566e46874","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.3--canary.3.4087646755.0.tgz","fileCount":98,"integrity":"sha512-llMCXd7llrxGS0G/HgrzICnS3AIKsxbJ44lz6cjVYnQ9Mdu/I6Pl6ccz6W70lkf4mg51kUAp6u9kxJXzK8QfyA==","signatures":[{"sig":"MEUCIQCQt04EB6xww7hNzIkjcLVsxRLCnkB/6hBE1ktbWn/umAIgCic98iev12ear3zTiuFUrZjgYIPpcaO+f6hM4w7BwMI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96558,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj3W4YACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr5rA//S0WgaHiMfPE9TD57WLZ5J8hg4kd9azotM53OvAG1zyljigDW\r\nQFLQSsOOONd5Xlp1kcLEjwScFK/MGxc9ctr7K9xwWNutP+tVKy5AQ0l+tVKI\r\nDtZvTYU+CW3kzN2qsLal85LFovgualwoiUsy0b47+iYj4M484L/Uk/PJDyA4\r\nsqGvlJuc2HcXfV4JpxxYpDvUFCH/gwkaMqzX+ex6K2CVe2IN0IU6F6mnZXcD\r\nxSy21qA1LA+YAnhjvZRBsLnZJ3D6GGlf0oDjX1ZtX9zyfZxAtramz8JJQONp\r\nrseSNDtGPTqq9v6P5Sw+ozg/+BDkYf/gp7HV+R9P0lUI2c5D/7qKQk+X+O3S\r\ndBv+U05HD2ALvirx0b+WE2G12ywaqQIuVBk9WrNQYlJKeCn8+3bVxQH1yi1U\r\nknYEOxzyocALbnObJwdR802jln/CHTP1nRqCvl59Vfo0aCmKmT9Nzct4uFha\r\n2Wx9Kv2qMHCsGG5cOq08DvBji3Aew38dtEa0g3Rk3Yksnp1wZMQqatVlb1BV\r\nrOlbuJOuLFNCXS+agxxCIvH1BrfYPkhrVHZyrkDsLgEkxxgHZvmrvgfAbUqY\r\nNt6qjEIE49MaNlHp11WxKEnc2VAxpnw7d6N2J8C8akivj0Oqwj779k6cUNEI\r\nVmT+Bj9Wdv3HDI8DZ3TBbuMVcZs/a2+EWZs=\r\n=wMEO\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование а указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        () => spatnavInstance.focus('@suggest');\n    }, []);\n\n    <Suggest>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"43288c8e52642987807acda3e0a69445c5116026","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.0","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.3--canary.3.4087646755.0_1675456024727_0.26363139152142856","host":"s3://npm-registry-packages"}},"3.0.3--canary.3.4087649949.0":{"name":"@salutejs/spatial","version":"3.0.3--canary.3.4087649949.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.3--canary.3.4087649949.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"4ecbd94c8939e49a48ae724af434830186dc30ec","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.3--canary.3.4087649949.0.tgz","fileCount":98,"integrity":"sha512-YdARCGwp/357lLQ6+bcymcFBqjpLK2yuqr8iON8Hg95LhSXojQkdo6VHgA5wUdgafjhbQORttASoEJdrSGsUnA==","signatures":[{"sig":"MEUCIF2KfrlE9sSKa2ap5z8eD/n0XhS2gTUBvNZGSDrlSLhpAiEA4lgcWeB//++fVe7ACgbAtXC4cw++jvS8dixbM6hrxHw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96559,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj3W4nACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrxNQ/+PJnQAtadkBl96YRha0TMcITLyzttJD9Fn8Wq4QonQf3DGEJ0\r\nN3dHeOJMdPVJyhoCOWiy2uPIigOiTI75rn4i0k5ekEaKyI7M4FddbHjt0oNa\r\n6gTIcnXZsx0QSVLDcKqOTn7H0RCABojv+nYzHVXs6BRIhUy5CqlVdF/hYI8c\r\nF1jX5EtK6Sg93Cj8REw936Wgf9IW+wtrQdnzWkBYiYMCAJZW1i29j6GY7z+M\r\nFGHO7wYc0OlzpRQTyjwoccd8CPgen+McoTd22UAM75DEnW6lXF5gsLSDTyX0\r\ng+hxQbem5a8ciHsbRP98qjaPsS6Miqu5cTitHbqdGx7/SJgl2KXOWPexI1kU\r\nMrGHljn85Wjeh/gcSVCuXggDmTm8vbbdNx6IAm1F8xJ/MqZziLmDjgjHKrXv\r\nLRcjRuCEu9hgPUfC/cj/Cc18baeWAHLaIitR4j1hC1GhfXjQ8WpiqazQVZJk\r\nM5CwoXcNcVo36AZbnzrHIicV2MxqY+/78dBi/DO6njnrYSD5FTHOWWeSbKcR\r\nytiEPYxNcMnmgxGbu+ABO5wfqCDGCFyvleZotxguah8nLH6cgiaSphNVTKb3\r\nR1MWjXS2xosKAYtKSE5AMZdEy5GGw119mI7Q/R1X7sndRpMEweHGIhufglye\r\naSfSwLPxv4mrEe/jM7wCgTzpPOlDv0JZH7M=\r\n=OueD\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование а указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        () => spatnavInstance.focus('@suggest');\n    }, []);\n\n    <Suggest>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"4335871187785798003eebf166eee18cdc583f74","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.0","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.3--canary.3.4087649949.0_1675456039648_0.17850584215244414","host":"s3://npm-registry-packages"}},"3.0.3--canary.3.4087657390.0":{"name":"@salutejs/spatial","version":"3.0.3--canary.3.4087657390.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.3--canary.3.4087657390.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"f1afd941b1ac3761227f45066748f54ddfa7d81c","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.3--canary.3.4087657390.0.tgz","fileCount":98,"integrity":"sha512-ydrO7/Mz5UwPLECEFE7BTZgAtaGcUrTrMRQPigAwTEDeTwKxm51DXEaGSPZS5oBYZ2apz/LaMfdSTi7AlGSp6A==","signatures":[{"sig":"MEQCIHkOwaRnzKf1p9NyWKGgiB3givL/VW6yGJzfx9DQ4rmNAiAbMXhn8Qz3CHQnxr/46LaWNevD7tufd+UOjqzzSDc4XQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96571,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj3W5qACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqswQ/7B+oYvAhP0M849WBCw7hdNCID1ZCGyIWV0XUPCCHJHkwWrqRJ\r\nGocnboGLkDTgeZ9n4lL65NEiVhPi08j5WGlbJ/8CAKOCBjp0GT8fd0s9Uten\r\nVtVhWMgfEMB9dMfYtTvN1T+gG9pU4zZsQftMwzN/H29Wbt4qS5kYDt7ZUBWL\r\nd7RAht5dF0ejNw/P5ANmQsWn3BgcpiV+1YTIRQ2H6D9MUbtLiBE50A966hHZ\r\nh0e8IXA4UdhokIajht5ZEGNyFNQ8eGOGECPdarQF5FnQP+nqevzfIgBaH9Ia\r\nww4ptpHa3z7UUNd41vyjYLip5C22ZYCXX9NF9s5X3U67BtN3Yg11sznOqJIx\r\nOBjt8iaYlttZpB/yXTzyglwJdDcRrMSeJl9OIcF4TFoGQiwsDXR9iXK6bqoI\r\nEyG0eJePW8jOsNWYkzD7P5NFDwLnQuDRMePhqWms7IDN/G2h6mUaE0e8zppU\r\nXysr7BQijqa5TVBIzTYA5WzK5pqEcqya2SIgjPPCnZCerdKGW6XtvZHt4PQZ\r\nyYBaeBa/GOWrwusFdhgtnFZe69KOJuezf6KxMDlabRj7Ax8JVg8vtpsKopPO\r\nh1r5KxMPzS34ENYz2V4vhMHLugqlouUk3FDGsXb8OHSM83gomk7c/SUvbW/o\r\nNGm62gUmqOyC08XAK1fEdSjNorXrCkC2+1U=\r\n=cpm1\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование а указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('@suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"c6cdbc85ef53e3165e2b2d7bd95e78e338855190","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.0","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.3--canary.3.4087657390.0_1675456106311_0.13611071792344376","host":"s3://npm-registry-packages"}},"3.0.3--canary.3.4087668167.0":{"name":"@salutejs/spatial","version":"3.0.3--canary.3.4087668167.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.3--canary.3.4087668167.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"f77926192dc6f013d1e1270842c8489784a27196","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.3--canary.3.4087668167.0.tgz","fileCount":98,"integrity":"sha512-DGBSpGOMYuXwNITk+/L4X1JHV5LyQg4kF3zYxYGcMjv/LVgjsFI9mP4w4c7ptC0SbhXKXVxBBqg+rgWsH934KA==","signatures":[{"sig":"MEUCIQCtVPP+Sq+JHQPtaSMSBx/XnyS/t0c9KrEMb9VPgnfkIwIgLMzXxdb1NPEgHYN4lmGkq/8oHOUWf2CrNCjnatAYHpQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96570,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj3W7MACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq2KA/+Jw6JHUi68MN726HKz/ap0W7wvykT8WOvZF5QuHlCHug/vwCB\r\nDRfe8ZqCed1D+xU4c0FLZKVlhBIk+pISVhqTpBWaMP7Sq6Ur4CbNqhm4x4gl\r\n68/rqpxn8NIRqWyXH0hh2M4Tt4iJ1auWh4ZcJam+kK/WmoDWluEMp0VVYHTM\r\nNj5ViCpi+2Wq+MFCg4ldxvxzmvHtI4TQyrG1Id0vqV1ipy7TdLO+BMdzPZ4H\r\nk5j1+vAn16gI0k3HE22ljjlbzotg/4bfy7IkO9roRC/ndJtXmt6WcbNi4LGH\r\nR/2iIB7ymUNC5XzcXnDj+kFBU3nEJsn35TYGZuuttzKK4rlsxuTkAUqytgOR\r\njlLznz4tHBL+xZ0k0WhOktnVs/OxcQSW0SoMQLkeD9Mo8noK3oLcct24/5rr\r\nLdYQlZF2X19faBZ6YaRRp1xlXJppsbtgVy2AWJ8xIss6FBzs0zZ2wc8qTugE\r\n17+96mZicHbi/bdUx+PEvF/mLTdzQgtg8vZLWrdOGJQATDMLStmgepqVP7NT\r\nOXeWXpKc09hBhy/884NoYNecsDdsV9KC0tX3CIO0NVdqnxsZJWGNWjPRUqef\r\ngv7qk75on988+t/BC4qMpffBfvnwTFI6zoNS1oGjQNynNQm04dMsqitcXJ8/\r\nF/0IKcugvBosJyHXC+hhYdfKQKuELjZfFPE=\r\n=waiu\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование а указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"46c1c8d39212311740b7bc681e686c26e472a171","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.0","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.3--canary.3.4087668167.0_1675456203971_0.30857886321961314","host":"s3://npm-registry-packages"}},"3.0.3--canary.3.4103451699.0":{"name":"@salutejs/spatial","version":"3.0.3--canary.3.4103451699.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.3--canary.3.4103451699.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"84274ffa3001dd6c7ad566ac530d9e9c20f99151","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.3--canary.3.4103451699.0.tgz","fileCount":98,"integrity":"sha512-YqVyREx8Bf+uDSYqaSEW5UaK9B98BDRtw5byS9a/gR5gGsNarbmYOrMCAaisznTo2CzZFYEfoJa6dn2XfPernA==","signatures":[{"sig":"MEUCIQCfXoKB4bzPjus3tuZk6qg+Q7OAArRIDAKEOI7IcYJKQwIgaz0RdBqLXP3KAbtpm327StNHP5fZDCYAWnKyUh8Q0Kk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4O34ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmraOw//ThP1uVbaS1MnDE2+oDAzb7aQHnDG0KQtCFyHb9es23hr0r86\r\n5Q4ygGsIN+SUVe1p++0Gu4mcE0Xn+9Uq8yGF6eUgxP39ht4PeshyZ9C7Rplt\r\nHK1Q7qOdCbipKxCZa9isUGt/5pTeF1Z1Av1PaZIQQs6KvsATYoLUFD/dXdri\r\nGfpM5pPvbIEbnkJ0J1UWnG2mnAJe5LVks7wfPAGTq8Tm5xXx5TdTShTR9DuT\r\nWZet7maYeBNCBYpL+lJw26flCTvHY6PaNGfHYvGySA8PZAMbI6gxCkpF8a71\r\nEanXnAKk6TduKhiNfI4N+io+1X5DfOSdiWmeD6CEneznJIUMiEKM+b5ZNIX/\r\nX+h0TUbGyMdI8jdZC6JysSptNIM1/1Zufc/Wcf0xPygL06kXCNLjqQB1snSf\r\n9A6jFbDYQ7Zs1xk3y3w3zAesP2g80MBKPNLrOwuGBRyYBhhH1DOFMuQLmETs\r\nvk9f5jZ0YUqGnAKSm8fOGSZ50RBp/2BpiOc7b9m2iCFIIEi4WTRlrYBhoLWB\r\nXmVQyvL5fEy7k0kQ95KCYP3i2SIKf9uNbgI33geOxjJ4O5rXImvA9ysCfmf+\r\n9TJrxQwLI3TX7IRoSasT+7tDzH3q6nT4N0l3oOwa1R+1BujKjJaEFg3L3oJ/\r\n9LSJjWYbKdQ0+B5uudot13ESsB8hb8B0woI=\r\n=m8Ln\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"7a006298544e83214b57fe768a55156fe52b2c8d","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.0","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.3--canary.3.4103451699.0_1675685368689_0.38456179059603657","host":"s3://npm-registry-packages"}},"3.0.3":{"name":"@salutejs/spatial","version":"3.0.3","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.3","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"9fdb087bd87b5406b3fbce88eb226b83e066f785","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.3.tgz","fileCount":98,"integrity":"sha512-KXfcCAOyKDHuXod9PaD1Vw93vIPXoH8K7ICoXcRBF+wt4B+eC+t0QxfEBcB1ab0znpHm4k+ZU1wDnM6Jflt3qQ==","signatures":[{"sig":"MEUCIHbG9UXLtiurUgxjdayepzeBXUYJfWLx0Httixt4WukaAiEAghCYXGT7q3tBqHt/e9C075ZOy2L9YwupCNjID1eNDhY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96544,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4jfJACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrn4BAAmFgmlly87Bqmo2KnpDDxENukZoxZLR63kmiFpvHw+un9kZo4\r\nNRDnM0eFnX/Wp+s+mh0vLEinOTP7FS15oEHSjITFS3AnPoFLJskl8LVdD3UV\r\n4tqdYUxAmoKDiInMWpysT2kIlsc2+ViMKPfVD4XQ3omxIoQv2Lb54z2Px8V8\r\n+1Lv1Yp5I1CZxIARsUS/IkYOS0W6bAJcGccsAF8iY9fK8xg0FhQ55GgoXMbb\r\nIvcosI41q0be5BLnM2SWlvuSL7jKQ6xR7NF4QmZCNE08pNIR2YYVTWAxSkTW\r\njt0vzjwSKZF46jCpJtfUjfn3ekEasU2TtEtRV0fvBkEDvyW/PhUwwNetr/0v\r\n+83B2kchUhBtZ987wGzwxNWD0c6GfSzElyRsqrIZBsUEmuoJJgkkpxb1i/TA\r\nUk8xYG4Mxs5RU4LaZcZ7/jeXKar/wNx1Swqs55CRqT+CltYWto/gXCiatBss\r\n+dN1AVTWpl0ulmSIA8W0GPx7C7tV2grhQyiEd4PHvQFocPu6Zv7SljVVpeUE\r\ngFHIODD0NG25STZpecTItDhz7S3eDfZZQsK6BECEJXJDek3TpX0ADdT9JxsU\r\nKWkIEMHZhHoSLbnqWIkIF98/6DMubpjM46o8aSKSvN9ijG/McfERGYNoApJt\r\n4lAZmeyx7vSWk9SeU8vcnTvSIfY5NBXLN88=\r\n=pWZC\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=16.18.0"},"gitHead":"971a4279697e6a7da38c1d028b7be3c8edae6e28","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.0","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.3_1675769801660_0.5264425850389423","host":"s3://npm-registry-packages"}},"3.0.4--canary.4.4325805884.0":{"name":"@salutejs/spatial","version":"3.0.4--canary.4.4325805884.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.4--canary.4.4325805884.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"b8ab5adf2bcf53f67f13a012341e296e0a143101","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.4--canary.4.4325805884.0.tgz","fileCount":98,"integrity":"sha512-UeV3ry9OsnNesRlY7EhuSyjOp81QVJj+9VyN/ozI72CT8zmcJTlF2xjFNkOfisUD3vVjwqsFmTvxB32/OwK/pw==","signatures":[{"sig":"MEUCIDiSSgj/d096GpBdk+1B65/DdwSh5rJgmoFv87F5nejoAiEAswZN6hgXsmyMp5p4olaEwbRKqF759io/4eEro+Y6Gj0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkAi4SACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqS/w/9EUzjyvi/CLE9FuB21+RhS+IzjYrsdNSxJPN85+erwxH8zZ3G\r\nH8FxnaTCqIC4DDUFZEUt/5rGzsOIDGU2PPzxSDxSKc7dyLQJjyQpIH7Q42Zt\r\nAJxNOreuQSmy73pCyvLqqET8H+YDk+agMMS+hXmLAs1V03Tmt+//JNLn6SVu\r\n6yI3MqjX6rqfoXDLYpErxkrgOtTZyWLui9n+nQdkXq0CFueirc3/L4kzK2f2\r\nPqycbNGshsxbxll7YdgE/OFtvCa6l+gEHfKQgR/KAyVGWp8IUoBD2/jpMQ2/\r\nvfKV60dz65zsdIvRMzAZ51iNouviVOzW6BOfoUJLHHHeJLYew8CtAmo9vqog\r\nqZRZuDtld9XDWAyrf5x1No4FVaLYacipAhfsK2IpJ7Hyce5b/+1ZK6ZQ4FfC\r\nLjcfcWrNDAe0NARKTE1ksjrtnhRYtglUgWsfY+bw4E31+H8H4qpUbV4O2tMi\r\nknwByW9kBQpBkySDGLe5c+Rv/JDrPyn55j9GotsAA3askwAF5NiqUUfW7d28\r\n0waSY4waX2OH5jKo/N06REbbZSqgUcDEj+uGecGf6Nui7JJQUL2fGOLs4oOe\r\n6Dybms90eZaG2bIHd7+A60EHcX2CvxDB9eN0qelQxns0s7KkTFrxIZE9yXFD\r\nWanB55czmlHdgHlSQdQ+6iHA/TvW6Y8ngTE=\r\n=nLFq\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"9f6a7a806f6dee67107e21e5d74869aafa3474c3","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.4--canary.4.4325805884.0_1677864465912_0.7839937621392115","host":"s3://npm-registry-packages"}},"3.0.4--canary.4.4325864037.0":{"name":"@salutejs/spatial","version":"3.0.4--canary.4.4325864037.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.4--canary.4.4325864037.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"bd53a12228f22a8fd05ca177bec098b8b9637303","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.4--canary.4.4325864037.0.tgz","fileCount":98,"integrity":"sha512-G+rwU9brIsV8zQPp6FNoMepuafjSj4V6iI0VX3s7V1b1oRgjE/7xPDOuos5M/Ax57nZvLqYcJMz1F6yYSgzjsw==","signatures":[{"sig":"MEUCIE+2wkGIo0qGDkxc9j2jyAnopybHZwMmN2Wwum3aqrXLAiEApTia/HGnRZGuHDwIYUafTnxYYVY4yuhwr3i90nGrlPA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkAi/ZACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoH4g/8D5gR4MZ33B140dxu5g8U8ksH7tke6lllHdVx4qJo37B07ziD\r\nBkUvccWJxhB/4Qulybi1UukZxq6VjW1JzEHsuMWNoQnO8UUq8/Smj/LWGlzr\r\njmrAlSabWPa6HlniZzQop8SP0h5AAay770c4z6vNEKQSvw0ZCxTJUmdNKQsm\r\nQcVy88UI4a0DLmBHAM5KJETqNS8UZ1TdhHAnv+W2JTEJOz5j3W4iXfxDIy/W\r\nbrLy5x4pySmk66Ba7xuT1Obt5Oq4WvS+8hVNlrz5vgBd8edLQGqc/hq92DlI\r\nfedPvBkBj1YZfAkDi2lqlRrw54eEGDsLb8lcWArmDVVgjNNcz9Tzrid/4Av4\r\nzNoZbDGVUmIgyldz7GSsL/I/DpvmpkMZD8AO3V3gB6rHWis984/9SEsGnk7E\r\nrA4athcLMLtOx7n16v1U4RsGcK2+n30tfIZtRmcM+tUGF2Vz5j2+evcIsMJo\r\n6Rzo3wkr9qzgt91DEB0OBMlrE8ybbagsuTZ9ineIYkGUCaZnQ606LXWsCoyg\r\nEp1dMENg+br4IDQe6dcYZBcY4MEsAr42zbBBV/yTXIkYn5BtlKOXGp/r7ObL\r\n1m+ZkfhS6YTmKcJuQKBSqLgLiU+BgW8aYRrXLUeYEu6oNpHZS+7GYJsCx2VW\r\nCqsE081PDyhRUI+3wxmYMehT6PsO0+yhaxk=\r\n=da4C\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"65371ff31dd1005e200e615cb5657e8aaf71ec44","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.4--canary.4.4325864037.0_1677864921232_0.7427493998451302","host":"s3://npm-registry-packages"}},"3.0.4--canary.4.4325949860.0":{"name":"@salutejs/spatial","version":"3.0.4--canary.4.4325949860.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.4--canary.4.4325949860.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"d73e9aa3beedb4c9a4ee7c65e8f2a0dcb971fac5","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.4--canary.4.4325949860.0.tgz","fileCount":98,"integrity":"sha512-qfwvD4k/dbJ8BpdJ5j0t6uvcnDiHmiGmO3m4YO/BItoftuajBh8zmshPxbDBrxy0R/8g1xiMBNKgLx/3JKgqXw==","signatures":[{"sig":"MEQCICaS4ej6QLMDzwULynBB0AP8g88kC0bOOIKsPQYIVDkbAiAROelZ9Ud6ZxhG49ul/J/LyiWRAaPSV/4IamNBnceD+Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkAjLXACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrjYhAAmkaH8oTGwviC53QyATZw8SldU5SADjresCtZ9kYJagfFCOQX\r\nzM0aikjBjobjL6O1OM7Ny3XhQyDHmA/jiEHTjBxRPAguRR/4lsQegaOV4Ua5\r\nju0Ae4NqxGKKWqiri22YsOJzOIIeZYnDs8MGge+XV7MXX7NjiJTxul3Fs5gc\r\nM3n4b6fXsXkl4F7due3dcov1BWem2JtWDgGRVdvUzviF0kwiM0MDee6SaCTM\r\nb3jn5z3bRXVudctLFSXg9IBjcBaLYeMjXFKOTvh7VxQTdF6oF/PXfK8b8cuB\r\nir6l7lBRK8Oyhv8hNHtYF5RBiCSt7JKNTwn/kYrby29mm2BwNkgQv2giBJhV\r\nhIbdQiVjZYh+/DLeu6TYcQY3t80mbt9jSVK3B0mMbWatnY9RPdyK69BNL8Jo\r\nlVByFP7AA1L7HIn6FWtrXeOyQhyg3tFGKxyVEmUVvTrFqAEwXHnoqidUC0C4\r\nbOZ+jEMD/fbmBszVS35HbAJkuOLI2EG08yAlpoweyYhHJhlXuLIKE59I16uv\r\nz39QIy8/GFz4XmvtkG1AxkHREM/EQqU5Ga8F9Z5pjebq7DSvx7HbkMR5ra+w\r\nIqCcWns9FTzWAE3YmHILdwBg5tGFJrMBLxbOr/eS3NhItdjJQnvE8Ybpzv35\r\nu7oDtavpNfUDC3xdvL1SdGT04AYIuuW4NEI=\r\n=byjV\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"72209dfc5e2a2ebb55c0a813b9211edaa971c7b0","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.4--canary.4.4325949860.0_1677865687394_0.852835820225534","host":"s3://npm-registry-packages"}},"3.0.4--canary.4.4326068880.0":{"name":"@salutejs/spatial","version":"3.0.4--canary.4.4326068880.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.4--canary.4.4326068880.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"0314b75ce74a3e314d59b366646dad498adf05c3","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.4--canary.4.4326068880.0.tgz","fileCount":98,"integrity":"sha512-qX4J/nK9VnnDAJYnVatH6JdMm8tJOFaZK/4j0ITqIFtXMT0613jCesl5icdjmEOhflZTI9EPGWvEz+LLBd7qtg==","signatures":[{"sig":"MEUCIACGrHKg0HOyp2jUGn+uRS4cvK0yH/TjFtUXeaaU5X6yAiEA6mxBOR38b7FBIcjUQ5DT183Ydoi+6vYhzKDjC1YtBvc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkAjbsACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpsTRAAliV54AUKoP8Zg/vLcHqdUroSrzxCXPhaeASUSRHGfDTFgXXy\r\nzLR0Z4CE4EKOnPlUwvqmILIRbNI/pbdMy4S0UYUEi5m1DrDGA7RK/N0wJA8l\r\n0JozaOoLZkOcecJZnwhZXq7dXMmQCXZEURd1jTys8S3nTMRTM3VxNZ1RjyjX\r\n19+/6Ddy/IdC/mdv29Zt9qzaJoazocpmYDEgxmRtgWZrh7sYnkimOFOsfaoN\r\neXZEiDNNYbLZ/wlt0oI20eQLIEG6OdeySWhS/7kAR0LTX2ZP3CQ5mbmkiifR\r\n2rDQhB6RT41kXLtcHQwBfbEx6emZyQ80TM25LI0TTCiIyVaX5L7iT+p2/A55\r\nLBEV+W4UgjSPMgC5FtTdOIJW044C7fs6ow34hzowm4qfqvhM5dfxSZCZlXRp\r\nqi92uawuSZVIJxpud3zYzZPbH9eQ2dabjIpIsnEr1rHmqwmD9oouXdpM/TAq\r\nYQQYDy58OOA3IWf9T9f7CHdoa+esxQLCkwMLJEeas6xRsr1lW/LEwlS/IMOh\r\nvXNyqOLcJdMqrXA1Lv4wILl9/IDzb2q0YALUmHizxrbL2uJJxMBz0a1RGu4A\r\niN9/Ro7q1o1srqRTRf8/NWj363qdpnTyynB5GrRhQuT5iM+uoiZMMmfJkoOA\r\nVV8PvKZfmpRUUhDIVSVjTj5CLixBEtM+kK8=\r\n=GVVU\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"0e8883457af15d3b09509387e5b3aa82ee3a048a","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.4--canary.4.4326068880.0_1677866731916_0.4759174433791842","host":"s3://npm-registry-packages"}},"3.0.4--canary.4.4326308174.0":{"name":"@salutejs/spatial","version":"3.0.4--canary.4.4326308174.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.4--canary.4.4326308174.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"c29ddd2f0fc7c1707d7cc5136511538493a117be","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.4--canary.4.4326308174.0.tgz","fileCount":98,"integrity":"sha512-1/R6vGpQGzyBKEK47RqR4CNG4P6h5MTZw98Um9Fo5ngGxQwdBsx/jzALIoAUXdAV8nY9qz457HI4j5f03NfbFg==","signatures":[{"sig":"MEUCIQDP0Qzdc2ECGIDlgsA7Vhzx3f7YAXvGM36GVy2HMRxoLAIgJzdo/RBobfTzX54Sh5KweLfW3ypzltcueCN6OtB5gnc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkAj6ZACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpGAxAAiqYhSqyDLGZuRb2FIwr5L25sOxMIOxK0yO5aBSds51DdHUHL\r\nmztgEtFQwdQPWtmO9DobYvyr8olW2KrpUpyOZ8g5dzA9D5/37pt5pOAJXJlJ\r\nuj/A6Iqlz9KCqAaN7Y5H99Yqr22PDs95F0ICRKnVsQyIK6HcnFFqkiPDGl8W\r\nblZtrDUtpadTVdSi3seHx23owAcg3syaevP/lSjhrn0SJv6U4aOEZon4Vxqx\r\nsgEhUM/Vho8f8YbuVJHB7F0jjKEhY5B2agL1f44X0yTQLIR/gSXCLEhnfRbP\r\nlhGFZXP54+EGXGSbmuy9YsoPg5MOyB4a5cAGrYRY5zC2l0Aq6uuuNqlEELW1\r\nqXXC5r03Q6Gui+voeoMt9sVbJ3tJiTHLZe8OKIzxVH+fiJ68DBg5vTF927OX\r\nRYdoyEp1oKtAlrg/uHD2s/NjOSIOC4vFfYO+IrHUjZX2fLXmMzGfT/vAYE3Y\r\nZTsYR3+RiNpbpIMspMqIB+zERqeFizof7GCO3lJZJ/umWrVAIwfTzt7MVOGl\r\n/sALq+fjDntQFSYuPSHKAqlLD+aoO5uB7pW0wXuIjp8mpIw8Aw3UOeZfbRzB\r\nuRj+j+flzONy4Y5sC4iAx/badzf3QNAprt1cLm3q1SvOlNUlYFEw2E0An/Xp\r\nLT4F4ExLnqqumDKLC18zZI5p/7YdrSjPM6k=\r\n=Aklo\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"fe4b9e3a5d47780664da7048cf1ffa67c565191e","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.4--canary.4.4326308174.0_1677868697691_0.671544542785709","host":"s3://npm-registry-packages"}},"3.0.4--canary.4.4345357659.0":{"name":"@salutejs/spatial","version":"3.0.4--canary.4.4345357659.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.4--canary.4.4345357659.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"28aa0e7f1d4af6689d71bee9e53b2da3fa767da9","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.4--canary.4.4345357659.0.tgz","fileCount":98,"integrity":"sha512-rtoX08/4HZdMqADbR3keO2qTRKCfGlZigE/vXehHUgJQ8TDBBX2EzTfIpaWST/l+82Fzmh+hmnb1Po4+jTVnkA==","signatures":[{"sig":"MEYCIQDl32tGpp22nemwO5M6rQSh5iujeNBUJ6g5z1bZIx7d6wIhALf9hakjQqalyWmHxMy2+h3jlofw6NozTtQ16udwewSo","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkBg2nACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo3og//X/adgudRmILUX71FeWtZiNY56PSWfhNx73GWpiGHhUYdxm6f\r\nmIjFAZGXGFf8K5ysNKabRZ1VGNkiEf2kLO28xpuUALrN2l/M39N2lKEcy27Q\r\ntd2mivszXQscB13XEQraLa+A1As6418d6CwCSV/spUhX9qqwDu94g/VjgCgQ\r\nBpsPzooAhkwbSAsnCfNFoe8qBKF5kTgmvO0QZNEWROWoSI0/HjksWPEh+Rfv\r\ntENaMX1Dso45Tfkx48kpGZvyhkYTl7MbqoU69+B3pzF6G/SgTzhafzvH18ND\r\nysDiskiavjUecXacMR2wn93jatp7oeCEQTjrKq0nJTAgWsotqboSpZ9HV3vb\r\ncZGZN/pAETHhy1JpfAH0Z+Q5dTf0vMRY77swdftUDtzXOrin4iDtquRCEIPK\r\nrSV6Top7s5d9m5jvgQEUuPjn0H1//iyfvEx7yDQNkrNlTUWVyIdzQfPniLZA\r\nCOcMI7731lGT1CkB10fRAM71rhFW90Vl4QJ9CF9we+82XFBEwxbrSr2zfj45\r\nq0Dyxh3Yfgn0tb3YFV+jkPdHtvrv1PALU1ol1LNOgsIS1lNa+f7MSsoHxOFO\r\nAgtuhvB3aWJcqm7EoO6ZQFAyk94kQgYozIh8q81Wanga6R/huMj3vlhSyrI7\r\nvDK5HEydKEpoFM8AJyl8/KzEDh+6WqwB6uU=\r\n=1T84\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"1a7a499576a5fb20d301732ca1ecf4200d95e702","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.4--canary.4.4345357659.0_1678118311510_0.7460607556677248","host":"s3://npm-registry-packages"}},"3.0.4--canary.4.4377057885.0":{"name":"@salutejs/spatial","version":"3.0.4--canary.4.4377057885.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.4--canary.4.4377057885.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"974f0fccea3f411a86050cd5b74c424f142a4a32","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.4--canary.4.4377057885.0.tgz","fileCount":98,"integrity":"sha512-xyU8/IRvoSES8U/9D8io6h8b2sMxgNR/xBxRQQCutdi+uOQoUxYSpsIT8IYtQs2xrgIK+WU4ulX0mrkCc83kdw==","signatures":[{"sig":"MEYCIQCHe0L0fPwxSUT845S34RmcVuhupstUh+b9Ox3eJtzXuAIhAKpVw2eJfleURhb/ARz08y/YWgF/HWl5WolK6XAVZW4Y","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkChWlACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpIhRAAguW5FtnjIXJ9TI6AIGjl1EV6VMoCQP9U29Qec7TnL92uVkE2\r\neB2XIzPrwDH3Qu6Ke1xqwFjPUWcG4jEtxCbghEmELM3pWbUBhebrQhnWTHmE\r\nKC7pES8/ussBDHFY/J5PtpKnkRmOdvmb3PT2rzWgnKcQ0KHmBg40+BExQoub\r\nOfVLPiVqNMFTPiA95KvgTvopMRH7wwbsnevVD+9Vp+HXZ+YSCHoF6TBK2BQu\r\nq1EEdl425Ig4n7j/3+fByOVKVCwMqbKipY8/g0o5echlMJwMQIxyj23gydz7\r\neg/F//3MvWl2SO+i2yJnAUv25JGxgd5U8cfVjUKXpy6lNp8/858IdOQFBqMt\r\npbTm80Bdhdd86EdH0M3cpBWylJWD8tA60IgdemeQMUQFbB8SF5jmrShml3AH\r\nDXO64ZpKeetU3FH/AX+eZt3sUuk3thRDsgBAkVt91pgG1HfyqukQWIXhoBS1\r\nL60Ev1nK9R6wkXHw9cODFXV0XCZwL/0A74hOE5EaRPig69L97R4waAXsCVwE\r\npBUGP1obAusCfOAP6muLmv8hqYQ3TGvmd7+I8SCxrwnOPLHRdjMxssy8CLU6\r\njD3XOAOpCQYvglVlXjIykDHpDX1K6pWimwGzims+uJmFgmQ7WpGt7N7y3DpB\r\n4bOSEbmuJZUonSMsw0gmgSfCU/y9Jb2VluE=\r\n=GEWp\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"0e50633d09a26851c657fe4347a509b5cd2eade6","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.4--canary.4.4377057885.0_1678382500930_0.2579317599220976","host":"s3://npm-registry-packages"}},"3.0.4--canary.4.4377094370.0":{"name":"@salutejs/spatial","version":"3.0.4--canary.4.4377094370.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.4--canary.4.4377094370.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"0a16cbd2aa1980de46b9eace6e9c66810aebe3c3","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.4--canary.4.4377094370.0.tgz","fileCount":98,"integrity":"sha512-sEXWRiFPZ7+jXYXg2tcRAzld6b3wV+ykx0uTiTdbbRSwQQXQex+fDnkwF+e4XN8Um3LTOjB/XXDGS99O1lixFQ==","signatures":[{"sig":"MEUCIQDXexOOAa60Q0/urzId9MQMjuo7dsQCQQuNayLijpoLlgIgRMp8uX65HlpQOccQRejSSl/lioMUHmJKp3zhSUyVGmk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkChabACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoYBw//SL5mUea7MbmEe/n1bGa3llmhEGFxbt4J3nt218L8p67UsuEi\r\naBAVSKjANJ8sa9BpN6vZKxPPD7hmScjBnlf31AJvKrQjfRgiew2HVz/xX8VD\r\n+ssNLXjP481HSujGMNmA4seTXU0YVHdMPA2wMA0cyEscYhiNyyCKN32AcKzv\r\nOWo+ZMJ+rzEbYHKyUsHHLazsSUYgoArXZ796cztKxJ7tZ6g6/tGq4rNY5aLr\r\ncDwBE4o2qZEUdOJ05TgkPu+pQROgH6m4oRtdyxQpcr0t6vM8FkYD9+yu2U1N\r\nq9+ZSZhAM/umv2lcTU9U84TqMdeyDp1c3ZWTLAKopC04zIIRcecw0m+blzEf\r\nmh4mDPe2aYW249xoi30uAxEpk/IsKDtlQCjQEXb2/QbU8IIKYkJpA/0fzf9k\r\nhGwgA0m7kzyBMWHdKONiyGhoBKoAW+w6Gm+X9ZxjJANOTKKpQnrx1DM9dmaV\r\n4hSQaCbapnYH5gk8Ob1kCYd/EC2cmCJPl8n7Yp5srf+fqEc1Dm+kxkHtkN6k\r\nkTX4iSqCzs336Pdbzaug0+HvNleu+v7M3sS8udqDsGr6v8Fbztfj3SjpHRJs\r\nqp+2RjxFISmZdpqSwTvllamFAzt2Pr7RVUWD9abSuCPvU8kycYhgm8MxfQkb\r\nCwedMn+uQa040ACmwe7cZsQsqubZNuJt+8U=\r\n=Av9N\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](./src/core/SpatialNavigation.ts)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"4913bf7088ddfa28a57a2be8ddca3b19563645ba","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.4--canary.4.4377094370.0_1678382747525_0.21150449231903345","host":"s3://npm-registry-packages"}},"3.0.4--canary.4.4377215783.0":{"name":"@salutejs/spatial","version":"3.0.4--canary.4.4377215783.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.4--canary.4.4377215783.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"4d108d1de639e70c24f6fc43c83a6bcc6f54a17b","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.4--canary.4.4377215783.0.tgz","fileCount":98,"integrity":"sha512-MCfs/lW9jCbyYiVZeXr5qn+W6LNQkf0cK8sAvSoGG4Av9Kv9szU1Rp2U3mSXOs33URZa4iaz65VWNkX4m3uaZg==","signatures":[{"sig":"MEUCIBIZ8Vy1gxeWrz4CGLZ8hOI6xapFtq2qug+uCR7pj6jRAiEA2huHZ5ud/N9OYcVpeRLknBgZKGyTIvXGnAF5SsGq6tc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96915,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkChnaACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqDvg/8ChCu3mGhl6evVCSC4A1eSmBLWaFPAgYPiF/7QXwut/KAZQ00\r\nPrX/NgbYWJnZUNGzKAAT9I7XNGCD2Y9NnPNZ4SJWU+qgGTalllwsPibKr20O\r\nbkuY4VT5rW63P2YBIFTn/q9rG75fzyHk4YAGqiSm9KSLjvI2kIg5xUSs4NGd\r\nijYOTUK+KcOO1s1oLESPdP7jWl6QGVyVAhanwvJMSujpvfnOuVAuCaticYX7\r\nHf8xT699iFngL1Eakd+z7KFyTdr0CJ2gwjQ6rBc4/1znhfq+DnpmIXJ8Hzt8\r\nTdhhelGj7i0jnEc8+qWDQdbB9Sv9VK9m+PoWozhhz/vdbYXnrryAXGKb+biM\r\nZLMr+kCQaDpx02CNBaoc+dm2IXj7qoeCSIRairdTwxJUKGrgkVhdHVpBPztM\r\nAdBSWBSM6pVrPvHnvSgZCYLBJg3bTFchXsNuWDyUlo10+4I35QJKtKz3bR5s\r\nQUy1Hf1OjcrCE/nY2gZCLp98OijM2VbCfhi5dvnyfXBW4bqvA8y8Wyug6Vkn\r\n9B+TOBXs+savAGDTu3sXXpK9/9593seZzHcNAWZIcITTF5elmFrZgnHNkqxt\r\n0RwDZcyp5UEyYIc+zJapKnu9ACPJZjxCTXvnU2VY3z2hRHWLRRdMixkd67bh\r\nreutXV3WF/KEVbJNVTXOktTawdWcnJ8n7Ps=\r\n=W+Be\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"4ecd4ec4791d7c332d0a430b5c6a5de83f8d4f67","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.4--canary.4.4377215783.0_1678383577948_0.3088751756524388","host":"s3://npm-registry-packages"}},"3.0.4":{"name":"@salutejs/spatial","version":"3.0.4","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.4","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"47918774ff946edefcb2806ca429aac6c7f92ed5","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.4.tgz","fileCount":98,"integrity":"sha512-e1lyl4Yf9eYjz2JFfAhSZ+2QL9MVQhIzfETQrp7eHshY4fXy+5xGnnn4C6bQxb+K6M0I0NntKoxO3B/3biUVfw==","signatures":[{"sig":"MEYCIQC7eVK12NCT8jT6bvXPliAnFIVt0ui75R1R1IwrFSX7qAIhAMJauYIirhLkhfdjhxfnKzKEwkcxuTOmuO+C8tEoHQmu","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96892,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkCuLeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmob0xAAg0LhCg5VcDMDRulkKNl3zNmEOcULkq+3rwxpbjRN8Dacb69D\r\nOhJk7DKDXqA55GSz7QPkv0aPhbbfbqQOV5D1wL4XLI1I87eYnHW2RqdNhx9U\r\nyPCXX8Qp2K2IStYI7K6c6VCPuggOckDYragjAaq4G/uy2z0Z3GsAQd36IlVM\r\nrhCDKpMLPWt493vVbibcMUzJmdz1aI7BfIpnEKEAyBCOhC+Pf26Zz4uoj4TG\r\nRO4ba9grp2pBjG6e+LiQA3aH3uKCoN7Y0aP8ij/XSoC0+77k5hyG1uSowac7\r\nXVS+fmpnEzDTNZ9TZ1MlxoECBITlj1rjA0MP7qkrTfpgahfZiDCIwWufIxVj\r\nXgmKDsRy51GhJu4eN6PBIp1hIVRSuJxfzbbhnUGknLx/JQ8Qh4VacrFIJT9J\r\n8JA8PdEwMSROYJPt01Xx5AEhrzzl8uPQ2KIS6F79Fhnydt+qL+0Y3mHoTtaD\r\nFH3XPbbO9KUWnBYvZTk5X+U4KLin+kRa0Om2m99oNq50i/RFsTajDzmO3D/M\r\nBt6iM9km+smn8mCft2BIrX0tcR3RuG0s0/OmFdKb0HMxTcCNZ1/c75NwSrhw\r\ni2HnoOkOaTOe0YuEEMNwrpzcYdjIioqpHR0ks58hn3vEkMOl8inx1s0yXAfF\r\nmzsFCcV47UdD+6cHoDhWI8nSL6QmKS/5WLQ=\r\n=kTEc\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=16.18.0"},"gitHead":"7ca9208d8d33679cdc9cfb379b5e16b44a64585a","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.4_1678435038629_0.7649486708197093","host":"s3://npm-registry-packages"}},"3.0.5":{"name":"@salutejs/spatial","version":"3.0.5","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.5","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"112a9a5cc848b43d3b28cde5543ad34ed9b3d617","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.5.tgz","fileCount":98,"integrity":"sha512-3vNYhtZK9evHdX3xhAs+DgdlGablKnKYW1o1YBxHVZi9M6t3JSYWKwx5MCSXFZJwkju9h4isUgWZRzLGks2tgg==","signatures":[{"sig":"MEYCIQClV5vVCX1Oaif+htwPYDLzp/p/QOGdHmPit7FFNluiigIhAIcWGTF0qJ/2J3GWPrH4fHpMtXRb6SItLxSqCuqQfJ3a","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96892,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkCuShACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpR8g//Ww94ziBVtx1PiYWJapUCM1dW9Phrb5yfXvL0fT9vuEUtzmBz\r\nH++ueKfaZJZRAekRgn8UsBWTVNNE+QKkknTeNpNIwVtdEK0QIG8waqLrZCuk\r\nEWgtsq9B8Ofsr7Cjn4A2MhOy6d3gIG6e0VbovLtuWtUuWQ9KCldqNP6DoBiS\r\namjft4jQcrtkYqyHRchvbAYmGGe2dxBtIo4tEIZtHFfPLOxzt7SXd+3Dn8g8\r\n5h3J7dtBP1BCOElusGPIc1QdQmCGdFo/zGCcrkk8Wzh+ZIEmsVyjYZlu24O1\r\ny/q7LJfNbzT/CpIaeHTtvGUCfuiSeavBHYb4AgB8X4NFRbSWIJNV0hES3iTL\r\n9qj64669QZg+hOAppw5w5K4gpFKAF9N544zM53e+dMMv4tMD0q9OCwJv6yQL\r\nBB8E4tgXi7Bty6wh68xrKEzybzaQX+Sumg4OzrUtRCKCirhwyXN8oSaLBxoi\r\n4pENnjXzs9jL1gDDrXpkIvNIBvjLge2QAUijG7+wmi8DnytpblxSc4vNGjgl\r\n+CVPY+EUgUMDMeJekoPzWCJpny+/zMOk5cJw5tWIskOiKDu03wdktHoEsLFJ\r\nvFshEgQipetAhxv1menBl6WDFpC8iKAPS494gIJLqw14v+8JE94IRtCqIybb\r\nAp7QH4WjS3AKmuw+YeHlJRtFCAS0LYkNzhE=\r\n=om8R\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=16.18.0"},"gitHead":"a5826e240d1638c15f37ac0f896dd3c38ece6479","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.5_1678435488946_0.3087564496759372","host":"s3://npm-registry-packages"}},"3.0.6--canary.5.4382669952.0":{"name":"@salutejs/spatial","version":"3.0.6--canary.5.4382669952.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.6--canary.5.4382669952.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"b5fa176f6826e64b9cdf019f4cb1e091ba20ad9d","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.6--canary.5.4382669952.0.tgz","fileCount":98,"integrity":"sha512-J3PzlOZhmDAennMgq2c37jKQMz8c18IqEh3ICObRqXKVBJSc3vSqOdA4A2JTJmW2GmPM4SjoB5Nn1WeHOBp26w==","signatures":[{"sig":"MEYCIQD+u5+ZAqkJkADuXAOMIelO3CbJycfNdVUpDC7pj2C04wIhAOYj45ayDpWponKOdc0YiaV+H5VnZhXzpYb7yQS8Fp8e","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkCuoBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo19Q/7BkAC5sQruPWIyVH2BcjwN4nOQPtQm8NjowqWN9mMlASJyDS9\r\na7IQCc7xMm9st5MfQDc1OGGgRghoGK+6nR9EfdvhYUdL/urZWZNUcFnPVu6I\r\nB05IyKWKO+2DEWbsSkD2JQNVBSC8s2ZEYrjnjLAUPs1JGMBThOCR7epTw4qG\r\nr/OonJt7sGBdwLeUMlsRHPohjMrUt0HqED6qItX+n6XkrAC7garR9Ivb/QaS\r\n4wz8/iU3kq3GAdf8LLjBbLkUUgc+DjbWAC85ShfEB6vTrx64l06x7jFkiiYg\r\n1Tin3KU6Q3M3obTfXv+fsoshA9M/aJEnqxwYEK9KhPaMT8KqMadSDsYDjfbt\r\nvNshWJqKUeBUEP/SioZJKc2EPz0Uj8wHO+adACJVabSyXxpRgUVnV75NEr5E\r\nDuQdzxxHEcTXn568I1BhI9kIMJDhNqYo/HRmUHKO5SPkdF5WA/nRBlllreYd\r\n0eMbyS0HRuMftNwn9HpEdudtbMESYcgFFt4ouO8yPIOA8aQdQb4nKQE1jNpB\r\ncys4ZSoXlkBgd5L2A4uBSJ6cb8o/N24DRdmfji6Vbk++Afmb8fDPA7FI5k+Q\r\nQhE4HlsNXTMG+OC1Z5I8BNaLTuvxOl0BrTbzPo9xuilRAKX4jmbnBAmkspZw\r\nECOEGfnEZj2yJy6+EPkmtCefqFzZRlE7uys=\r\n=cHpn\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"d111edc8c79ba659c1d89c00fc3cdff57df4121b","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.6--canary.5.4382669952.0_1678436865538_0.8342218487131687","host":"s3://npm-registry-packages"}},"3.0.6--canary.5.4465504180.0":{"name":"@salutejs/spatial","version":"3.0.6--canary.5.4465504180.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.6--canary.5.4465504180.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"1c137f98b2b9eca2bf73fda965fcf2504b85f714","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.6--canary.5.4465504180.0.tgz","fileCount":98,"integrity":"sha512-TqePRas/Xue3ITX4dpZn6a4Oe7IjY+6h+VVcF5zryMN50jf49IYA/oClvJVG0cnmHhrxcLajL0vn0R4Rh7n9ww==","signatures":[{"sig":"MEUCIBxi8/g8XYcw1eGFpae16jJ0sKwfBe9Y9Ry3u2nMcwl/AiEA9ATf3nXcYRViJ8bSsC2B1TAdgn83F28/fLIrlxeTHBM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97244,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkGANjACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqDLg/+MT9WDA/V22pZ5Pn6Om+sShdy5dJvjeoUbnqfGFjtI944L2/K\r\nrSKT8Lv2PZ4VKA+dY0mSMt9uxqFRDHw7p+YclIRE+dfAtmt0w7SnLsQ3xYuJ\r\nm/772bI6WywXsbXEfcPUDJ+yUhKfEkIrRHwrN+F7HcvewBYNbOxIhNPuCWsF\r\nnGl8U3oqSmFXsyH1tf/kUPpFie0vMPC50TWvP6hk8FoL0EpqT6x4OXVko8Q1\r\nHHQiNYeLRMEr8seDJf0HWlTAg1mf5+hoLwDB/wkh4hxSdHiRhVo+E5UD7vhe\r\nlXwi1ZXO0AGWLsWjUGnX89EYmVRGxfQs6e2rDF8tGQvXCoeyh+i+mIpnPyr5\r\nbSDs/4sKw2NWjsXlJCZCuAplUG3NoFSNCGUAwS6fcrlO5doLQqqDg0lJrj53\r\nbZhRvboTygf+D2NaarklQWy84XzdrNuBD6gPozKAC5L5c6GyYm8zpU1wu5Iu\r\n26AWpV9gxruEdoWuD97hHE4VbXK5VdGjBBsXxiNV7OYxsAJbAvR9qiQh8MSA\r\ncbtRYVM6y6PGZAc6+5aZf79NxtMzxxp3r3h3m4Nba5KJ8pWwt5AbIL/6xyZ+\r\ngXXtRyKAcVImY9oOH+JIzMtEwaj2vjn7EGDsDps++3eBUJ2SpuZLLNLeEew7\r\nrjIAQFRAczbBysBQ5EGnvCMlmIsT7hXi58U=\r\n=MVYv\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n - Включение и выключение навигации для данной секции или полностью\n - Удаление или добавление секции в навигацию\n - Изменение глобального конфига или конфига секции\n - Фокусирование указанной секции\n\n Например, фокусирование на определенной секции на монтирование компонента.\n\n ```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n    const [sectionProps] = useSection('suggest');\n\n    useEffect(() => {\n        spatnavInstance.focus('suggest');\n    }, []);\n\n    <Suggest {...sectionProps}>\n        ...\n    <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=16.18.0"},"gitHead":"e121bc488fb9ebce43c45e85ab20dae23ed66ed3","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.3","description":"Spatial navigation","directories":{},"_nodeVersion":"16.19.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"^10.37.6","react":"^18.2.0","eslint":"^8.30.0","parcel":"^2.8.2","cypress":"^10.11.0","typedoc":"^0.23.23","prettier":"2.8.1","react-dom":"^18.2.0","typescript":"^4.9.4","@types/node":"^18.11.17","http-server":"^14.1.1","@types/react":"^18.0.26","eslint-config-prettier":"^8.5.0","@typescript-eslint/parser":"^5.47.0","eslint-plugin-react-hooks":"^4.6.0","@typescript-eslint/eslint-plugin":"^5.47.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.6--canary.5.4465504180.0_1679295331243_0.5561413311282968","host":"s3://npm-registry-packages"}},"3.0.6--canary.6.4530393990.0":{"name":"@salutejs/spatial","version":"3.0.6--canary.6.4530393990.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.6--canary.6.4530393990.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"2a9d5c19defbfa3a783347de3bf051b0b29fb14a","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.6--canary.6.4530393990.0.tgz","fileCount":98,"integrity":"sha512-UcAYxYufC+rXf58GLoku3/6rK+M36SAV0lhY2QesIZJsh+wsUOx5S4tGe3ko7NW4bVjGoI6qMz75hig9Q2hNmA==","signatures":[{"sig":"MEUCIQD4zBWBefacDhZ7yMWV1qGd7kT0J13+6v5eMvtzQQbmIwIgYDT/80/RnXAu1wz/T7jR0CMl/WI8lotc3aogsJIfhBI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96917,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkIVXDACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqZSA/+Jtu0Wq9kJQpTvNbUW1WqG90biIRUFHqRYtjJboaqEhFnRlQ3\r\nk7/zrYGnVgK/tdGrWwrup9Ql6TLVnAkYkDRJkzL81HWAfOS+I8W23RCqs6vy\r\nD+Css1Ra/RQTNTRL1v10a0+aY8xW0Rf+nOPFGv3MxcLkmQr0uZkvVTPSoZn+\r\nJhPOZxjhnxzd8EmONF8K4AAWJ/AAolow/1tyY3/1XLvPQac6FTopEAalEuP4\r\nlmyk6T2zq4pFWKrmACLwXvAXAKa9ATjOGUQpvQpI5+eCswRxCU3M1TOE//V+\r\nNPW/uYR5TBcZC2fQpl++nvMg4+2pwqFipTBvYNtqAiODoaToNreEH1L0uSi/\r\ncJtgIuK4iGf2Z7y+51sccefZal3Fr++ortuK5gVgKMi/0eLz7I0mhznvTXh2\r\nILoVwsJEmEgsxQunxvjQXBYRUy1RYSVKqPLgQQszWYipMwVomNomBvoeFYno\r\nRpTG1M15R7S4GlIYlJvcb28s15zGV2JSaaRVrQTGoXGSJYShkkjEWDgGOKc+\r\nuFzpypwhK8oURwrXc1Y9e41kpp0e2FBHkG9pqGHiih7aBOyLcINAQZhe9GVB\r\ns110jbXuz285Fwg4+nYsUrLBa/DWiPiM5iq+rCNCw0T//LeokDO8+VmoD6Vh\r\nqgDs1KAKz+zaQVy+LfX1ZonwPBB2eNDr/QU=\r\n=aLS+\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"eb129d7608ec75722cb352aba97280a1ecd6e670","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.6--canary.6.4530393990.0_1679906243004_0.6590599408149898","host":"s3://npm-registry-packages"}},"3.0.6":{"name":"@salutejs/spatial","version":"3.0.6","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.6","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"83490adcd7edaa2ce0079550db79cb94ba9146e8","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.6.tgz","fileCount":98,"integrity":"sha512-oQb2bLOmJ+aR7ZA6QYmGzeTn6eoQowAHVqhm+5dfbVWQGMeVIrnTJlM8Mnk/gtzozjSLVMlyvF25b2ek1VpPdA==","signatures":[{"sig":"MEQCIAysdJJjrRIVtf6p9jHd8WpfVCvUrz2JPD3DrA1g0G+NAiAWCMyBPlaoE5rPEPt722GELv2nAKc7kJyx7s3dtic36g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96894,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkIWWZACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpB0g/9HmkxEEpGnjbHNfzSZWIoUdhzN19CHBPcMmGYrb35Tpsep0+3\r\nQv1vpJEtWNCDhGFuBiW9ghdVopQWNPjGHiFzAN1016NGU2G5qrjij9ucVGGn\r\nx6QgSuHcJpCh1o9PpNbre0FHG9Xf41zcUMBaq2Cairzji1MPXGxvYthuzVA3\r\nKO5nWtz+yD0B6t3HNEcaMwbMyJXZaVnfSunSyisAGfa6w4aBWoGVZwKiW51r\r\nh7WQiIFD3V/nV9wqa603pAiOwkKdEKUSF/d/S4jj08PuAsnaJdJjgqySWwHQ\r\nswvTQyYDh5Gyi/jy0B5gIz6CNZmq+n8Rd1yQoInf4v0e7ziuTGoo4Ikd/dq0\r\n4t277ZomjppyMDtgcIwG66IznL6gLYM7TPztthvOz9mzjYTK7FJtemqrd6AJ\r\nl9WQqZ9VdOVI16FFK3r4IHVkFnK+o7sCABq8l9nEVBH/lpv3Tq3QpZt0CI4D\r\no/Xi42BoakuAYDRoHBmvPJnSs4glYqNlj0H2dTVRmBhNuX12KH/iolWxS0QO\r\nBAxrOWNN6ZASVlu09Q49ULATyhBt0Cfiuo1+GHb8Jo7Q9ncbFCl5YxR/2ky8\r\n2+bnwiGbW9rS+ytIRlsXtHY3ZIj0Vck4tkfnpWNcZLZ7XlGdWWePWdiLRi5S\r\n1qI7U45bNIin03KxdaqlZDPzOOIIWmN+150=\r\n=ImSA\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.12"},"gitHead":"3826d472ec09bfefb7acc67edd0d4f22b7296786","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.6_1679910297397_0.901552544064081","host":"s3://npm-registry-packages"}},"3.0.7--canary.5.4531085021.0":{"name":"@salutejs/spatial","version":"3.0.7--canary.5.4531085021.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.7--canary.5.4531085021.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"8a1bf471ad713db56f2f6d9665ae8a41e250a676","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.7--canary.5.4531085021.0.tgz","fileCount":98,"integrity":"sha512-fcR0vWtJa8wL2iWGyTUcsAPu5lewwXaNy0uAJ4D6zeWgRGG9lu1iyudDIkmsAX3H6xMAHaOSq4V5DcSBX3jQiQ==","signatures":[{"sig":"MEYCIQDJ3h8OCTx4hudMEbjk5+AL5faykwnm5STYMxB062iQoAIhAIxhsN7wwuubWWAxWJIQeXW/wjaeZYZiTV0NxCJ+PfvP","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97246,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkIWgtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrtag//TrtEThLrIIr/3HrsAK0ABFDRMB58qOs0Lj5KlCej+z1SPITL\r\nBawTqSwfH/xJMTVJwU+O5Uur2968AsNghM5cGKwVPjhzWmSDsaeQtG1ijtTO\r\nRrYhf6UblAuKZAuEVykytDGxf3/iLKp2LvX5qGyFHvVyvzayTFl0yt7oOC0t\r\nyl9W/uIqApb6GEKjG1NChlaLKCUAK+xXnuYLFbP7xisWMUvHuNeyzclgfH2/\r\nYfOYx5jrc8w3sJBuoVGY6BlZ5el9KzpOPhjJ957H9tdBqU91lW7kbBuCYSQA\r\nf2vZrduLT8jTwceIpgQ6DAAd1v1Ej1vClsXQujueDGOKx+8drIwvQAK1CJA0\r\ntqiNdATWSJ+7MUUkMeCfo0UREsuFTPKYZuvjVZKtJxjfai97EoNzCiUHw7w0\r\nEp3vBhD2IyGsN96jbxlXXM0OkmCj0vp4Yb6ScCaukU8VedWnHTBVpgPDpxFg\r\nL0T6xvcuoTvYnlYYh6TpnyCOfzbf8BJCN1Y1wtRHu4QGJOpiSbJ0kjQvXPCa\r\nJXPVim0SGVZhNSVHiAP4niLyz2dN+Vjb9PBpa3aFhuygYbzA8q9/lbj4EmD4\r\np/kxeW3maq8FgrFlSAIGiVIpaUDd37WQDScksU0LuiYtZC5vJNAt9+FsMmvh\r\nXdbtsltUvOMmjfH7PT19K7yL1hcbrFjykrE=\r\n=jGCd\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"0c6d0a0c4611418242701c1488cdba53db4b8fbb","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.7--canary.5.4531085021.0_1679910957740_0.808808060208565","host":"s3://npm-registry-packages"}},"3.0.7":{"name":"@salutejs/spatial","version":"3.0.7","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.7","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"27efe6a41864c37b76552803b411ff5174e9dfd6","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.7.tgz","fileCount":98,"integrity":"sha512-xWgbZcw8UCJ7Z7f2JKtstH24SuYyRu9DflWFT+dNIV2OOM3hKpYLPA8Zxf3QmXCJNSiu7MrUjYXhJjcj1wxG5A==","signatures":[{"sig":"MEYCIQD+MV3O1ard917/rI4mbi8OuXilnJXscURJmsxc5fmdBQIhAI3sEx8BxFwXtvfz9eoW4HkDSCOsz3anDICRFeHNE7AA","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97223,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkIZ0tACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqkUA//YvomgeknZtlCHhF5oBV8hpR/BQye1kedL7Zo2aktR3h5Wmv6\r\nVGVtGVbyICTRSdcGq0L9GMU89i7NySQeHwR+ANyyoH1v1O01IMFCSOQuo3bq\r\n+ztUkpeWHWblgEaWllGQiQhXbuugclhBysr3JVhARQsOe1guOEt+PIzTo7KN\r\nheJOZV5NR0pTIzpo8z/MR8jvgBg/GZEBNIGCGkEKHpFvE9yiA+jTuzzKuI3P\r\n3qX6JgVQk8JLnn8/Iko+St6Rn0sg5wuDJZzf9dbaMvBB3Rj0/X8OPJO7Spqe\r\nFjTcJSEt382sT0E/ooF9qLtsrvTNPMMOVbeEQG52uZYeun4gWCdWbjzC+mS+\r\nEtc0f1d9EGv1J6esm5gZH+nDK7A48EE8K1n3ue1ueVqES3e7ek0nBe1QB7Z1\r\nK7vpOblkYNpjilKMgaAgJ2JvgSePZ4je7/65a7L0qwGO+kKfO1kWOvwJ2xhH\r\nFQIZzTin08zISNd4nxEnuYCD2YuN5EFIzq/jxgLk+kMYictW/5AjL/m5Yts6\r\nEujcZZeBPnkNrMJwvMjrKV46nCV/xVxfq3B6n/2LKD7XECn2dHsCWB+BM/em\r\nsS7SCkJsyuepUlJzmbRjNlX290tqBp32TSYj17Yby/heZyr/YCzHubPhjn5Y\r\niYCACiZ7BGVV2diorMzVonf34d/AOMoiCXk=\r\n=1qmZ\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.12"},"gitHead":"03a59f75f55b608061d4f2fff3c17ca69922cefd","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.7_1679924524788_0.1950442185560768","host":"s3://npm-registry-packages"}},"3.0.8--canary.7.4733382043.0":{"name":"@salutejs/spatial","version":"3.0.8--canary.7.4733382043.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.8--canary.7.4733382043.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"2fa54fa5129907af2d3dfc7c36f872c6f4bf035d","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.8--canary.7.4733382043.0.tgz","fileCount":98,"integrity":"sha512-XUhvS8nzoKV0JdZFLSmjePD/4SmINQohYahNz/uF8XpkuROLMf0QjjlfvmmI7d9xgDtlaQ4YCAbMD1a9KXlSDw==","signatures":[{"sig":"MEUCIGoy0KSsJcaV0nfMDrFhwpH/sXF0XjX4C7g99cqc5mgiAiEA3XpWvVPuoc8k+YlmoKOLMvPqcpxMkmYqzuPFe2Uo7ik=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97246,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPqVmACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqwbw//S+nyxNRnriVk9Ac8GbqWYfcTGqna5ok47Q5sAx9BmS211SVX\r\nla2FtrR46cKs14X7iLBxWtfZZ3WxTJiT2YbGnAuX3o2JZQDOnZuyDl7KCGv8\r\nDFC7zszV5enMb2E8j4TgWK6HZVkjlCP47S2yz4YeItefq6GDpi7rO1zFMiVt\r\n6vdOE9IysrPBhjHJmcpvwbqPpYRRGcKKxxLQOEpHrncA56h/tZC1s9fxamx7\r\ndnRDvO1FKjZtVcxxvdzuBuCDkKIEgsxRhclwKvTjAViV+y1u52ZQOuQTJ6Tx\r\nYWyv/nrndu0TwI3C/vVohrFRLO0AZ3woLz5gSR4J181c5FWx8bawYim+Ge6Q\r\nh+Ypi67euxE00KGRqHfMKhygsEdSYLL6NYZqvO/iVotRQ9OntXNo+p5UjmyF\r\nV4/Jh8kM1wXzZWg/j6hPIfa4d5aRm5vFUKzyNWbYaXeloL97H7eOdcVOKbpV\r\ng9PutfX7zZv7Pcun4ywkqnoIP1INhCBPVbmGyRVORCdN+AZdLp4L1AnejBGm\r\n8lad4rncJzZlf1+lqN4DzEDZdfAGqBjr9ebGHJBbkG1yXCFpR5D/8vp4QjfB\r\nzo6UPH6uOSWGgsW103eA1Ydf5lNKqcu2sgBITQt2MWixbzSJi7pyv05tbb4Q\r\nmkfKF5/B7tiTZCwbko0qYuH/LhIQ7Yw2QKI=\r\n=szs0\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"a3bfd5b9d021b1b1abb620c64690f9fef77f827c","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.8--canary.7.4733382043.0_1681827174193_0.9519757057618681","host":"s3://npm-registry-packages"}},"3.0.8--canary.7.4733398673.0":{"name":"@salutejs/spatial","version":"3.0.8--canary.7.4733398673.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.8--canary.7.4733398673.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"2ccad1e7debfcd96e5c27a0bcba60f00e08af36b","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.8--canary.7.4733398673.0.tgz","fileCount":98,"integrity":"sha512-SoKN38WdKOxWw1iGTDZ5ZL2C+LZg2VOWayTNVJ73qRiEfBGcY1FUZWv0fuwmEmo7QdoWvp0deIF/M5/kLqlEkQ==","signatures":[{"sig":"MEUCIQCbS7h79+oIXiUxwufcSTEknFpnq/FQmK7Cr5U+kZ2WGgIgRhyPcVJSe683FqLE8jSMguJX5XtfLFYmeO3QzGo1+xw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97246,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPqYAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpdFQ//SEF6ozyZyFQbLBUOwsl5s8Iia7jCY47wRlRyNj4BQMqZVtcS\r\nIq3DGQ3FV0wpZsMs4cj8USOtTnaUHy0Gh2stEgS+PO2UvfzZ8c4bc1gC/sJT\r\nva/Q+qOx/eXh+FCJWGTVed6XIuj/On6kBq85OkcNnubwDwEzDwUTHXUzwbq/\r\nZxrERzNjroP11Yyi/P+YNHKiSpiUEbD+m8N/brFf660eJowaiHEmP9Nj1Tqv\r\nE/Z2SPs99Y3/cJmBOyZb8SRC2ImwHywkkCKThSOSwfHqK0oIHxxkia1fOIWb\r\nuJaFxc/6WAiBA8kBGurcHViRjQTrTegEU1/2WyFU5cU4LRk/8HyByK3I5TZt\r\nYggBoh3jMFAOU3U35Lkn3+tmrAROh7VSqBiWtCjTAKTEmIDgAmGVWr0ZWlvc\r\n/P1A6Pf57+qslKt1N4fN02bokQpzTZmfRSY2e4hbRgYUmQyZvQ0Qj4YbQ0A9\r\n6KSveVbM0ByJuac2ZcpE+7KBzZuYaoXSuqJqNTOBj8hGx2/wKDZoIAwYtJsp\r\nbofz4oCbDewlDzCRxghm0ymPCEaCEAaQV/w7XDYr4rrWedU+Key9FoSxXQ3E\r\ngEefJwhnGxsAwFESNI3uCN1Rir3do37Q+slBQIRumTWT7Vk6rJTOLpJN2mLN\r\nj6AnhMsG4eOR9XmADkwqxaGrys87Vj1X5Xk=\r\n=kdcb\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"bc6c9e335eb6e64c685bac90c66f8d262b133b8e","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.8--canary.7.4733398673.0_1681827327877_0.8301736610598256","host":"s3://npm-registry-packages"}},"3.0.8--canary.8.4733427022.0":{"name":"@salutejs/spatial","version":"3.0.8--canary.8.4733427022.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.8--canary.8.4733427022.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"ab5af1ca2f8ae5fb1f33852898f7ef6c91879eb1","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.8--canary.8.4733427022.0.tgz","fileCount":98,"integrity":"sha512-QxgMLofzJd8/NdY0ehwFdLp1g8y3kc3huC1PS2vHc9aKtwAYQa4jOj4XBeab4GLNgNCTUXvT1Ow1mjIXM/x5vw==","signatures":[{"sig":"MEUCIQCSek4Td83Iozythq/HFd6qJ5yOce84jPguMh5QO096dwIgf2Ib7jh7itFlVy1i+ThirmH4FpD0bTRyqlliCUQrenY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPqbWACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrLDA/7B3eWxZse0fFcawON78BMiQmb/qeq9QyGW88XXR20bN2IzkZF\r\nB3OTTBH8LJyYSbDRTxi274zHdmFTjbtrfpPTif9aaMTB4m47BE0h85z564+N\r\nrfkvQ0mc3wbE/3G+DLIcp+kgSvGvj587YwJ9c3Bi4USAWvCbJ7UkRSETmT2L\r\nbOPMeK6Kc0/VOwrY5Ji5sQhUxo/Tez+Sc5VOc+Ya9A9mjOx9vjAQZ4MfWCq4\r\nQkyyUiOezeY7wDGXuWgLleclyPp/yQJLDylzvN6OXFXKEkVR+yygVfziB0RQ\r\ndHnvz9jqySF3yF2lzHIftgwRHl1TTucwagFCJVuXvM1FXQcfjFahj9w5xvGY\r\nXAEv+4qfuXAFysVFCAqdC7/JV9dBbIMQic8SA2sxRW6nd7Po55xk6mIWG71w\r\n4Q0uFrfQqsY60mLIuwWnENUMGc6gO96lvyMbik/lC529qJTQC+r9XeQr/hN6\r\n0pE9kNbKwGEqypQnk9Uk6PMFrw2GqSD4NOWjql2USSr8vvKb1h0UZ8hRsbzj\r\niCQlRI5wcTx0zlktYMhHVx7WmVr26BUwgsHH9ZQoMW6bRGwc1h+01B2HD2d7\r\nql30vH4kbR6GI3ItQT/goDfRsRIbgYIvnxZt9XrLeFCA6eJXmldPL7UIRFim\r\naCrnp/IG/ELYXO5sYWsgPVk0G2OXOA9nfv8=\r\n=/Ej+\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"9acef911a991c6258263103aa68fef2663d6aa5e","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.8--canary.8.4733427022.0_1681827542115_0.817687689635294","host":"s3://npm-registry-packages"}},"3.0.8--canary.8.4765645159.0":{"name":"@salutejs/spatial","version":"3.0.8--canary.8.4765645159.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.8--canary.8.4765645159.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"d9c894588b47dd3f1e8969b5525c6c1afa8185a9","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.8--canary.8.4765645159.0.tgz","fileCount":98,"integrity":"sha512-SQ88nUAEpBlrcRU71BettJ1D0kc7lkC8u0wGxyNJUIz4tiCpcVzWDprqjcPEOnh8qGfETP6cDx146XkGPSj1Fg==","signatures":[{"sig":"MEQCIEUt/pWz30t+cnX2NrRMMIXTPMYEk8Tm89l7YnBiXiXjAiBMEGeAwDhJzg5jw8pnMMRZk6S4rcuLxD4XycrZq0HhTA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97497,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkQpbeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmoakg/+Jp6v3PM3aVtNMBAlHxLdnH/VugkukUx5chZiQrYtCDfmiP+A\r\nsGvYXo9kLru1TiST/IFv0bz2PRFHughbt7tljGJDsn60VgsqZLf8Mk1WJB5H\r\nS1ACHYtXITJANPCIfRagLE9NGEqjAlern4ctZG2kk0XEt5B3Y12peCfPi4S/\r\neI3T911mk1m3tXdsLgmKyWW0PYzHtOcEpBwT50osqfZimNMgv6rfGyyiQKSk\r\nHBHCVBs/FJT3ZcAVCcNZqLk10tUddy3RSFf3LEkNAMYhNIa+PbXjHNAw0NW/\r\nq6tC11bphYYafEtdigowuiq4gZVEn2EzadyF+ycnkhw43pzf1aJSd6knJ1YN\r\nuHwjnf6e8fRjg0IsVS47/5DCABOtEN7gEoT6JdJrHU5hqS4kAKZrgCuoSM3u\r\nmoBzWmHCqtFN3e0a8kbCr1SKUtknnKh6ONtp2yJnZHyPPPrgjgmfwggg0Cvg\r\nNZmQmOrLh+JDBFpJeSpyzeLCTQmnkcO736aiEUHFn7meNsUG2HDKKS5FXeWA\r\nGQeGVXWeoOB48daSt9EihqrzTrbAokb3yXl+oqKXrYidcHBn6fVtr79TZoJb\r\nj1wV7wWD8tcMSHQchxKHAX2qIqGut5f8tWpxsGTXXNUMO0SN2qPRUdFOaTql\r\n+JSDI9Sggxnpc75odKCVOqFBvcCR9xuboRk=\r\n=RAxk\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена.\n\nЗдесь были рассмотрены только необходимые действия. Для упрощения кода и более гибкой настройки читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [[customize1, customize2]]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в документации к типу `Config`. Параметры передаются в функцию `customize`.\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n# Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"4c1bae7955b5ae9902e0546246825f640ddcada8","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.8--canary.8.4765645159.0_1682085598170_0.5362036220965483","host":"s3://npm-registry-packages"}},"3.0.8":{"name":"@salutejs/spatial","version":"3.0.8","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.8","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"0e23941e74eb285ae3db89f82d9e604950a5b97a","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.8.tgz","fileCount":98,"integrity":"sha512-yQaJi1Ws/JR9IwrZkf1P2ddVuoSXccqwLyp7s5brJnMBC4T8zj6oTQiS44zVkDQeb6gOPNf7aB4xuzgxpTGUkA==","signatures":[{"sig":"MEUCIA2aKU5Pa2S+Utp8RtnESusXxTbgS60wVmgOWzz+Kn/jAiEAxM4xZwExa7vlW1rC8micZEktLAzyJUh48/eugujuZPc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97474,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkRkiWACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpgkw/+Nwb32OvHCyBnKu7S86kiaFwX118aeOFeT1nCO//hsE+nuMR6\r\nTCSCDX81eV/cqZlnu23bYK5xS00ytnFFnnYGYzkdYFs0GsGI1hLd6+04+XcM\r\nTNgNCC0Hhz1pP8rGfDgkMD7OZXXqJQyK8ETiavJTXldBNfWS+ohT973XnYpv\r\naDKHs/ghFYRdV3G80abNpYorUNRQr2BkDPudJJs16CWXDbel0bJqAdZ9zhDI\r\n0DNP01ZG6CPt+5ZFVqCdGTHiqEEwSlHv3f5jk+nVGyksSEpASAPd6sz2gxrJ\r\noz3VWGt/NVzHShEJw4zjJBllyr+N7XS2sO3Rdip/TVFQuHg/qafphMFMVXQz\r\nMfra6GXz7unxwN5JzAhLr6XVbH7/eaR5hjLEGp8g0xFDsZT98VOHilsxOHti\r\n7tfA2eh++59oE3rYmTNha+0DDyyw7yQRniO9RdHMqT4jCreSkOW+otb+5DzR\r\n8Nb4j3r4gwXPbBBA1hsG/idkKzmNCiOqCkH2n0lOyDZmFD+Aj85h16Xc+nvd\r\n4dEhPYCpHRidm8tJQcy0av3Q82ikkQXTXvEJv250nSe7iRoe8opKStTDbG+v\r\nfKUzbWyh21aMLm5RLTu2ByxDsYmmMpYT7yRmLFpobEWn4X68xLqlySZNWvk/\r\nHeRGqXgMmAI+GBUuNsAC3icbPcME0l8qP9U=\r\n=hZ/m\r\n-----END PGP SIGNATURE-----\r\n"},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.12"},"gitHead":"eef5268541be82bdc1d85c685a8002188dff20c4","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.8_1682327701910_0.9514323881733984","host":"s3://npm-registry-packages"}},"3.0.9--canary.9.5054829293.0":{"name":"@salutejs/spatial","version":"3.0.9--canary.9.5054829293.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.9--canary.9.5054829293.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"30f1c69d7670aa702189f112f619b12f334dafcd","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.9--canary.9.5054829293.0.tgz","fileCount":98,"integrity":"sha512-ftzyHTIcHM6+ws1Ie4pb50NwJniwRGWC/ELHWXbXF4EE6sXmp+HYTGB2zUrKZKABxb+klztnn6S2sWaRXrps9g==","signatures":[{"sig":"MEYCIQCBdvmn9hQjMgA38Q2qTrzcmST4ThA0ydPaADw6KH3DdQIhAPnlTjdBGB4fYqQswzmhTFF9KV/fH3T0gw4nbnLXR3i5","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":106980},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n* [Установка](#Установка)\n* [Минимальная настройка приложения для работы с `@salutejs/spatial`](#Минимальная-настройка-приложения-для-работы-с-`@salutejs/spatial`)\n    * [Инициализация в родительском компоненте всего приложения](#Инициализация-в-родительском-компоненте-всего-приложения)\n    * [Добавление секции](#Добавление-секции)\n    * [Включение навигации на DOM элементе](#Включение-навигации-на-DOM-элементе)\n* [Углубление в `@salutejs/spatial`](#Углубление-в-`@salutejs/spatial`)\n    * [Варианты инициализации](#Варианты-инициализации)\n    * [Подробнее о секциях](#Подробнее-о-секциях)\n        * [Настройка параметров секции](#Настройка-параметров-секции)\n* [Хуки](#Хуки)\n* [Полезные методы SpatialNavigation](#Полезные-методы-SpatialNavigation)\n* [Оптимизация и ускорение работы](#Оптимизация-и-ускорение-работы)\n    * [Intersection и Mutation observer](#Intersection-и-Mutation-observer)\n    * [Простые секции](#Простые-секции)\n* [Запуск тестов](#Запуск-тестов)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React, { useEffect } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    useEffect(() => {\n        const focusable = document.querySelector('sn-section-item');\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, [])\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n- [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n- [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n- [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n- [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"680c2191ba4e3c5781e806c540cdaa4fd2a2edfe","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.9--canary.9.5054829293.0_1684829459948_0.3372100489372152","host":"s3://npm-registry-packages"}},"3.0.9":{"name":"@salutejs/spatial","version":"3.0.9","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.9","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"7af11c40182065f8446f485ee7668ef2bd7737b5","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.9.tgz","fileCount":98,"integrity":"sha512-UmPRHNOiXQdM/kw9Oktu7sx/fWpSaEY6YZevyGkMd52TYN3oOLrfw38OkeWhVpuixH4Bfd3O+/OZmBwGq+tWWA==","signatures":[{"sig":"MEQCIH4qgPlZYat/0T/KK2igPhk5ShnOgIRf7JR43FPKf/FmAiA56VLWTI3PNXJyqfNUg08E0Nbo5B9J5TGrR7MwkNEmTA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":106957},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.12"},"gitHead":"fa8aba62692a85f3bdaae719b6895dde35bfee61","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.9_1685095359074_0.0250232533663326","host":"s3://npm-registry-packages"}},"3.0.10--canary.10.5110963905.0":{"name":"@salutejs/spatial","version":"3.0.10--canary.10.5110963905.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.10--canary.10.5110963905.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"c0f8feb1d2be0da23ba9ea67e7d778b0d385d8a6","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.10--canary.10.5110963905.0.tgz","fileCount":98,"integrity":"sha512-jN2K0B/bjqa1XfEmHHxg6+2lHtcYdi5AQcpwQPs41CDe22t0QeYTnKCyDI8KFg8iPgD8m3NQFYMVWASzEkdbLg==","signatures":[{"sig":"MEUCIGPPMKnNf37eHCyFYZbbRv5Y4FjKFP+NQHjEXL+IN4iDAiEAqV0X4cfjCPG88VPH26vCS6CfNQcdkMk76mCUaUwNnHw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108121},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n* [Установка](#Установка)\n* [Минимальная настройка приложения для работы с `@salutejs/spatial`](#Минимальная-настройка-приложения-для-работы-с-`@salutejs/spatial`)\n    * [Инициализация в родительском компоненте всего приложения](#Инициализация-в-родительском-компоненте-всего-приложения)\n    * [Добавление секции](#Добавление-секции)\n    * [Включение навигации на DOM элементе](#Включение-навигации-на-DOM-элементе)\n* [Углубление в `@salutejs/spatial`](#Углубление-в-`@salutejs/spatial`)\n    * [Варианты инициализации](#Варианты-инициализации)\n    * [Подробнее о секциях](#Подробнее-о-секциях)\n        * [Настройка параметров секции](#Настройка-параметров-секции)\n* [Хуки](#Хуки)\n* [Полезные методы SpatialNavigation](#Полезные-методы-SpatialNavigation)\n* [Оптимизация и ускорение работы](#Оптимизация-и-ускорение-работы)\n    * [Intersection и Mutation observer](#Intersection-и-Mutation-observer)\n    * [Простые секции](#Простые-секции)\n* [Запуск тестов](#Запуск-тестов)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React, { useEffect } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    useEffect(() => {\n        const focusable = document.querySelector('sn-section-item');\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, [])\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n- [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n- [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n- [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n- [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"caa65aefe1b51bfe5cce9ce04308a96d99a9d70f","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.10--canary.10.5110963905.0_1685355993326_0.38209694282571083","host":"s3://npm-registry-packages"}},"3.0.10--canary.10.5110974549.0":{"name":"@salutejs/spatial","version":"3.0.10--canary.10.5110974549.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.10--canary.10.5110974549.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"c15613dbf85cd10036f8636e05b0e28c70662fa8","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.10--canary.10.5110974549.0.tgz","fileCount":98,"integrity":"sha512-zioBjjPNgb3MztwVc5k3ZDhiXYPDLeHXFQP2JZ4MyKanhsxFFScVmT7dkXexNjA5dHT/TIQjwWC1QLesbWKBnA==","signatures":[{"sig":"MEUCIQC4CSYeiGEq0q4uxZiiSB0hLbm4wGmVnz/T/H89E3ZcfwIgWyI0Z0b2/8Bgwrq9q4CyfuzlxVWnlsWVrnQRwSnJzUs=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108121},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n* [Установка](#Установка)\n* [Минимальная настройка приложения для работы с `@salutejs/spatial`](#Минимальная-настройка-приложения-для-работы-с-`@salutejs/spatial`)\n    * [Инициализация в родительском компоненте всего приложения](#Инициализация-в-родительском-компоненте-всего-приложения)\n    * [Добавление секции](#Добавление-секции)\n    * [Включение навигации на DOM элементе](#Включение-навигации-на-DOM-элементе)\n* [Углубление в `@salutejs/spatial`](#Углубление-в-`@salutejs/spatial`)\n    * [Варианты инициализации](#Варианты-инициализации)\n    * [Подробнее о секциях](#Подробнее-о-секциях)\n        * [Настройка параметров секции](#Настройка-параметров-секции)\n* [Хуки](#Хуки)\n* [Полезные методы SpatialNavigation](#Полезные-методы-SpatialNavigation)\n* [Оптимизация и ускорение работы](#Оптимизация-и-ускорение-работы)\n    * [Intersection и Mutation observer](#Intersection-и-Mutation-observer)\n    * [Простые секции](#Простые-секции)\n* [Запуск тестов](#Запуск-тестов)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React, { useEffect } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    useEffect(() => {\n        const focusable = document.querySelector('sn-section-item');\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, [])\n\n    return (\n        <div {...sectionProps}>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n- [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n- [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n- [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n- [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"f6fed425eb1b8db5a997798a91f232018c586dda","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.10--canary.10.5110974549.0_1685356072820_0.9170200012137335","host":"s3://npm-registry-packages"}},"3.0.10":{"name":"@salutejs/spatial","version":"3.0.10","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.10","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"985527cbbe0889a2aacf710bc64783145e91f259","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.10.tgz","fileCount":98,"integrity":"sha512-Wk7Rvbf1ufitVuoEAOFaYvsbbyNH6BcMjIvEIcX57KShlcoKj37A85JIt98fak7psH29u39JQz0kcTYR63gWKQ==","signatures":[{"sig":"MEQCIF86JLoS6Y6OZ6fgcUeRY2zZBhzlPv3CXewnEqtk1t3RAiAIA6JX7XQlRXt93NZoGVw5sAVKt3Vzg+QJahbaGup9zw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108097},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.12"},"gitHead":"fe8f04dd17e5a56c96fd11e8f1f8b6211e86efb9","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.10_1685364166538_0.38861257507908675","host":"s3://npm-registry-packages"}},"3.0.11--canary.11.5210081674.0":{"name":"@salutejs/spatial","version":"3.0.11--canary.11.5210081674.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.11--canary.11.5210081674.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"ac5b8856e0f0c4fdc86f8144184a1df562c0e9ba","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.11--canary.11.5210081674.0.tgz","fileCount":98,"integrity":"sha512-i416vVRlhnqmgeK37p9Dsi5Pp5fnV3mE8Uu2CVa7yCCXk2jWUCheTufJHq6o/20Hes8gYmI6lhrrx2bCzJGU1g==","signatures":[{"sig":"MEUCIQCqfihT4xm5604041rENxFOh59ywm4vEFlxq4YD37r7WAIgPvn3pesBnkZanFzqxWpFlpbvS4PdTTWoBStzGOcVUWY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108152},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n* [Установка](#Установка)\n* [Минимальная настройка приложения для работы с `@salutejs/spatial`](#Минимальная-настройка-приложения-для-работы-с-`@salutejs/spatial`)\n    * [Инициализация в родительском компоненте всего приложения](#Инициализация-в-родительском-компоненте-всего-приложения)\n    * [Добавление секции](#Добавление-секции)\n    * [Включение навигации на DOM элементе](#Включение-навигации-на-DOM-элементе)\n* [Углубление в `@salutejs/spatial`](#Углубление-в-`@salutejs/spatial`)\n    * [Варианты инициализации](#Варианты-инициализации)\n    * [Подробнее о секциях](#Подробнее-о-секциях)\n        * [Настройка параметров секции](#Настройка-параметров-секции)\n* [Хуки](#Хуки)\n* [Полезные методы SpatialNavigation](#Полезные-методы-SpatialNavigation)\n* [Оптимизация и ускорение работы](#Оптимизация-и-ускорение-работы)\n    * [Intersection и Mutation observer](#Intersection-и-Mutation-observer)\n    * [Простые секции](#Простые-секции)\n* [Запуск тестов](#Запуск-тестов)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React, { useEffect } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef<HTMLElement | null>(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, [])\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n- [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n- [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n- [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n- [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"18d93484d0f0ab856ca3b8f2900e6988116eb7bb","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.11--canary.11.5210081674.0_1686219593700_0.6502963124839298","host":"s3://npm-registry-packages"}},"3.0.11--canary.11.5210089047.0":{"name":"@salutejs/spatial","version":"3.0.11--canary.11.5210089047.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.11--canary.11.5210089047.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"f9f6e90c7e26477df17964f54f8f8bd888728717","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.11--canary.11.5210089047.0.tgz","fileCount":98,"integrity":"sha512-v2hO9UmWYiasZHDyEjEfxs4x78dNYEuI7/jg/eQJ3ploZW8b5ZaHX2/LwqAIzy++BQCwzGzipfJx8asgMediXQ==","signatures":[{"sig":"MEYCIQCY0R0qwegEdWNfvrvnvljM04D2OA2tKfeVn7JCcxdQCAIhAOEd0Fp8D8lWZ4Iqk7vMb+KVx6Dj82hRokbBHKWh8rrS","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108160},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n* [Установка](#Установка)\n* [Минимальная настройка приложения для работы с `@salutejs/spatial`](#Минимальная-настройка-приложения-для-работы-с-`@salutejs/spatial`)\n    * [Инициализация в родительском компоненте всего приложения](#Инициализация-в-родительском-компоненте-всего-приложения)\n    * [Добавление секции](#Добавление-секции)\n    * [Включение навигации на DOM элементе](#Включение-навигации-на-DOM-элементе)\n* [Углубление в `@salutejs/spatial`](#Углубление-в-`@salutejs/spatial`)\n    * [Варианты инициализации](#Варианты-инициализации)\n    * [Подробнее о секциях](#Подробнее-о-секциях)\n        * [Настройка параметров секции](#Настройка-параметров-секции)\n* [Хуки](#Хуки)\n* [Полезные методы SpatialNavigation](#Полезные-методы-SpatialNavigation)\n* [Оптимизация и ускорение работы](#Оптимизация-и-ускорение-работы)\n    * [Intersection и Mutation observer](#Intersection-и-Mutation-observer)\n    * [Простые секции](#Простые-секции)\n* [Запуск тестов](#Запуск-тестов)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport React from 'react';\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport React, { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef<HTMLElement | null>(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, [])\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n- [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n- [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n- [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n- [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport React from 'react';\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport React from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"282c12ffda19f591992848c91dc966e6d4ec95af","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.11--canary.11.5210089047.0_1686219647549_0.8737713457018741","host":"s3://npm-registry-packages"}},"3.0.11":{"name":"@salutejs/spatial","version":"3.0.11","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.11","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"e41a97d087e7ed39e7b1bbb399752f22715bca73","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.11.tgz","fileCount":98,"integrity":"sha512-bzvbyIVmOiaLHCP2AdLvLlnTtv8NDwp9wjhzstQTOmfMtyWqqcnGfMMQDiyTi4gp0LhQ10vqTYpGdoCX9kKF0w==","signatures":[{"sig":"MEUCIQCh0azRUnU8rPj25nTxdsYkur3E9qaabIizFYY/ybZL8QIgcxX7YGaakeHz5X64u3etnvU1kkPMz1N77byl3c8I9xY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108136},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.12"},"gitHead":"7abe9421796893245dc54a2705b682774bc072d0","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.11_1686219726042_0.40370325892072945","host":"s3://npm-registry-packages"}},"3.0.12--canary.12.6010406207.0":{"name":"@salutejs/spatial","version":"3.0.12--canary.12.6010406207.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.12--canary.12.6010406207.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"bd91a3ad1513c06ce6993147cdd3c4ccd8be8a68","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.12--canary.12.6010406207.0.tgz","fileCount":98,"integrity":"sha512-pQ+MwsWKBKIBz9JdBANkZnhOptcIFFWZnSiTyYBBC6PeLvBQK0Dc40MOMxFP+AcN5EbIdO8adhu6LCNcxgtJ5A==","signatures":[{"sig":"MEYCIQCHcbgbtIgDk+IKQkKL9Edz2OLj5exB3P7YAf4uFLflqwIhAO5SKCQxGVT/gEdR3t/F5qzxXPmYjAkOd2cndkLB30Fx","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":112186},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#Установка)\n    -   [Next.js 13](#Next.js-13)\n    -   [Next.js 12](#Next.js-12)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#Минимальная-настройка-приложения-для-работы-с-`@salutejs/spatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#Инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#Добавление-секции)\n    -   [Включение навигации на DOM элементе](#Включение-навигации-на-DOM-элементе)\n-   [Углубление в `@salutejs/spatial`](#Углубление-в-`@salutejs/spatial`)\n    -   [Варианты инициализации](#Варианты-инициализации)\n    -   [Подробнее о секциях](#Подробнее-о-секциях)\n        -   [Настройка параметров секции](#Настройка-параметров-секции)\n-   [Хуки](#Хуки)\n-   [Полезные методы SpatialNavigation](#Полезные-методы-SpatialNavigation)\n-   [Оптимизация и ускорение работы](#Оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#Intersection-и-Mutation-observer)\n    -   [Простые секции](#Простые-секции)\n-   [Запуск тестов](#Запуск-тестов)\n\n## Установка\n\n```sh\nnpm install @salutejs/spatial\n```\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js, то в `next.congig.js` вам необходимо добавить следующее\n\n### Next.js 13\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages)\n\n### Next.js 12\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules)\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\nФункция `spatnavInstance.init()` и хук `useSpatnavInitialization` принимают параметр `options`. С помощью этого параметра можно включить `debug` режим и отключить IntersectionObserver и MutationObserver.\n\n```js\nspatnavInstance.init({ debug: true });\n```\n\nВ таком случае после инициализации объект `spatnavInstance` станет доступен в `window.spatnavInstance`.\n\nА IntersectionObserver и MutationObserver выключаются так:\n\n```jsx\nuseSpatnavInitialization({ noObservers: true });\n```\n\n[Подробнее о том, что делают IntersectionObserver и MutationObserver](#Intersection-и-Mutation-observer)\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n","engines":{"node":">=18.12"},"gitHead":"7bd66a7a5dcc652f2e44093465ef0ccda1d625a6","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache --port 1337"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.48.0","parcel":"2.9.3","cypress":"12.17.4","process":"0.11.10","typedoc":"0.25.0","prettier":"3.0.2","react-dom":"18.2.0","typescript":"5.2.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.2.21","eslint-config-prettier":"9.0.0","@typescript-eslint/parser":"6.5.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"6.5.0"},"peerDependencies":{"react":">=16.13"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.12--canary.12.6010406207.0_1693301140570_0.6389767845894958","host":"s3://npm-registry-packages"}},"3.0.12--canary.12.6011301996.0":{"name":"@salutejs/spatial","version":"3.0.12--canary.12.6011301996.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.12--canary.12.6011301996.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"0a4b6b423254b6edb239f812fdc7aac6f05a1a3c","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.12--canary.12.6011301996.0.tgz","fileCount":98,"integrity":"sha512-79Jmyw0MCapR/kyO5e7QR26qMU+dx+MSjtXDn36YveJEATH8fLJpf9AvitLFIK/ipVkzmIzkoBwncECRIT7vPw==","signatures":[{"sig":"MEUCIQC+8bFLJMtxeHAvck1fiIrfmBzVCOQcJNqh/30jxu11VQIgT0x3Lsqv615w7h255m5tWxJHBs/7u4DBa20T8f2oi1g=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":113500},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n        -   [Режим отладки](#режим-отладки)\n        -   [Выключение IntersectionObserver и MutationObserver](#выключение-intersectionobserver-и-mutationobserver)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\nФункция `spatnavInstance.init()` и хук `useSpatnavInitialization` принимают параметр `options`. С помощью этого параметра можно включить `debug` режим и отключить IntersectionObserver и MutationObserver.\n\n#### Режим отладки\n\nВ режиме отладки объект `spatnavInstance` станет доступен в `window.spatnavInstance`. Это можно использовать для того, чтобы посмотреть список и конфигурации секций, а так же список видимых элементов или всех элементов по который выполняется навигация.\n\n```js\nconst spatnavInitializationOptions = {\n    debug: process.env.NODE_ENV === 'development',\n};\n\nuseSpatnavInitialization(spatnavInitializationOptions);\n```\n\nДалее вы можете получить доступ к объекту `spatnavInstance` из консоли браузера.\n\nПо умолчанию свойство `debug` установлено в `false`.\n\n#### Выключение IntersectionObserver и MutationObserver\n\nВыключить IntersectionObserver и MutationObserver можно, если в вашем приложении нет большого количества элементов вне экрана.\n\n```js\nconst spatnavInitializationOptions = {\n    noObservers: true,\n};\n\nuseSpatnavInitialization(spatnavInitializationOptions);\n```\n\nПо умолчанию IntersectionObserver и MutationObserver включены.\n\n[Подробнее о том, что делают IntersectionObserver и MutationObserver](#Intersection-и-Mutation-observer).\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"f6a170ce14db99aae28916316b0f456979106226","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache --port 1337"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.48.0","parcel":"2.9.3","cypress":"12.17.4","process":"0.11.10","typedoc":"0.25.0","prettier":"3.0.2","react-dom":"18.2.0","typescript":"5.2.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.2.21","eslint-config-prettier":"9.0.0","@typescript-eslint/parser":"6.5.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"6.5.0"},"peerDependencies":{"react":">=16.13"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.12--canary.12.6011301996.0_1693306553762_0.5357217971318284","host":"s3://npm-registry-packages"}},"3.0.12--canary.12.6011320601.0":{"name":"@salutejs/spatial","version":"3.0.12--canary.12.6011320601.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.12--canary.12.6011320601.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"9e2d2c7973553258b516a068525c93db1bb95adb","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.12--canary.12.6011320601.0.tgz","fileCount":98,"integrity":"sha512-e0ya05YpFNH02RAeaThdfDFt27e/jNQEjhHoeXjv22gwIQyAcKA91YOx6vUgdXD2nDYoyKYORAC85L/XkOckig==","signatures":[{"sig":"MEQCIHDbfd+UgV9jOfWV0QQ/sn8CEERZPAUzDy5wy8lwu8dhAiB7ccayM3wAgeBCdU9jUb3NzNRHIPLJOrIeXgCT+aYe2g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":113461},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n        -   [Режим отладки](#режим-отладки)\n        -   [Выключение IntersectionObserver и MutationObserver](#выключение-intersectionobserver-и-mutationobserver)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\nФункция `spatnavInstance.init()` и хук `useSpatnavInitialization` принимают параметр `options`. С помощью этого параметра можно включить `debug` режим и отключить IntersectionObserver и MutationObserver.\n\n#### Режим отладки\n\nВ режиме отладки объект `spatnavInstance` станет доступен в `window.spatnavInstance`. Это можно использовать для того, чтобы посмотреть список и конфигурации секций, а так же список видимых элементов или всех элементов по который выполняется навигация.\n\n```js\nconst spatnavInitializationOptions = {\n    debug: process.env.NODE_ENV === 'development',\n};\n\nuseSpatnavInitialization(spatnavInitializationOptions);\n```\n\nДалее вы можете получить доступ к объекту `spatnavInstance` из консоли браузера.\n\nПо умолчанию свойство `debug` установлено в `false`.\n\n#### Выключение IntersectionObserver и MutationObserver\n\nВыключить IntersectionObserver и MutationObserver можно, если в вашем приложении нет большого количества элементов вне экрана.\n\n```js\nconst spatnavInitializationOptions = {\n    noObservers: true,\n};\n\nuseSpatnavInitialization(spatnavInitializationOptions);\n```\n\nПо умолчанию IntersectionObserver и MutationObserver включены.\n\n[Подробнее о том, что делают IntersectionObserver и MutationObserver](#Intersection-и-Mutation-observer).\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"ff26701d9d467d87e6bb8284a9f12776464b3254","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache --port 1337"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.48.0","parcel":"2.9.3","cypress":"12.17.4","process":"0.11.10","typedoc":"0.25.0","prettier":"3.0.2","react-dom":"18.2.0","typescript":"5.2.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.2.21","eslint-config-prettier":"9.0.0","@typescript-eslint/parser":"6.5.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"6.5.0"},"peerDependencies":{"react":">=16.13"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.12--canary.12.6011320601.0_1693306665755_0.23032649778730607","host":"s3://npm-registry-packages"}},"3.0.12--canary.12.6022009642.0":{"name":"@salutejs/spatial","version":"3.0.12--canary.12.6022009642.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.12--canary.12.6022009642.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"2fd3b929a0e8897cbb16e8b344ae58550b7681fd","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.12--canary.12.6022009642.0.tgz","fileCount":98,"integrity":"sha512-QNTci/bZvLNF0I7HG+FncltKzmYRCWdCoUlgGfLGtHMisxovRr6qRtbR+slrW47wmrmqScLDPiyuHJfEYh+ApA==","signatures":[{"sig":"MEUCIFKBYTM00EY6m/byve7j2KNURve4sHDBZ/7Kotwta2B9AiEAmuxETYCu3LY7wjvMPCAgP+W7WfmLacdrnHNX0UKHLbE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":113739},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n        -   [Режим отладки](#режим-отладки)\n        -   [Выключение IntersectionObserver и MutationObserver](#выключение-intersectionobserver-и-mutationobserver)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\nФункция `spatnavInstance.init()` и хук `useSpatnavInitialization` принимают параметр `options`. С помощью этого параметра можно включить `debug` режим и отключить IntersectionObserver и MutationObserver.\n\n#### Режим отладки\n\nВ режиме отладки объект `spatnavInstance` станет доступен в `window.spatnavInstance`. Это можно использовать для того, чтобы посмотреть список и конфигурации секций, а так же список видимых элементов или всех элементов по который выполняется навигация.\n\n```js\nconst spatnavInitializationOptions = {\n    debug: process.env.NODE_ENV === 'development',\n};\n\nuseSpatnavInitialization(spatnavInitializationOptions);\n```\n\nДалее вы можете получить доступ к объекту `spatnavInstance` из консоли браузера.\n\nПо умолчанию свойство `debug` установлено в `false`.\n\n#### Выключение IntersectionObserver и MutationObserver\n\nВыключить IntersectionObserver и MutationObserver можно, если в вашем приложении нет большого количества элементов вне экрана.\n\n```js\nconst spatnavInitializationOptions = {\n    noObservers: true,\n};\n\nuseSpatnavInitialization(spatnavInitializationOptions);\n```\n\nПо умолчанию IntersectionObserver и MutationObserver включены и свойство `noObservers` установлено в `false`.\n\n[Подробнее о том, что делают IntersectionObserver и MutationObserver](#Intersection-и-Mutation-observer).\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"c6f22843d7a820662e29657b16c467989de75d8d","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache --port 1337"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.48.0","parcel":"2.9.3","cypress":"12.17.4","process":"0.11.10","typedoc":"0.25.0","prettier":"3.0.2","react-dom":"18.2.0","typescript":"5.2.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.2.21","eslint-config-prettier":"9.0.0","@typescript-eslint/parser":"6.5.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"6.5.0"},"peerDependencies":{"react":">=16.13"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.12--canary.12.6022009642.0_1693381466584_0.9960454385317961","host":"s3://npm-registry-packages"}},"3.0.12--canary.13.6023934745.0":{"name":"@salutejs/spatial","version":"3.0.12--canary.13.6023934745.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.12--canary.13.6023934745.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"748a06542b0bed08a55ea37c5ad4a373cbaccd19","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.12--canary.13.6023934745.0.tgz","fileCount":98,"integrity":"sha512-cbAd9i5o5aC/9Em5EklsAr1LkKcrI1eeIRCn4HXa0Iu0IJdnKOJnXymhfbdCJy28qavMEzlxhia5uDjdxnem9A==","signatures":[{"sig":"MEQCIF4YtOByUpHMUpdDkGRgPqjzElY9annItaFnLoJ+UXXDAiBUQ2dD20/R4097SNC+Numd0heOYLtL/BSstozPWNshcw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108772},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"620b267a4fb538a2e3c6bb210cf4c4f6a159e6f7","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.12--canary.13.6023934745.0_1693393000977_0.29491540906854286","host":"s3://npm-registry-packages"}},"3.0.12":{"name":"@salutejs/spatial","version":"3.0.12","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.12","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"892061df9a2231d1db1c9c20d2d4fb73bf88595f","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.12.tgz","fileCount":98,"integrity":"sha512-v2jIUwacpSb2477RduZyTX8ffJa/2YgxKPrOLsi+4ZpPXg1f48qBJCQjSH+IOvILZ9wuN3o8xY9412IkLColGg==","signatures":[{"sig":"MEYCIQDVnvbce35rjXGhdwHr1vtQrDxqq9LvvH2N1yzcQsstkwIhAJexaGwekP1Fkn4X8r3BuIFADDr1UwAkxZCCxwlBJtAH","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108748},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.12"},"gitHead":"3b955ac9f430a5f1c61a5f23724ee4c69af61a33","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.12_1693393584902_0.6826525658519529","host":"s3://npm-registry-packages"}},"3.0.13--canary.16.6492434348.0":{"name":"@salutejs/spatial","version":"3.0.13--canary.16.6492434348.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.13--canary.16.6492434348.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"0108a53189cc40a7495d0ba47a8c7009a17bafc7","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.13--canary.16.6492434348.0.tgz","fileCount":98,"integrity":"sha512-4Mm7gon/yedRnq3/u7Y5YEGLnEB9N0pHnoU0ie0BG5HBu1W037WCVi38UMiMJZjSDOhrzVC3+71QRXdhoxM3Gg==","signatures":[{"sig":"MEUCIQDH9tI/ljDsmHJNec6D0GKjinAnH4DDUhAbaLZ9FHStJAIgcZGhkVWmCNluvHUwDUlmtaNzcYAJLeEfdyXC8Mkyf8Y=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108772},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"a1d1f37cb039c58b371a993d7b33692a7ada6eb2","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.13--canary.16.6492434348.0_1697094657114_0.5572864690918424","host":"s3://npm-registry-packages"}},"3.0.13--canary.16.6492584559.0":{"name":"@salutejs/spatial","version":"3.0.13--canary.16.6492584559.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.13--canary.16.6492584559.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"46e57d4ea6b4030d2d1e6280e173077bec60ad74","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.13--canary.16.6492584559.0.tgz","fileCount":98,"integrity":"sha512-ZHgjdjDWN4YHbvvP+VWVn+kfXtcDWb7A56ILmXRfx5C/ehR7TGOPAQaL3UGC+yc+r+IzQausJXZCsf3FKjng8w==","signatures":[{"sig":"MEUCIEpfmJO5fJiraZOI8HHVtFeE/EzBl2zKhJjqZ7LO8UFTAiEAwpGquEQvwF78lGNXuXp+/8dL7syibdJ5HC6wQBuqKSI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108772},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"c6ac19ce3f840a896292b7b49daa607c956088e3","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.13--canary.16.6492584559.0_1697095624164_0.5964529717187599","host":"s3://npm-registry-packages"}},"3.0.13":{"name":"@salutejs/spatial","version":"3.0.13","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.13","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"e981f7d3f441272dd91826a8baf1bea7b02b54ec","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.13.tgz","fileCount":98,"integrity":"sha512-oxBKYwtjuLbGfDgL6ic6UZYjJvYNBL7/H5/+RV1LX0C0W/75kJk7h1bslLi7M8reCsAZELS9PrgHt3dehbf+ZQ==","signatures":[{"sig":"MEUCIQDZ/ta2Hn5Libz7Hv5ckHiZBk2a9mdAZ8SisCX8/p6x1QIgfJm+APBPBjZ4aTw1R2rZvJj21ZUC5CPgceyEyjCsjEI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108748},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.12"},"gitHead":"4c119969db220876111a839a881bf48bde96d318","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.13_1697098408337_0.8158831065866996","host":"s3://npm-registry-packages"}},"3.0.14--canary.17.6692819948.0":{"name":"@salutejs/spatial","version":"3.0.14--canary.17.6692819948.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.14--canary.17.6692819948.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"e3e620ec8612433ec3741a38e36755d5518e21c0","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.14--canary.17.6692819948.0.tgz","fileCount":98,"integrity":"sha512-7gOA4lw5Ly4cEJV4n93ZiJQEroWdZ6/WHdewK2B/w4l5E+uM1S3Air1S1S/z3f924M7OAkgHW6EzuoTQ4CITpA==","signatures":[{"sig":"MEUCIQCUEdc/vSvPXUuyJwzbj7lWqfE3XbzIueYAcvHLaZ64kAIgAt4Wn878auV8Kjiynu94jPJ4feCwp2jornRPSuKXkDc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108806},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"cb6b761262aa87a86f3253a15ca0c8eaf1a2c21e","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:run":"cypress run","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.14--canary.17.6692819948.0_1698669310222_0.7531589494049267","host":"s3://npm-registry-packages"}},"3.0.14--canary.17.6692848159.0":{"name":"@salutejs/spatial","version":"3.0.14--canary.17.6692848159.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.14--canary.17.6692848159.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"4241832f6bac49b05eaad59d9f4c83a84aed3cfd","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.14--canary.17.6692848159.0.tgz","fileCount":98,"integrity":"sha512-T+0OROkoS3bH5JLRBODVrNFeoUtXp2WxMzMyMUlBcBWvXFVhnGGGHXTSaZW4rJKfwLCOkunIdgYgYm/cw2sbNg==","signatures":[{"sig":"MEUCIC+W5M4wph/QU1il0LsoiH0fzBqak3pn/E/OS96YkhFAAiEAsBTSx24LUh9u+ikfYtlEmhS5JwzsiY0SE5xJVH1iCFE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108806},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"7622b864a3312a409411913dc6b07d8ba3f1bc2c","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:run":"cypress run","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.14--canary.17.6692848159.0_1698669447812_0.23065430833082856","host":"s3://npm-registry-packages"}},"3.0.14--canary.17.6693037545.0":{"name":"@salutejs/spatial","version":"3.0.14--canary.17.6693037545.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.14--canary.17.6693037545.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"56cf34449052c866ce170e88ed82f8c4697ab5a1","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.14--canary.17.6693037545.0.tgz","fileCount":98,"integrity":"sha512-Uqxb0KJCcMnYROP8B/zqWSh90bDX4WAljvlcgQNxEF3LeP+PpP3jqVAYeHEE6qojDqQ4HdLXnQ5N9V99CLleBA==","signatures":[{"sig":"MEUCIFhixA6jlpH6og7EdFIjpvR1msoR715Eiz+oFmyc0tvvAiEA14SEg1CNUNWeL8Im7zN+AwVaYLtWPyOByuYrc62Z2C8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108806},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"e7d8542fb7ed1be1d75620e8dfb02569c853c58f","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:run":"cypress run","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.14--canary.17.6693037545.0_1698670398221_0.8081760868894687","host":"s3://npm-registry-packages"}},"3.0.14--canary.17.6693128865.0":{"name":"@salutejs/spatial","version":"3.0.14--canary.17.6693128865.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.14--canary.17.6693128865.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"c9fd2e979ec89b3de88d5520156d4c025c183f5e","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.14--canary.17.6693128865.0.tgz","fileCount":98,"integrity":"sha512-Kt1RGLAI420YByB0foz4hBF5oNT1hSDUXQhhFs9yfHC35IxpTRpHcurLNZ7RMlHmPOvpGAqSi3Sb73IZy1HJ+A==","signatures":[{"sig":"MEYCIQDhfjA0+oh9pE1iMm8KleaGRW078ZObFKI9tJTthI94OwIhAKgKkIW1S+OPJJ7AejWpcai0xFedU9LOVab/YhOvVmOz","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108806},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"3717dbd974b72c6f57ee25260582296d9be03bcf","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:run":"cypress run","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:build":"parcel build ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.14--canary.17.6693128865.0_1698670834632_0.8911959402414613","host":"s3://npm-registry-packages"}},"3.0.14--canary.17.6693235005.0":{"name":"@salutejs/spatial","version":"3.0.14--canary.17.6693235005.0","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.14--canary.17.6693235005.0","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"d5c8ae80d33982421427eb792bb83e93d23b4fa1","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.14--canary.17.6693235005.0.tgz","fileCount":98,"integrity":"sha512-1V7Z9iNerH/t0w9awUGwdKKyH3uI5KDo0YPqTh95YSZqj+1Kx+nP4uhF+NtWV3KCKzm1qCIanh7CuGXWJuQR7g==","signatures":[{"sig":"MEQCIFKQU4oqpsTyCy1sLw/ROwth1rZ0bgukN9GgNEcTXU8eAiBpgr3zwcZRd1WXRw60RwYzOBfOcBI7VpofTBnllKRhrQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108708},"main":"dist/index.js","types":"./dist/index.d.ts","readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","engines":{"node":">=18.12"},"gitHead":"a577e913b8ba9e14af4fed7c075ce3786bc6e5d7","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:run":"cypress run","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.14--canary.17.6693235005.0_1698671321678_0.7141567540602323","host":"s3://npm-registry-packages"}},"3.0.14":{"name":"@salutejs/spatial","version":"3.0.14","keywords":["spatial navigation","spatnav","spat nav"],"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"_id":"@salutejs/spatial@3.0.14","maintainers":[{"name":"salute-eva","email":"salute.developers@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"salute-assistant","email":"winogradov.aa@ya.ru"}],"contributors":[{"name":"Арсений Кругликов"}],"homepage":"https://github.com/salute-developers/spatial#readme","bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"dist":{"shasum":"03aee3bd5baa1dc1a52089036d0713f9b5e0a594","tarball":"https://registry.npmjs.org/@salutejs/spatial/-/spatial-3.0.14.tgz","fileCount":98,"integrity":"sha512-YGMlRtaQmq1pyzu6bauCYLDHY7nPwRRtlktLVkWbFTA6AMEft4sHQIw7e7rwl+oiPq95/llMw8vHrMIxEHD6+g==","signatures":[{"sig":"MEUCIBOL9524rD8iRr/RZZDe8iGgIFoLEPAnx5IddBPvse75AiEAp+/Crev1dTJPNF6d6VIcgRZ1/68+r+9AAngK+KbRyQY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":108684},"main":"dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.12"},"gitHead":"e1e277043ac77b244de48c35da17d21b7ca22ac3","scripts":{"lint":"eslint src --ext .ts,.tsx","build":"npm run clean && npm run compile","clean":"rm -rf dist tsconfig.tsbuildinfo","compile":"tsc","prepare":"npm run build","release":"git diff && auto shipit","lint:fix":"eslint src --ext .ts,.tsx --fix","docs:build":"typedoc src/index.ts","docs:serve":"http-server ./docs -c-1","cypress:run":"cypress run","cypress:open":"cypress open","prettier:fix":"prettier --write .","test-app:start":"rm -rf dist-test-app .parcel-cache && parcel ./tests/pages/**/*.html --dist-dir dist-test-app --no-cache"},"_npmUser":{"name":"salute-eva","email":"salute.developers@gmail.com"},"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"_npmVersion":"8.19.2","description":"Spatial navigation","directories":{},"_nodeVersion":"18.12.1","browserslist":["last 1 Chrome versions"],"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"auto":"10.43.0","react":"18.2.0","eslint":"8.36.0","parcel":"2.8.3","cypress":"10.11.0","typedoc":"0.23.28","prettier":"2.8.7","react-dom":"18.2.0","typescript":"5.0.2","@types/node":"18.15.10","http-server":"14.1.1","@types/react":"18.0.29","eslint-config-prettier":"8.8.0","@typescript-eslint/parser":"5.56.0","eslint-plugin-react-hooks":"4.6.0","@typescript-eslint/eslint-plugin":"5.56.0"},"peerDependencies":{"react":"^16.13 || ^17.0 || ^18.0"},"_npmOperationalInternal":{"tmp":"tmp/spatial_3.0.14_1698739483375_0.8562305734477285","host":"s3://npm-registry-packages"}}},"time":{"created":"2022-12-26T11:37:14.734Z","modified":"2026-08-05T13:52:01.845Z","3.0.1":"2022-12-26T11:37:14.970Z","3.0.2--canary.1.4027832972.0":"2023-01-27T20:11:17.720Z","3.0.2--canary.2.4028016401.0":"2023-01-27T20:39:48.208Z","3.0.2":"2023-01-30T13:11:28.131Z","3.0.3--canary.3.4078167671.0":"2023-02-02T19:58:49.258Z","3.0.3--canary.3.4087646755.0":"2023-02-03T20:27:04.962Z","3.0.3--canary.3.4087649949.0":"2023-02-03T20:27:19.892Z","3.0.3--canary.3.4087657390.0":"2023-02-03T20:28:26.504Z","3.0.3--canary.3.4087668167.0":"2023-02-03T20:30:04.289Z","3.0.3--canary.3.4103451699.0":"2023-02-06T12:09:28.864Z","3.0.3":"2023-02-07T11:36:41.845Z","3.0.4--canary.4.4325805884.0":"2023-03-03T17:27:46.117Z","3.0.4--canary.4.4325864037.0":"2023-03-03T17:35:21.445Z","3.0.4--canary.4.4325949860.0":"2023-03-03T17:48:07.898Z","3.0.4--canary.4.4326068880.0":"2023-03-03T18:05:32.168Z","3.0.4--canary.4.4326308174.0":"2023-03-03T18:38:17.848Z","3.0.4--canary.4.4345357659.0":"2023-03-06T15:58:31.701Z","3.0.4--canary.4.4377057885.0":"2023-03-09T17:21:41.148Z","3.0.4--canary.4.4377094370.0":"2023-03-09T17:25:47.726Z","3.0.4--canary.4.4377215783.0":"2023-03-09T17:39:38.119Z","3.0.4":"2023-03-10T07:57:18.815Z","3.0.5":"2023-03-10T08:04:49.120Z","3.0.6--canary.5.4382669952.0":"2023-03-10T08:27:45.750Z","3.0.6--canary.5.4465504180.0":"2023-03-20T06:55:31.405Z","3.0.6--canary.6.4530393990.0":"2023-03-27T08:37:23.222Z","3.0.6":"2023-03-27T09:44:57.555Z","3.0.7--canary.5.4531085021.0":"2023-03-27T09:55:57.879Z","3.0.7":"2023-03-27T13:42:04.988Z","3.0.8--canary.7.4733382043.0":"2023-04-18T14:12:54.400Z","3.0.8--canary.7.4733398673.0":"2023-04-18T14:15:28.042Z","3.0.8--canary.8.4733427022.0":"2023-04-18T14:19:02.330Z","3.0.8--canary.8.4765645159.0":"2023-04-21T13:59:58.353Z","3.0.8":"2023-04-24T09:15:02.119Z","3.0.9--canary.9.5054829293.0":"2023-05-23T08:11:00.119Z","3.0.9":"2023-05-26T10:02:39.343Z","3.0.10--canary.10.5110963905.0":"2023-05-29T10:26:33.568Z","3.0.10--canary.10.5110974549.0":"2023-05-29T10:27:53.002Z","3.0.10":"2023-05-29T12:42:46.734Z","3.0.11--canary.11.5210081674.0":"2023-06-08T10:19:53.926Z","3.0.11--canary.11.5210089047.0":"2023-06-08T10:20:47.711Z","3.0.11":"2023-06-08T10:22:06.262Z","3.0.12--canary.12.6010406207.0":"2023-08-29T09:25:40.787Z","3.0.12--canary.12.6011301996.0":"2023-08-29T10:55:53.998Z","3.0.12--canary.12.6011320601.0":"2023-08-29T10:57:45.926Z","3.0.12--canary.12.6022009642.0":"2023-08-30T07:44:26.758Z","3.0.12--canary.13.6023934745.0":"2023-08-30T10:56:41.133Z","3.0.12":"2023-08-30T11:06:25.066Z","3.0.13--canary.16.6492434348.0":"2023-10-12T07:10:57.307Z","3.0.13--canary.16.6492584559.0":"2023-10-12T07:27:04.385Z","3.0.13":"2023-10-12T08:13:28.849Z","3.0.14--canary.17.6692819948.0":"2023-10-30T12:35:10.426Z","3.0.14--canary.17.6692848159.0":"2023-10-30T12:37:28.008Z","3.0.14--canary.17.6693037545.0":"2023-10-30T12:53:18.438Z","3.0.14--canary.17.6693128865.0":"2023-10-30T13:00:34.866Z","3.0.14--canary.17.6693235005.0":"2023-10-30T13:08:41.843Z","3.0.14":"2023-10-31T08:04:43.739Z"},"bugs":{"url":"https://github.com/salute-developers/spatial/issues"},"author":{"name":"Salute Frontend Team","email":"salute.developers@gmail.com"},"homepage":"https://github.com/salute-developers/spatial#readme","keywords":["spatial navigation","spatnav","spat nav"],"repository":{"url":"git+ssh://git@github.com/salute-developers/spatial.git","type":"git"},"description":"Spatial navigation","contributors":[{"name":"Арсений Кругликов"}],"maintainers":[{"email":"yeti.or@gmail.com","name":"yeti-or"},{"email":"salute.developers@gmail.com","name":"salute-eva"}],"readme":"# Spatial navigation для Canvas Apps\n\nПространственная навигация (Spatial navigation) — это возможность перемещаться между фокусируемыми элементами в зависимости от их положения в документе. Пространственная навигация часто называется «направленной навигацией», которая обеспечивает четырехнаправленную навигацию: сверху, слева, снизу, справа. Пользователи обычно знакомы с двусторонней навигацией с использованием «клавиш табуляции» для направления вперед, так и «клавиши Shift+Tab» для обратного направления.\n\n[Есть спецификация для браузеров](https://drafts.csswg.org/css-nav-1/), которая пока в статусе \"draft\".\n\nМы рекомендуем использовать эту библиотеку для Canvas Apps на наших девайсах: TV, SberBox и др,\nа [библиотеку](https://github.com/salute-developers/spatial-navigation) считать устаревшей.\n\nДля мобильных устройств или других устройств с сенсорным экраном это не надо.\n\nДокументации с аннотацией типов также доступна на сайте - https://plasma.sberdevices.ru/spatial/\n\n## Оглавление\n\n-   [Установка](#установка)\n-   [Минимальная настройка приложения для работы с `@salutejs/spatial`](#минимальная-настройка-приложения-для-работы-с-salutejsspatial`)\n    -   [Инициализация в родительском компоненте всего приложения](#инициализация-в-родительском-компоненте-всего-приложения)\n    -   [Добавление секции](#добавление-секции)\n    -   [Включение навигации на DOM элементе](#включение-навигации-на-dom-элементе)\n-   [Углубление в `@salutejs/spatial`](#углубление-в-salutejsspatial`)\n    -   [Варианты инициализации](#варианты-инициализации)\n    -   [Подробнее о секциях](#подробнее-о-секциях)\n        -   [Настройка параметров секции](#настройка-параметров-секции)\n-   [Хуки](#хуки)\n-   [Полезные методы SpatialNavigation](#полезные-методы-spatialnavigation)\n-   [Оптимизация и ускорение работы](#оптимизация-и-ускорение-работы)\n    -   [Intersection и Mutation observer](#intersection-и-mutation-observer)\n    -   [Простые секции](#простые-секции)\n-   [Запуск тестов](#запуск-тестов)\n-   [Pitfalls](#pitfalls)\n    -   [Next.js >= 13.0.0](#nextjs--1300)\n    -   [Next.js < 13.0.0](#nextjs--1300-1)\n\n## Установка\n\n```sh\nnpm install --save @salutejs/spatial\n```\n\n## Минимальная настройка приложения для работы с `@salutejs/spatial`\n\nНужно выполнить три обязательных шага.\n\n### Инициализация в родительском компоненте всего приложения\n\n```jsx\nimport { useSpatnavInitialization } from '@salutejs/spatial';\n\nimport { Page } from './pages/Page';\n\nconst App = () => {\n    useSpatnavInitialization();\n\n    return <Page />;\n};\n```\n\n### Добавление секции\n\nДля навигации `@salutejs/spatial` использует [секции](#подробнее-о-секциях). Секцию можно добавить с помощью хука `useSection`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [sectionProps] = useSection('sectionName');\n\n    return <div {...sectionProps} />;\n};\n\nexport default Page;\n```\n\n### Включение навигации на DOM элементе\n\nДля того, чтобы браузер имел возможность фокусироваться на DOM элемент, этот элемент должен иметь атрибут tabindex=\"-1\".\n\nДалее для работы внутренних функций `@salutejs/spatial` необходимо добавить DOM элементу CSS класс \"sn-section-item\".\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"sn-section-item any-class my-class\" tabIndex={-1}>\n        навигация работает\n    </div>\n</>\n```\n\nЕсли убрать класс `sn-section-item`, то элемент исключается из навигации.\n\n```jsx\n<>\n    <div className=\"sn-section-item\" tabIndex={-1}>\n        навигация работает\n    </div>\n    <div className=\"any-class my-class\" tabIndex={-1}>\n        навигация НЕ работает\n    </div>\n</>\n```\n\nДобавим элементы в секцию.\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    // создание секции\n    const [sectionProps] = useSection('sectionName');\n\n    // установка фокуса на элемент\n    const ref = useRef(null);\n\n    useEffect(() => {\n        const focusable = ref.current;\n\n        if (focusable) {\n            focusable.focus();\n        }\n    }, []);\n\n    return (\n        <div {...sectionProps}>\n            <div ref={ref} className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает (после выполнения useEffect, фокус будет установлен на этот элемент)\n            </div>\n            <div className=\"sn-section-item\" tabIndex={-1}>\n                навигация работает\n            </div>\n            <div>навигация НЕ работает</div>\n        </div>\n    );\n};\n\nexport default Page;\n```\n\nГотово! Навигация настроена. Но надо подчеркнуть, что после инициализации и добавления секций фокус автоматически не устанавливается ни на один элемент. Это надо делать вручную или с помощью хука `useDefaultSectionFocus`.\n\nЗдесь были рассмотрены только необходимые действия. Для более гибкой настройки секций и навигации читайте далее.\n\n## Углубление в `@salutejs/spatial`\n\n### Варианты инициализации\n\nМожно инициализировать `@salutejs/spatial` без использования хука `useSpatnavInitialization`, если такое требуется.\n\n```js\nimport { spatnavInstance } from '@salutejs/spatial';\n\n// вызывать только на клиенте\nspatnavInstance.init();\n```\n\nАналогично можно и отменить инициализацию. Например, при переходе на страницу, где пространственная навигация не нужна.\n\n```js\nspatnavInstance.unInit();\n```\n\n### Подробнее о секциях\n\nСекция — это элементы навигации, объединённые в группу. У секции есть корневой элемент.\nВключение секции в навигацию происходит с помощью хука `useSection`. У корневого элемента секции должны быть установлены аттрибуты `id=\"имя секции, переданное в useSection\"` и `className=\"sn-section-root\"`, которые возвращает хук `useSection`.\nЭлементы секции должны быть потомками корневого элемента и иметь аттрибут `className=\"sn-section-item\"`.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1] = useSection('section1');\n    const [section2] = useSection('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\n#### Настройка параметров секции\n\nТакже хук `useSection` возвращает функцию кастомизации. С её помощью можно гибко настроить правила навигации внутри и между секциями. А также включить или выключить секцию целиком.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        // выключаем навигацию в секции section1 целиком\n        customize1({\n            disabled: true,\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n            // установка элемента по умолчанию для секции\n            getDefaultElement: (section2Root) => section2Root.firstElementChild,\n            enterTo: 'default-element',\n        });\n    }, [[customize1, customize2]]);\n\n    // установка секции по умолчанию и установка фокуса на элемент из этой секции, выбранный по правилам определённым в её конфиге\n    // https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html\n    useDefaultSectionFocus('section2');\n\n    return (\n        <>\n            <div {...section1}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section1, навигация выключена\n                </div>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1}>\n                            принадлежит секции section1, навигация выключена\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2. После выполнения всех хуков, фокус будет установлен на этот элемент\n                </div>\n                <div className=\"sn-section-item\" tabIndex={-1}>\n                    принадлежит секции section2\n                </div>\n            </div>\n        </>\n    );\n};\n```\n\nО всех параметрах секции можно почитать в [документации к типу `Config`](https://plasma.sberdevices.ru/spatial/types/Config.html). Параметры передаются в функцию `customize`.\n\n## Хуки\n\n-   [`useSpatnavInitialization`](https://plasma.sberdevices.ru/spatial/functions/useSpatnavInitialization.html) - инициализация навигации;\n-   [`useSection`](https://plasma.sberdevices.ru/spatial/functions/useSection.html) - создание секции;\n-   [`useSelfSection`](https://plasma.sberdevices.ru/spatial/functions/useSelfSection.html) - создание секции, состоящей только из одного элемента;\n-   [`useDefaultSectionFocus`](https://plasma.sberdevices.ru/spatial/functions/useDefaultSectionFocus.html).\n\n## Полезные методы SpatialNavigation\n\nУ инстанса SpatialNavigation есть ряд методов для упрощения некоторых сценариев:\n\n-   Включение и выключение навигации для данной секции или полностью\n-   Удаление или добавление секции в навигацию\n-   Изменение глобального конфига или конфига секции\n-   Фокусирование указанной секции\n\nНапример, фокусирование на определенной секции на монтирование компонента.\n\n```jsx\nimport { useSection, spatnavInstance } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n\n   const [sectionProps] = useSection('suggest');\n\n   useEffect(() => {\n       spatnavInstance.focus('suggest');\n   }, []);\n\n   <Suggest {...sectionProps}>\n       ...\n   <Suggest/>\n};\n```\n\nПолный список методов и их сигнатуры смотрите в коде [SpatialNavigation](https://github.com/salute-developers/spatial/blob/master/src/core/SpatialNavigation.ts) или на [советующей странице документации](https://plasma.sberdevices.ru/spatial/classes/SpatialNavigation.html)\n\n## Оптимизация и ускорение работы\n\n### Intersection и Mutation observer\n\nДля ускорения расчётов в `@salutejs/spatial` используются Intersection и Mutation observer. Первый следит за тем какой элемент находится во вьюпорте браузера. `@salutejs/spatial` в первую очередь будет пытаться найти подходящий для навигации элемент именно среди видимых элементов.\nMutation observer нужен для того, чтобы при обновлении DOM дерева, новые элементы навигации были обработаны Intersection observer'ом.\n\n### Простые секции\n\n`@salutejs/spatial` делает довольно много расчётов, чтобы понять какой элемент больше подходит для навигации.\nНо если в вашей вёрстке есть списки, в которых элементы всегда расположенны в ряд или столбик, то лучше включить режим простых секций. Этот режим переопределяет поведение навигации при выборе следующего или предыдущего элемента.\nВместо расчётов `@salutejs/spatial` просто возьмёт нужный элемент из DOM с помощью `nextSiblingElement` или `previousSiblingElement`.\nДля того, чтобы этот режим работал необходимо передать соответствующую опцию в конфиг секции. И, обратите внимание, что все элементы секции должны быть на одном уровне в DOM.\n\nВ обоих примерах ниже режим простых секций будет работать.\n\n```jsx\nimport { useSection } from '@salutejs/spatial';\n\nconst Page = ({ children }) => {\n    const [section1, customize1] = useSection('section1');\n    const [section2, customize2] = useSection('section2');\n\n    useEffect(() => {\n        customize1({\n            simpleSectionOptions: { type: 'column' },\n        });\n\n        customize2({\n            simpleSectionOptions: { type: 'row' },\n        });\n    }, [customize1, customize2]);\n\n    return (\n        <>\n            <div {...section1}>\n                <div>\n                    <div>\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                        <div className=\"sn-section-item\" tabIndex={-1} />\n                    </div>\n                </div>\n            </div>\n            <div {...section2}>\n                <div className=\"sn-section-item\" tabIndex={-1} />\n                <div className=\"sn-section-item\" tabIndex={-1} />\n            </div>\n        </>\n    );\n};\n```\n\n## Запуск тестов\n\nДля запуска тесов нужно собрать пакет spatial, запустить `test-app` и `cypress`.\n\n```sh\nnpm run build\n\nnpm run test-app:start\n\nnpm run cypress:open\n```\n\nВ открывшемся окне Cypress выбрать `E2E Testing`, тестировать можно как в Chrome так и в Electron.\n\n## Pitfalls\n\n@salutejs/spatial не компилируется в CommonJS, поэтому если вы используете Next.js выполните следующее:\n\n### Next.js >= 13.0.0\n\nВ `next.config.js` вам необходимо добавить свойство `transpilePackages`.\n\n```js\nconst config = {\n    transpilePackages: ['@salutejs/spatial'],\n};\n```\n\nДокументация по [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages).\n\n### Next.js < 13.0.0\n\nВоспользуйтесь пакетом [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules).\n","readmeFilename":"README.md"}