导入
鄙人的需求是实现一款在线论文排版编辑器,导出 LaTeX 生成的 pdf
项目地址:Eorg
成功的案例有 Overleaf,简历生成器 Resumake。
因为对 React.js 的偏好,所以选择了同为 Facebook 研发的富文本编辑器——Draft.js,论文写作躲不过表格插入,而 Draft.js 又没有现成的表格插件 论生成表格,工具其实很多,大可不用自己再写一个表格支持,但方便了开发者,折腾了用户,表格也不需多复杂,能导出三线表即可,所以打算自己尝试做一下表格插入
正文
实现思路:
高端的表格实现是 new ContentBlock 把 metadata 写入 block ,符合 Draft.js 的实现
鄙人的实现比较 trick,仿官方 TeX 例子 ,使用AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, ' ')API 直接传入 React.js 的 props:
- 行列值
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>)
}
}
row, column, caption, cell 都已传入 props
- 获取单元格位置:
刚开始的思路是计算 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
cell-2,1 的内容可以被修改后存入 props
总结
表格的功能尚未完善,比如
- 光标的处理
- mutable
下一步
支持添加/删除行列