feat: Add table sorting controls (#6678)

* wip

* refactor

* fix: Missing shadow styling
This commit is contained in:
Tom Moor
2024-03-14 20:21:56 -06:00
committed by GitHub
parent 04c4d787ff
commit 1a386c9900
10 changed files with 229 additions and 92 deletions

View File

@@ -14,7 +14,27 @@ import {
} from "prosemirror-tables";
import { getCellsInColumn } from "../queries/table";
export function createTable(
export function createTable({
rowsCount,
colsCount,
}: {
rowsCount: number;
colsCount: number;
}): Command {
return (state, dispatch) => {
if (dispatch) {
const offset = state.tr.selection.anchor + 1;
const nodes = createTableInner(state, rowsCount, colsCount);
const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView();
const resolvedPos = tr.doc.resolve(offset);
tr.setSelection(TextSelection.near(resolvedPos));
dispatch(tr);
}
return true;
};
}
export function createTableInner(
state: EditorState,
rowsCount: number,
colsCount: number,
@@ -62,6 +82,97 @@ export function createTable(
return types.table.createChecked(null, rows);
}
export function sortTable({
index,
direction,
}: {
index: number;
direction: "asc" | "desc";
}): Command {
return (state, dispatch) => {
if (!isInTable(state)) {
return false;
}
if (dispatch) {
const rect = selectedRect(state);
const table: Node[][] = [];
for (let r = 0; r < rect.map.height; r++) {
const cells = [];
for (let c = 0; c < rect.map.width; c++) {
const cell = state.doc.nodeAt(
rect.tableStart + rect.map.map[r * rect.map.width + c]
);
if (cell) {
cells.push(cell);
}
}
table.push(cells);
}
// check if all the cells in the column are a number
const compareAsText = table.some((row) => {
const cell = row[index]?.textContent;
return cell === "" ? false : isNaN(parseFloat(cell));
});
// remove the header row
const header = table.shift();
// sort table data based on column at index
table.sort((a, b) => {
if (compareAsText) {
return (a[index]?.textContent ?? "").localeCompare(
b[index]?.textContent ?? ""
);
} else {
return (
parseFloat(a[index]?.textContent ?? "") -
parseFloat(b[index]?.textContent ?? "")
);
}
});
if (direction === "desc") {
table.reverse();
}
// add the header row back
if (header) {
table.unshift(header);
}
// create the new table
const rows = [];
for (let i = 0; i < table.length; i += 1) {
rows.push(state.schema.nodes.tr.createChecked(null, table[i]));
}
// replace the original table with this sorted one
const nodes = state.schema.nodes.table.createChecked(null, rows);
const { tr } = state;
tr.replaceRangeWith(
rect.tableStart - 1,
rect.tableStart - 1 + rect.table.nodeSize,
nodes
);
dispatch(
tr
// .setSelection(
// CellSelection.create(
// tr.doc,
// rect.map.positionAt(0, index, rect.table)
// )
// )
.scrollIntoView()
);
}
return true;
};
}
export function addRowAndMoveSelection({
index,
}: {

View File

@@ -1274,6 +1274,10 @@ table {
border-bottom: 1px solid ${props.theme.tableDivider};
}
tr:first-of-type {
background: ${props.theme.secondaryBackground};
}
th {
background: transparent;
}
@@ -1436,7 +1440,8 @@ table {
top: 0;
bottom: 0;
${props.rtl ? "right" : "left"}: -1em;
width: 16px;
width: 32px;
z-index: 1;
transition: box-shadow 250ms ease-in-out;
border: 0px solid transparent;
border-${props.rtl ? "right" : "left"}-width: 1em;

View File

@@ -1,6 +1,6 @@
import { chainCommands } from "prosemirror-commands";
import { NodeSpec, Node as ProsemirrorNode } from "prosemirror-model";
import { Command, Plugin, TextSelection } from "prosemirror-state";
import { Plugin } from "prosemirror-state";
import {
addColumnAfter,
addColumnBefore,
@@ -18,6 +18,7 @@ import {
addRowAndMoveSelection,
setColumnAttr,
createTable,
sortTable,
} from "../commands/table";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import tablesRule from "../rules/tables";
@@ -55,26 +56,9 @@ export default class Table extends Node {
commands() {
return {
createTable:
({
rowsCount,
colsCount,
}: {
rowsCount: number;
colsCount: number;
}): Command =>
(state, dispatch) => {
if (dispatch) {
const offset = state.tr.selection.anchor + 1;
const nodes = createTable(state, rowsCount, colsCount);
const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView();
const resolvedPos = tr.doc.resolve(offset);
tr.setSelection(TextSelection.near(resolvedPos));
dispatch(tr);
}
return true;
},
createTable,
setColumnAttr,
sortTable,
addColumnBefore: () => addColumnBefore,
addColumnAfter: () => addColumnAfter,
deleteColumn: () => deleteColumn,

View File

@@ -21,6 +21,7 @@ export type MenuItem = {
keywords?: string;
tooltip?: string;
label?: string;
dangerous?: boolean;
children?: MenuItem[];
defaultHidden?: boolean;
attrs?: