tiptap/packages/extension-table/src/table.ts

255 lines
6.8 KiB
TypeScript
Raw Normal View History

2021-01-25 06:28:51 +08:00
import {
Node,
ParentConfig,
2021-01-25 06:28:51 +08:00
mergeAttributes,
2021-04-16 03:14:33 +08:00
getExtensionField,
2021-02-19 16:54:39 +08:00
callOrReturn,
2021-01-25 06:28:51 +08:00
} from '@tiptap/core'
import {
tableEditing,
columnResizing,
goToNextCell,
addColumnBefore,
addColumnAfter,
deleteColumn,
addRowBefore,
addRowAfter,
deleteRow,
deleteTable,
mergeCells,
splitCell,
toggleHeaderColumn,
toggleHeaderRow,
toggleHeaderCell,
setCellAttr,
fixTables,
2021-04-27 17:41:38 +08:00
CellSelection,
} from 'prosemirror-tables'
2021-01-23 20:11:22 +08:00
import { NodeView } from 'prosemirror-view'
2021-01-23 05:37:43 +08:00
import { TextSelection } from 'prosemirror-state'
import { createTable } from './utilities/createTable'
import { deleteTableWhenAllCellsSelected } from './utilities/deleteTableWhenAllCellsSelected'
import { TableView } from './TableView'
2021-01-21 06:53:53 +08:00
export interface TableOptions {
2021-04-21 15:43:31 +08:00
HTMLAttributes: Record<string, any>,
resizable: boolean,
2021-01-23 06:45:50 +08:00
handleWidth: number,
cellMinWidth: number,
2021-01-23 20:11:22 +08:00
View: NodeView,
2021-01-23 06:45:50 +08:00
lastColumnResizable: boolean,
allowTableNodeSelection: boolean,
2021-01-21 06:53:53 +08:00
}
2021-01-21 06:42:01 +08:00
2021-02-10 16:59:35 +08:00
declare module '@tiptap/core' {
2021-06-05 03:56:29 +08:00
interface Commands<ReturnType> {
2021-02-16 18:27:58 +08:00
table: {
2021-06-05 03:56:29 +08:00
insertTable: (options?: { rows?: number, cols?: number, withHeaderRow?: boolean }) => ReturnType,
addColumnBefore: () => ReturnType,
addColumnAfter: () => ReturnType,
deleteColumn: () => ReturnType,
addRowBefore: () => ReturnType,
addRowAfter: () => ReturnType,
deleteRow: () => ReturnType,
deleteTable: () => ReturnType,
mergeCells: () => ReturnType,
splitCell: () => ReturnType,
toggleHeaderColumn: () => ReturnType,
toggleHeaderRow: () => ReturnType,
toggleHeaderCell: () => ReturnType,
mergeOrSplit: () => ReturnType,
setCellAttribute: (name: string, value: any) => ReturnType,
goToNextCell: () => ReturnType,
goToPreviousCell: () => ReturnType,
fixTables: () => ReturnType,
setCellSelection: (position: { anchorCell: number, headCell?: number }) => ReturnType,
2021-02-16 18:27:58 +08:00
}
2021-02-10 16:59:35 +08:00
}
2021-02-19 16:54:39 +08:00
2021-02-19 17:54:47 +08:00
interface NodeConfig<Options> {
2021-02-19 16:54:39 +08:00
/**
* Table Role
*/
tableRole?: string | ((this: {
2021-04-21 05:12:15 +08:00
name: string,
options: Options,
2021-04-21 05:12:15 +08:00
parent: ParentConfig<NodeConfig<Options>>['tableRole'],
}) => string),
2021-02-19 16:54:39 +08:00
}
2021-02-10 16:59:35 +08:00
}
2021-02-11 01:25:08 +08:00
export const Table = Node.create<TableOptions>({
2021-01-21 06:42:01 +08:00
name: 'table',
2021-01-21 06:53:53 +08:00
2021-02-11 01:25:08 +08:00
defaultOptions: {
2021-01-21 06:53:53 +08:00
HTMLAttributes: {},
resizable: false,
2021-01-23 06:45:50 +08:00
handleWidth: 5,
cellMinWidth: 25,
2021-02-11 01:25:08 +08:00
// TODO: fix
// @ts-ignore
2021-01-23 06:45:50 +08:00
View: TableView,
lastColumnResizable: true,
allowTableNodeSelection: false,
2021-01-21 06:53:53 +08:00
},
content: 'tableRow+',
2021-01-21 06:53:53 +08:00
tableRole: 'table',
2021-01-21 06:53:53 +08:00
isolating: true,
group: 'block',
parseHTML() {
2021-01-24 05:48:34 +08:00
return [
{ tag: 'table' },
]
2021-01-21 06:53:53 +08:00
},
renderHTML({ HTMLAttributes }) {
return ['table', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), ['tbody', 0]]
},
addCommands() {
return {
2021-02-10 16:59:35 +08:00
insertTable: ({ rows = 3, cols = 3, withHeaderRow = true } = {}) => ({ tr, dispatch, editor }) => {
const node = createTable(editor.schema, rows, cols, withHeaderRow)
2021-01-23 20:11:22 +08:00
if (dispatch) {
2021-01-27 01:18:45 +08:00
const offset = tr.selection.anchor + 1
2021-01-24 05:48:34 +08:00
tr.replaceSelectionWith(node)
.scrollIntoView()
.setSelection(TextSelection.near(tr.doc.resolve(offset)))
2021-01-23 20:11:22 +08:00
}
2021-01-23 20:11:22 +08:00
return true
2021-01-23 05:37:43 +08:00
},
2021-02-10 16:59:35 +08:00
addColumnBefore: () => ({ state, dispatch }) => {
return addColumnBefore(state, dispatch)
},
2021-02-10 16:59:35 +08:00
addColumnAfter: () => ({ state, dispatch }) => {
return addColumnAfter(state, dispatch)
},
2021-02-10 16:59:35 +08:00
deleteColumn: () => ({ state, dispatch }) => {
return deleteColumn(state, dispatch)
},
2021-02-10 16:59:35 +08:00
addRowBefore: () => ({ state, dispatch }) => {
return addRowBefore(state, dispatch)
},
2021-02-10 16:59:35 +08:00
addRowAfter: () => ({ state, dispatch }) => {
return addRowAfter(state, dispatch)
},
2021-02-10 16:59:35 +08:00
deleteRow: () => ({ state, dispatch }) => {
return deleteRow(state, dispatch)
},
2021-02-10 16:59:35 +08:00
deleteTable: () => ({ state, dispatch }) => {
return deleteTable(state, dispatch)
},
2021-02-10 16:59:35 +08:00
mergeCells: () => ({ state, dispatch }) => {
return mergeCells(state, dispatch)
},
2021-02-10 16:59:35 +08:00
splitCell: () => ({ state, dispatch }) => {
return splitCell(state, dispatch)
},
2021-02-10 16:59:35 +08:00
toggleHeaderColumn: () => ({ state, dispatch }) => {
return toggleHeaderColumn(state, dispatch)
},
2021-02-10 16:59:35 +08:00
toggleHeaderRow: () => ({ state, dispatch }) => {
return toggleHeaderRow(state, dispatch)
},
2021-02-10 16:59:35 +08:00
toggleHeaderCell: () => ({ state, dispatch }) => {
return toggleHeaderCell(state, dispatch)
},
2021-02-10 16:59:35 +08:00
mergeOrSplit: () => ({ state, dispatch }) => {
2021-01-23 06:45:50 +08:00
if (mergeCells(state, dispatch)) {
return true
}
2021-01-23 06:45:50 +08:00
return splitCell(state, dispatch)
},
2021-02-10 16:59:35 +08:00
setCellAttribute: (name, value) => ({ state, dispatch }) => {
return setCellAttr(name, value)(state, dispatch)
},
2021-02-10 16:59:35 +08:00
goToNextCell: () => ({ state, dispatch }) => {
return goToNextCell(1)(state, dispatch)
},
2021-02-10 16:59:35 +08:00
goToPreviousCell: () => ({ state, dispatch }) => {
return goToNextCell(-1)(state, dispatch)
},
2021-02-10 16:59:35 +08:00
fixTables: () => ({ state, dispatch }) => {
2021-01-24 05:26:35 +08:00
if (dispatch) {
fixTables(state)
2021-01-23 06:45:50 +08:00
}
2021-04-27 17:41:38 +08:00
return true
},
setCellSelection: position => ({ tr, dispatch }) => {
if (dispatch) {
const selection = CellSelection.create(tr.doc, position.anchorCell, position.headCell)
// @ts-ignore
tr.setSelection(selection)
}
2021-01-24 05:26:35 +08:00
return true
2021-01-23 06:45:50 +08:00
},
}
},
addKeyboardShortcuts() {
return {
Tab: () => {
if (this.editor.commands.goToNextCell()) {
return true
}
2021-01-23 07:32:43 +08:00
if (!this.editor.can().addRowAfter()) {
return false
}
2021-01-23 07:32:43 +08:00
return this.editor
.chain()
.addRowAfter()
.goToNextCell()
.run()
},
'Shift-Tab': () => this.editor.commands.goToPreviousCell(),
2021-01-25 06:28:51 +08:00
Backspace: deleteTableWhenAllCellsSelected,
'Mod-Backspace': deleteTableWhenAllCellsSelected,
Delete: deleteTableWhenAllCellsSelected,
'Mod-Delete': deleteTableWhenAllCellsSelected,
}
},
addProseMirrorPlugins() {
const isResizable = this.options.resizable && this.editor.isEditable
return [
...(isResizable ? [columnResizing({
2021-01-23 06:45:50 +08:00
handleWidth: this.options.handleWidth,
cellMinWidth: this.options.cellMinWidth,
View: this.options.View,
2021-01-24 03:59:19 +08:00
// TODO: PR for @types/prosemirror-tables
// @ts-ignore (incorrect type)
lastColumnResizable: this.options.lastColumnResizable,
2021-01-23 06:45:50 +08:00
})] : []),
2021-01-23 20:11:22 +08:00
tableEditing({
allowTableNodeSelection: this.options.allowTableNodeSelection,
}),
]
},
2021-02-19 17:54:47 +08:00
extendNodeSchema(extension) {
2021-04-16 03:14:33 +08:00
const context = {
2021-04-21 05:12:15 +08:00
name: extension.name,
options: extension.options,
2021-04-16 03:14:33 +08:00
}
2021-02-19 17:54:47 +08:00
return {
2021-04-16 03:14:33 +08:00
tableRole: callOrReturn(getExtensionField(extension, 'tableRole', context)),
2021-02-19 17:54:47 +08:00
}
},
2021-01-21 06:42:01 +08:00
})