diff --git a/examples/Components/App/style.scss b/examples/Components/App/style.scss index c700022a..8a877b66 100644 --- a/examples/Components/App/style.scss +++ b/examples/Components/App/style.scss @@ -79,6 +79,53 @@ border-radius: 3px; } + table { + border-collapse: collapse; + table-layout: fixed; + width: 100%; + margin: 0; + overflow: hidden; + td, th { + min-width: 1em; + border: 1px solid #ddd; + padding: 3px 5px; + vertical-align: top; + box-sizing: border-box; + position: relative; + > * { + margin-bottom: 0; + } + } + th { + font-weight: bold; + text-align: left; + } + .selectedCell:after { + z-index: 2; + position: absolute; + content: ""; + left: 0; right: 0; top: 0; bottom: 0; + background: rgba(200, 200, 255, 0.4); + pointer-events: none; + } + .column-resize-handle { + position: absolute; + right: -2px; top: 0; bottom: 0; + width: 4px; + z-index: 20; + background-color: #adf; + pointer-events: none; + } + } + .tableWrapper { + margin: 1em 0; + overflow-x: auto; + } + .resize-cursor { + cursor: ew-resize; + cursor: col-resize; + } + } } diff --git a/examples/Components/Routes/Table/index.vue b/examples/Components/Routes/Table/index.vue new file mode 100644 index 00000000..2f00fd35 --- /dev/null +++ b/examples/Components/Routes/Table/index.vue @@ -0,0 +1,274 @@ + + + diff --git a/examples/Components/Subnavigation/index.vue b/examples/Components/Subnavigation/index.vue index aad69c2e..c9971119 100644 --- a/examples/Components/Subnavigation/index.vue +++ b/examples/Components/Subnavigation/index.vue @@ -21,6 +21,9 @@ Todo List + + Table + Suggestions diff --git a/examples/assets/images/icons/add_col_after.svg b/examples/assets/images/icons/add_col_after.svg new file mode 100644 index 00000000..1c898d60 --- /dev/null +++ b/examples/assets/images/icons/add_col_after.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/assets/images/icons/add_col_before.svg b/examples/assets/images/icons/add_col_before.svg new file mode 100644 index 00000000..dbb5a6f3 --- /dev/null +++ b/examples/assets/images/icons/add_col_before.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/assets/images/icons/add_row_after.svg b/examples/assets/images/icons/add_row_after.svg new file mode 100644 index 00000000..670681b0 --- /dev/null +++ b/examples/assets/images/icons/add_row_after.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/assets/images/icons/add_row_before.svg b/examples/assets/images/icons/add_row_before.svg new file mode 100644 index 00000000..ab400f09 --- /dev/null +++ b/examples/assets/images/icons/add_row_before.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/assets/images/icons/combine_cells.svg b/examples/assets/images/icons/combine_cells.svg new file mode 100644 index 00000000..f7fc3b9f --- /dev/null +++ b/examples/assets/images/icons/combine_cells.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/assets/images/icons/delete_col.svg b/examples/assets/images/icons/delete_col.svg new file mode 100644 index 00000000..42745536 --- /dev/null +++ b/examples/assets/images/icons/delete_col.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/assets/images/icons/delete_row.svg b/examples/assets/images/icons/delete_row.svg new file mode 100644 index 00000000..e384a25f --- /dev/null +++ b/examples/assets/images/icons/delete_row.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/assets/images/icons/delete_table.svg b/examples/assets/images/icons/delete_table.svg new file mode 100644 index 00000000..a5afb9de --- /dev/null +++ b/examples/assets/images/icons/delete_table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/assets/images/icons/table.svg b/examples/assets/images/icons/table.svg new file mode 100644 index 00000000..e7dad46f --- /dev/null +++ b/examples/assets/images/icons/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/main.js b/examples/main.js index fae30d10..80b399a9 100644 --- a/examples/main.js +++ b/examples/main.js @@ -54,6 +54,13 @@ const routes = [ githubUrl: 'https://github.com/heyscrumpy/tiptap/tree/master/examples/Components/Routes/HidingMenuBar', }, }, + { + path: '/table', + component: () => import('Components/Routes/Table'), + meta: { + githubUrl: 'https://github.com/heyscrumpy/tiptap/tree/master/examples/Components/Routes/Table', + }, + }, { path: '/todo-list', component: () => import('Components/Routes/TodoList'), diff --git a/packages/tiptap-extensions/src/index.js b/packages/tiptap-extensions/src/index.js index 591157fa..8e20e247 100644 --- a/packages/tiptap-extensions/src/index.js +++ b/packages/tiptap-extensions/src/index.js @@ -8,6 +8,10 @@ export { default as Image } from './nodes/Image' export { default as ListItem } from './nodes/ListItem' export { default as Mention } from './nodes/Mention' export { default as OrderedList } from './nodes/OrderedList' +export { default as Table } from './nodes/Table' +export { default as TableHeader } from './nodes/TableHeader' +export { default as TableCell } from './nodes/TableCell' +export { default as TableRow } from './nodes/TableRow' export { default as TodoItem } from './nodes/TodoItem' export { default as TodoList } from './nodes/TodoList' diff --git a/packages/tiptap-extensions/src/nodes/Table.js b/packages/tiptap-extensions/src/nodes/Table.js new file mode 100644 index 00000000..8ddd3cbc --- /dev/null +++ b/packages/tiptap-extensions/src/nodes/Table.js @@ -0,0 +1,82 @@ +import { Node } from 'tiptap' +import { + tableEditing, + columnResizing, + goToNextCell, + addColumnBefore, + addColumnAfter, + deleteColumn, + addRowBefore, + addRowAfter, + deleteRow, + deleteTable, + mergeCells, + splitCell, + toggleHeaderColumn, + toggleHeaderRow, + toggleHeaderCell, + setCellAttr, + fixTables, +} from 'prosemirror-tables' +import { createTable } from 'prosemirror-utils' +import TableNodes from './TableNodes' + +export default class Table extends Node { + + get name() { + return 'table' + } + + get schema() { + return TableNodes.table + } + + commands({ schema }) { + return { + createTable: ({ rowsCount, colsCount, withHeaderRow }) => ( + (state, dispatch) => { + const nodes = createTable(schema, rowsCount, colsCount, withHeaderRow) + const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView() + dispatch(tr) + } + ), + addColumnBefore: () => addColumnBefore, + addColumnAfter: () => addColumnAfter, + deleteColumn: () => deleteColumn, + addRowBefore: () => addRowBefore, + addRowAfter: () => addRowAfter, + deleteRow: () => deleteRow, + deleteTable: () => deleteTable, + toggleCellMerge: () => ( + (state, dispatch) => { + if (mergeCells(state, dispatch)) { + return + } + splitCell(state, dispatch) + } + ), + mergeCells: () => mergeCells, + splitCell: () => splitCell, + toggleHeaderColumn: () => toggleHeaderColumn, + toggleHeaderRow: () => toggleHeaderRow, + toggleHeaderCell: () => toggleHeaderCell, + setCellAttr: () => setCellAttr, + fixTables: () => fixTables, + } + } + + keys() { + return { + Tab: goToNextCell(1), + 'Shift-Tab': goToNextCell(-1), + } + } + + get plugins() { + return [ + columnResizing(), + tableEditing(), + ] + } + +} diff --git a/packages/tiptap-extensions/src/nodes/TableCell.js b/packages/tiptap-extensions/src/nodes/TableCell.js new file mode 100644 index 00000000..b7fb691a --- /dev/null +++ b/packages/tiptap-extensions/src/nodes/TableCell.js @@ -0,0 +1,14 @@ +import { Node } from 'tiptap' +import TableNodes from './TableNodes' + +export default class TableCell extends Node { + + get name() { + return 'table_cell' + } + + get schema() { + return TableNodes.table_cell + } + +} diff --git a/packages/tiptap-extensions/src/nodes/TableHeader.js b/packages/tiptap-extensions/src/nodes/TableHeader.js new file mode 100644 index 00000000..d9bce0c9 --- /dev/null +++ b/packages/tiptap-extensions/src/nodes/TableHeader.js @@ -0,0 +1,14 @@ +import { Node } from 'tiptap' +import TableNodes from './TableNodes' + +export default class TableHeader extends Node { + + get name() { + return 'table_header' + } + + get schema() { + return TableNodes.table_header + } + +} diff --git a/packages/tiptap-extensions/src/nodes/TableNodes.js b/packages/tiptap-extensions/src/nodes/TableNodes.js new file mode 100644 index 00000000..95a492ab --- /dev/null +++ b/packages/tiptap-extensions/src/nodes/TableNodes.js @@ -0,0 +1,20 @@ +import { tableNodes } from 'prosemirror-tables' + +export default tableNodes({ + tableGroup: 'block', + cellContent: 'block+', + cellAttributes: { + background: { + default: null, + getFromDOM(dom) { + return dom.style.backgroundColor || null + }, + setDOMAttr(value, attrs) { + if (value) { + const style = { style: `${(attrs.style || '')}background-color: ${value};` } + Object.assign(attrs, style) + } + }, + }, + }, +}) diff --git a/packages/tiptap-extensions/src/nodes/TableRow.js b/packages/tiptap-extensions/src/nodes/TableRow.js new file mode 100644 index 00000000..40a2d85d --- /dev/null +++ b/packages/tiptap-extensions/src/nodes/TableRow.js @@ -0,0 +1,14 @@ +import { Node } from 'tiptap' +import TableNodes from './TableNodes' + +export default class TableRow extends Node { + + get name() { + return 'table_row' + } + + get schema() { + return TableNodes.table_row + } + +}