Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/table #3090

Merged
merged 13 commits into from
Apr 2, 2024
Merged
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
5 changes: 5 additions & 0 deletions .changeset/swift-eggs-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@udecode/plate-table": patch
---

fix can not remove table column when selection is expanded
6 changes: 6 additions & 0 deletions packages/table/src/merge/deleteColumn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
getAboveNode,
getPluginOptions,
getPluginType,
isExpanded,
PlateEditor,
removeNodes,
setNodes,
Expand All @@ -15,6 +16,7 @@ import { ELEMENT_TABLE, ELEMENT_TR } from '../createTablePlugin';
import { getColSpan } from '../queries/getColSpan';
import { TablePlugin, TTableCellElement, TTableElement } from '../types';
import { getCellTypes } from '../utils';
import { deleteColumnWhenExpanded } from './deleteColumnWhenExpanded';
import { findCellByIndexes } from './findCellByIndexes';
import { getCellIndices } from './getCellIndices';
import { getCellPath } from './getCellPath';
Expand All @@ -36,6 +38,10 @@ export const deleteTableMergeColumn = <V extends Value>(
match: { type: getPluginType(editor, ELEMENT_TABLE) },
});
if (!tableEntry) return;

if (isExpanded(editor.selection))
return deleteColumnWhenExpanded(editor, tableEntry);

const table = tableEntry[0] as TTableElement;

const selectedCellEntry = getAboveNode(editor, {
Expand Down
59 changes: 59 additions & 0 deletions packages/table/src/merge/deleteColumnWhenExpanded.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
createPathRef,
getAboveNode,
PlateEditor,
removeNodes,
TNodeEntry,
Value,
} from '@udecode/plate-common';
import { Node, PathRef, Range } from 'slate';

import { ELEMENT_TR } from '../createTablePlugin';
import { getTableGridAbove } from '../queries';
import { TTableCellElement } from '../types';

export const deleteColumnWhenExpanded = <V extends Value>(
editor: PlateEditor<V>,
tableEntry: TNodeEntry<TTableCellElement>
) => {
const [start, end] = Range.edges(editor.selection!);
const firstRow = Node.child(tableEntry[0], 0) as TTableCellElement;
const lastRow = Node.child(
tableEntry[0],
tableEntry[0].children.length - 1
) as TTableCellElement;

const firstSelectionRow = getAboveNode(editor, {
at: start,
match: (n) => n.type === ELEMENT_TR,
});

const lastSelectionRow = getAboveNode(editor, {
at: end,
match: (n) => n.type === ELEMENT_TR,
});

if (!firstSelectionRow || !lastSelectionRow) return;

if (
firstRow.id === firstSelectionRow[0].id &&
lastSelectionRow[0].id === lastRow.id
)
deleteSelection(editor);
};

const deleteSelection = <V extends Value>(editor: PlateEditor<V>) => {
const cells = getTableGridAbove(editor, {
format: 'cell',
}) as TNodeEntry<TTableCellElement>[];

const pathRefs: PathRef[] = [];

cells.forEach(([cell, cellPath], index) => {
pathRefs.push(createPathRef(editor, cellPath));
});

pathRefs.forEach((pathRef) => {
removeNodes(editor, { at: pathRef.unref()! });
});
};
6 changes: 6 additions & 0 deletions packages/table/src/merge/deleteRow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getPluginOptions,
getPluginType,
insertElements,
isExpanded,
PlateEditor,
removeNodes,
setNodes,
Expand All @@ -21,6 +22,7 @@ import {
TTableRowElement,
} from '../types';
import { getCellTypes } from '../utils';
import { deleteRowWhenExpanded } from './deleteRowWhenExpanded';
import { findCellByIndexes } from './findCellByIndexes';
import { getCellIndices } from './getCellIndices';

Expand All @@ -41,6 +43,10 @@ export const deleteTableMergeRow = <V extends Value>(
match: { type: getPluginType(editor, ELEMENT_TABLE) },
});
if (!currentTableItem) return;

if (isExpanded(editor.selection))
return deleteRowWhenExpanded(editor, currentTableItem);

const table = currentTableItem[0] as TTableElement;

const selectedCellEntry = getAboveNode(editor, {
Expand Down
67 changes: 67 additions & 0 deletions packages/table/src/merge/deleteRowWhenExpanded.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
createPathRef,
PlateEditor,
removeNodes,
TNodeEntry,
Value,
} from '@udecode/plate-common';
import { PathRef } from 'slate';

import { getRowSpan, getTableGridAbove } from '../queries';
import { TTableCellElement } from '../types';
import { getCellRowIndexByPath } from '../utils/getCellRowIndexByPath';
import { getTableMergedColumnCount } from './getTableMergedColumnCount';

export const deleteRowWhenExpanded = <V extends Value>(
editor: PlateEditor<V>,
[table, tablePath]: TNodeEntry<TTableCellElement>
) => {
const columnCount = getTableMergedColumnCount(table);

const cells = getTableGridAbove(editor, {
format: 'cell',
}) as TNodeEntry<TTableCellElement>[];

const firsRowIndex = getCellRowIndexByPath(cells[0][1]);

if (firsRowIndex === null) return;

let acrossColumn = 0;
let lastRowIndex = -1;
let rowSpanCarry = 0;
let acrossRow = 0;

cells.forEach(([cell, cellPath], index) => {
if (cellPath.at(-2) === firsRowIndex) {
acrossColumn += cell.colSpan ?? 1;
}

const currentRowIndex = getCellRowIndexByPath(cellPath);

if (lastRowIndex !== currentRowIndex) {
if (rowSpanCarry !== 0) {
rowSpanCarry--;
return;
}

const rowSpan = getRowSpan(cell);

rowSpanCarry = rowSpan && rowSpan > 1 ? rowSpan - 1 : 0;
acrossRow += rowSpan ?? 1;
}

lastRowIndex = currentRowIndex;
});

if (acrossColumn === columnCount) {
const pathRefs: PathRef[] = [];
for (let i = firsRowIndex; i < firsRowIndex + acrossRow; i++) {
const removedPath = tablePath.concat(i);
pathRefs.push(createPathRef(editor, removedPath));
}

pathRefs.forEach((item) => {
removeNodes(editor, { at: item.unref()! });
});
}
};
29 changes: 29 additions & 0 deletions packages/table/src/merge/getSelectionWidth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Path } from 'slate';

import { TTableCellElement } from '../types';
import { getCellRowIndexByPath } from '../utils/getCellRowIndexByPath';

export const getSelectionWidth = <T extends [TTableCellElement, Path]>(
cells: T[]
) => {
// default = firstRowIndex

let max = 0;
let lastCellRowIndex = getCellRowIndexByPath(cells[0][1]);
let total = 0;
cells.forEach(([cell, cellPath]) => {
const currentCellRowIndex = getCellRowIndexByPath(cellPath);
// on the same line
if (currentCellRowIndex === lastCellRowIndex) {
const colSpan = cell.colSpan ?? cell.attributes?.colspan;
const colSpanNumbered = colSpan ? Number(colSpan) : 1;
total += colSpanNumbered;
} else {
max = Math.max(total, max);
// easy to error
total = 0;
}
lastCellRowIndex = currentCellRowIndex;
});
return Math.max(total, max);
};
11 changes: 11 additions & 0 deletions packages/table/src/merge/getTableMergedColumnCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { TElement } from '@udecode/plate-common';

import { getColSpan } from '../queries';

export const getTableMergedColumnCount = (tableNode: TElement) => {
return (tableNode.children as TElement[])?.[0]?.children?.reduce(
// @ts-ignore
(prev, cur) => prev + (getColSpan(cur) ?? 1),
0
);
};
138 changes: 74 additions & 64 deletions packages/table/src/transforms/deleteColumn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
getAboveNode,
getPluginOptions,
getPluginType,
isExpanded,
PlateEditor,
removeNodes,
setNodes,
Expand All @@ -18,6 +19,7 @@ import {
ELEMENT_TR,
} from '../createTablePlugin';
import { deleteTableMergeColumn } from '../merge/deleteColumn';
import { deleteColumnWhenExpanded } from '../merge/deleteColumnWhenExpanded';
import { TablePlugin, TTableElement } from '../types';

export const deleteColumn = <V extends Value>(editor: PlateEditor<V>) => {
Expand All @@ -30,75 +32,83 @@ export const deleteColumn = <V extends Value>(editor: PlateEditor<V>) => {
}

if (
someNode(editor, {
!someNode(editor, {
match: { type: getPluginType(editor, ELEMENT_TABLE) },
})
) {
const tdEntry = getAboveNode(editor, {
match: {
type: [
getPluginType(editor, ELEMENT_TD),
getPluginType(editor, ELEMENT_TH),
],
},
});
const trEntry = getAboveNode(editor, {
match: { type: getPluginType(editor, ELEMENT_TR) },
});
const tableEntry = getAboveNode<TTableElement>(editor, {
match: { type: getPluginType(editor, ELEMENT_TABLE) },
});
return;
}

if (
tdEntry &&
trEntry &&
tableEntry &&
// Cannot delete the last cell
trEntry[0].children.length > 1
) {
const [tableNode, tablePath] = tableEntry;

const tdPath = tdEntry[1];
const colIndex = tdPath.at(-1)!;

const pathToDelete = tdPath.slice();
const replacePathPos = pathToDelete.length - 2;

withoutNormalizing(editor, () => {
tableNode.children.forEach((row, rowIdx) => {
pathToDelete[replacePathPos] = rowIdx;

// for tables containing rows of different lengths
// - don't delete if only one cell in row
// - don't delete if row doesn't have this cell
if (
(row.children as TElement[]).length === 1 ||
colIndex > (row.children as TElement[]).length - 1
)
return;

removeNodes(editor, {
at: pathToDelete,
});
});
const tableEntry = getAboveNode<TTableElement>(editor, {
match: { type: getPluginType(editor, ELEMENT_TABLE) },
});

const { colSizes } = tableNode;

if (colSizes) {
const newColSizes = [...colSizes];
newColSizes.splice(colIndex, 1);

setNodes<TTableElement>(
editor,
{
colSizes: newColSizes,
},
{
at: tablePath,
}
);
}
if (!tableEntry) return;

if (isExpanded(editor.selection))
return deleteColumnWhenExpanded(editor, tableEntry);

const tdEntry = getAboveNode(editor, {
match: {
type: [
getPluginType(editor, ELEMENT_TD),
getPluginType(editor, ELEMENT_TH),
],
},
});
const trEntry = getAboveNode(editor, {
match: { type: getPluginType(editor, ELEMENT_TR) },
});

if (
tdEntry &&
trEntry &&
tableEntry &&
// Cannot delete the last cell
trEntry[0].children.length > 1
) {
const [tableNode, tablePath] = tableEntry;

const tdPath = tdEntry[1];
const colIndex = tdPath.at(-1)!;

const pathToDelete = tdPath.slice();
const replacePathPos = pathToDelete.length - 2;

withoutNormalizing(editor, () => {
tableNode.children.forEach((row, rowIdx) => {
pathToDelete[replacePathPos] = rowIdx;

// for tables containing rows of different lengths
// - don't delete if only one cell in row
// - don't delete if row doesn't have this cell
if (
(row.children as TElement[]).length === 1 ||
colIndex > (row.children as TElement[]).length - 1
)
return;

removeNodes(editor, {
at: pathToDelete,
});
});
}

const { colSizes } = tableNode;

if (colSizes) {
const newColSizes = [...colSizes];
newColSizes.splice(colIndex, 1);

setNodes<TTableElement>(
editor,
{
colSizes: newColSizes,
},
{
at: tablePath,
}
);
}
});
}
};
Loading