feat: Add move commands for columns and rows (#10143)

* feat: Add move commands for columns and rows

closes #7673

* Reuse icon
This commit is contained in:
Tom Moor
2025-09-10 03:28:17 +02:00
committed by GitHub
parent 19f9245e17
commit a35d84976e
9 changed files with 85 additions and 30 deletions

View File

@@ -0,0 +1,16 @@
import { ArrowIcon as ArrowRightIcon } from "outline-icons";
import styled from "styled-components";
export { ArrowIcon as ArrowRightIcon } from "outline-icons";
export const ArrowUpIcon = styled(ArrowRightIcon)`
transform: rotate(-90deg);
`;
export const ArrowDownIcon = styled(ArrowRightIcon)`
transform: rotate(90deg);
`;
export const ArrowLeftIcon = styled(ArrowRightIcon)`
transform: rotate(180deg);
`;

View File

@@ -5,15 +5,15 @@ import {
AlignCenterIcon,
InsertLeftIcon,
InsertRightIcon,
ArrowIcon,
MoreIcon,
TableHeaderColumnIcon,
TableMergeCellsIcon,
TableSplitCellsIcon,
AlphabeticalSortIcon,
AlphabeticalReverseSortIcon,
} from "outline-icons";
import { EditorState } from "prosemirror-state";
import { CellSelection } from "prosemirror-tables";
import styled from "styled-components";
import { CellSelection, selectedRect } from "prosemirror-tables";
import { isNodeActive } from "@shared/editor/queries/isNodeActive";
import {
isMergedCellSelection,
@@ -21,6 +21,7 @@ import {
} from "@shared/editor/queries/table";
import { MenuItem } from "@shared/editor/types";
import { Dictionary } from "~/hooks/useDictionary";
import { ArrowLeftIcon, ArrowRightIcon } from "~/components/Icons/ArrowIcon";
export default function tableColMenuItems(
state: EditorState,
@@ -34,6 +35,8 @@ export default function tableColMenuItems(
return [];
}
const tableMap = selectedRect(state);
return [
{
name: "setColumnAttr",
@@ -75,13 +78,13 @@ export default function tableColMenuItems(
name: "sortTable",
tooltip: dictionary.sortAsc,
attrs: { index, direction: "asc" },
icon: <SortAscIcon />,
icon: <AlphabeticalSortIcon />,
},
{
name: "sortTable",
tooltip: dictionary.sortDesc,
attrs: { index, direction: "desc" },
icon: <SortDescIcon />,
icon: <AlphabeticalReverseSortIcon />,
},
{
name: "separator",
@@ -107,6 +110,23 @@ export default function tableColMenuItems(
icon: <InsertRightIcon />,
attrs: { index },
},
{
name: "moveTableColumn",
label: dictionary.moveColumnLeft,
icon: <ArrowLeftIcon />,
attrs: { from: index, to: index - 1 },
visible: index > 0,
},
{
name: "moveTableColumn",
label: dictionary.moveColumnRight,
icon: <ArrowRightIcon />,
attrs: { from: index, to: index + 1 },
visible: index < tableMap.map.width - 1,
},
{
name: "separator",
},
{
name: "mergeCells",
label: dictionary.mergeCells,
@@ -132,11 +152,3 @@ export default function tableColMenuItems(
},
];
}
const SortAscIcon = styled(ArrowIcon)`
transform: rotate(-90deg);
`;
const SortDescIcon = styled(ArrowIcon)`
transform: rotate(90deg);
`;

View File

@@ -8,13 +8,14 @@ import {
TableMergeCellsIcon,
} from "outline-icons";
import { EditorState } from "prosemirror-state";
import { CellSelection } from "prosemirror-tables";
import { CellSelection, selectedRect } from "prosemirror-tables";
import {
isMergedCellSelection,
isMultipleCellSelection,
} from "@shared/editor/queries/table";
import { MenuItem } from "@shared/editor/types";
import { Dictionary } from "~/hooks/useDictionary";
import { ArrowDownIcon, ArrowUpIcon } from "~/components/Icons/ArrowIcon";
export default function tableRowMenuItems(
state: EditorState,
@@ -22,10 +23,13 @@ export default function tableRowMenuItems(
dictionary: Dictionary
): MenuItem[] {
const { selection } = state;
if (!(selection instanceof CellSelection)) {
return [];
}
const tableMap = selectedRect(state);
return [
{
icon: <MoreIcon />,
@@ -48,6 +52,23 @@ export default function tableRowMenuItems(
icon: <InsertBelowIcon />,
attrs: { index },
},
{
name: "moveTableRow",
label: dictionary.moveRowUp,
icon: <ArrowUpIcon />,
attrs: { from: index, to: index - 1 },
visible: index > 0,
},
{
name: "moveTableRow",
label: dictionary.moveRowDown,
icon: <ArrowDownIcon />,
attrs: { from: index, to: index + 1 },
visible: index < tableMap.map.height - 1,
},
{
name: "separator",
},
{
name: "mergeCells",
label: dictionary.mergeCells,

View File

@@ -11,10 +11,14 @@ export default function useDictionary() {
return useMemo(
() => ({
addColumnAfter: t("Add column after"),
addColumnBefore: t("Add column before"),
addRowAfter: t("Add row after"),
addRowBefore: t("Add row before"),
addColumnAfter: t("Insert after"),
addColumnBefore: t("Insert before"),
moveRowUp: t("Move up"),
moveRowDown: t("Move down"),
moveColumnLeft: t("Move left"),
moveColumnRight: t("Move right"),
addRowAfter: t("Insert after"),
addRowBefore: t("Insert before"),
alignCenter: t("Align center"),
alignLeft: t("Align left"),
alignRight: t("Align right"),

View File

@@ -1,6 +1,5 @@
import { AnimatePresence } from "framer-motion";
import { observer } from "mobx-react";
import { ArrowIcon } from "outline-icons";
import { useRef, useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useRouteMatch } from "react-router-dom";
@@ -24,6 +23,7 @@ import CommentForm from "./CommentForm";
import CommentSortMenu from "./CommentSortMenu";
import CommentThread from "./CommentThread";
import Sidebar from "./SidebarLayout";
import { ArrowDownIcon } from "~/components/Icons/ArrowIcon";
function Comments() {
const { ui, comments, documents } = useStores();
@@ -230,10 +230,6 @@ const JumpToRecent = styled(ButtonSmall)`
}
`;
const ArrowDownIcon = styled(ArrowIcon)`
transform: rotate(90deg);
`;
const NewCommentForm = styled(CommentForm)<{ dir?: "ltr" | "rtl" }>`
padding: 12px;
padding-right: ${(props) => (props.dir !== "rtl" ? "18px" : "12px")};

View File

@@ -2,10 +2,9 @@ import { PluginSimple } from "markdown-it";
import { InputRule } from "prosemirror-inputrules";
import { NodeType, MarkType, Schema } from "prosemirror-model";
import { Command, Plugin } from "prosemirror-state";
import { Primitive } from "utility-types";
import type { Editor } from "../../../app/editor";
export type CommandFactory = (attrs?: Record<string, Primitive>) => Command;
export type CommandFactory = (attrs?: unknown) => Command;
export type WidgetProps = { rtl: boolean; readOnly: boolean | undefined };

View File

@@ -10,6 +10,7 @@ import { Command } from "prosemirror-state";
import { toggleMark } from "../commands/toggleMark";
import Extension, { CommandFactory } from "../lib/Extension";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import { Primitive } from "utility-types";
export default abstract class Mark extends Extension {
get type() {
@@ -46,6 +47,6 @@ export default abstract class Mark extends Extension {
type: MarkType;
schema: Schema;
}): Record<string, CommandFactory> | CommandFactory | undefined {
return (attrs) => toggleMark(type, attrs);
return (attrs) => toggleMark(type, attrs as Record<string, Primitive>);
}
}

View File

@@ -10,6 +10,8 @@ import {
deleteRow,
deleteTable,
goToNextCell,
moveTableColumn,
moveTableRow,
tableEditing,
toggleHeader,
} from "prosemirror-tables";
@@ -87,6 +89,8 @@ export default class Table extends Node {
deleteColumn: () => deleteColumn,
addRowBefore,
addRowAfter: () => addRowAfter,
moveTableRow,
moveTableColumn,
deleteRow: () => deleteRow,
deleteTable: () => deleteTable,
exportTable,

View File

@@ -475,10 +475,12 @@
"Keep as link": "Keep as link",
"Mention": "Mention",
"Embed": "Embed",
"Add column after": "Add column after",
"Add column before": "Add column before",
"Add row after": "Add row after",
"Add row before": "Add row before",
"Insert after": "Insert after",
"Insert before": "Insert before",
"Move up": "Move up",
"Move down": "Move down",
"Move left": "Move left",
"Move right": "Move right",
"Align center": "Align center",
"Align left": "Align left",
"Align right": "Align right",