Skip to content

Commit c6449f3

Browse files
committed
Added missing Delete key handlers
1 parent 9a502b6 commit c6449f3

File tree

2 files changed

+154
-37
lines changed

2 files changed

+154
-37
lines changed

packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,27 @@ export const getPrevBlockInfo = (doc: Node, beforePos: number) => {
5050
return prevBlockInfo;
5151
};
5252

53+
/**
54+
* Returns the block info from the sibling block after (below) the given block,
55+
* or undefined if the given block is the last sibling.
56+
*/
57+
export const getNextBlockInfo = (doc: Node, beforePos: number) => {
58+
const $pos = doc.resolve(beforePos);
59+
60+
const indexInParent = $pos.index();
61+
62+
if (indexInParent === $pos.node().childCount - 1) {
63+
return undefined;
64+
}
65+
66+
const nextBlockBeforePos = $pos.posAtIndex(indexInParent + 1);
67+
68+
const nextBlockInfo = getBlockInfoFromResolvedPos(
69+
doc.resolve(nextBlockBeforePos),
70+
);
71+
return nextBlockInfo;
72+
};
73+
5374
/**
5475
* If a block has children like this:
5576
* A
@@ -88,6 +109,7 @@ const mergeBlocks = (
88109
prevBlockInfo: BlockInfo,
89110
nextBlockInfo: BlockInfo,
90111
) => {
112+
debugger;
91113
// Un-nests all children of the next block.
92114
if (!nextBlockInfo.isBlockContainer) {
93115
throw new Error(
@@ -143,6 +165,7 @@ export const mergeBlocksCommand =
143165
state: EditorState;
144166
dispatch: ((args?: any) => any) | undefined;
145167
}) => {
168+
debugger;
146169
const $pos = state.doc.resolve(posBetweenBlocks);
147170
const nextBlockInfo = getBlockInfoFromResolvedPos($pos);
148171

packages/core/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts

Lines changed: 131 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { TextSelection } from "prosemirror-state";
44
import { ReplaceAroundStep } from "prosemirror-transform";
55
import {
66
getBottomNestedBlockInfo,
7+
getNextBlockInfo,
78
getParentBlockInfo,
89
getPrevBlockInfo,
910
mergeBlocksCommand,
@@ -296,20 +297,13 @@ export const KeyboardShortcutsExtension = Extension.create<{
296297
} else if (
297298
prevBlockInfo.blockContent.node.type.spec.content === ""
298299
) {
299-
const nonEditableBlockContentStartPos =
300-
prevBlockInfo.blockContent.afterPos -
301-
prevBlockInfo.blockContent.node.nodeSize;
302-
303300
chainedCommands = chainedCommands.setNodeSelection(
304-
nonEditableBlockContentStartPos,
301+
prevBlockInfo.blockContent.beforePos,
305302
);
306303
} else {
307-
const blockContentStartPos =
308-
prevBlockInfo.blockContent.afterPos -
309-
prevBlockInfo.blockContent.node.nodeSize;
310-
311-
chainedCommands =
312-
chainedCommands.setTextSelection(blockContentStartPos);
304+
chainedCommands = chainedCommands.setTextSelection(
305+
prevBlockInfo.blockContent.afterPos - 1,
306+
);
313307
}
314308

315309
return chainedCommands
@@ -383,50 +377,150 @@ export const KeyboardShortcutsExtension = Extension.create<{
383377
]);
384378

385379
const handleDelete = () =>
386-
this.editor.commands.first(({ commands }) => [
380+
this.editor.commands.first(({ chain, commands }) => [
387381
// Deletes the selection if it's not empty.
388382
() => commands.deleteSelection(),
389383
// Merges block with the next one (at the same nesting level or lower),
390384
// if one exists, the block has no children, and the selection is at the
391385
// end of the block.
392386
() =>
393387
commands.command(({ state }) => {
394-
// TODO: Change this to not rely on offsets & schema assumptions
395388
const blockInfo = getBlockInfoFromSelection(state);
396389
if (!blockInfo.isBlockContainer) {
397390
return false;
398391
}
399-
const {
400-
bnBlock: blockContainer,
401-
blockContent,
402-
childContainer,
403-
} = blockInfo;
392+
const { bnBlock: blockContainer, blockContent } = blockInfo;
404393

405-
const { depth } = state.doc.resolve(blockContainer.beforePos);
406-
const blockAtDocEnd =
407-
blockContainer.afterPos === state.doc.nodeSize - 3;
408394
const selectionAtBlockEnd =
409395
state.selection.from === blockContent.afterPos - 1;
410396
const selectionEmpty = state.selection.empty;
411-
const hasChildBlocks = childContainer !== undefined;
412397

413-
if (
414-
!blockAtDocEnd &&
415-
selectionAtBlockEnd &&
416-
selectionEmpty &&
417-
!hasChildBlocks
418-
) {
419-
let oldDepth = depth;
420-
let newPos = blockContainer.afterPos + 1;
421-
let newDepth = state.doc.resolve(newPos).depth;
422-
423-
while (newDepth < oldDepth) {
424-
oldDepth = newDepth;
425-
newPos += 2;
426-
newDepth = state.doc.resolve(newPos).depth;
398+
const posBetweenBlocks = blockContainer.afterPos;
399+
400+
if (selectionAtBlockEnd && selectionEmpty) {
401+
return chain()
402+
.command(mergeBlocksCommand(posBetweenBlocks))
403+
.scrollIntoView()
404+
.run();
405+
}
406+
407+
return false;
408+
}),
409+
// Deletes the current block if it's an empty block with inline content,
410+
// and moves the selection to the next block.
411+
() =>
412+
commands.command(({ state }) => {
413+
const blockInfo = getBlockInfoFromSelection(state);
414+
if (!blockInfo.isBlockContainer) {
415+
return false;
416+
}
417+
418+
const blockEmpty =
419+
blockInfo.blockContent.node.childCount === 0 &&
420+
blockInfo.blockContent.node.type.spec.content === "inline*";
421+
422+
if (blockEmpty) {
423+
const nextBlockInfo = getNextBlockInfo(
424+
state.doc,
425+
blockInfo.bnBlock.beforePos,
426+
);
427+
if (!nextBlockInfo || !nextBlockInfo.isBlockContainer) {
428+
return false;
429+
}
430+
431+
let chainedCommands = chain();
432+
433+
if (
434+
nextBlockInfo.blockContent.node.type.spec.content ===
435+
"tableRow+"
436+
) {
437+
const tableBlockStartPos = blockInfo.bnBlock.afterPos + 1;
438+
const tableBlockContentStartPos = tableBlockStartPos + 1;
439+
const firstRowStartPos = tableBlockContentStartPos + 1;
440+
const firstCellStartPos = firstRowStartPos + 1;
441+
const firstCellParagraphStartPos = firstCellStartPos + 1;
442+
443+
chainedCommands = chainedCommands.setTextSelection(
444+
firstCellParagraphStartPos,
445+
);
446+
} else if (
447+
nextBlockInfo.blockContent.node.type.spec.content === ""
448+
) {
449+
chainedCommands = chainedCommands.setNodeSelection(
450+
nextBlockInfo.blockContent.beforePos,
451+
);
452+
} else {
453+
chainedCommands = chainedCommands.setTextSelection(
454+
nextBlockInfo.blockContent.beforePos + 1,
455+
);
427456
}
428457

429-
return commands.command(mergeBlocksCommand(newPos - 1));
458+
return chainedCommands
459+
.deleteRange({
460+
from: blockInfo.bnBlock.beforePos,
461+
to: blockInfo.bnBlock.afterPos,
462+
})
463+
.scrollIntoView()
464+
.run();
465+
}
466+
467+
return false;
468+
}),
469+
// Deletes next block if it contains no content and isn't a table,
470+
// when the selection is empty and at the end of the block. Moves the
471+
// current block into the deleted block's place.
472+
() =>
473+
commands.command(({ state }) => {
474+
const blockInfo = getBlockInfoFromSelection(state);
475+
476+
if (!blockInfo.isBlockContainer) {
477+
// TODO
478+
throw new Error(`todo`);
479+
}
480+
481+
const selectionAtBlockEnd =
482+
state.selection.from === blockInfo.blockContent.afterPos - 1;
483+
const selectionEmpty = state.selection.empty;
484+
485+
const nextBlockInfo = getNextBlockInfo(
486+
state.doc,
487+
blockInfo.bnBlock.beforePos,
488+
);
489+
if (!nextBlockInfo) {
490+
return false;
491+
}
492+
if (!nextBlockInfo.isBlockContainer) {
493+
// TODO
494+
throw new Error(`todo`);
495+
}
496+
497+
if (nextBlockInfo && selectionAtBlockEnd && selectionEmpty) {
498+
const nextBlockNotTableAndNoContent =
499+
nextBlockInfo.blockContent.node.type.spec.content === "" ||
500+
(nextBlockInfo.blockContent.node.type.spec.content ===
501+
"inline*" &&
502+
nextBlockInfo.blockContent.node.childCount === 0);
503+
504+
if (nextBlockNotTableAndNoContent) {
505+
if (nextBlockInfo.bnBlock.node.childCount === 2) {
506+
const childBlocks =
507+
nextBlockInfo.bnBlock.node.lastChild!.content;
508+
return chain()
509+
.deleteRange({
510+
from: nextBlockInfo.bnBlock.beforePos,
511+
to: nextBlockInfo.bnBlock.afterPos,
512+
})
513+
.insertContentAt(blockInfo.bnBlock.afterPos, childBlocks)
514+
.run();
515+
}
516+
517+
return chain()
518+
.deleteRange({
519+
from: nextBlockInfo.bnBlock.beforePos,
520+
to: nextBlockInfo.bnBlock.afterPos,
521+
})
522+
.run();
523+
}
430524
}
431525

432526
return false;

0 commit comments

Comments
 (0)