Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions packages/core/src/editor/transformPasted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,51 @@ import { Fragment, Schema, Slice } from "@tiptap/pm/model";
import { EditorView } from "@tiptap/pm/view";

import { getBlockInfoFromSelection } from "../api/getBlockInfoFromPos.js";
import { findParentNodeClosestToPos } from "@tiptap/core";

/**
* Checks if the current selection is inside a table cell.
* Returns the depth of the tableCell/tableHeader node if found, -1 otherwise.
*/
function isInTableCell(view: EditorView): boolean {
return (
findParentNodeClosestToPos(view.state.selection.$from, (n) => {
return n.type.name === "tableCell" || n.type.name === "tableHeader";
}) !== undefined
);
}

/**
* Converts block content to inline content with hard breaks.
* This is used when pasting into table cells which can only contain inline content.
*/
function convertBlocksToInlineContent(
fragment: Fragment,
schema: Schema,
): Fragment {
const hardBreak = schema.nodes.hardBreak;
let flattenedFragment = Fragment.empty;

fragment.forEach((node) => {
if (node.isInline) {
// This is a paragraph or similar - extract its inline content
flattenedFragment = flattenedFragment.append(
convertBlocksToInlineContent(node.content, schema),
);
// Add hard break after each block (except we'll remove the last one)
flattenedFragment = flattenedFragment.addToEnd(hardBreak.create());
} else if (node.isText) {
flattenedFragment = flattenedFragment.addToEnd(node);
}
});

// Remove the last hard break if present
if (flattenedFragment.lastChild?.type?.name === "hardBreak") {
flattenedFragment.cut(0, flattenedFragment.childCount - 1);
}

return flattenedFragment;
}

// helper function to remove a child from a fragment
function removeChild(node: Fragment, n: number) {
Expand Down Expand Up @@ -65,6 +110,25 @@ export function transformPasted(slice: Slice, view: EditorView) {
let f = Fragment.from(slice.content);
f = wrapTableRows(f, view.state.schema);

if (isInTableCell(view)) {
// If the pasted content has block-level elements, convert to inline content
let hasBlockContent = false;
f.descendants((node) => {
if (node.isInline && node.childCount > 0) {
// This is a paragraph with content
hasBlockContent = true;
}
});

if (hasBlockContent && f.childCount > 1) {
return new Slice(
convertBlocksToInlineContent(f, view.state.schema),
0,
0,
);
}
}

if (!shouldApplyFix(f, view)) {
// Don't apply the fix.
return new Slice(f, slice.openStart, slice.openEnd);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
[
{
"children": [],
"content": {
"columnWidths": [
undefined,
],
"headerCols": undefined,
"headerRows": undefined,
"rows": [
{
"cells": [
{
"content": [
{
"styles": {},
"text": "Cell 1Paragraph 1",
"type": "text",
},
],
"props": {
"backgroundColor": "default",
"colspan": 1,
"rowspan": 1,
"textAlignment": "left",
"textColor": "default",
},
"type": "tableCell",
},
],
},
],
"type": "tableContent",
},
"id": "1",
"props": {
"textColor": "default",
},
"type": "table",
},
{
"children": [],
"content": [
{
"styles": {},
"text": "Paragraph 2",
"type": "text",
},
],
"id": "2",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
"textColor": "default",
},
"type": "paragraph",
},
{
"children": [],
"content": [
{
"styles": {},
"text": "Paragraph 3",
"type": "text",
},
],
"id": "3",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
"textColor": "default",
},
"type": "paragraph",
},
{
"children": [],
"content": {
"columnWidths": [
undefined,
],
"headerCols": undefined,
"headerRows": undefined,
"rows": [
{
"cells": [
{
"content": [
{
"styles": {},
"text": "Cell 2",
"type": "text",
},
],
"props": {
"backgroundColor": "default",
"colspan": 1,
"rowspan": 1,
"textAlignment": "left",
"textColor": "default",
},
"type": "tableCell",
},
],
},
],
"type": "tableContent",
},
"id": "4",
"props": {
"textColor": "default",
},
"type": "table",
},
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[
{
"children": [],
"content": {
"columnWidths": [
undefined,
undefined,
],
"headerCols": undefined,
"headerRows": undefined,
"rows": [
{
"cells": [
{
"content": [
{
"styles": {},
"text": "Cell 1<p>Paragraph 1</p><p>Paragraph 2</p><p>Paragraph 3</p>",
"type": "text",
},
],
"props": {
"backgroundColor": "default",
"colspan": 1,
"rowspan": 1,
"textAlignment": "left",
"textColor": "default",
},
"type": "tableCell",
},
{
"content": [
{
"styles": {},
"text": "Cell 2",
"type": "text",
},
],
"props": {
"backgroundColor": "default",
"colspan": 1,
"rowspan": 1,
"textAlignment": "left",
"textColor": "default",
},
"type": "tableCell",
},
],
},
],
"type": "tableContent",
},
"id": "1",
"props": {
"textColor": "default",
},
"type": "table",
},
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
[
{
"children": [],
"content": {
"columnWidths": [
undefined,
],
"headerCols": undefined,
"headerRows": undefined,
"rows": [
{
"cells": [
{
"content": [
{
"styles": {},
"text": "Cell 1Line 1",
"type": "text",
},
],
"props": {
"backgroundColor": "default",
"colspan": 1,
"rowspan": 1,
"textAlignment": "left",
"textColor": "default",
},
"type": "tableCell",
},
],
},
],
"type": "tableContent",
},
"id": "1",
"props": {
"textColor": "default",
},
"type": "table",
},
{
"children": [],
"content": [
{
"styles": {},
"text": "Line 2",
"type": "text",
},
],
"id": "2",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
"textColor": "default",
},
"type": "paragraph",
},
{
"children": [],
"content": [
{
"styles": {},
"text": "Line 3",
"type": "text",
},
],
"id": "3",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
"textColor": "default",
},
"type": "paragraph",
},
{
"children": [],
"content": {
"columnWidths": [
undefined,
],
"headerCols": undefined,
"headerRows": undefined,
"rows": [
{
"cells": [
{
"content": [
{
"styles": {},
"text": "Cell 2",
"type": "text",
},
],
"props": {
"backgroundColor": "default",
"colspan": 1,
"rowspan": 1,
"textAlignment": "left",
"textColor": "default",
},
"type": "tableCell",
},
],
},
],
"type": "tableContent",
},
"id": "4",
"props": {
"textColor": "default",
},
"type": "table",
},
]
Loading
Loading