尝试让 Draft.js 支持简单的表格功能

时间:2020-8-28 作者:admin

导入

鄙人的需求是实现一款在线论文排版编辑器,导出 LaTeX 生成的 pdf

项目地址:Eorg

成功的案例有 Overleaf,简历生成器 Resumake

因为对 React.js 的偏好,所以选择了同为 Facebook 研发的富文本编辑器——Draft.js,论文写作躲不过表格插入,而 Draft.js 又没有现成的表格插件
论生成表格,工具其实很多,大可不用自己再写一个表格支持,但方便了开发者,折腾了用户,表格也不需多复杂,能导出三线表即可,所以打算自己尝试做一下表格插入

正文

实现思路:

高端的表格实现是 new ContentBlock把 metadata 写入 block,符合 Draft.js 的实现

尝试让 Draft.js 支持简单的表格功能

鄙人的实现比较 trick,仿官方 TeX 例子,使用AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, ' ')API 直接传入 React.js 的 props:

  1. 行列值
const contentStateWithEntity = contentState.createEntity(
    'TABLE',
    'IMMUTABLE',
    {
        row, column, caption,  // data
    },
)

// ...

const { row, column, caption } = props // Table Component

表格数据

// createTable.js

/**
 * cell = {
 *     0: ["cell-0,0", "cell-0,1", ..., "cell-0,m"],
 *     1: ["cell-1,0", "cell-1,1", ..., "cell-1,m"],
 *     ...,
 *     n: ["cell-n,0", "cell-n,1", ..., "cell-n,m"],
 * }
 */
const cell = Object.fromEntries(Array.from(
    { length: row },
    (_, i) => [
        i,
        Array.from({ length: column }, (_, j) => `cell-${i},${j}`)
    ])
)

const contentStateWithEntity = contentState.createEntity(
    'TABLE',
    'IMMUTABLE',
    {
        ..., cell, // data
    },
)

// ...

const { ..., cell } = props // Table Component

然后初始化表格:

// TableBlock.js

// tbody -- version 1
const coordinate = []

if (row > 1) {
    for (let i = 1; i < row; i += 1) {
    const cols = []
    for (let j = 0; j < column; j += 1) {
        cols.push(
        <td key={i + j} >
            {cell[i][j]}
        </td>,
        )
    }
    rows.push(<tr key={i}>{cols}</tr>)
    }
}

尝试让 Draft.js 支持简单的表格功能

row, column, caption, cell 都已传入 props

  1. 获取单元格位置:

刚开始的思路是计算 Dom Node 位置,即点击的单元格是 closest('table')的第几个 <tr>元素中的第几个td来获得其坐标

后来发现可以直接根据 <tr><td>传入的 key 值,即可计算出其行列坐标 <i, j>:

// TableBlock.js

// tbody -- version 2
const coordinate = []

if (row > 1) { // thead 单独计算
    for (let i = 1; i < row; i += 1) {
    const cols = []
    for (let j = 0; j < column; j += 1) {
        cols.push(
        <td
            key={i + j} // TODO key-1
            onDoubleClick={() => coordinate.push([i, j])}
        >
            {cell[i][j]}
        </td>,
        )
    }
    rows.push(<tr key={i}>{cols}</tr>)
    }
}

上述代码中 key-1不稳定,可以借助 nanoid 库,即:

key = {`i+j+${nanoid()}`}

使其达到稳定
这样一来,就可以保存修改后的单元格内容:

// find the coordinate of the node clicked
const x1 = coordinate[coordinate.length - 1][0]
const y1 = coordinate[coordinate.length - 1][1]

// update cell[i][j]
cell[x1][y1] = evt.target.innerHTML

尝试让 Draft.js 支持简单的表格功能

cell-2,1 的内容可以被修改后存入 props

总结

表格的功能尚未完善,比如

  1. 光标的处理
  2. mutable

下一步

支持添加/删除行列

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。