99 lines
6.2 KiB
JSON
99 lines
6.2 KiB
JSON
{
|
||
"slug": "enhancedtabnav",
|
||
"end": "react",
|
||
"curated": true,
|
||
"summary": "可拖拽页签栏:不接收任何 props,页签数据、拖拽排序与右键菜单全部是组件内部状态。",
|
||
"usage": [
|
||
"React 端这个组件没有 props——函数签名就是无参数的 EnhancedTabNav(),页签来自模块内的 INITIAL 常量(EnhancedTabNav.jsx:4-12)。",
|
||
"拖拽排序用原生 HTML5 拖放:页签上设 draggable=\"true\" 与 data-id,onDragStart 记 id、容器 onDrop 里读 dataset.id 重排(EnhancedTabNav.jsx:77-80,55-68)。",
|
||
"右键菜单是自绘的:onContextMenu 阻止默认菜单后,在 clientX/clientY 处渲染三项菜单(关闭/关闭其他/关闭全部)(EnhancedTabNav.jsx:40-44,91-95)。",
|
||
"关闭逻辑带保底:只剩一个页签时不做删除,关闭当前项后自动激活它左边的一项(EnhancedTabNav.jsx:30-38)。",
|
||
"未保存标记是数据字段 dirty,为真时在标题前渲染一个圆点(EnhancedTabNav.jsx:5-8,83)。",
|
||
"点击外部收起菜单靠 document 级 click 监听,在 useEffect 清理函数里解绑(EnhancedTabNav.jsx:20-26)。"
|
||
],
|
||
"bestPractices": [
|
||
"要接入真实业务必须先改这份组件:它把演示数据与状态写死在内部,没有 items/onChange 之类的出口(EnhancedTabNav.jsx:13-17)。",
|
||
"onDrop 用 e.target.closest 找页签,因此页签内部的子元素(圆点、×)也能正确落到同一行目标上(EnhancedTabNav.jsx:56)。",
|
||
"拖拽只在同一容器内排序,没有跨容器或跨窗口能力。"
|
||
],
|
||
"apiCarrier": {
|
||
"kind": "props",
|
||
"note": "React 端的 API 载体是 props 与回调:属性驱动外观(type/size/disabled 等布尔与枚举),交互经 onXxx 回传(如 onChange/onClick/onSelectionChange);没有 v-model,受控组件固定写成 value + onChange,非受控则用 defaultValue。",
|
||
"table": [
|
||
{
|
||
"name": "tabs",
|
||
"type": "INITIAL 常量(内部 state)",
|
||
"desc": "页签数组,由内部 setTabs 维护,组件不接收它作为 prop"
|
||
},
|
||
{
|
||
"name": "activeId",
|
||
"type": "string",
|
||
"desc": "当前激活页签 id,内部 state(默认 u),组件不接收它作为 prop"
|
||
},
|
||
{
|
||
"name": "draggable",
|
||
"type": "\"true\"",
|
||
"desc": "页签节点上的原生拖放开关(DOM 属性,非组件 props)"
|
||
},
|
||
{
|
||
"name": "data-id",
|
||
"type": "string",
|
||
"desc": "页签 DOM 上的 id 数据属性,onDrop 靠它定位(DOM 属性,非组件 props)"
|
||
},
|
||
{
|
||
"name": "onDragStart",
|
||
"type": "() => void",
|
||
"desc": "页签上的内部处理器,拖起时记录 dragId(非组件 props)"
|
||
},
|
||
{
|
||
"name": "onDrop",
|
||
"type": "(e) => void",
|
||
"desc": "容器上的内部处理器,落到页签上时按 data-id 重排 tabs(非组件 props)"
|
||
},
|
||
{
|
||
"name": "onContextMenu",
|
||
"type": "(e, id) => void",
|
||
"desc": "页签上的内部处理器,右键时记录坐标与目标 id 并打开菜单(非组件 props)"
|
||
},
|
||
{
|
||
"name": "onClick",
|
||
"type": "() => void",
|
||
"desc": "页签上的内部处理器,点击切换到该 id(非组件 props)"
|
||
}
|
||
]
|
||
},
|
||
"a11y": [
|
||
"页签是带 onClick 的 div(EnhancedTabNav.jsx:74-82),没有 role=\"tab\"/aria-selected,读屏不知当前激活项。",
|
||
"拖拽排序只能用鼠标,键盘无法移动页签位置;右键菜单同样没有键盘入口(无 Shift+F10/菜单键处理)。",
|
||
"关闭标记是带 onClick 的 span 且已 stopPropagation(EnhancedTabNav.jsx:85),但它不可聚焦,键盘用户无法关闭页签;底部提示文案是纯文本说明(EnhancedTabNav.jsx:97)。"
|
||
],
|
||
"resources": [
|
||
{
|
||
"file": "frameworks/EnhancedTabNav.jsx",
|
||
"kind": "React(React)",
|
||
"desc": "React 端组件实现源码,props 与回调的唯一定义处"
|
||
},
|
||
{
|
||
"file": "frameworks/EnhancedTabNav.css",
|
||
"kind": "样式",
|
||
"desc": "该 React 实现依赖的样式文件"
|
||
}
|
||
],
|
||
"exampleIds": [
|
||
"enhancedtabnav-ex-1"
|
||
],
|
||
"notes": [
|
||
"frameworks/EnhancedTabNav.jsx:13 中可指到 tabs(INITIAL 常量(内部 state))——页签数组,由内部 setTabs 维护,组件不接收它作为 prop",
|
||
"frameworks/EnhancedTabNav.jsx:14 中可指到 activeId(string)——当前激活页签 id,内部 state(默认 u),组件不接收它作为 prop",
|
||
"frameworks/EnhancedTabNav.jsx:77 中可指到 draggable(\"true\")——页签节点上的原生拖放开关(DOM 属性,非组件 props)",
|
||
"frameworks/EnhancedTabNav.jsx:78 中可指到 data-id(string)——页签 DOM 上的 id 数据属性,onDrop 靠它定位(DOM 属性,非组件 props)",
|
||
"frameworks/EnhancedTabNav.jsx:80 中可指到 onDragStart(() => void)——页签上的内部处理器,拖起时记录 dragId(非组件 props)",
|
||
"frameworks/EnhancedTabNav.jsx:55 中可指到 onDrop((e) => void)——容器上的内部处理器,落到页签上时按 data-id 重排 tabs(非组件 props)",
|
||
"frameworks/EnhancedTabNav.jsx:72 中可指到 onContextMenu((e, id) => void)——页签上的内部处理器,右键时记录坐标与目标 id 并打开菜单(非组件 props)",
|
||
"frameworks/EnhancedTabNav.jsx:79 中可指到 onClick(() => void)——页签上的内部处理器,点击切换到该 id(非组件 props)",
|
||
"props 解构清单见 frameworks/EnhancedTabNav.jsx:12-12;本文件 table 只列该端真正消费的属性与回调。",
|
||
"过滤口径:事实包 consumedApi 里还包含绑在组件内部节点上的同名处理器(如 onClick/onChange 绑在内部 button、input、checkbox 上,见 frameworks/EnhancedTabNav.jsx 全文),它们不是对外 props,因此未进 table。",
|
||
"table 构成核验:8 行中 0 行是 frameworks/EnhancedTabNav.jsx 解构出的顶层 props(解构块 frameworks/EnhancedTabNav.jsx:12-12);另 8 行不是顶层 props,而是 frameworks/EnhancedTabNav.jsx 内部 state 或 items[*] 数据字段,desc 已逐行标明其来源(组件不接收这些名字作为 prop)。"
|
||
]
|
||
}
|