mirror of
https://github.com/outline/outline.git
synced 2025-12-29 15:00:31 -06:00
fix: Allow formatting toolbar to appear with cell selection (#10299)
This commit is contained in:
@@ -119,5 +119,6 @@ export function EmbedLinkEditor({ node, view, dictionary }: Props) {
|
||||
|
||||
const Wrapper = styled(Flex)`
|
||||
pointer-events: all;
|
||||
gap: 8px;
|
||||
gap: 6px;
|
||||
padding: 6px;
|
||||
`;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NodeSelection } from "prosemirror-state";
|
||||
import { CellSelection, selectedRect } from "prosemirror-tables";
|
||||
import { selectedRect } from "prosemirror-tables";
|
||||
import * as React from "react";
|
||||
import { Portal as ReactPortal } from "react-portal";
|
||||
import styled, { css } from "styled-components";
|
||||
@@ -15,6 +15,9 @@ import useMobile from "~/hooks/useMobile";
|
||||
import useWindowSize from "~/hooks/useWindowSize";
|
||||
import Logger from "~/utils/Logger";
|
||||
import { useEditor } from "./EditorContext";
|
||||
import { ColumnSelection } from "@shared/editor/selection/ColumnSelection";
|
||||
import { RowSelection } from "@shared/editor/selection/RowSelection";
|
||||
import { isTableSelected } from "@shared/editor/queries/table";
|
||||
|
||||
type Props = {
|
||||
align?: "start" | "end" | "center";
|
||||
@@ -104,11 +107,11 @@ function usePosition({
|
||||
|
||||
// tables are an oddity, and need their own positioning logic
|
||||
const isColSelection =
|
||||
selection instanceof CellSelection && selection.isColSelection();
|
||||
selection instanceof ColumnSelection && selection.isColSelection();
|
||||
const isRowSelection =
|
||||
selection instanceof CellSelection && selection.isRowSelection();
|
||||
selection instanceof RowSelection && selection.isRowSelection();
|
||||
|
||||
if (isColSelection && isRowSelection) {
|
||||
if (isTableSelected(view.state)) {
|
||||
const rect = selectedRect(view.state);
|
||||
const table = view.domAtPos(rect.tableStart);
|
||||
const bounds = (table.node as HTMLElement).getBoundingClientRect();
|
||||
@@ -349,7 +352,6 @@ const Background = styled.div<{ align: Props["align"] }>`
|
||||
box-shadow: ${s("menuShadow")};
|
||||
border-radius: 4px;
|
||||
height: 36px;
|
||||
padding: 6px;
|
||||
|
||||
${(props) =>
|
||||
props.align === "start" &&
|
||||
|
||||
@@ -282,7 +282,8 @@ const LinkEditor: React.FC<Props> = ({
|
||||
|
||||
const Wrapper = styled(Flex)`
|
||||
pointer-events: all;
|
||||
gap: 8px;
|
||||
gap: 6px;
|
||||
padding: 6px;
|
||||
`;
|
||||
|
||||
const SearchResults = styled(Scrollable)<{ $hasResults: boolean }>`
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import some from "lodash/some";
|
||||
import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
|
||||
import { CellSelection } from "prosemirror-tables";
|
||||
import * as React from "react";
|
||||
import filterExcessSeparators from "@shared/editor/lib/filterExcessSeparators";
|
||||
import { getMarkRange } from "@shared/editor/queries/getMarkRange";
|
||||
@@ -8,7 +7,11 @@ import { isInCode } from "@shared/editor/queries/isInCode";
|
||||
import { isInNotice } from "@shared/editor/queries/isInNotice";
|
||||
import { isMarkActive } from "@shared/editor/queries/isMarkActive";
|
||||
import { isNodeActive } from "@shared/editor/queries/isNodeActive";
|
||||
import { getColumnIndex, getRowIndex } from "@shared/editor/queries/table";
|
||||
import {
|
||||
getColumnIndex,
|
||||
getRowIndex,
|
||||
isTableSelected,
|
||||
} from "@shared/editor/queries/table";
|
||||
import { MenuItem } from "@shared/editor/types";
|
||||
import useBoolean from "~/hooks/useBoolean";
|
||||
import useDictionary from "~/hooks/useDictionary";
|
||||
@@ -23,7 +26,6 @@ import getImageMenuItems from "../menus/image";
|
||||
import getNoticeMenuItems from "../menus/notice";
|
||||
import getReadOnlyMenuItems from "../menus/readOnly";
|
||||
import getTableMenuItems from "../menus/table";
|
||||
import getTableCellMenuItems from "../menus/tableCell";
|
||||
import getTableColMenuItems from "../menus/tableCol";
|
||||
import getTableRowMenuItems from "../menus/tableRow";
|
||||
import { useEditor } from "./EditorContext";
|
||||
@@ -185,8 +187,6 @@ export default function SelectionToolbar(props: Props) {
|
||||
const isDividerSelection = isNodeActive(state.schema.nodes.hr)(state);
|
||||
const colIndex = getColumnIndex(state);
|
||||
const rowIndex = getRowIndex(state);
|
||||
const isTableSelection = colIndex !== undefined && rowIndex !== undefined;
|
||||
const isCellSelection = selection instanceof CellSelection;
|
||||
const link = getMarkRange(selection.$from, state.schema.marks.link);
|
||||
const isImageSelection =
|
||||
selection instanceof NodeSelection && selection.node.type.name === "image";
|
||||
@@ -204,14 +204,12 @@ export default function SelectionToolbar(props: Props) {
|
||||
if (isCodeSelection && selection.empty) {
|
||||
items = getCodeMenuItems(state, readOnly, dictionary);
|
||||
align = "end";
|
||||
} else if (isTableSelection) {
|
||||
} else if (isTableSelected(state)) {
|
||||
items = getTableMenuItems(state, dictionary);
|
||||
} else if (colIndex !== undefined) {
|
||||
items = getTableColMenuItems(state, colIndex, rtl, dictionary);
|
||||
} else if (rowIndex !== undefined) {
|
||||
items = getTableRowMenuItems(state, rowIndex, dictionary);
|
||||
} else if (isCellSelection) {
|
||||
items = getTableCellMenuItems(state, dictionary);
|
||||
} else if (isImageSelection) {
|
||||
items = readOnly ? [] : getImageMenuItems(state, dictionary);
|
||||
} else if (isAttachmentSelection) {
|
||||
|
||||
@@ -31,6 +31,9 @@ export default styled.button.attrs((props) => ({
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
|
||||
// extraArea overlaps slightly, this ensures the currently hovered button is on top
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
@@ -44,7 +47,7 @@ export default styled.button.attrs((props) => ({
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
${extraArea(4)}
|
||||
${extraArea(5)}
|
||||
|
||||
${(props) =>
|
||||
props.active &&
|
||||
|
||||
@@ -157,6 +157,7 @@ const FlexibleWrapper = styled.div`
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
padding: 6px;
|
||||
|
||||
${breakpoint("mobile", "tablet")`
|
||||
justify-content: space-evenly;
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
IndentIcon,
|
||||
CopyIcon,
|
||||
Heading3Icon,
|
||||
TableMergeCellsIcon,
|
||||
TableSplitCellsIcon,
|
||||
} from "outline-icons";
|
||||
import { EditorState } from "prosemirror-state";
|
||||
import styled from "styled-components";
|
||||
@@ -34,6 +36,11 @@ import {
|
||||
isMobile as isMobileDevice,
|
||||
isTouchDevice,
|
||||
} from "@shared/utils/browser";
|
||||
import {
|
||||
isMergedCellSelection,
|
||||
isMultipleCellSelection,
|
||||
} from "@shared/editor/queries/table";
|
||||
import { CellSelection } from "prosemirror-tables";
|
||||
|
||||
export default function formattingMenuItems(
|
||||
state: EditorState,
|
||||
@@ -46,6 +53,7 @@ export default function formattingMenuItems(
|
||||
const isEmpty = state.selection.empty;
|
||||
const isMobile = isMobileDevice();
|
||||
const isTouch = isTouchDevice();
|
||||
const isTableCell = state.selection instanceof CellSelection;
|
||||
|
||||
const highlight = getMarksBetween(
|
||||
state.selection.from,
|
||||
@@ -166,11 +174,25 @@ export default function formattingMenuItems(
|
||||
icon: <BlockQuoteIcon />,
|
||||
active: isNodeActive(schema.nodes.blockquote),
|
||||
attrs: { level: 2 },
|
||||
visible: !isCodeBlock && (!isMobile || isEmpty),
|
||||
visible: !isCodeBlock && !isTableCell && (!isMobile || isEmpty),
|
||||
},
|
||||
{
|
||||
name: "separator",
|
||||
},
|
||||
{
|
||||
name: "mergeCells",
|
||||
tooltip: dictionary.mergeCells,
|
||||
icon: <TableMergeCellsIcon />,
|
||||
visible: isMultipleCellSelection(state),
|
||||
},
|
||||
{
|
||||
name: "splitCell",
|
||||
tooltip: dictionary.splitCell,
|
||||
icon: <TableSplitCellsIcon />,
|
||||
visible: isMergedCellSelection(state),
|
||||
},
|
||||
{
|
||||
name: "separator",
|
||||
visible: !isCodeBlock,
|
||||
},
|
||||
{
|
||||
name: "checkbox_list",
|
||||
@@ -179,7 +201,7 @@ export default function formattingMenuItems(
|
||||
icon: <TodoListIcon />,
|
||||
keywords: "checklist checkbox task",
|
||||
active: isNodeActive(schema.nodes.checkbox_list),
|
||||
visible: !isCodeBlock && (!isMobile || isEmpty),
|
||||
visible: !isCodeBlock && !isTableCell && (!isMobile || isEmpty),
|
||||
},
|
||||
{
|
||||
name: "bullet_list",
|
||||
@@ -187,7 +209,7 @@ export default function formattingMenuItems(
|
||||
shortcut: `⇧+Ctrl+8`,
|
||||
icon: <BulletedListIcon />,
|
||||
active: isNodeActive(schema.nodes.bullet_list),
|
||||
visible: !isCodeBlock && (!isMobile || isEmpty),
|
||||
visible: !isCodeBlock && !isTableCell && (!isMobile || isEmpty),
|
||||
},
|
||||
{
|
||||
name: "ordered_list",
|
||||
@@ -195,7 +217,7 @@ export default function formattingMenuItems(
|
||||
shortcut: `⇧+Ctrl+9`,
|
||||
icon: <OrderedListIcon />,
|
||||
active: isNodeActive(schema.nodes.ordered_list),
|
||||
visible: !isCodeBlock && (!isMobile || isEmpty),
|
||||
visible: !isCodeBlock && !isTableCell && (!isMobile || isEmpty),
|
||||
},
|
||||
{
|
||||
name: "outdentList",
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { TableSplitCellsIcon, TableMergeCellsIcon } from "outline-icons";
|
||||
import { EditorState } from "prosemirror-state";
|
||||
import { CellSelection } from "prosemirror-tables";
|
||||
import {
|
||||
isMergedCellSelection,
|
||||
isMultipleCellSelection,
|
||||
} from "@shared/editor/queries/table";
|
||||
import { MenuItem } from "@shared/editor/types";
|
||||
import { Dictionary } from "~/hooks/useDictionary";
|
||||
|
||||
export default function tableCellMenuItems(
|
||||
state: EditorState,
|
||||
dictionary: Dictionary
|
||||
): MenuItem[] {
|
||||
const { selection } = state;
|
||||
|
||||
// Only show menu items if we have a CellSelection
|
||||
if (!(selection instanceof CellSelection)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
name: "mergeCells",
|
||||
label: dictionary.mergeCells,
|
||||
icon: <TableMergeCellsIcon />,
|
||||
visible: isMultipleCellSelection(state),
|
||||
},
|
||||
{
|
||||
name: "splitCell",
|
||||
label: dictionary.splitCell,
|
||||
icon: <TableSplitCellsIcon />,
|
||||
visible: isMergedCellSelection(state),
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -3,7 +3,10 @@ export default {
|
||||
// TypeScript files
|
||||
"**/*.[tj]s?(x)": [
|
||||
(f) => `prettier --write ${f.join(" ")}`,
|
||||
(f) => (f.length > 20 ? `yarn lint --fix` : `oxlint ${f.join(" ")} --fix --type-aware`),
|
||||
(f) =>
|
||||
f.length > 20
|
||||
? `yarn lint --fix`
|
||||
: `oxlint ${f.join(" ")} --fix --type-aware`,
|
||||
() => `yarn build:i18n`,
|
||||
() => "git add shared/i18n/locales/en_US/translation.json",
|
||||
],
|
||||
|
||||
@@ -31,6 +31,8 @@ import {
|
||||
} from "../queries/table";
|
||||
import { TableLayout } from "../types";
|
||||
import { collapseSelection } from "./collapseSelection";
|
||||
import { RowSelection } from "../selection/RowSelection";
|
||||
import { ColumnSelection } from "../selection/ColumnSelection";
|
||||
|
||||
export function createTable({
|
||||
rowsCount,
|
||||
@@ -492,8 +494,8 @@ export function selectRow(index: number, expand = false): Command {
|
||||
const $pos = state.doc.resolve(rect.tableStart + pos);
|
||||
const rowSelection =
|
||||
expand && state.selection instanceof CellSelection
|
||||
? CellSelection.rowSelection(state.selection.$anchorCell, $pos)
|
||||
: CellSelection.rowSelection($pos);
|
||||
? RowSelection.rowSelection(state.selection.$anchorCell, $pos)
|
||||
: RowSelection.rowSelection($pos);
|
||||
dispatch(state.tr.setSelection(rowSelection));
|
||||
return true;
|
||||
}
|
||||
@@ -509,8 +511,8 @@ export function selectColumn(index: number, expand = false): Command {
|
||||
const $pos = state.doc.resolve(rect.tableStart + pos);
|
||||
const colSelection =
|
||||
expand && state.selection instanceof CellSelection
|
||||
? CellSelection.colSelection(state.selection.$anchorCell, $pos)
|
||||
: CellSelection.colSelection($pos);
|
||||
? ColumnSelection.colSelection(state.selection.$anchorCell, $pos)
|
||||
: ColumnSelection.colSelection($pos);
|
||||
dispatch(state.tr.setSelection(colSelection));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,8 @@ export default class TableCell extends Node {
|
||||
}
|
||||
|
||||
const className = cn(EditorStyleHelper.tableGripRow, {
|
||||
selected: isRowSelected(index)(state),
|
||||
selected:
|
||||
isRowSelected(index)(state) || isTableSelected(state),
|
||||
first: index === 0,
|
||||
last: visualIndex === rows.length - 1,
|
||||
});
|
||||
|
||||
@@ -4,7 +4,11 @@ import { Plugin } from "prosemirror-state";
|
||||
import { DecorationSet, Decoration, EditorView } from "prosemirror-view";
|
||||
import { addColumnBefore, selectColumn } from "../commands/table";
|
||||
import { getCellAttrs, setCellAttrs } from "../lib/table";
|
||||
import { getCellsInRow, isColumnSelected } from "../queries/table";
|
||||
import {
|
||||
getCellsInRow,
|
||||
isColumnSelected,
|
||||
isTableSelected,
|
||||
} from "../queries/table";
|
||||
import { EditorStyleHelper } from "../styles/EditorStyleHelper";
|
||||
import { cn } from "../styles/utils";
|
||||
import Node from "./Node";
|
||||
@@ -115,7 +119,8 @@ export default class TableHeader extends Node {
|
||||
if (cols) {
|
||||
cols.forEach((pos, index) => {
|
||||
const className = cn(EditorStyleHelper.tableGripColumn, {
|
||||
selected: isColumnSelected(index)(state),
|
||||
selected:
|
||||
isColumnSelected(index)(state) || isTableSelected(state),
|
||||
first: index === 0,
|
||||
last: index === cols.length - 1,
|
||||
});
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
isInTable,
|
||||
selectedRect,
|
||||
} from "prosemirror-tables";
|
||||
import { ColumnSelection } from "../selection/ColumnSelection";
|
||||
import { RowSelection } from "../selection/RowSelection";
|
||||
|
||||
/**
|
||||
* Checks if the current selection is a column selection.
|
||||
@@ -12,7 +14,7 @@ import {
|
||||
* @returns True if the selection is a column selection, false otherwise.
|
||||
*/
|
||||
export function isColSelection(state: EditorState): boolean {
|
||||
if (state.selection instanceof CellSelection) {
|
||||
if (state.selection instanceof ColumnSelection) {
|
||||
return state.selection.isColSelection();
|
||||
}
|
||||
return false;
|
||||
@@ -24,14 +26,14 @@ export function isColSelection(state: EditorState): boolean {
|
||||
* @returns True if the selection is a row selection, false otherwise.
|
||||
*/
|
||||
export function isRowSelection(state: EditorState): boolean {
|
||||
if (state.selection instanceof CellSelection) {
|
||||
if (state.selection instanceof RowSelection) {
|
||||
return state.selection.isRowSelection();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getColumnIndex(state: EditorState): number | undefined {
|
||||
if (state.selection instanceof CellSelection) {
|
||||
if (state.selection instanceof ColumnSelection) {
|
||||
if (state.selection.isColSelection()) {
|
||||
const rect = selectedRect(state);
|
||||
return rect.left;
|
||||
@@ -42,7 +44,7 @@ export function getColumnIndex(state: EditorState): number | undefined {
|
||||
}
|
||||
|
||||
export function getRowIndex(state: EditorState): number | undefined {
|
||||
if (state.selection instanceof CellSelection) {
|
||||
if (state.selection instanceof RowSelection) {
|
||||
if (state.selection.isRowSelection()) {
|
||||
const rect = selectedRect(state);
|
||||
return rect.top;
|
||||
|
||||
73
shared/editor/selection/ColumnSelection.ts
Normal file
73
shared/editor/selection/ColumnSelection.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { ResolvedPos, type Node } from "prosemirror-model";
|
||||
import { Selection } from "prosemirror-state";
|
||||
import { CellSelection, inSameTable, TableMap } from "prosemirror-tables";
|
||||
import { Mappable } from "prosemirror-transform";
|
||||
|
||||
export class ColumnSelection extends CellSelection {
|
||||
getBookmark(): ColumnBookmark {
|
||||
return new ColumnBookmark(this.$anchorCell.pos, this.$headCell.pos);
|
||||
}
|
||||
|
||||
public static colSelection(
|
||||
$anchorCell: ResolvedPos,
|
||||
$headCell: ResolvedPos = $anchorCell
|
||||
): CellSelection {
|
||||
const table = $anchorCell.node(-1);
|
||||
const map = TableMap.get(table);
|
||||
const tableStart = $anchorCell.start(-1);
|
||||
|
||||
const anchorRect = map.findCell($anchorCell.pos - tableStart);
|
||||
const headRect = map.findCell($headCell.pos - tableStart);
|
||||
const doc = $anchorCell.node(0);
|
||||
|
||||
if (anchorRect.top <= headRect.top) {
|
||||
if (anchorRect.top > 0) {
|
||||
$anchorCell = doc.resolve(tableStart + map.map[anchorRect.left]);
|
||||
}
|
||||
if (headRect.bottom < map.height) {
|
||||
$headCell = doc.resolve(
|
||||
tableStart +
|
||||
map.map[map.width * (map.height - 1) + headRect.right - 1]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (headRect.top > 0) {
|
||||
$headCell = doc.resolve(tableStart + map.map[headRect.left]);
|
||||
}
|
||||
if (anchorRect.bottom < map.height) {
|
||||
$anchorCell = doc.resolve(
|
||||
tableStart +
|
||||
map.map[map.width * (map.height - 1) + anchorRect.right - 1]
|
||||
);
|
||||
}
|
||||
}
|
||||
return new ColumnSelection($anchorCell, $headCell);
|
||||
}
|
||||
}
|
||||
|
||||
export class ColumnBookmark {
|
||||
constructor(
|
||||
public anchor: number,
|
||||
public head: number
|
||||
) {}
|
||||
|
||||
map(mapping: Mappable): ColumnBookmark {
|
||||
return new ColumnBookmark(mapping.map(this.anchor), mapping.map(this.head));
|
||||
}
|
||||
|
||||
resolve(doc: Node): CellSelection | Selection {
|
||||
const $anchorCell = doc.resolve(this.anchor),
|
||||
$headCell = doc.resolve(this.head);
|
||||
if (
|
||||
$anchorCell.parent.type.spec.tableRole === "row" &&
|
||||
$headCell.parent.type.spec.tableRole === "row" &&
|
||||
$anchorCell.index() < $anchorCell.parent.childCount &&
|
||||
$headCell.index() < $headCell.parent.childCount &&
|
||||
inSameTable($anchorCell, $headCell)
|
||||
) {
|
||||
return new ColumnSelection($anchorCell, $headCell);
|
||||
} else {
|
||||
return Selection.near($headCell, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
shared/editor/selection/RowSelection.ts
Normal file
73
shared/editor/selection/RowSelection.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { ResolvedPos, type Node } from "prosemirror-model";
|
||||
import { Selection } from "prosemirror-state";
|
||||
import { CellSelection, inSameTable, TableMap } from "prosemirror-tables";
|
||||
import { Mappable } from "prosemirror-transform";
|
||||
|
||||
export class RowSelection extends CellSelection {
|
||||
getBookmark(): RowBookmark {
|
||||
return new RowBookmark(this.$anchorCell.pos, this.$headCell.pos);
|
||||
}
|
||||
|
||||
public static rowSelection(
|
||||
$anchorCell: ResolvedPos,
|
||||
$headCell: ResolvedPos = $anchorCell
|
||||
): CellSelection {
|
||||
const table = $anchorCell.node(-1);
|
||||
const map = TableMap.get(table);
|
||||
const tableStart = $anchorCell.start(-1);
|
||||
|
||||
const anchorRect = map.findCell($anchorCell.pos - tableStart);
|
||||
const headRect = map.findCell($headCell.pos - tableStart);
|
||||
const doc = $anchorCell.node(0);
|
||||
|
||||
if (anchorRect.left <= headRect.left) {
|
||||
if (anchorRect.left > 0) {
|
||||
$anchorCell = doc.resolve(
|
||||
tableStart + map.map[anchorRect.top * map.width]
|
||||
);
|
||||
}
|
||||
if (headRect.right < map.width) {
|
||||
$headCell = doc.resolve(
|
||||
tableStart + map.map[map.width * (headRect.top + 1) - 1]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (headRect.left > 0) {
|
||||
$headCell = doc.resolve(tableStart + map.map[headRect.top * map.width]);
|
||||
}
|
||||
if (anchorRect.right < map.width) {
|
||||
$anchorCell = doc.resolve(
|
||||
tableStart + map.map[map.width * (anchorRect.top + 1) - 1]
|
||||
);
|
||||
}
|
||||
}
|
||||
return new RowSelection($anchorCell, $headCell);
|
||||
}
|
||||
}
|
||||
|
||||
export class RowBookmark {
|
||||
constructor(
|
||||
public anchor: number,
|
||||
public head: number
|
||||
) {}
|
||||
|
||||
map(mapping: Mappable): RowBookmark {
|
||||
return new RowBookmark(mapping.map(this.anchor), mapping.map(this.head));
|
||||
}
|
||||
|
||||
resolve(doc: Node): CellSelection | Selection {
|
||||
const $anchorCell = doc.resolve(this.anchor),
|
||||
$headCell = doc.resolve(this.head);
|
||||
if (
|
||||
$anchorCell.parent.type.spec.tableRole === "row" &&
|
||||
$headCell.parent.type.spec.tableRole === "row" &&
|
||||
$anchorCell.index() < $anchorCell.parent.childCount &&
|
||||
$headCell.index() < $headCell.parent.childCount &&
|
||||
inSameTable($anchorCell, $headCell)
|
||||
) {
|
||||
return new RowSelection($anchorCell, $headCell);
|
||||
} else {
|
||||
return Selection.near($headCell, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user