From 32a41b49d0bedb4fcc3668fb95dc8e18318abac6 Mon Sep 17 00:00:00 2001 From: Luka Trovic Date: Thu, 17 Oct 2024 17:56:22 +0200 Subject: [PATCH] feat(table): add row, column buttons outside of table Signed-off-by: Luka Trovic --- cypress/e2e/nodes/Table.spec.js | 42 +++++++ src/css/prosemirror.scss | 4 +- src/nodes/Table/TableCell.js | 12 ++ src/nodes/Table/TableHeader.js | 12 ++ src/nodes/Table/TableView.vue | 117 +++++++++++++++--- .../tables/handbook/handbook.out.html | 40 +++--- 6 files changed, 190 insertions(+), 37 deletions(-) diff --git a/cypress/e2e/nodes/Table.spec.js b/cypress/e2e/nodes/Table.spec.js index d3dd1c6d84a..eb5f4d2aa64 100644 --- a/cypress/e2e/nodes/Table.spec.js +++ b/cypress/e2e/nodes/Table.spec.js @@ -144,6 +144,48 @@ describe('table plugin', () => { expect($el.get(0).innerHTML).to.equal(multilinesContent.replace(/\n/g, '
')) }) }) + + it('Button add row below', () => { + cy.getActionEntry('table').click() + + cy.getContent() + .find('table tr') + .should('have.length', 3) + + cy.getContent() + .find('.table-wrapper .table-add-row') + .should('be.visible') + + cy.getContent() + .find('.table-wrapper .table-add-row') + .click() + + // Added a row + cy.getContent() + .find('table tr') + .should('have.length', 4) + }) + + it('Button add column after', () => { + cy.getActionEntry('table').click() + + cy.getContent() + .find('table tr') + .should('have.length', 3) + + cy.getContent() + .find('.table-wrapper .table-add-column') + .should('be.visible') + + cy.getContent() + .find('.table-wrapper .table-add-column') + .click() + + // Added a column + cy.getContent() + .find('table tr th') + .should('have.length', 4) + }) }) describe('Table extension integrated in the editor', () => { diff --git a/src/css/prosemirror.scss b/src/css/prosemirror.scss index a6327e2c9eb..344c2fa996d 100644 --- a/src/css/prosemirror.scss +++ b/src/css/prosemirror.scss @@ -322,10 +322,10 @@ div.ProseMirror { table { border-spacing: 0; - width: calc(100% - 50px); + width: calc(100% - 50px - var(--default-clickable-area)); table-layout: auto; white-space: normal; // force text to wrapping - margin-bottom: 1em; + margin-bottom: calc(var(--clickable-area-small) + var(--default-clickable-area)); &+ * { margin-top: 1em; } diff --git a/src/nodes/Table/TableCell.js b/src/nodes/Table/TableCell.js index 69666465e11..deba05d875d 100644 --- a/src/nodes/Table/TableCell.js +++ b/src/nodes/Table/TableCell.js @@ -6,6 +6,7 @@ import { TableCell } from '@tiptap/extension-table-cell' import { Plugin } from '@tiptap/pm/state' import { Fragment } from '@tiptap/pm/model' +import { mergeAttributes } from '@tiptap/core' export default TableCell.extend({ content: 'inline*', @@ -58,6 +59,17 @@ export default TableCell.extend({ } }, + renderHTML({ HTMLAttributes }) { + const attributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes) + if (attributes.colspan === 1) { + delete attributes.colspan + } + if (attributes.rowspan === 1) { + delete attributes.rowspan + } + return ['td', attributes, 0] + }, + addProseMirrorPlugins() { return [ new Plugin({ diff --git a/src/nodes/Table/TableHeader.js b/src/nodes/Table/TableHeader.js index 5f6a31afda1..85ba79bd649 100644 --- a/src/nodes/Table/TableHeader.js +++ b/src/nodes/Table/TableHeader.js @@ -4,6 +4,7 @@ */ import { TableHeader } from '@tiptap/extension-table-header' +import { mergeAttributes } from '@tiptap/core' export default TableHeader.extend({ content: 'inline*', @@ -39,6 +40,17 @@ export default TableHeader.extend({ ] }, + renderHTML({ HTMLAttributes }) { + const attributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes) + if (attributes.colspan === 1) { + delete attributes.colspan + } + if (attributes.rowspan === 1) { + delete attributes.rowspan + } + return ['th', attributes, 0] + }, + addAttributes() { return { ...this.parent?.(), diff --git a/src/nodes/Table/TableView.vue b/src/nodes/Table/TableView.vue index b91ab5dccd8..40026d250a0 100644 --- a/src/nodes/Table/TableView.vue +++ b/src/nodes/Table/TableView.vue @@ -4,7 +4,7 @@ --> @@ -69,6 +121,53 @@ export default { .table-wrapper { position: relative; overflow-x: auto; + + &.focused, &:hover { + .table-add-column, .table-add-row { + visibility: visible; + } + } + + .table-settings { + padding-left: 3px; + opacity: .5; + position: absolute; + top: 0; + right: var(--default-clickable-area); + + &:hover { + opacity: 1; + } + } + + .table-add-column { + visibility: hidden; + padding-left: 3px; + opacity: .5; + position: absolute; + top: var(--default-clickable-area); + right: 0; + bottom: 60px; + margin-top: 0 !important; + + &:hover { + opacity: 1; + } + } + + .table-add-row { + visibility: hidden; + padding-left: 3px; + opacity: .5; + position: absolute; + left: 0; + bottom: 12px; + width: calc(100% - 80px) !important; + + &:hover { + opacity: 1; + } + } } .clearfix { @@ -78,16 +177,4 @@ export default { table { float: left; } - -.table-settings { - padding-left: 3px; - opacity: .5; - position: absolute; - top: 0; - right: 3px; - - &:hover { - opacity: 1; - } -} diff --git a/src/tests/fixtures/tables/handbook/handbook.out.html b/src/tests/fixtures/tables/handbook/handbook.out.html index f871b940c01..51ddc914136 100644 --- a/src/tests/fixtures/tables/handbook/handbook.out.html +++ b/src/tests/fixtures/tables/handbook/handbook.out.html @@ -1,32 +1,32 @@
- - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + +
Heading 0Heading 1Heading 2Heading 3Heading 4Heading 0Heading 1Heading 2Heading 3Heading 4
LetterabcdLetterabcd
Number1234Number1234
Square14916Square14916