Back to Plate

组合框(Combobox)

docs/(plugins)/(functionality)/(combobox)/combobox.cn.mdx

1.0.08.1 KB
Original Source
<Cards> <Card icon="mention" title="提及功能" href="/docs/mention"> 使用`@`插入用户、页面或任何参考的提及 </Card> <Card icon="slash-command" title="斜杠命令" href="/docs/slash-command"> 通过`/`快速访问编辑器命令和块 </Card> <Card icon="emoji" title="表情符号" href="/docs/emoji"> 使用`:`自动补全插入表情符号 </Card> </Cards> <PackageInfo>

功能特性

  • 创建基于触发器的组合框功能的实用工具
  • 可配置的触发字符和模式
  • 键盘导航和选择处理
</PackageInfo>

创建组合框插件

<Steps>

安装

bash
npm install @platejs/combobox

创建输入插件

首先创建一个输入插件,当触发器激活时将被插入:

tsx
import { createSlatePlugin } from 'platejs';

const TagInputPlugin = createSlatePlugin({
  key: 'tag_input',
  editOnly: true,
  node: {
    isElement: true,
    isInline: true,
    isVoid: true,
  },
});

创建主插件

使用withTriggerCombobox创建主插件:

tsx
import { createTSlatePlugin, type PluginConfig } from 'platejs';
import { 
  type TriggerComboboxPluginOptions, 
  withTriggerCombobox 
} from '@platejs/combobox';

type TagConfig = PluginConfig<'tag', TriggerComboboxPluginOptions>;

export const TagPlugin = createTSlatePlugin<TagConfig>({
  key: 'tag',
  node: { isElement: true, isInline: true, isVoid: true },
  options: {
    trigger: '#',
    triggerPreviousCharPattern: /^\s?$/,
    createComboboxInput: () => ({
      children: [{ text: '' }],
      type: 'tag_input',
    }),
  },
  plugins: [TagInputPlugin],
}).overrideEditor(withTriggerCombobox);
  • node.isElement: 定义此为元素节点(非文本)
  • node.isInline: 使标签元素内联(非块级)
  • node.isVoid: 防止在标签元素内编辑
  • options.trigger: 触发组合框的字符(本例为#)
  • options.triggerPreviousCharPattern: 必须匹配触发器前字符的正则表达式。/^\s?$/允许在行首或空白后触发
  • options.createComboboxInput: 触发器激活时创建输入元素节点的函数

创建组件

使用InlineCombobox创建输入元素组件:

tsx
import { PlateElement, useFocused, useReadOnly, useSelected } from 'platejs/react';
import {
  InlineCombobox,
  InlineComboboxContent,
  InlineComboboxEmpty,
  InlineComboboxInput,
  InlineComboboxItem,
} from '@/components/ui/inline-combobox';
import { cn } from '@/lib/utils';

const tags = [
  { id: 'frontend', name: '前端', color: 'blue' },
  { id: 'backend', name: '后端', color: 'green' },
  { id: 'design', name: '设计', color: 'purple' },
  { id: 'urgent', name: '紧急', color: 'red' },
];

export function TagInputElement({ element, ...props }) {
  return (
    <PlateElement as="span" {...props}>
      <InlineCombobox element={element} trigger="#">
        <InlineComboboxInput />
        
        <InlineComboboxContent>
          <InlineComboboxEmpty>未找到标签</InlineComboboxEmpty>
          
          {tags.map((tag) => (
            <InlineComboboxItem
              key={tag.id}
              value={tag.name}
              onClick={() => {
                // 插入实际标签元素
                editor.tf.insertNodes({
                  type: 'tag',
                  tagId: tag.id,
                  children: [{ text: tag.name }],
                });
              }}
            >
              <span 
                className={`w-3 h-3 rounded-full bg-${tag.color}-500 mr-2`}
              />
              #{tag.name}
            </InlineComboboxItem>
          ))}
        </InlineComboboxContent>
      </InlineCombobox>
      
      {props.children}
    </PlateElement>
  );
}

export function TagElement({ element, ...props }) {
  const selected = useSelected();
  const focused = useFocused();
  const readOnly = useReadOnly();

  return (
    <PlateElement
      {...props}
      className={cn(
        'inline-block rounded-md bg-primary/10 px-1.5 py-0.5 align-baseline text-sm font-medium text-primary',
        !readOnly && 'cursor-pointer',
        selected && focused && 'ring-2 ring-ring'
      )}
      attributes={{
        ...props.attributes,
        contentEditable: false,
        'data-slate-value': element.value,
      }}
    >
      #{element.value}
      {props.children}
    </PlateElement>
  );
}

添加到编辑器

tsx
import { createPlateEditor } from 'platejs/react';
import { TagPlugin, TagInputPlugin } from './tag-plugin';
import { TagElement, TagInputElement } from './tag-components';

const editor = createPlateEditor({
  plugins: [
    // ...其他插件
    TagPlugin.configure({
      options: {
        triggerQuery: (editor) => {
          // 在代码块中禁用
          return !editor.api.some({ match: { type: 'code_block' } });
        },
      },
    }).withComponent(TagElement),
    TagInputPlugin.withComponent(TagInputElement),
  ],
});
  • options.triggerQuery: 根据编辑器状态有条件启用/禁用触发器的可选函数
</Steps>

示例

<ComponentPreview name="mention-demo" /> <ComponentPreview name="slash-command-demo" /> <ComponentPreview name="emoji-demo" />

配置选项

TriggerComboboxPluginOptions

基于触发器的组合框插件的配置选项。

<API name="TriggerComboboxPluginOptions"> <APIOptions> <APIItem name="createComboboxInput" type="(trigger: string) => TElement"> 触发器激活时创建输入节点的函数。 </APIItem> <APIItem name="trigger" type="RegExp | string[] | string"> 触发组合框的字符。可以是: - 单个字符(如'@') - 字符数组 - 正则表达式 </APIItem> <APIItem name="triggerPreviousCharPattern" type="RegExp" optional> 匹配触发器前字符的模式。 - **示例:** `/^\s?$/` 匹配行首或空格 </APIItem> <APIItem name="triggerQuery" type="(editor: SlateEditor) => boolean" optional> 控制触发器何时激活的自定义查询函数。 </APIItem> </APIOptions> </API>

钩子函数

useComboboxInput

管理组合框输入行为和键盘交互的钩子。

<API name="useComboboxInput"> <APIOptions> <APIItem name="ref" type="RefObject<HTMLElement>"> 输入元素的引用。 </APIItem> <APIItem name="autoFocus" type="boolean" optional> 挂载时自动聚焦输入。 - **默认:** `true` </APIItem> <APIItem name="cancelInputOnArrowLeftRight" type="boolean" optional> 方向键取消输入。 - **默认:** `true` </APIItem> <APIItem name="cancelInputOnBackspace" type="boolean" optional> 起始位置退格键取消输入。 - **默认:** `true` </APIItem> <APIItem name="cancelInputOnBlur" type="boolean" optional> 失去焦点时取消输入。 - **默认:** `true` </APIItem> <APIItem name="cancelInputOnDeselect" type="boolean" optional> 取消选择时取消输入。 - **默认:** `true` </APIItem> <APIItem name="cancelInputOnEscape" type="boolean" optional> Escape键取消输入。 - **默认:** `true` </APIItem> <APIItem name="cursorState" type="ComboboxInputCursorState" optional> 当前光标位置状态。 </APIItem> <APIItem name="forwardUndoRedoToEditor" type="boolean" optional> 将撤销/重做转发给编辑器。 - **默认:** `true` </APIItem> <APIItem name="onCancelInput" type="(cause: CancelComboboxInputCause) => void" optional> 输入取消时的回调函数。 </APIItem> </APIOptions> <APIReturns> <APIItem name="cancelInput" type="(cause?: CancelComboboxInputCause, focusEditor?: boolean) => void"> 取消输入的函数。 </APIItem> <APIItem name="props" type="object"> 输入元素的属性。 </APIItem> <APIItem name="removeInput" type="(focusEditor?: boolean) => void"> 移除输入节点的函数。 </APIItem> </APIReturns> </API>

useHTMLInputCursorState

跟踪HTML输入元素中光标位置的钩子。

<API name="useHTMLInputCursorState"> <APIParameters> <APIItem name="ref" type="RefObject<HTMLInputElement>"> 要跟踪的输入元素的引用。 </APIItem> </APIParameters> <APIReturns> <APIItem name="atStart" type="boolean"> 光标是否在输入起始位置。 </APIItem> <APIItem name="atEnd" type="boolean"> 光标是否在输入结束位置。 </APIItem> </APIReturns> </API>