2021-01-25 06:28:51 +08:00
|
|
|
import {
|
|
|
|
Node,
|
2021-04-12 17:11:02 +08:00
|
|
|
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'
|
2021-01-21 07:16:45 +08:00
|
|
|
import {
|
2021-01-22 07:31:00 +08:00
|
|
|
tableEditing,
|
|
|
|
columnResizing,
|
2021-01-23 06:00:43 +08:00
|
|
|
goToNextCell,
|
2021-01-21 07:16:45 +08:00
|
|
|
addColumnBefore,
|
|
|
|
addColumnAfter,
|
|
|
|
deleteColumn,
|
|
|
|
addRowBefore,
|
|
|
|
addRowAfter,
|
|
|
|
deleteRow,
|
|
|
|
deleteTable,
|
|
|
|
mergeCells,
|
|
|
|
splitCell,
|
|
|
|
toggleHeaderColumn,
|
|
|
|
toggleHeaderRow,
|
|
|
|
toggleHeaderCell,
|
2021-01-23 06:25:13 +08:00
|
|
|
setCellAttr,
|
2021-01-23 04:51:36 +08:00
|
|
|
fixTables,
|
2021-04-27 17:41:38 +08:00
|
|
|
CellSelection,
|
2021-01-21 07:16:45 +08:00
|
|
|
} 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'
|
2021-04-21 04:58:59 +08:00
|
|
|
import { deleteTableWhenAllCellsSelected } from './utilities/deleteTableWhenAllCellsSelected'
|
2021-01-23 04:51:36 +08:00
|
|
|
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>,
|
2021-01-22 07:31:00 +08:00
|
|
|
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
|
|
|
|
*/
|
2021-04-12 17:11:02 +08:00
|
|
|
tableRole?: string | ((this: {
|
2021-04-21 05:12:15 +08:00
|
|
|
name: string,
|
2021-04-12 17:11:02 +08:00
|
|
|
options: Options,
|
2021-04-21 05:12:15 +08:00
|
|
|
parent: ParentConfig<NodeConfig<Options>>['tableRole'],
|
2021-04-12 17:11:02 +08:00
|
|
|
}) => 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: {},
|
2021-01-22 07:31:00 +08:00
|
|
|
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
|
|
|
},
|
|
|
|
|
2021-01-23 06:33:08 +08:00
|
|
|
content: 'tableRow+',
|
2021-01-21 06:53:53 +08:00
|
|
|
|
2021-01-21 07:16:45 +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]]
|
|
|
|
},
|
2021-01-21 07:16:45 +08:00
|
|
|
|
|
|
|
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-21 07:16:45 +08:00
|
|
|
|
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-21 07:16:45 +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 }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return addColumnBefore(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
addColumnAfter: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return addColumnAfter(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
deleteColumn: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return deleteColumn(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
addRowBefore: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return addRowBefore(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
addRowAfter: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return addRowAfter(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
deleteRow: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return deleteRow(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
deleteTable: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return deleteTable(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
mergeCells: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return mergeCells(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
splitCell: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return splitCell(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
toggleHeaderColumn: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return toggleHeaderColumn(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
toggleHeaderRow: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
return toggleHeaderRow(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
toggleHeaderCell: () => ({ state, dispatch }) => {
|
2021-01-21 07:16:45 +08:00
|
|
|
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 04:51:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-23 06:45:50 +08:00
|
|
|
return splitCell(state, dispatch)
|
2021-01-23 04:51:36 +08:00
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
setCellAttribute: (name, value) => ({ state, dispatch }) => {
|
2021-01-23 06:25:13 +08:00
|
|
|
return setCellAttr(name, value)(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
goToNextCell: () => ({ state, dispatch }) => {
|
2021-01-23 06:00:43 +08:00
|
|
|
return goToNextCell(1)(state, dispatch)
|
|
|
|
},
|
2021-02-10 16:59:35 +08:00
|
|
|
goToPreviousCell: () => ({ state, dispatch }) => {
|
2021-01-23 06:00:43 +08:00
|
|
|
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
|
|
|
},
|
2021-01-23 06:00:43 +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 06:00:43 +08:00
|
|
|
}
|
|
|
|
|
2021-01-23 07:32:43 +08:00
|
|
|
return this.editor
|
|
|
|
.chain()
|
|
|
|
.addRowAfter()
|
|
|
|
.goToNextCell()
|
|
|
|
.run()
|
2021-01-23 06:00:43 +08:00
|
|
|
},
|
|
|
|
'Shift-Tab': () => this.editor.commands.goToPreviousCell(),
|
2021-01-25 06:28:51 +08:00
|
|
|
Backspace: deleteTableWhenAllCellsSelected,
|
|
|
|
'Mod-Backspace': deleteTableWhenAllCellsSelected,
|
|
|
|
Delete: deleteTableWhenAllCellsSelected,
|
|
|
|
'Mod-Delete': deleteTableWhenAllCellsSelected,
|
2021-01-21 07:16:45 +08:00
|
|
|
}
|
|
|
|
},
|
2021-01-22 07:31:00 +08:00
|
|
|
|
|
|
|
addProseMirrorPlugins() {
|
2021-08-09 23:41:17 +08:00
|
|
|
const isResizable = this.options.resizable && this.editor.isEditable
|
|
|
|
|
2021-01-22 07:31:00 +08:00
|
|
|
return [
|
2021-08-09 23:41:17 +08:00
|
|
|
...(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-01-22 07:31:00 +08:00
|
|
|
]
|
|
|
|
},
|
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,
|
2021-04-12 17:11:02 +08:00
|
|
|
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
|
|
|
})
|