๐Ÿ“ Sign Up | ๐Ÿ” Log In

โ† Root | โ†‘ Up

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ“„ shadcn/directory/udecode/plate/(plugins)/(functionality)/(combobox)/combobox.cn โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘

title: ็ป„ๅˆๆก†(Combobox) docs:

  • route: /docs/components/inline-combobox title: ๅ†…่”็ป„ๅˆๆก†

<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>

ๅฎ‰่ฃ…

npm install @platejs/combobox

ๅˆ›ๅปบ่พ“ๅ…ฅๆ’ไปถ

้ฆ–ๅ…ˆๅˆ›ๅปบไธ€ไธช่พ“ๅ…ฅๆ’ไปถ๏ผŒๅฝ“่งฆๅ‘ๅ™จๆฟ€ๆดปๆ—ถๅฐ†่ขซๆ’ๅ…ฅ๏ผš

import { createSlatePlugin } from 'platejs';

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

ๅˆ›ๅปบไธปๆ’ไปถ

ไฝฟ็”จwithTriggerComboboxๅˆ›ๅปบไธปๆ’ไปถ๏ผš

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ๅˆ›ๅปบ่พ“ๅ…ฅๅ…ƒ็ด ็ป„ไปถ๏ผš

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>
  );
}

ๆทปๅŠ ๅˆฐ็ผ–่พ‘ๅ™จ

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>
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

โ† Root | โ†‘ Up