autoformat-classic-kit

PreviousNext
Docs
platecomponent

Preview

Loading preview…
registry/components/editor/plugins/autoformat-classic-kit.tsx
'use client';

import type { AutoformatBlockRule, AutoformatRule } from '@platejs/autoformat';
import type { SlateEditor } from 'platejs';

import {
  autoformatArrow,
  autoformatLegal,
  autoformatLegalHtml,
  autoformatMath,
  AutoformatPlugin,
  autoformatPunctuation,
  autoformatSmartQuotes,
} from '@platejs/autoformat';
import { insertEmptyCodeBlock } from '@platejs/code-block';
import { toggleList, toggleTaskList, unwrapList } from '@platejs/list-classic';
import { ElementApi, isType, KEYS } from 'platejs';

const preFormat: AutoformatBlockRule['preFormat'] = (editor) =>
  unwrapList(editor);

const format = (editor: SlateEditor, customFormatting: any) => {
  if (editor.selection) {
    const parentEntry = editor.api.parent(editor.selection);

    if (!parentEntry) return;

    const [node] = parentEntry;

    if (ElementApi.isElement(node) && !isType(editor, node, KEYS.codeBlock)) {
      customFormatting();
    }
  }
};

const formatTaskList = (editor: SlateEditor, defaultChecked = false) => {
  format(editor, () => toggleTaskList(editor, defaultChecked));
};

const formatList = (editor: SlateEditor, elementType: string) => {
  format(editor, () =>
    toggleList(editor, {
      type: elementType,
    })
  );
};

const autoformatMarks: AutoformatRule[] = [
  {
    match: '***',
    mode: 'mark',
    type: [KEYS.bold, KEYS.italic],
  },
  {
    match: '__*',
    mode: 'mark',
    type: [KEYS.underline, KEYS.italic],
  },
  {
    match: '__**',
    mode: 'mark',
    type: [KEYS.underline, KEYS.bold],
  },
  {
    match: '___***',
    mode: 'mark',
    type: [KEYS.underline, KEYS.bold, KEYS.italic],
  },
  {
    match: '**',
    mode: 'mark',
    type: KEYS.bold,
  },
  {
    match: '__',
    mode: 'mark',
    type: KEYS.underline,
  },
  {
    match: '*',
    mode: 'mark',
    type: KEYS.italic,
  },
  {
    match: '_',
    mode: 'mark',
    type: KEYS.italic,
  },
  {
    match: '~~',
    mode: 'mark',
    type: KEYS.strikethrough,
  },
  {
    match: '^',
    mode: 'mark',
    type: KEYS.sup,
  },
  {
    match: '~',
    mode: 'mark',
    type: KEYS.sub,
  },
  {
    match: '==',
    mode: 'mark',
    type: KEYS.highlight,
  },
  {
    match: '≡',
    mode: 'mark',
    type: KEYS.highlight,
  },
  {
    match: '`',
    mode: 'mark',
    type: KEYS.code,
  },
];

const autoformatBlocks: AutoformatRule[] = [
  {
    match: '# ',
    mode: 'block',
    preFormat,
    type: KEYS.h1,
  },
  {
    match: '## ',
    mode: 'block',
    preFormat,
    type: KEYS.h2,
  },
  {
    match: '### ',
    mode: 'block',
    preFormat,
    type: KEYS.h3,
  },
  {
    match: '#### ',
    mode: 'block',
    preFormat,
    type: KEYS.h4,
  },
  {
    match: '##### ',
    mode: 'block',
    preFormat,
    type: KEYS.h5,
  },
  {
    match: '###### ',
    mode: 'block',
    preFormat,
    type: KEYS.h6,
  },
  {
    match: '> ',
    mode: 'block',
    preFormat,
    type: KEYS.blockquote,
  },
  {
    match: '```',
    mode: 'block',
    preFormat,
    type: KEYS.codeBlock,
    format: (editor) => {
      insertEmptyCodeBlock(editor, {
        defaultType: KEYS.p,
        insertNodesOptions: { select: true },
      });
    },
  },
  // {
  //   match: '+ ',
  //   mode: 'block',
  //   preFormat: openNextToggles,
  //   type: KEYS.toggle,
  // },
  {
    match: ['---', '—-', '___ '],
    mode: 'block',
    type: KEYS.hr,
    format: (editor) => {
      editor.tf.setNodes({ type: KEYS.hr });
      editor.tf.insertNodes({
        children: [{ text: '' }],
        type: KEYS.p,
      });
    },
  },
];

const autoformatLists: AutoformatRule[] = [
  {
    match: ['* ', '- '],
    mode: 'block',
    preFormat,
    type: KEYS.li,
    format: (editor) => formatList(editor, KEYS.ulClassic),
  },
  {
    match: [String.raw`^\d+\.$ `, String.raw`^\d+\)$ `],
    matchByRegex: true,
    mode: 'block',
    preFormat,
    type: KEYS.li,
    format: (editor) => formatList(editor, KEYS.olClassic),
  },
  {
    match: '[] ',
    mode: 'block',
    type: KEYS.taskList,
    format: (editor) => formatTaskList(editor, false),
  },
  {
    match: '[x] ',
    mode: 'block',
    type: KEYS.taskList,
    format: (editor) => formatTaskList(editor, true),
  },
];

export const AutoformatKit = [
  AutoformatPlugin.configure({
    options: {
      enableUndoOnDelete: true,
      rules: [
        ...autoformatBlocks,
        ...autoformatMarks,
        ...autoformatSmartQuotes,
        ...autoformatPunctuation,
        ...autoformatLegal,
        ...autoformatLegalHtml,
        ...autoformatArrow,
        ...autoformatMath,
        ...autoformatLists,
      ].map((rule) => ({
        ...rule,
        query: (editor) =>
          !editor.api.some({
            match: { type: editor.getType(KEYS.codeBlock) },
          }),
      })),
    },
  }),
];

Installation

npx shadcn@latest add @plate/autoformat-classic-kit

Usage

import { AutoformatClassicKit } from "@/components/autoformat-classic-kit"
<AutoformatClassicKit />