Skip to content

Commit

Permalink
feat: table module
Browse files Browse the repository at this point in the history
  • Loading branch information
wangfupeng1988 committed Jun 15, 2021
1 parent f691c4b commit a397116
Show file tree
Hide file tree
Showing 12 changed files with 380 additions and 15 deletions.
8 changes: 7 additions & 1 deletion packages/editor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,13 @@ let editor = createEditor(
{
// @ts-ignore
match: (editor, n) => n.type === 'table',
menuKeys: ['deleteTable'],
menuKeys: [
'insertTableRow',
'deleteTableRow',
'insertTableCol',
'deleteTableCol',
'deleteTable',
],
},
// other hover bar ...
],
Expand Down
3 changes: 3 additions & 0 deletions packages/table-module/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,8 @@
"is-hotkey": "^0.2.0",
"slate": "^0.63.0",
"snabbdom": "^3.0.1"
},
"dependencies": {
"lodash-es": "^4.17.21"
}
}
3 changes: 2 additions & 1 deletion packages/table-module/src/assets/index.less
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
.w-e-text-container [data-slate-editor] table {
border-collapse: collapse;
width: 100%;

td {
border: 1px solid #ccc;
padding: 3px 5px;
min-width: 80px;
min-width: 50px;
}
}

Expand Down
14 changes: 7 additions & 7 deletions packages/table-module/src/constants/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@
export const TABLE_SVG =
'<svg viewBox="0 0 1024 1024"><path d="M0 64v896h1024V64H0z m384 576v-192h256v192h-256z m256 64v192h-256v-192h256z m0-512v192h-256V192h256zM320 192v192H64V192h256z m-256 256h256v192H64v-192z m640 0h256v192h-256v-192z m0-64V192h256v192h-256zM64 704h256v192H64v-192z m640 192v-192h256v192h-256z"></path></svg>'

// 表格删除
export const REMOVE_SVG =
'<svg viewBox="0 0 1024 1024"><path d="M660 678.016L720 616l90.016 92 92-92 60 62.016-92 90.016 92 90.016-60 62.016-92-92-90.016 92-60-62.016 90.016-90.016-90.016-90.016zM170.016 128h598.016q36 0 60.992 24.992t24.992 60.992v302.016q-50.016-8-96.992 2.016t-87.008 36H512v172h46.016q-6.016 42.016 0 84H170.016q-34.016 0-59.008-24.992t-24.992-59.008v-512q0-36 24.992-60.992t59.008-24.992z m0 170.016v172h256V298.016h-256z m341.984 0v172h256V298.016h-256z m-341.984 256v172h256v-172h-256z"></path></svg>'
// 垃圾桶(删除)
export const TRASH_SVG =
'<svg viewBox="0 0 1024 1024"><path d="M826.8032 356.5312c-19.328 0-36.3776 15.6928-36.3776 35.0464v524.2624c0 19.328-16 34.56-35.328 34.56H264.9344c-19.328 0-35.5072-15.3088-35.5072-34.56V390.0416c0-19.328-14.1568-35.0464-33.5104-35.0464s-33.5104 15.6928-33.5104 35.0464V915.712c0 57.9328 44.6208 108.288 102.528 108.288H755.2c57.9328 0 108.0832-50.4576 108.0832-108.288V391.4752c-0.1024-19.2512-17.1264-34.944-36.48-34.944z" p-id="9577"></path><path d="M437.1712 775.7568V390.6048c0-19.328-14.1568-35.0464-33.5104-35.0464s-33.5104 15.616-33.5104 35.0464v385.152c0 19.328 14.1568 35.0464 33.5104 35.0464s33.5104-15.7184 33.5104-35.0464zM649.7024 775.7568V390.6048c0-19.328-17.0496-35.0464-36.3776-35.0464s-36.3776 15.616-36.3776 35.0464v385.152c0 19.328 17.0496 35.0464 36.3776 35.0464s36.3776-15.7184 36.3776-35.0464zM965.0432 217.0368h-174.6176V145.5104c0-57.9328-47.2064-101.76-104.6528-101.76h-350.976c-57.8304 0-105.3952 43.8528-105.3952 101.76v71.5264H54.784c-19.4304 0-35.0464 14.1568-35.0464 33.5104 0 19.328 15.616 33.5104 35.0464 33.5104h910.3616c19.328 0 35.0464-14.1568 35.0464-33.5104 0-19.3536-15.6928-33.5104-35.1488-33.5104z m-247.3728 0H297.3952V145.5104c0-19.328 18.2016-34.7648 37.4272-34.7648h350.976c19.1488 0 31.872 15.1296 31.872 34.7648v71.5264z"></path></svg>'

// 表格 添加行
export const ADD_ROW_SVG =
'<svg viewBox="0 0 1253 1024"><path d="M0 0v1022.493039h1252.031045V0H0z m782.519403 699.051176v229.539534H469.511642V699.051176H782.519403z m0-626.015522v229.539534H469.511642V73.035654H782.519403z m-386.043415 0v229.539534h-313.007761V73.035654h313.007761z m-313.007761 313.007761h1095.527164V626.015522h-1095.527164V386.043415z m788.632836-73.035654V73.035654h313.007761v229.539534h-313.007761v10.432573z m-788.632836 386.043415h313.007761v229.539534h-313.007761V699.051176z m782.519403 239.972108V699.051176h313.007761v229.539534h-313.007761v10.432574z"></path><path d="M654.577481 479.46431V401.958209h-52.844514v77.506101H524.226866v52.844514h77.506101V609.814925h52.844514v-77.506101H732.083582v-52.844514h-77.506101z"></path></svg>'
'<svg viewBox="0 0 1048 1024"><path d="M707.7888 521.0112h-147.456v-147.456H488.2432v147.456h-147.456v68.8128h147.456v147.456h72.0896v-147.456h147.456zM0 917.504V0h1048.576v917.504H0zM327.68 65.536H65.536v196.608H327.68V65.536z m327.68 0H393.216v196.608h262.144V65.536z m327.68 0h-262.144v196.608h262.144V65.536z m0 258.8672H65.536v462.0288H983.04V324.4032z"></path></svg>'

// 表格 删除行
export const DEL_ROW_SVG =
'<svg viewBox="0 0 1253 1024"><path d="M0 0v1022.493039h1252.031045V0H0z m782.519403 699.051176v229.539534H469.511642V699.051176H782.519403z m0-626.015522v229.539534H469.511642V73.035654H782.519403z m-386.043415 0v229.539534h-313.007761V73.035654h313.007761z m-313.007761 313.007761h1095.527164V626.015522h-1095.527164V386.043415z m788.632836-73.035654V73.035654h313.007761v229.539534h-313.007761v10.432573z m-788.632836 386.043415h313.007761v229.539534h-313.007761V699.051176z m782.519403 239.972108V699.051176h313.007761v229.539534h-313.007761v10.432574z"></path><path d="M668.329648 511.235821l60.044609-60.043081-40.940132-40.940131-60.04308 60.044609-60.043081-60.044609-40.940131 40.940131 60.044609 60.043081-60.044609 60.04308 40.940131 40.940132 60.043081-60.044609 60.04308 60.044609 40.940132-40.940132-60.044609-60.04308z"></path></svg>'
'<svg viewBox="0 0 1048 1024"><path d="M907.6736 586.5472L747.1104 425.984l163.84-163.84-78.6432-78.6432-163.84 163.84L507.904 186.7776 429.2608 262.144l163.84 163.84-167.1168 167.1168 78.6432 78.6432 167.1168-167.1168 160.5632 160.5632 75.3664-78.6432zM0 917.504V0h1048.576v917.504H0z m983.04-327.68h-22.9376l-65.536-65.536H983.04V327.68h-91.7504l65.536-65.536h26.2144V65.536H65.536v196.608h317.8496l65.536 65.536H65.536v196.608h380.1088l-65.536 65.536H65.536v196.608H983.04v-196.608z"></path></svg>'

// 表格 添加列
export const ADD_COL_SVG =
'<svg viewBox="0 0 1253 1024"><path d="M0 0v1022.493039h1252.031045V0H0z m782.519403 73.035654v855.555056H469.511642V73.035654H782.519403z m-386.043415 0v229.539534h-313.007761V73.035654h313.007761z m-313.007761 313.007761h313.007761V626.015522h-313.007761V386.043415z m782.519403 0h313.007761V626.015522h-313.007761V386.043415z m0-73.035654V73.035654h313.007761v229.539534h-313.007761v10.432573z m-782.519403 386.043415h313.007761v229.539534h-313.007761V699.051176z m782.519403 239.972108V699.051176h313.007761v229.539534h-313.007761v10.432574z"></path><path d="M656.338149 482.288716V397.373134h-57.894209v84.915582H513.528358v57.894209h84.915582V625.098507h57.894209v-84.915582H741.253731v-57.894209z"></path></svg>'
'<svg viewBox="0 0 1048 1024"><path d="M327.68 193.3312v186.7776H140.9024v91.7504H327.68v186.7776h88.4736V471.8592h190.0544V380.1088H416.1536V193.3312zM0 917.504V0h1048.576v917.504H0zM655.36 65.536H65.536v720.896H655.36V65.536z m327.68 0h-262.144v196.608h262.144V65.536z m0 262.144h-262.144v196.608h262.144V327.68z m0 262.144h-262.144v196.608h262.144v-196.608z"></path></svg>'

// 表格 删除列
export const DEL_COL_SVG =
'<svg viewBox="0 0 1253 1024"><path d="M0 0v1022.493039h1252.031045V0H0z m782.519403 73.035654v855.555056H469.511642V73.035654H782.519403z m-386.043415 0v229.539534h-313.007761V73.035654h313.007761z m-313.007761 313.007761h313.007761V626.015522h-313.007761V386.043415z m782.519403 0h313.007761V626.015522h-313.007761V386.043415z m0-73.035654V73.035654h313.007761v229.539534h-313.007761v10.432573z m-782.519403 386.043415h313.007761v229.539534h-313.007761V699.051176z m782.519403 239.972108V699.051176h313.007761v229.539534h-313.007761v10.432574z"></path><path d="M668.329648 511.235821l60.044609-60.043081-40.940132-40.940131-60.04308 60.044609-60.043081-60.044609-40.940131 40.940131 60.044609 60.043081-60.044609 60.04308 40.940131 40.940132 60.043081-60.044609 60.04308 60.044609 40.940132-40.940132-60.044609-60.04308z"></path></svg>'
'<svg viewBox="0 0 1048 1024"><path d="M327.68 510.976L393.216 445.44v-13.1072L327.68 366.7968V510.976z m327.68-78.4384l65.536-65.536V507.904L655.36 442.368v-9.8304z m393.216 484.9664V0H0v917.504h1048.576z m-65.536-131.072h-262.144v-52.4288l-13.1072 13.1072-52.4288-52.4288v91.7504H393.216v-91.7504l-52.4288 52.4288-13.1072-13.1072v52.4288H65.536V65.536H327.68v121.2416l36.0448-36.0448 29.4912 29.4912V62.2592h262.144V180.224l49.152-49.152 16.384 16.384V62.2592h262.144V786.432z m-294.912-108.1344l-160.5632-160.5632-167.1168 167.1168-78.6432-78.6432 167.1168-167.1168L288.3584 278.528l78.6432-78.6432 160.5632 160.5632 163.84-163.84 78.6432 78.6432-163.84 163.84 160.5632 160.5632-78.6432 78.6432z"></path></svg>'
18 changes: 16 additions & 2 deletions packages/table-module/src/module/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,25 @@
import { IModuleConf } from '@wangeditor/core'
import withTable from './plugin'
import { renderTableConf, renderTableRowConf, renderTableCellConf } from './render-elem'
import { insertTableMenuConf, deleteTableMenuConf } from './menu/index'
import {
insertTableMenuConf,
deleteTableMenuConf,
insertTableRowConf,
deleteTableRowConf,
insertTableColConf,
deleteTableColConf,
} from './menu/index'

const table: IModuleConf = {
renderElems: [renderTableConf, renderTableRowConf, renderTableCellConf],
menus: [insertTableMenuConf, deleteTableMenuConf],
menus: [
insertTableMenuConf,
deleteTableMenuConf,
insertTableRowConf,
deleteTableRowConf,
insertTableColConf,
deleteTableColConf,
],
editorPlugin: withTable,
}

Expand Down
92 changes: 92 additions & 0 deletions packages/table-module/src/module/menu/DeleteCol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @description del col menu
* @author wangfupeng
*/

import { isEqual } from 'lodash-es'
import { Editor, Transforms, Range, Node } from 'slate'
import { IButtonMenu, IDomEditor, DomEditor } from '@wangeditor/core'
import { getSelectedNodeByType } from '../_helpers/node'
import { DEL_COL_SVG } from '../../constants/svg'

class DeleteCol implements IButtonMenu {
title = '删除列'
iconSvg = DEL_COL_SVG
tag = 'button'

getValue(editor: IDomEditor): string | boolean {
// 无需获取 val
return ''
}

isActive(editor: IDomEditor): boolean {
// 无需 active
return false
}

isDisabled(editor: IDomEditor): boolean {
const { selection } = editor
if (selection == null) return true
if (!Range.isCollapsed(selection)) return true

const cellNode = getSelectedNodeByType(editor, 'table-cell')
if (cellNode == null) {
// 选区未处于 table cell node ,则禁用
return true
}
return false
}

exec(editor: IDomEditor, value: string | boolean) {
if (this.isDisabled(editor)) return

const [cellEntry] = Editor.nodes(editor, {
// @ts-ignore
match: n => n.type === 'table-cell',
universal: true,
})
const [selectedCellNode, selectedCellPath] = cellEntry

// 如果只有一列,则删除整个表格
const rowNode = DomEditor.getParentNode(editor, selectedCellNode)
const colLength = rowNode?.children.length || 0
if (!rowNode || colLength <= 1) {
Transforms.removeNodes(editor, { mode: 'highest' }) // 删除整个表格
return
}

// ------------------------- 不只有 1 列,则继续 -------------------------

// 【注意】临时记录 tableNode path ,重要!!! 执行完之后再删除
// 这样做,可以避免被 normalize 误伤
const selectedTablePath = selectedCellPath.slice(0, 1)
DomEditor.recordChangingPath(editor, selectedTablePath)

const tableNode = DomEditor.getParentNode(editor, rowNode)
if (tableNode == null) return

// 遍历所有 rows ,挨个删除 cell
const rows = tableNode.children || []
rows.forEach(row => {
// @ts-ignore
const cells = row.children || []
// 遍历一个 row 的所有 cells
cells.forEach((cell: Node) => {
const path = DomEditor.findPath(editor, cell)
if (
path.length === selectedCellPath.length &&
isEqual(path.slice(-1), selectedCellPath.slice(-1)) // 俩数组,最后一位相同
) {
// 如果当前 td 的 path 和选中 td 的 path ,最后一位相同,说明是同一列
// 删除当前的 cell
Transforms.removeNodes(editor, { at: path })
}
})
})

// 及时删除记录,重要!!!
DomEditor.deleteChangingPath(editor)
}
}

export default DeleteCol
62 changes: 62 additions & 0 deletions packages/table-module/src/module/menu/DeleteRow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @description del row menu
* @author wangfupeng
*/

import { Editor, Transforms, Range } from 'slate'
import { IButtonMenu, IDomEditor, DomEditor } from '@wangeditor/core'
import { getSelectedNodeByType } from '../_helpers/node'
import { DEL_ROW_SVG } from '../../constants/svg'

class DeleteRow implements IButtonMenu {
title = '删除行'
iconSvg = DEL_ROW_SVG
tag = 'button'

getValue(editor: IDomEditor): string | boolean {
// 无需获取 val
return ''
}

isActive(editor: IDomEditor): boolean {
// 无需 active
return false
}

isDisabled(editor: IDomEditor): boolean {
const { selection } = editor
if (selection == null) return true
if (!Range.isCollapsed(selection)) return true

const rowNode = getSelectedNodeByType(editor, 'table-row')
if (rowNode == null) {
// 选区未处于 table row node ,则禁用
return true
}
return false
}

exec(editor: IDomEditor, value: string | boolean) {
if (this.isDisabled(editor)) return

const [rowEntry] = Editor.nodes(editor, {
// @ts-ignore
match: n => n.type === 'table-row',
universal: true,
})
const [rowNode, rowPath] = rowEntry

const tableNode = DomEditor.getParentNode(editor, rowNode)
const rowsLength = tableNode?.children.length || 0
if (rowsLength <= 1) {
// row 只有一行,则删掉整个表格
Transforms.removeNodes(editor, { mode: 'highest' })
return
}

// row > 1 行,则删掉这一行
Transforms.removeNodes(editor, { at: rowPath })
}
}

export default DeleteRow
4 changes: 2 additions & 2 deletions packages/table-module/src/module/menu/DeleteTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
import { Transforms } from 'slate'
import { IButtonMenu, IDomEditor } from '@wangeditor/core'
import { getSelectedNodeByType } from '../_helpers/node'
import { REMOVE_SVG } from '../../constants/svg'
import { TRASH_SVG } from '../../constants/svg'

class DeleteTable implements IButtonMenu {
title = '删除表格'
iconSvg = REMOVE_SVG
iconSvg = TRASH_SVG
tag = 'button'

getValue(editor: IDomEditor): string | boolean {
Expand Down
85 changes: 85 additions & 0 deletions packages/table-module/src/module/menu/InsertCol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* @description insert col menu
* @author wangfupeng
*/

import { isEqual } from 'lodash-es'
import { Editor, Transforms, Range, Node } from 'slate'
import { IButtonMenu, IDomEditor, DomEditor } from '@wangeditor/core'
import { getSelectedNodeByType } from '../_helpers/node'
import { ADD_COL_SVG } from '../../constants/svg'

class InsertCol implements IButtonMenu {
title = '插入列'
iconSvg = ADD_COL_SVG
tag = 'button'

getValue(editor: IDomEditor): string | boolean {
// 无需获取 val
return ''
}

isActive(editor: IDomEditor): boolean {
// 无需 active
return false
}

isDisabled(editor: IDomEditor): boolean {
const { selection } = editor
if (selection == null) return true
if (!Range.isCollapsed(selection)) return true

const tableNode = getSelectedNodeByType(editor, 'table')
if (tableNode == null) {
// 选区未处于 table cell node ,则禁用
return true
}
return false
}

exec(editor: IDomEditor, value: string | boolean) {
if (this.isDisabled(editor)) return

const [cellEntry] = Editor.nodes(editor, {
// @ts-ignore
match: n => n.type === 'table-cell',
universal: true,
})
const [selectedCellNode, selectedCellPath] = cellEntry

// 【注意】临时记录 tableNode path ,重要!!! 执行完之后再删除
// 这样做,可以避免被 normalize 误伤
const selectedTablePath = selectedCellPath.slice(0, 1)
DomEditor.recordChangingPath(editor, selectedTablePath)

const rowNode = DomEditor.getParentNode(editor, selectedCellNode)
if (rowNode == null) return
const tableNode = DomEditor.getParentNode(editor, rowNode)
if (tableNode == null) return

// 遍历所有 rows ,挨个添加 cell
const rows = tableNode.children || []
rows.forEach(row => {
// @ts-ignore
const cells = row.children || []
// 遍历一个 row 的所有 cells
cells.forEach((cell: Node) => {
const path = DomEditor.findPath(editor, cell)
if (
path.length === selectedCellPath.length &&
isEqual(path.slice(-1), selectedCellPath.slice(-1)) // 俩数组,最后一位相同
) {
// 如果当前 td 的 path 和选中 td 的 path ,最后一位相同,说明是同一列
// 则在其后插入一个 cell
const newCell = { type: 'table-cell', children: [{ text: '' }] }
Transforms.insertNodes(editor, newCell, { at: path })
}
})
})

// 及时删除记录,重要!!!
DomEditor.deleteChangingPath(editor)
}
}

export default InsertCol
Loading

0 comments on commit a397116

Please sign in to comment.