204 | {mediaType === "img" && (
205 |

213 | )}
214 |
215 | {mediaType === "video" && (
216 |
225 | )}
226 |
227 |
setLastClientX(clientX)}
231 | onMouseDown={startHorizontalResize}
232 | onMouseUp={stopHorizontalResize}
233 | />
234 |
235 |
236 | {resizableMediaActions.map((btn) => {
237 | return (
238 | // TODO: figure out why tooltips are not working
239 |
253 | );
254 | })}
255 |
256 |
257 |
258 | );
259 | };
260 |
--------------------------------------------------------------------------------
/src/tiptap/extensions/supercharged-table/extension-table/table.ts:
--------------------------------------------------------------------------------
1 | import {
2 | addColumnAfter,
3 | addColumnBefore,
4 | addRowAfter,
5 | addRowBefore,
6 | CellSelection,
7 | columnResizing,
8 | deleteColumn,
9 | deleteRow,
10 | deleteTable,
11 | fixTables,
12 | goToNextCell,
13 | mergeCells,
14 | setCellAttr,
15 | splitCell,
16 | tableEditing,
17 | toggleHeader,
18 | toggleHeaderCell,
19 | } from "@_ueberdosis/prosemirror-tables";
20 | import {
21 | callOrReturn,
22 | getExtensionField,
23 | mergeAttributes,
24 | Node,
25 | ParentConfig,
26 | } from "@tiptap/core";
27 | import { TextSelection } from "prosemirror-state";
28 | import { NodeView } from "prosemirror-view";
29 |
30 | import { TableView } from "./TableView";
31 | import { createTable } from "./utilities/createTable";
32 | import { deleteTableWhenAllCellsSelected } from "./utilities/deleteTableWhenAllCellsSelected";
33 |
34 | export interface TableOptions {
35 | HTMLAttributes: Record
;
36 | resizable: boolean;
37 | handleWidth: number;
38 | cellMinWidth: number;
39 | View: NodeView;
40 | lastColumnResizable: boolean;
41 | allowTableNodeSelection: boolean;
42 | }
43 |
44 | declare module "@tiptap/core" {
45 | interface Commands {
46 | table: {
47 | insertTable: (options?: {
48 | rows?: number;
49 | cols?: number;
50 | withHeaderRow?: boolean;
51 | }) => ReturnType;
52 | addColumnBefore: () => ReturnType;
53 | addColumnAfter: () => ReturnType;
54 | deleteColumn: () => ReturnType;
55 | addRowBefore: () => ReturnType;
56 | addRowAfter: () => ReturnType;
57 | deleteRow: () => ReturnType;
58 | deleteTable: () => ReturnType;
59 | mergeCells: () => ReturnType;
60 | splitCell: () => ReturnType;
61 | toggleHeaderColumn: () => ReturnType;
62 | toggleHeaderRow: () => ReturnType;
63 | toggleHeaderCell: () => ReturnType;
64 | mergeOrSplit: () => ReturnType;
65 | setCellAttribute: (name: string, value: any) => ReturnType;
66 | goToNextCell: () => ReturnType;
67 | goToPreviousCell: () => ReturnType;
68 | fixTables: () => ReturnType;
69 | setCellSelection: (position: {
70 | anchorCell: number;
71 | headCell?: number;
72 | }) => ReturnType;
73 | };
74 | }
75 |
76 | interface NodeConfig {
77 | /**
78 | * Table Role
79 | */
80 | tableRole?:
81 | | string
82 | | ((this: {
83 | name: string;
84 | options: Options;
85 | storage: Storage;
86 | parent: ParentConfig>["tableRole"];
87 | }) => string);
88 | }
89 | }
90 |
91 | export const Table = Node.create({
92 | name: "table",
93 |
94 | // @ts-ignore
95 | addOptions() {
96 | return {
97 | HTMLAttributes: {},
98 | resizable: false,
99 | handleWidth: 5,
100 | cellMinWidth: 25,
101 | // TODO: fix
102 | View: TableView,
103 | lastColumnResizable: true,
104 | allowTableNodeSelection: false,
105 | };
106 | },
107 |
108 | content: "tableRow+",
109 |
110 | tableRole: "table",
111 |
112 | isolating: true,
113 |
114 | group: "block",
115 |
116 | parseHTML() {
117 | return [{ tag: "table" }];
118 | },
119 |
120 | renderHTML({ HTMLAttributes }) {
121 | return [
122 | "table",
123 | mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
124 | ["tbody", 0],
125 | ];
126 | },
127 |
128 | addCommands() {
129 | return {
130 | insertTable:
131 | ({ rows = 3, cols = 3, withHeaderRow = true } = {}) =>
132 | ({ tr, dispatch, editor }) => {
133 | const node = createTable(editor.schema, rows, cols, withHeaderRow);
134 |
135 | if (dispatch) {
136 | const offset = tr.selection.anchor + 1;
137 |
138 | tr.replaceSelectionWith(node)
139 | .scrollIntoView()
140 | .setSelection(TextSelection.near(tr.doc.resolve(offset)));
141 | }
142 |
143 | return true;
144 | },
145 | addColumnBefore:
146 | () =>
147 | ({ state, dispatch }) => {
148 | return addColumnBefore(state, dispatch);
149 | },
150 | addColumnAfter:
151 | () =>
152 | ({ state, dispatch }) => {
153 | return addColumnAfter(state, dispatch);
154 | },
155 | deleteColumn:
156 | () =>
157 | ({ state, dispatch }) => {
158 | return deleteColumn(state, dispatch);
159 | },
160 | addRowBefore:
161 | () =>
162 | ({ state, dispatch }) => {
163 | return addRowBefore(state, dispatch);
164 | },
165 | addRowAfter:
166 | () =>
167 | ({ state, dispatch }) => {
168 | return addRowAfter(state, dispatch);
169 | },
170 | deleteRow:
171 | () =>
172 | ({ state, dispatch }) => {
173 | return deleteRow(state, dispatch);
174 | },
175 | deleteTable:
176 | () =>
177 | ({ state, dispatch }) => {
178 | return deleteTable(state, dispatch);
179 | },
180 | mergeCells:
181 | () =>
182 | ({ state, dispatch }) => {
183 | return mergeCells(state, dispatch);
184 | },
185 | splitCell:
186 | () =>
187 | ({ state, dispatch }) => {
188 | return splitCell(state, dispatch);
189 | },
190 | toggleHeaderColumn:
191 | () =>
192 | ({ state, dispatch }) => {
193 | return toggleHeader("column")(state, dispatch);
194 | },
195 | toggleHeaderRow:
196 | () =>
197 | ({ state, dispatch }) => {
198 | return toggleHeader("row")(state, dispatch);
199 | },
200 | toggleHeaderCell:
201 | () =>
202 | ({ state, dispatch }) => {
203 | return toggleHeaderCell(state, dispatch);
204 | },
205 | mergeOrSplit:
206 | () =>
207 | ({ state, dispatch }) => {
208 | if (mergeCells(state, dispatch)) {
209 | return true;
210 | }
211 |
212 | return splitCell(state, dispatch);
213 | },
214 | setCellAttribute:
215 | (name, value) =>
216 | ({ state, dispatch }) => {
217 | return setCellAttr(name, value)(state, dispatch);
218 | },
219 | goToNextCell:
220 | () =>
221 | ({ state, dispatch }) => {
222 | return goToNextCell(1)(state, dispatch);
223 | },
224 | goToPreviousCell:
225 | () =>
226 | ({ state, dispatch }) => {
227 | return goToNextCell(-1)(state, dispatch);
228 | },
229 | fixTables:
230 | () =>
231 | ({ state, dispatch }) => {
232 | if (dispatch) {
233 | fixTables(state);
234 | }
235 |
236 | return true;
237 | },
238 | setCellSelection:
239 | (position) =>
240 | ({ tr, dispatch }) => {
241 | if (dispatch) {
242 | const selection = CellSelection.create(
243 | tr.doc,
244 | position.anchorCell,
245 | position.headCell
246 | );
247 |
248 | tr.setSelection(selection as any);
249 | }
250 |
251 | return true;
252 | },
253 | };
254 | },
255 |
256 | addKeyboardShortcuts() {
257 | return {
258 | Tab: () => {
259 | if (this.editor.commands.goToNextCell()) {
260 | return true;
261 | }
262 |
263 | if (!this.editor.can().addRowAfter()) {
264 | return false;
265 | }
266 |
267 | return this.editor.chain().addRowAfter().goToNextCell().run();
268 | },
269 | "Shift-Tab": () => this.editor.commands.goToPreviousCell(),
270 | Backspace: deleteTableWhenAllCellsSelected,
271 | "Mod-Backspace": deleteTableWhenAllCellsSelected,
272 | Delete: deleteTableWhenAllCellsSelected,
273 | "Mod-Delete": deleteTableWhenAllCellsSelected,
274 | };
275 | },
276 |
277 | addProseMirrorPlugins() {
278 | const isResizable = this.options.resizable && this.editor.isEditable;
279 |
280 | return [
281 | ...(isResizable
282 | ? [
283 | columnResizing({
284 | handleWidth: this.options.handleWidth,
285 | cellMinWidth: this.options.cellMinWidth,
286 | View: this.options.View,
287 | // TODO: PR for @types/prosemirror-tables
288 | // @ts-ignore (incorrect type)
289 | lastColumnResizable: this.options.lastColumnResizable,
290 | }),
291 | ]
292 | : []),
293 | tableEditing({
294 | allowTableNodeSelection: this.options.allowTableNodeSelection,
295 | }),
296 | ];
297 | },
298 |
299 | extendNodeSchema(extension) {
300 | const context = {
301 | name: extension.name,
302 | options: extension.options,
303 | storage: extension.storage,
304 | };
305 |
306 | return {
307 | tableRole: callOrReturn(
308 | getExtensionField(extension, "tableRole", context)
309 | ),
310 | };
311 | },
312 | });
313 |
--------------------------------------------------------------------------------
/src/tiptap/mocks/defaultContent.ts:
--------------------------------------------------------------------------------
1 | import { Content } from "@tiptap/react";
2 |
3 | export const content: Content = {
4 | type: "doc",
5 | content: [
6 | {
7 | type: "dBlock",
8 | content: [
9 | {
10 | type: "heading",
11 | attrs: {
12 | level: 1,
13 | },
14 | content: [
15 | {
16 | type: "text",
17 | text: "Welcome to notitap.",
18 | },
19 | ],
20 | },
21 | ],
22 | },
23 | {
24 | type: "dBlock",
25 | content: [
26 | {
27 | type: "heading",
28 | attrs: {
29 | level: 3,
30 | },
31 | content: [
32 | {
33 | type: "text",
34 | text: "A notion like editor built with Tiptap",
35 | },
36 | ],
37 | },
38 | ],
39 | },
40 | {
41 | type: "dBlock",
42 | content: [
43 | {
44 | type: "heading",
45 | attrs: {
46 | level: 2,
47 | },
48 | content: [
49 | {
50 | type: "text",
51 | text: "Features:",
52 | },
53 | ],
54 | },
55 | ],
56 | },
57 | {
58 | type: "dBlock",
59 | content: [
60 | {
61 | type: "bulletList",
62 | content: [
63 | {
64 | type: "listItem",
65 | content: [
66 | {
67 | type: "paragraph",
68 | content: [
69 | {
70 | type: "text",
71 | text: "Block Level Drag and Drop",
72 | },
73 | ],
74 | },
75 | ],
76 | },
77 | {
78 | type: "listItem",
79 | content: [
80 | {
81 | type: "paragraph",
82 | content: [
83 | {
84 | type: "text",
85 | text: "Resizable Media(Images, Videos)",
86 | },
87 | ],
88 | },
89 | ],
90 | },
91 | {
92 | type: "listItem",
93 | content: [
94 | {
95 | type: "paragraph",
96 | content: [
97 | {
98 | type: "text",
99 | text: "Supercharged Tables",
100 | },
101 | ],
102 | },
103 | ],
104 | },
105 | {
106 | type: "listItem",
107 | content: [
108 | {
109 | type: "paragraph",
110 | content: [
111 | {
112 | type: "text",
113 | text: "Link Previews",
114 | },
115 | ],
116 | },
117 | ],
118 | },
119 | ],
120 | },
121 | ],
122 | },
123 | {
124 | type: "dBlock",
125 | content: [
126 | {
127 | type: "heading",
128 | attrs: {
129 | level: 2,
130 | },
131 | content: [
132 | {
133 | type: "text",
134 | text: "Resizable Media",
135 | },
136 | ],
137 | },
138 | ],
139 | },
140 | {
141 | type: "dBlock",
142 | content: [
143 | {
144 | type: "heading",
145 | attrs: {
146 | level: 3,
147 | },
148 | content: [
149 | {
150 | type: "text",
151 | text: "Images:",
152 | },
153 | ],
154 | },
155 | ],
156 | },
157 | {
158 | type: "dBlock",
159 | content: [
160 | {
161 | type: "resizableMedia",
162 | attrs: {
163 | src: "https://source.unsplash.com/8xznAGy4HcY/800x400",
164 | "media-type": "img",
165 | alt: "Something else",
166 | title: "Something",
167 | width: 574,
168 | height: 287,
169 | dataAlign: "center",
170 | dataFloat: null,
171 | },
172 | },
173 | ],
174 | },
175 | {
176 | type: "dBlock",
177 | content: [
178 | {
179 | type: "heading",
180 | attrs: {
181 | level: 3,
182 | },
183 | content: [
184 | {
185 | type: "text",
186 | text: "Videos:",
187 | },
188 | ],
189 | },
190 | ],
191 | },
192 | {
193 | type: "dBlock",
194 | content: [
195 | {
196 | type: "resizableMedia",
197 | attrs: {
198 | src: "https://user-images.githubusercontent.com/45892659/178123048-0257e732-8cc2-466b-8447-1e2b7cd1b5d9.mov",
199 | "media-type": "video",
200 | alt: "Some Video",
201 | title: "Some Title Video",
202 | width: 400,
203 | height: null,
204 | dataAlign: "center",
205 | dataFloat: null,
206 | },
207 | },
208 | ],
209 | },
210 | {
211 | type: "dBlock",
212 | content: [
213 | {
214 | type: "heading",
215 | attrs: {
216 | level: 2,
217 | },
218 | },
219 | ],
220 | },
221 | {
222 | type: "dBlock",
223 | content: [
224 | {
225 | type: "heading",
226 | attrs: {
227 | level: 2,
228 | },
229 | content: [
230 | {
231 | type: "text",
232 | text: "SuperCharged Tables:",
233 | },
234 | ],
235 | },
236 | ],
237 | },
238 | {
239 | type: "dBlock",
240 | content: [
241 | {
242 | type: "table",
243 | content: [
244 | {
245 | type: "tableRow",
246 | content: [
247 | {
248 | type: "tableHeader",
249 | attrs: {
250 | colspan: 1,
251 | rowspan: 1,
252 | colwidth: null,
253 | },
254 | content: [
255 | {
256 | type: "paragraph",
257 | content: [
258 | {
259 | type: "text",
260 | text: "Number",
261 | },
262 | ],
263 | },
264 | ],
265 | },
266 | {
267 | type: "tableHeader",
268 | attrs: {
269 | colspan: 1,
270 | rowspan: 1,
271 | colwidth: null,
272 | },
273 | content: [
274 | {
275 | type: "paragraph",
276 | content: [
277 | {
278 | type: "text",
279 | text: "Name",
280 | },
281 | ],
282 | },
283 | ],
284 | },
285 | {
286 | type: "tableHeader",
287 | attrs: {
288 | colspan: 1,
289 | rowspan: 1,
290 | colwidth: null,
291 | },
292 | content: [
293 | {
294 | type: "paragraph",
295 | content: [
296 | {
297 | type: "text",
298 | text: "Importance",
299 | },
300 | ],
301 | },
302 | ],
303 | },
304 | {
305 | type: "tableHeader",
306 | attrs: {
307 | colspan: 1,
308 | rowspan: 1,
309 | colwidth: null,
310 | },
311 | content: [
312 | {
313 | type: "paragraph",
314 | content: [
315 | {
316 | type: "text",
317 | text: "Reason",
318 | },
319 | ],
320 | },
321 | ],
322 | },
323 | ],
324 | },
325 | {
326 | type: "tableRow",
327 | content: [
328 | {
329 | type: "tableCell",
330 | attrs: {
331 | colspan: 1,
332 | rowspan: 1,
333 | colwidth: null,
334 | },
335 | content: [
336 | {
337 | type: "paragraph",
338 | content: [
339 | {
340 | type: "text",
341 | text: "1",
342 | },
343 | ],
344 | },
345 | ],
346 | },
347 | {
348 | type: "tableCell",
349 | attrs: {
350 | colspan: 1,
351 | rowspan: 1,
352 | colwidth: null,
353 | },
354 | content: [
355 | {
356 | type: "paragraph",
357 | content: [
358 | {
359 | type: "text",
360 | text: "Laptop",
361 | },
362 | ],
363 | },
364 | ],
365 | },
366 | {
367 | type: "tableCell",
368 | attrs: {
369 | colspan: 1,
370 | rowspan: 1,
371 | colwidth: null,
372 | },
373 | content: [
374 | {
375 | type: "paragraph",
376 | content: [
377 | {
378 | type: "text",
379 | text: "High Importance ",
380 | },
381 | ],
382 | },
383 | ],
384 | },
385 | {
386 | type: "tableCell",
387 | attrs: {
388 | colspan: 1,
389 | rowspan: 1,
390 | colwidth: null,
391 | },
392 | content: [
393 | {
394 | type: "paragraph",
395 | content: [
396 | {
397 | type: "text",
398 | text: "Can do almost everything that a watch and a phone can do ",
399 | },
400 | ],
401 | },
402 | ],
403 | },
404 | ],
405 | },
406 | {
407 | type: "tableRow",
408 | content: [
409 | {
410 | type: "tableCell",
411 | attrs: {
412 | colspan: 1,
413 | rowspan: 1,
414 | colwidth: null,
415 | },
416 | content: [
417 | {
418 | type: "paragraph",
419 | content: [
420 | {
421 | type: "text",
422 | text: "2",
423 | },
424 | ],
425 | },
426 | ],
427 | },
428 | {
429 | type: "tableCell",
430 | attrs: {
431 | colspan: 1,
432 | rowspan: 1,
433 | colwidth: null,
434 | },
435 | content: [
436 | {
437 | type: "paragraph",
438 | content: [
439 | {
440 | type: "text",
441 | text: "Mobile",
442 | },
443 | ],
444 | },
445 | ],
446 | },
447 | {
448 | type: "tableCell",
449 | attrs: {
450 | colspan: 1,
451 | rowspan: 1,
452 | colwidth: null,
453 | },
454 | content: [
455 | {
456 | type: "paragraph",
457 | content: [
458 | {
459 | type: "text",
460 | text: "Medium importance",
461 | },
462 | ],
463 | },
464 | ],
465 | },
466 | {
467 | type: "tableCell",
468 | attrs: {
469 | colspan: 1,
470 | rowspan: 1,
471 | colwidth: null,
472 | },
473 | content: [
474 | {
475 | type: "paragraph",
476 | content: [
477 | {
478 | type: "text",
479 | text: "Can do everything a watch can do, but not everything a laptop can do ",
480 | },
481 | ],
482 | },
483 | ],
484 | },
485 | ],
486 | },
487 | {
488 | type: "tableRow",
489 | content: [
490 | {
491 | type: "tableCell",
492 | attrs: {
493 | colspan: 1,
494 | rowspan: 1,
495 | colwidth: null,
496 | },
497 | content: [
498 | {
499 | type: "paragraph",
500 | content: [
501 | {
502 | type: "text",
503 | text: "3",
504 | },
505 | ],
506 | },
507 | ],
508 | },
509 | {
510 | type: "tableCell",
511 | attrs: {
512 | colspan: 1,
513 | rowspan: 1,
514 | colwidth: null,
515 | },
516 | content: [
517 | {
518 | type: "paragraph",
519 | content: [
520 | {
521 | type: "text",
522 | text: "Watch",
523 | },
524 | ],
525 | },
526 | ],
527 | },
528 | {
529 | type: "tableCell",
530 | attrs: {
531 | colspan: 1,
532 | rowspan: 1,
533 | colwidth: null,
534 | },
535 | content: [
536 | {
537 | type: "paragraph",
538 | content: [
539 | {
540 | type: "text",
541 | text: "Low Importance",
542 | },
543 | ],
544 | },
545 | ],
546 | },
547 | {
548 | type: "tableCell",
549 | attrs: {
550 | colspan: 1,
551 | rowspan: 1,
552 | colwidth: null,
553 | },
554 | content: [
555 | {
556 | type: "paragraph",
557 | content: [
558 | {
559 | type: "text",
560 | text: "Can't do everything a phone or a laptop can do.",
561 | },
562 | ],
563 | },
564 | ],
565 | },
566 | ],
567 | },
568 | ],
569 | },
570 | ],
571 | },
572 | {
573 | type: "dBlock",
574 | content: [
575 | {
576 | type: "paragraph",
577 | },
578 | ],
579 | },
580 | {
581 | type: "dBlock",
582 | content: [
583 | {
584 | type: "paragraph",
585 | },
586 | ],
587 | },
588 | {
589 | type: "dBlock",
590 | content: [
591 | {
592 | type: "paragraph",
593 | },
594 | ],
595 | },
596 | {
597 | type: "dBlock",
598 | content: [
599 | {
600 | type: "paragraph",
601 | },
602 | ],
603 | },
604 | {
605 | type: "dBlock",
606 | content: [
607 | {
608 | type: "paragraph",
609 | },
610 | ],
611 | },
612 | {
613 | type: "dBlock",
614 | content: [
615 | {
616 | type: "paragraph",
617 | },
618 | ],
619 | },
620 | {
621 | type: "dBlock",
622 | content: [
623 | {
624 | type: "paragraph",
625 | },
626 | ],
627 | },
628 | {
629 | type: "dBlock",
630 | content: [
631 | {
632 | type: "paragraph",
633 | },
634 | ],
635 | },
636 | {
637 | type: "dBlock",
638 | content: [
639 | {
640 | type: "paragraph",
641 | },
642 | ],
643 | },
644 | {
645 | type: "dBlock",
646 | content: [
647 | {
648 | type: "paragraph",
649 | },
650 | ],
651 | },
652 | {
653 | type: "dBlock",
654 | content: [
655 | {
656 | type: "paragraph",
657 | },
658 | ],
659 | },
660 | ],
661 | };
662 |
--------------------------------------------------------------------------------
/sponsorkit/sponsors.svg:
--------------------------------------------------------------------------------
1 |
2 |
50 |
--------------------------------------------------------------------------------