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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions apps/api/plane/utils/content_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,6 @@ def validate_binary_data(data):
"rowspan",
"colwidth",
"background",
"hideContent",
"hidecontent",
"style",
},
"td": {
Expand All @@ -150,8 +148,6 @@ def validate_binary_data(data):
"background",
"textColor",
"textcolor",
"hideContent",
"hidecontent",
"style",
},
"tr": {"background", "textColor", "textcolor", "style"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
updateColDragMarker,
updateColDropMarker,
} from "../marker-utils";
import { updateCellContentVisibility } from "../utils";
import { showCellContent } from "../utils";
import { ColumnOptionsDropdown } from "./dropdown";
import { calculateColumnDropIndex, constructColumnDragPreview, getTableColumnNodesInfo } from "./utils";

Expand Down Expand Up @@ -152,8 +152,9 @@ export function ColumnDragHandle(props: ColumnDragHandleProps) {
hideDropMarker(dropMarker);
hideDragMarker(dragMarker);

// Show cell content by clearing decorations
if (isCellSelection(editor.state.selection)) {
updateCellContentVisibility(editor, false);
showCellContent(editor);
}

if (col !== dropIndex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { TableMap } from "@tiptap/pm/tables";
import { getSelectedRect, isCellSelection } from "@/extensions/table/table/utilities/helpers";
import type { TableNodeLocation } from "@/extensions/table/table/utilities/helpers";
// local imports
import { cloneTableCell, constructDragPreviewTable, updateCellContentVisibility } from "../utils";
import { cloneTableCell, constructDragPreviewTable, getSelectedCellPositions, hideCellContent } from "../utils";

type TableColumn = {
left: number;
Expand Down Expand Up @@ -151,7 +151,9 @@ export const constructColumnDragPreview = (
}
});

updateCellContentVisibility(editor, true);
// Hide the selected cells using decorations (local only, not persisted)
const cellPositions = getSelectedCellPositions(selection, table);
hideCellContent(editor, cellPositions);

return tableElement;
};
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
updateRowDragMarker,
updateRowDropMarker,
} from "../marker-utils";
import { updateCellContentVisibility } from "../utils";
import { showCellContent } from "../utils";
import { RowOptionsDropdown } from "./dropdown";
import { calculateRowDropIndex, constructRowDragPreview, getTableRowNodesInfo } from "./utils";

Expand Down Expand Up @@ -152,8 +152,9 @@ export function RowDragHandle(props: RowDragHandleProps) {
hideDropMarker(dropMarker);
hideDragMarker(dragMarker);

// Show cell content by clearing decorations
if (isCellSelection(editor.state.selection)) {
updateCellContentVisibility(editor, false);
showCellContent(editor);
}

if (row !== dropIndex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { TableMap } from "@tiptap/pm/tables";
import { getSelectedRect, isCellSelection } from "@/extensions/table/table/utilities/helpers";
import type { TableNodeLocation } from "@/extensions/table/table/utilities/helpers";
// local imports
import { cloneTableCell, constructDragPreviewTable, updateCellContentVisibility } from "../utils";
import { cloneTableCell, constructDragPreviewTable, getSelectedCellPositions, hideCellContent } from "../utils";

type TableRow = {
top: number;
Expand Down Expand Up @@ -150,7 +150,9 @@ export const constructRowDragPreview = (
}
});

updateCellContentVisibility(editor, true);
// Hide the selected cells using decorations (local only, not persisted)
const cellPositions = getSelectedCellPositions(selection, table);
hideCellContent(editor, cellPositions);

return tableElement;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@
*/

import type { Editor } from "@tiptap/core";
import type { Selection } from "@tiptap/pm/state";
import { TableMap } from "@tiptap/pm/tables";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
import { CORE_EDITOR_META } from "@/constants/meta";
// extensions
import { getSelectedRect, isCellSelection } from "@/extensions/table/table/utilities/helpers";
import type { TableNodeLocation } from "@/extensions/table/table/utilities/helpers";
// local imports
import { updateTransactionMeta } from "../drag-state";

/**
* @description Construct a pseudo table element which will act as a parent for column and row drag previews.
Expand Down Expand Up @@ -47,20 +53,41 @@ export const cloneTableCell = (
};

/**
* @description This function updates the `hideContent` attribute of the table cells and headers.
* @description Get positions of all cells in the current selection.
* @param {Selection} selection - The selection.
* @param {TableNodeLocation} table - The table node location.
* @returns {number[]} Array of cell positions.
*/
export const getSelectedCellPositions = (selection: Selection, table: TableNodeLocation): number[] => {
if (!isCellSelection(selection)) return [];

const tableMap = TableMap.get(table.node);
const selectedRect = getSelectedRect(selection, tableMap);
const cellsInSelection = tableMap.cellsInRect(selectedRect);

// Convert relative positions to absolute document positions
return cellsInSelection.map((cellPos) => table.start + cellPos);
};

/**
* @description Hide cell content using decorations (local only, not persisted).
* @param {Editor} editor - The editor instance.
* @param {boolean} hideContent - Whether to hide the content.
* @returns {boolean} Whether the content visibility was updated.
* @param {number[]} cellPositions - Array of cell positions to hide.
*/
export const updateCellContentVisibility = (editor: Editor, hideContent: boolean): boolean =>
editor
.chain()
.focus()
.setMeta(CORE_EDITOR_META.ADD_TO_HISTORY, false)
.updateAttributes(CORE_EXTENSIONS.TABLE_CELL, {
hideContent,
})
.updateAttributes(CORE_EXTENSIONS.TABLE_HEADER, {
hideContent,
})
.run();
export const hideCellContent = (editor: Editor, cellPositions: number[]): void => {
const tr = editor.view.state.tr;
updateTransactionMeta(tr, cellPositions);
tr.setMeta(CORE_EDITOR_META.ADD_TO_HISTORY, false);
editor.view.dispatch(tr);
};

/**
* @description Show cell content by clearing decorations.
* @param {Editor} editor - The editor instance.
*/
export const showCellContent = (editor: Editor): void => {
const tr = editor.view.state.tr;
updateTransactionMeta(tr, null);
tr.setMeta(CORE_EDITOR_META.ADD_TO_HISTORY, true);
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

showCellContent sets CORE_EDITOR_META.ADD_TO_HISTORY to true, whereas hideCellContent sets it to false and the previous updateCellContentVisibility helper always disabled history for these drag-only visibility changes. This makes the "show" operation behave differently from the previous implementation and from the "hide" operation, and may cause purely visual decoration updates to be recorded in the undo history. To keep drag-preview visibility changes invisible to undo/redo (as before), consider setting ADD_TO_HISTORY to false here as well.

Suggested change
tr.setMeta(CORE_EDITOR_META.ADD_TO_HISTORY, true);
tr.setMeta(CORE_EDITOR_META.ADD_TO_HISTORY, false);
Copilot uses AI. Check for mistakes.
editor.view.dispatch(tr);
};
64 changes: 64 additions & 0 deletions packages/editor/src/core/extensions/table/plugins/drag-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/

import type { Transaction } from "@tiptap/pm/state";
import { Plugin, PluginKey } from "@tiptap/pm/state";
import { Decoration, DecorationSet } from "@tiptap/pm/view";

const TABLE_DRAG_STATE_PLUGIN_KEY = new PluginKey("tableDragState");

export const updateTransactionMeta = (tr: Transaction, hiddenCellPositions: number[] | null) => {
tr.setMeta(TABLE_DRAG_STATE_PLUGIN_KEY, hiddenCellPositions);
};

/**
* @description Plugin to manage table drag state using decorations.
* This allows hiding cell content during drag operations without modifying the document.
* Decorations are local to each user and not persisted or shared.
*/
export const TableDragStatePlugin = new Plugin({
key: TABLE_DRAG_STATE_PLUGIN_KEY,
state: {
init() {
return DecorationSet.empty;
},
apply(tr, oldState) {
// Get metadata about which cells to hide
const hiddenCellPositions = tr.getMeta(TABLE_DRAG_STATE_PLUGIN_KEY) as number[] | null;

if (hiddenCellPositions === undefined) {
// No change, map decorations through the transaction
return oldState.map(tr.mapping, tr.doc);
}

if (hiddenCellPositions === null || !Array.isArray(hiddenCellPositions) || hiddenCellPositions.length === 0) {
// Clear all decorations
return DecorationSet.empty;
}

// Create decorations for hidden cells
const decorations: Decoration[] = [];
hiddenCellPositions.forEach((pos) => {
if (typeof pos !== "number") return;
const node = tr.doc.nodeAt(pos);
if (node) {
decorations.push(
Decoration.node(pos, pos + node.nodeSize, {
class: "content-hidden",
})
);
}
});

return DecorationSet.create(tr.doc, decorations);
},
},
props: {
decorations(state) {
return this.getState(state);
},
},
});
4 changes: 0 additions & 4 deletions packages/editor/src/core/extensions/table/table-cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ export const TableCell = Node.create<TableCellOptions>({
textColor: {
default: null,
},
hideContent: {
default: false,
},
};
},

Expand Down Expand Up @@ -116,7 +113,6 @@ export const TableCell = Node.create<TableCellOptions>({
return [
"td",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
class: node.attrs.hideContent ? "content-hidden" : "",
style: `background-color: ${node.attrs.background}; color: ${node.attrs.textColor};`,
}),
0,
Expand Down
4 changes: 0 additions & 4 deletions packages/editor/src/core/extensions/table/table-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ export const TableHeader = Node.create<TableHeaderOptions>({
background: {
default: "none",
},
hideContent: {
default: false,
},
};
},

Expand All @@ -63,7 +60,6 @@ export const TableHeader = Node.create<TableHeaderOptions>({
return [
"th",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
class: node.attrs.hideContent ? "content-hidden" : "",
style: `background-color: ${node.attrs.background};`,
}),
0,
Expand Down
2 changes: 2 additions & 0 deletions packages/editor/src/core/extensions/table/table/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
// local imports
import { TableDragStatePlugin } from "../plugins/drag-state";
import { TableColumnDragHandlePlugin } from "../plugins/drag-handles/column/plugin";
import { TableRowDragHandlePlugin } from "../plugins/drag-handles/row/plugin";
import { TableInsertPlugin } from "../plugins/insert-handlers/plugin";
Expand Down Expand Up @@ -281,6 +282,7 @@ export const Table = Node.create<TableOptions>({
tableEditing({
allowTableNodeSelection: this.options.allowTableNodeSelection,
}),
TableDragStatePlugin,
TableInsertPlugin(this.editor),
TableColumnDragHandlePlugin(this.editor),
TableRowDragHandlePlugin(this.editor),
Expand Down
Loading