mcpField
N
norigin-spatial-navigation-react

norigin-spatial-navigation-react

github.com/noriginmedia开发49391 次安装2 天前更新
代码仓库

关于 norigin-spatial-navigation-react

norigin-spatial-navigation-react 是使用 Norigin Spatial Navigation 管理 React 应用方向键和遥控器焦点的技能。它介绍可聚焦元素、嵌套容器、焦点边界及程序化焦点,适用于电视应用等界面。

主要功能

  1. 01React 空间焦点导航
  2. 02可聚焦叶节点与层级容器
  3. 03模态框和浮层的焦点边界
  4. 04程序化焦点与稳定焦点键
  5. 05自定义方向输入和事件回调

适用场景

  1. 01构建 Smart TV 界面的方向键导航
  2. 02管理菜单、网格和轮播中的焦点
  3. 03限制模态对话框内的焦点
  4. 04适配自定义遥控器按键

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/noriginmedia/norigin-spatial-navigation --skill norigin-spatial-navigation-react

安装后在 Claude Code 中输入/norigin-spatial-navigation-react确认已加载。

详细介绍

Norigin Spatial Navigation

用于在 React 应用(智能电视、机顶盒、浏览器)中管理方向键/遥控器焦点的库。它会根据空间位置计算下一个可聚焦元素,因此无需手动编写方向导航逻辑。

完整文档: https://github.com/NoriginMedia/Norigin-Spatial-Navigation/tree/main/docs

需要了解本技能未涵盖的详情时,请直接阅读相关页面:

  • 概念与层级:docs/guides/concepts.md、docs/guides/focus-hierarchy.md
  • 快速入门与安装:docs/guides/quick-start.md、docs/guides/installation.md
  • useFocusable API:docs/api-reference/useFocusable.md
  • SpatialNavigation API(init、setFocus、pause 等):docs/api-reference/SpatialNavigation.md
  • 边界/模态框:docs/guides/focus-boundaries.md
  • 程序化设置焦点:docs/guides/programmatic-focus.md
  • 距离计算调优:docs/guides/distance-calculation.md
  • 按键映射(自定义遥控器):docs/guides/key-mapping.md
  • 事件回调:docs/guides/event-callbacks.md
  • 性能:docs/guides/performance.md
  • 调试:docs/guides/debugging.md
  • 示例(列表、网格、虚拟化):docs/guides/recipes.md
  • 无障碍标签:docs/guides/accessibility-labels.md
  • RTL:docs/guides/rtl-support.md

心智模型

有两种组件形态:

  1. 叶节点 — 实际接收焦点的交互元素。通过 useFocusable 读取 focused。
  2. 容器 — 包含可聚焦子元素。读取 focusKey,并通过 FocusContext.Provider 提供该值,让子元素参与层级结构。也可以选择读取 hasFocusedChild(需要设置 trackChildren: true)。

每个可聚焦组件都必须将返回的 ref 附加到真实的 DOM 元素。容器必须将子元素包在 <FocusContext.Provider value={focusKey}> 中,否则子元素会成为根节点下的孤立元素,跨子元素导航将无法正常工作。

初始化

在应用启动时(例如在 App.tsx 中)调用一次 init:

import { init as initNavigation } from '@noriginmedia/norigin-spatial-navigation-core';

initNavigation({
  debug: false,
  visualDebug: false,
  throttle: 0,
  distanceCalculationMethod: 'corners' // 'center' | 'edges' | 'corners'
});

如果要发布自定义遥控器按键,也要配置按键映射(参见 docs/guides/key-mapping.md)。

模式

叶节点(按钮、菜单项)
import { useFocusable } from '@noriginmedia/norigin-spatial-navigation-react';

function Button({ label, onPress }: Props) {
  const { ref, focused } = useFocusable({ onEnterPress: onPress });
  return (
    <div ref={ref} className={focused ? 'focused' : ''}>
      {label}
    </div>
  );
}
容器
import {
  useFocusable,
  FocusContext
} from '@noriginmedia/norigin-spatial-navigation-react';

function Menu({ items }: Props) {
  const { ref, focusKey, hasFocusedChild } = useFocusable({
    trackChildren: true,
    saveLastFocusedChild: true
  });

  return (
    <FocusContext.Provider value={focusKey}>
      <div ref={ref} className={hasFocusedChild ? 'active' : ''}>
        {items.map((i) => (
          <MenuItem key={i.id} {...i} />
        ))}
      </div>
    </FocusContext.Provider>
  );
}
模态框/弹出窗口(限制焦点范围 + 挂载时获取焦点)
function Popup() {
  const { ref, focusKey, focusSelf } = useFocusable({ isFocusBoundary: true });

  useEffect(() => {
    focusSelf();
  }, [focusSelf]);

  return (
    <FocusContext.Provider value={focusKey}>
      <div ref={ref}>{/* buttons */}</div>
    </FocusContext.Provider>
  );
}

如需部分限制焦点范围(例如只阻止水平方向),请使用 focusBoundaryDirections。参见 docs/guides/focus-boundaries.md。

从任意位置以编程方式设置焦点
import {
  setFocus,
  doesFocusableExist
} from '@noriginmedia/norigin-spatial-navigation-core';

const KEY = 'primary-cta';
useFocusable({ focusKey: KEY });

if (doesFocusableExist(KEY)) setFocus(KEY);
条件性设置是否可聚焦
const { ref } = useFocusable({ focusable: !disabled });
阻止/覆盖方向输入

onArrowPress(direction, props) — 返回 true 以允许默认导航,返回 false 以阻止导航。适用于分页行、自定义轮播,或在容器边缘阻止离开当前容器的情况。

useFocusable — 值得了解的参数

参数用途
focusable切换元素是否参与导航。默认值为 true。
trackChildren启用 hasFocusedChild。会影响性能——仅在需要读取该值时启用。
saveLastFocusedChild容器重新进入时恢复上次聚焦的子元素。默认值为 true。
isFocusBoundary / focusBoundaryDirections将焦点限制在容器内。
focusKeysetFocus / doesFocusableExist 使用的稳定键。
preferredChildFocusKey进入容器时哪个子元素获得初始焦点。
onEnterPress、onArrowPress、onFocus、onBlur事件钩子。请参阅 docs/guides/event-callbacks.md。
extraProps转发给回调的任意负载(例如条目 ID)。

返回值:ref(必需)、focusKey、focused(叶节点)、hasFocusedChild(容器)、focusSelf()。

顶层 API 概览

import {
  init,
  setFocus,
  getCurrentFocusKey,
  doesFocusableExist,
  navigateByDirection,
  pause,
  resume,
  setKeyMap,
  setThrottle,
  destroy
} from '@noriginmedia/norigin-spatial-navigation-core';

完整列表和签名请参阅 docs/api-reference/SpatialNavigation.md。

最佳实践

应该

  • 始终将 ref 附加到 DOM 元素。
  • 使用 <FocusContext.Provider value={focusKey}> 包裹容器的子元素。
  • 仅在实际使用 hasFocusedChild 时启用 trackChildren。
  • 对需要以编程方式定位的元素使用稳定的 focusKey。
  • 调用 setFocus 前使用 doesFocusableExist 进行检查,尤其是在路由变更或异步数据场景中。
  • 对模态框、抽屉和覆盖层使用 isFocusBoundary。
  • 对禁用或隐藏的条目设置 focusable: false,而不是卸载它们(如果它们可能频繁切换)。

不应该

  • 不要在容器上读取 focused——应改用 hasFocusedChild。
  • 不要在容器上省略 FocusContext.Provider;否则子元素无法从同级元素访问。
  • 不要盲目地对可能尚未挂载的键调用 setFocus(这会导致焦点悄然丢失)。
  • 不要生成每次渲染都会变化的 focusKey——焦点状态会丢失。
  • 不要在生产构建中启用 debug / visualDebug。

调试

在 init 中启用诊断:

init({ debug: true, visualDebug: true });

常见问题:

  • 元素无法获得焦点 — 未附加 ref,或设置了 focusable: false,或祖先缺少 FocusContext.Provider。
  • 焦点跳到错误的相邻元素 — 尝试其他 distanceCalculationMethod(corners / center / edges);启用 visualDebug 查看命中框。
  • 重新渲染时焦点丢失 — focusKey 不稳定,或之前聚焦的节点已卸载且没有备用方案。使用 preferredChildFocusKey,或在新树挂载后调用 setFocus。
  • 模态框中焦点泄漏 — 模态框容器缺少 isFocusBoundary。

更多信息:docs/guides/debugging.md、docs/guides/performance.md。

快速参考

// Leaf
const { ref, focused } = useFocusable();

// Container
const { ref, focusKey } = useFocusable();
<FocusContext.Provider value={focusKey}>
  <div ref={ref}>{children}</div>
</FocusContext.Provider>;

// Focus on mount
const { focusSelf } = useFocusable({ isFocusBoundary: true });
useEffect(() => {
  focusSelf();
}, [focusSelf]);

// Manual focus
if (doesFocusableExist('my-key')) setFocus('my-key');

评论

支持 Markdown