64
64
@click =" editor.chain().focus().sinkListItem('listItem').run()"
65
65
/>
66
66
</template >
67
+
68
+ <v-divider vertical class =" mx-1" />
69
+
70
+ <TiptapToolbarButton
71
+ icon =" mdi-table-large-plus"
72
+ :disabled =" editor.can().addColumnBefore()"
73
+ @click =" editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()"
74
+ />
67
75
</div >
68
76
</v-toolbar >
77
+ <v-divider v-if =" $vuetify.breakpoint.smAndUp" />
78
+ <v-toolbar
79
+ v-if =" shouldShowTableOptions && $vuetify.breakpoint.smAndUp"
80
+ class =" elevation-0"
81
+ dense
82
+ color =" transparent"
83
+ >
84
+ <TiptapToolbarButton
85
+ icon =" mdi-table-column-plus-before"
86
+ :disabled =" !editor.can().addColumnBefore()"
87
+ @click =" editor.chain().focus().addColumnBefore().run()"
88
+ />
89
+ <TiptapToolbarButton
90
+ icon =" mdi-table-column-plus-after"
91
+ :disabled =" !editor.can().addColumnAfter()"
92
+ @click =" editor.chain().focus().addColumnAfter().run()"
93
+ />
94
+ <TiptapToolbarButton
95
+ icon =" mdi-table-column-remove"
96
+ :disabled =" !editor.can().deleteColumn()"
97
+ @click =" editor.chain().focus().deleteColumn().run()"
98
+ />
99
+ <v-divider vertical class =" mx-1" />
100
+ <TiptapToolbarButton
101
+ icon =" mdi-table-row-plus-before"
102
+ :disabled =" !editor.can().addRowBefore()"
103
+ @click =" editor.chain().focus().addRowBefore().run()"
104
+ />
105
+ <TiptapToolbarButton
106
+ icon =" mdi-table-row-plus-after"
107
+ :disabled =" !editor.can().addRowAfter()"
108
+ @click =" editor.chain().focus().addRowAfter().run()"
109
+ />
110
+ <TiptapToolbarButton
111
+ icon =" mdi-table-row-remove"
112
+ :disabled =" !editor.can().deleteRow()"
113
+ @click =" editor.chain().focus().deleteRow().run()"
114
+ />
115
+ <v-divider vertical class =" mx-1" />
116
+ <TiptapToolbarButton
117
+ icon =" mdi-table-border"
118
+ :disabled =" !editor.can().toggleHeaderCell()"
119
+ @click =" editor.chain().focus().toggleHeaderCell().run()"
120
+ />
121
+ </v-toolbar >
69
122
<v-divider class =" ec-tiptap-toolbar__mobile-divider" />
70
123
<v-toolbar
71
124
class =" elevation-0 ec-tiptap-toolbar--second"
@@ -120,6 +173,10 @@ import Bold from '@tiptap/extension-bold'
120
173
import Italic from ' @tiptap/extension-italic'
121
174
import Strike from ' @tiptap/extension-strike'
122
175
import Underline from ' @tiptap/extension-underline'
176
+ import Table from ' @tiptap/extension-table'
177
+ import TableCell from ' @tiptap/extension-table-cell'
178
+ import TableHeader from ' @tiptap/extension-table-header'
179
+ import TableRow from ' @tiptap/extension-table-row'
123
180
import History from ' @tiptap/extension-history'
124
181
import Placeholder from ' @tiptap/extension-placeholder'
125
182
import TiptapToolbarButton from ' @/components/form/tiptap/TiptapToolbarButton.vue'
@@ -180,6 +237,13 @@ export default {
180
237
AutoLinkDecoration,
181
238
// headings currently disabled (see issue #2657)
182
239
HardBreak,
240
+ Table .configure ({
241
+ resizable: true ,
242
+ allowTableNodeSelection: true ,
243
+ }),
244
+ TableRow,
245
+ TableHeader,
246
+ TableCell,
183
247
]
184
248
)
185
249
}
@@ -199,15 +263,6 @@ export default {
199
263
},
200
264
// copied from @tiptap/extension-bubble-menu
201
265
shouldShow : ({ view, state, from, to }) => {
202
- const { doc , selection } = state
203
- const { empty } = selection
204
-
205
- // Sometime check for `empty` is not enough.
206
- // Doubleclick an empty paragraph returns a node size of 2.
207
- // So we check also for an empty text size.
208
- const isEmptyTextBlock =
209
- ! doc .textBetween (from, to).length && isTextSelection (state .selection )
210
-
211
266
// Don't show if selection is within of an autolink
212
267
if (this .withExtensions ) {
213
268
const links = AutoLinkKey .getState (state).find (
@@ -227,12 +282,19 @@ export default {
227
282
228
283
const hasEditorFocus = view .hasFocus () || isChildOfMenu
229
284
230
- if (! hasEditorFocus || empty || isEmptyTextBlock || ! this .editor .isEditable ) {
285
+ if (! hasEditorFocus || ! this .editor .isEditable ) {
231
286
return false
232
287
}
233
288
234
289
return true
235
290
},
291
+ shouldShowTableOptions : ({ from, to }) => {
292
+ if (this .withExtensions && this .from > - 1 && this .to > - 1 ) {
293
+ return this .editor .can ().addColumnBefore ()
294
+ }
295
+
296
+ return false
297
+ },
236
298
}
237
299
},
238
300
computed: {
@@ -328,6 +390,57 @@ div.editor:deep(.editor__content .ProseMirror) {
328
390
line-height : 1.5 ;
329
391
}
330
392
393
+ div .editor :deep(.editor__content ) .resize-cursor {
394
+ cursor : ew-resize ;
395
+ cursor : col-resize ;
396
+ }
397
+
398
+ div .editor :deep(.editor__content table ) {
399
+ border-collapse : collapse ;
400
+ table-layout : fixed ;
401
+ width : 100% ;
402
+ margin : 0 ;
403
+ overflow : hidden ;
404
+
405
+ td , th {
406
+ padding : 0.2rem 0.5rem 0 ;
407
+ min-width : 1em ;
408
+ border : 1px solid rgba (0 , 0 , 0 , 0.38 );
409
+ vertical-align : top ;
410
+ text-align : left ;
411
+ box-sizing : border-box ;
412
+ position : relative ;
413
+
414
+ > * {
415
+ margin-bottom : 0 ;
416
+ }
417
+ }
418
+
419
+ th {
420
+ font-weight : bold ;
421
+ background-color : #f1f3f5 ;
422
+ }
423
+
424
+ .selectedCell :after {
425
+ z-index : 2 ;
426
+ position : absolute ;
427
+ content : " " ;
428
+ left : 0 ; right : 0 ; top : 0 ; bottom : 0 ;
429
+ background : rgba (200 , 200 , 255 , 0.4 );
430
+ pointer-events : none ;
431
+ }
432
+
433
+ .column-resize-handle {
434
+ position : absolute ;
435
+ right : -2px ;
436
+ top : 0 ;
437
+ bottom : -2px ;
438
+ width : 4px ;
439
+ background-color : #adf ;
440
+ pointer-events : none ;
441
+ }
442
+ }
443
+
331
444
.theme--light.v-input--is-disabled div .editor :deep(.editor__content .ProseMirror ) {
332
445
color : rgba (0 , 0 , 0 , 0.38 );
333
446
}
0 commit comments