tiptap Table 表格扩展

如果你想在 tiptap 编辑器设置显示表格,你可以是使用 table 扩展,它可以渲染成 htmltable 标签,他可以合并单元格、添加删除行等等。

Install 安装

npm install @tiptap/extension-table @tiptap/extension-table-row @tiptap/extension-table-header @tiptap/extension-table-cell

Settings 配置

Table 的配置项。

Table.configure({
  HTMLAttributes: {
    class: 'my-custom-class',
  },
  //是否允许缩放
  resizable:false,
  //拖动时边框大小
  handleWidth:5,
  //单元格最小宽度
  cellMinWidth:25,
  View:"TableView",
  //最好一列是否自适应宽度
  lastColumnResizable:true,
  allowTableNodeSelection:false
})

Commands 命令

insertTable 插入表格,你可以制定插入多少行、多少列。

//插入表格
editor.commands.insertTable();
//插入3行3列和表头
editor.commands.insertTable({ rows: 3, cols: 3, withHeaderRow: true }

addColumnBefore 

//当前选中列之前插入一个列
editor.commands.addColumnBefore();

addColumnAfter 

//当前选中列之后插入一个列
editor.commands.addColumnAfter();

deleteColumn 

//删除当前选中的列
editor.commands.deleteColumn();

addRowBefore 

//选中行之上插入一行
editor.commands.addRowBefore();

addRowAfter 

//在选中行之后插入一行
editor.commands.addRowAfter();

deleteRow 

//删除选中行
editor.commands.deleteRow();

deleteTable 

//删除整个表格
editor.commands.deleteTable();

mergeCells 

//合表选中的单元格
editor.commands.mergeCells();

splitCell 

//拆分选中的单元格
editor.commands.splitCell();

toggleHeaderColumn 

//切换选中列为表头列
editor.commands.toggleHeaderColumn();

toggleHeaderRow 

//切换选中行为表头行
editor.commands.toggleHeaderRow();

toggleHeaderCell 

//切换选中单元格为表头
editor.commands.toggleHeaderCell();

mergeOrSplit 

//合并或拆分选中项
editor.commands.mergeOrSplit();

setCellAttribute 

editor.commands.setCellAttribute('customAttribute', 'value')
editor.commands.setCellAttribute('backgroundColor', '#000')

goToNextCell 

editor.commands.goToNextCell();

goToPreviousCell 

editor.commands.goToPreviousCell();

fixTables 

editor.commands.fixTables();

不使用表头 

// Table rows without table headers
TableRow.extend({
  content: 'tableCell*',
})

源代码

table 源代码

table-row 源代码

table-cell 源代码

table-header 源代码

在线例子

vue 在线例子

Table 例子

  • Vue 例子

  • React 例子

  • React CSS

<template>
  <div v-if="editor">
    <button @click="editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()">
      insertTable
    </button>
    <button @click="editor.chain().focus().addColumnBefore().run()">
      addColumnBefore
    </button>
    <button @click="editor.chain().focus().addColumnAfter().run()">
      addColumnAfter
    </button>
    <button @click="editor.chain().focus().deleteColumn().run()">
      deleteColumn
    </button>
    <button @click="editor.chain().focus().addRowBefore().run()">
      addRowBefore
    </button>
    <button @click="editor.chain().focus().addRowAfter().run()">
      addRowAfter
    </button>
    <button @click="editor.chain().focus().deleteRow().run()">
      deleteRow
    </button>
    <button @click="editor.chain().focus().deleteTable().run()">
      deleteTable
    </button>
    <button @click="editor.chain().focus().mergeCells().run()">
      mergeCells
    </button>
    <button @click="editor.chain().focus().splitCell().run()">
      splitCell
    </button>
    <button @click="editor.chain().focus().toggleHeaderColumn().run()">
      toggleHeaderColumn
    </button>
    <button @click="editor.chain().focus().toggleHeaderRow().run()">
      toggleHeaderRow
    </button>
    <button @click="editor.chain().focus().toggleHeaderCell().run()">
      toggleHeaderCell
    </button>
    <button @click="editor.chain().focus().mergeOrSplit().run()">
      mergeOrSplit
    </button>
    <button @click="editor.chain().focus().setCellAttribute('colspan', 2).run()">
      setCellAttribute
    </button>
    <button @click="editor.chain().focus().fixTables().run()">
      fixTables
    </button>
    <button @click="editor.chain().focus().goToNextCell().run()">
      goToNextCell
    </button>
    <button @click="editor.chain().focus().goToPreviousCell().run()">
      goToPreviousCell
    </button>
    <editor-content :editor="editor" />
  </div>
</template>
<script>
import Document from '@tiptap/extension-document'
import Gapcursor from '@tiptap/extension-gapcursor'
import Paragraph from '@tiptap/extension-paragraph'
import Table from '@tiptap/extension-table'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TableRow from '@tiptap/extension-table-row'
import Text from '@tiptap/extension-text'
import { Editor, EditorContent } from '@tiptap/vue-3'

export default {
  components: {
    EditorContent,
  },
  data() {
    return {
      editor: null,
    }
  },
  mounted() {
    this.editor = new Editor({
      extensions: [
        Document,
        Paragraph,
        Text,
        Gapcursor,
        Table.configure({
          resizable: true,
        }),
        TableRow,
        TableHeader,
        TableCell,
      ],
      content: `
        <table>
          <tbody>
            <tr>
              <th>Name</th>
              <th colspan="3">Description</th>
            </tr>
            <tr>
              <td>Cyndi Lauper</td>
              <td>singer</td>
              <td>songwriter</td>
              <td>actress</td>
            </tr>
          </tbody>
        </table>
      `,
    })
  },
  beforeUnmount() {
    this.editor.destroy()
  },
}
</script>
<style>
.ProseMirror {
  table {
    border-collapse: collapse;
    table-layout: fixed;
    width: 100%;
    margin: 0;
    overflow: hidden;
    td,
    th {
      min-width: 1em;
      border: 2px solid #ced4da;
      padding: 3px 5px;
      vertical-align: top;
      box-sizing: border-box;
      position: relative;
      > * {
        margin-bottom: 0;
      }
    }
    th {
      font-weight: bold;
      text-align: left;
      background-color: #f1f3f5;
    }
    .selectedCell:after {
      z-index: 2;
      position: absolute;
      content: "";
      left: 0; right: 0; top: 0; bottom: 0;
      background: rgba(200, 200, 255, 0.4);
      pointer-events: none;
    }
    .column-resize-handle {
      position: absolute;
      right: -2px;
      top: 0;
      bottom: -2px;
      width: 4px;
      background-color: #adf;
      pointer-events: none;
    }
    p {
      margin: 0;
    }
  }
}
.tableWrapper {
  padding: 1rem 0;
  overflow-x: auto;
}
.resize-cursor {
  cursor: ew-resize;
  cursor: col-resize;
}
</style>
import './styles.scss'

import Document from '@tiptap/extension-document'
import Gapcursor from '@tiptap/extension-gapcursor'
import Paragraph from '@tiptap/extension-paragraph'
import Table from '@tiptap/extension-table'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TableRow from '@tiptap/extension-table-row'
import Text from '@tiptap/extension-text'
import { EditorContent, useEditor } from '@tiptap/react'
import React from 'react'

export default () => {
  const editor = useEditor({
    extensions: [
      Document,
      Paragraph,
      Text,
      Gapcursor,
      Table.configure({
        resizable: true,
      }),
      TableRow,
      TableHeader,
      TableCell,
    ],
    content: `
        <table>
          <tbody>
            <tr>
              <th>Name</th>
              <th colspan="3">Description</th>
            </tr>
            <tr>
              <td>Cyndi Lauper</td>
              <td>singer</td>
              <td>songwriter</td>
              <td>actress</td>
            </tr>
          </tbody>
        </table>
      `,
  })
  if (!editor) {
    return null
  }
  return (
    <>
      <button
        onClick={() => editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()
        }
      >
        insertTable
      </button>
      <button onClick={() => editor.chain().focus().addColumnBefore().run()}>
        addColumnBefore
      </button>
      <button onClick={() => editor.chain().focus().addColumnAfter().run()}>addColumnAfter</button>
      <button onClick={() => editor.chain().focus().deleteColumn().run()}>deleteColumn</button>
      <button onClick={() => editor.chain().focus().addRowBefore().run()}>addRowBefore</button>
      <button onClick={() => editor.chain().focus().addRowAfter().run()}>addRowAfter</button>
      <button onClick={() => editor.chain().focus().deleteRow().run()}>deleteRow</button>
      <button onClick={() => editor.chain().focus().deleteTable().run()}>deleteTable</button>
      <button onClick={() => editor.chain().focus().mergeCells().run()}>mergeCells</button>
      <button onClick={() => editor.chain().focus().splitCell().run()}>splitCell</button>
      <button onClick={() => editor.chain().focus().toggleHeaderColumn().run()}>
        toggleHeaderColumn
      </button>
      <button onClick={() => editor.chain().focus().toggleHeaderRow().run()}>
        toggleHeaderRow
      </button>
      <button onClick={() => editor.chain().focus().toggleHeaderCell().run()}>
        toggleHeaderCell
      </button>
      <button onClick={() => editor.chain().focus().mergeOrSplit().run()}>mergeOrSplit</button>
      <button onClick={() => editor.chain().focus().setCellAttribute('colspan', 2).run()}>
        setCellAttribute
      </button>
      <button onClick={() => editor.chain().focus().fixTables().run()}>fixTables</button>
      <button onClick={() => editor.chain().focus().goToNextCell().run()}>goToNextCell</button>
      <button onClick={() => editor.chain().focus().goToPreviousCell().run()}>
        goToPreviousCell
      </button>
      <EditorContent editor={editor} />
    </>
  )
}
.ProseMirror {
  table {
    border-collapse: collapse;
    margin: 0;
    overflow: hidden;
    table-layout: fixed;
    width: 100%;
    td,
    th {
      border: 2px solid #ced4da;
      box-sizing: border-box;
      min-width: 1em;
      padding: 3px 5px;
      position: relative;
      vertical-align: top;
      > * {
        margin-bottom: 0;
      }
    }
    th {
      background-color: #f1f3f5;
      font-weight: bold;
      text-align: left;
    }
    .selectedCell:after {
      background: rgba(200, 200, 255, 0.4);
      content: "";
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      pointer-events: none;
      position: absolute;
      z-index: 2;
    }
    .column-resize-handle {
      background-color: #adf;
      bottom: -2px;
      position: absolute;
      right: -2px;
      pointer-events: none;
      top: 0;
      width: 4px;
    }
    p {
      margin: 0;
    }
  }
}

.tableWrapper {
  padding: 1rem 0;
  overflow-x: auto;
}

.resize-cursor {
  cursor: ew-resize;
  cursor: col-resize;
}

下载教程 Demo

本教程Demo的源码点击这里下载,Tiptap Demo,下载完成后运行npm i初始化依赖包。