Tabbable
Maintain a consistent tab order for tabbable elements.
'use client';
import React from 'react';
import type { PlateElementProps } from '@udecode/plate-common/react';
import { cn } from '@udecode/cn';
import { Plate } from '@udecode/plate-common/react';
import { TabbablePlugin } from '@udecode/plate-tabbable/react';
import { useFocused, useSelected } from 'slate-react';
import { editorPlugins } from '@/components/editor/plugins/editor-plugins';
import { useCreateEditor } from '@/components/editor/use-create-editor';
import { tabbableValue } from '@/components/values/tabbable-value';
import { Editor, EditorContainer } from '@/components/plate-ui/editor';
import { PlateElement } from '@/components/plate-ui/plate-element';
export default function TabbableDemo() {
const editor = useCreateEditor({
plugins: [
...editorPlugins,
TabbablePlugin.configure({
node: { component: TabbableElement, isElement: true, isVoid: true },
}),
],
value: tabbableValue,
});
return (
<Plate editor={editor}>
<EditorContainer variant="demo">
<Editor />
</EditorContainer>
</Plate>
);
}
export function TabbableElement({ children, ...props }: PlateElementProps) {
const selected = useSelected();
const focused = useFocused();
return (
<PlateElement {...props}>
<div
className={cn(
'mb-2 p-2',
selected && focused
? 'border-2 border-blue-500'
: 'border border-gray-200'
)}
contentEditable={false}
>
<p>This is a void element.</p>
<button type="button">Button 1</button>{' '}
<button type="button">Button 2</button>
</div>
{children}
</PlateElement>
);
}
安装
npm install @udecode/plate-tabbable
Usage
import { TabbablePlugin } from '@udecode/plate-tabbable/react';
const plugins = [
// ...其他插件,
TabbablePlugin,
];
与其他插件冲突
Tabbable 插件可能会与其他处理 Tab
键的插件(如:
- 列表
- 代码块
- Indent 插件
使用 query
选项在 Tab
键应由其他插件处理时禁用 Tabbable 插件:
query: (editor) => {
const inList = findNode(editor, { match: { type: ListItemPlugin.key } });
const inCodeBlock = findNode(editor, { match: { type: CodeBlockPlugin.key } });
return !inList && !inCodeBlock;
},
或者,如果你正在使用 Indent 插件,你可以只在选择特定类型的节点时启用 Tabbable 插件,例如 voids:
query: (editor) => !!findNode(editor, {
match: (node) => isVoid(editor, node),
}),
非 void Slate 节点
一个 TabbableEntry
将创建每个可 tab 切换的 DOM 元素,使用 tabbable NPM 包确定。然后使用 isTabbable
过滤列表。
默认情况下,isTabbable
仅对 void Slate 节点内的entry返回 true。你可以覆盖 isTabbable
以添加对包含在其他类型 Slate 节点中的 DOM 元素的支持。
// Enable tabbable DOM elements inside CUSTOM_ELEMENT
isTabbable: (tabbableEntry) => (
tabbableEntry.slateNode.type === CUSTOM_ELEMENT ||
isVoid(editor, tabbableEntry.slateNode)
),
DOM elements outside the editor
在某些情况下,你可能希望允许用户从编辑器切换到渲染在编辑器之外的 DOM 元素,例如交互式弹出框。
要实现这一点,请覆盖 insertTabbableEntries
以返回 TabbableEntry
对象的数组,每个 DOM 元素一个,这些元素是你希望包含在可 tab 切换列表中的元素。TabbableEntry
的 slateNode
和 path
应指向用户光标将位于的 Slate 节点,当 DOM 元素应可 tab 切换时。例如,如果 DOM 元素在链接被选中时出现,则 slateNode
和 path
应为链接的。
设置 globalEventListener
选项为 true
以确保 Tabbable 插件能够将用户的焦点返回到编辑器。
// Add buttons inside .my-popover to the list of tabbables
globalEventListener: true,
insertTabbableEntries: (editor) => {
const [selectedNode, selectedNodePath] = getNodeEntry(editor, editor.selection);
return [
...document.querySelectorAll('.my-popover > button'),
].map((domNode) => ({
domNode,
slateNode: selectedNode,
path: selectedNodePath,
}));
},
插件
TabbablePlugin
Options
- 默认值:
() => true
- 默认值:
false
- 默认值:
() => []
- 默认值:
(tabbableEntry) => isVoid(editor, tabbableEntry.slateNode)
动态启用或禁用插件。默认返回 true
。
确定插件是否将其事件监听器添加到文档而不是编辑器,这允许它捕获来自编辑器外部的事件。
向可 tab 切换元素列表添加额外的entry。对于添加不在编辑器内的可 tab 切换元素很有用。它会忽略 isTabbable
。
确定一个元素是否应该包含在可 tab 切换列表中。如果 tabbableEntry 的 slateNode 是 void,则返回 true
。
API
TabbableEntry
定义可 tab 切换entry的属性。
Attributes
表示可 tab 切换entry的 HTML 元素。
可 tab 切换entry对应的 Slate 节点。
Slate 文档中 Slate 节点的路径。