$createParagraphNode,
$getSelection,
$isDecoratorNode,
- COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND,
+ COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND,
KEY_BACKSPACE_COMMAND,
KEY_DELETE_COMMAND,
KEY_ENTER_COMMAND, KEY_TAB_COMMAND,
import {$isImageNode} from "@lexical/rich-text/LexicalImageNode";
import {$isMediaNode} from "@lexical/rich-text/LexicalMediaNode";
import {getLastSelection} from "../utils/selection";
-import {$getNearestNodeBlockParent, $getParentOfType} from "../utils/nodes";
+import {$getNearestNodeBlockParent, $getParentOfType, $selectOrCreateAdjacent} from "../utils/nodes";
import {$setInsetForSelection} from "../utils/lists";
import {$isListItemNode} from "@lexical/list";
import {$isDetailsNode, DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
+import {$isDiagramNode} from "../utils/diagrams";
function isSingleSelectedNode(nodes: LexicalNode[]): boolean {
if (nodes.length === 1) {
const node = nodes[0];
- if ($isDecoratorNode(node) || $isImageNode(node) || $isMediaNode(node)) {
+ if ($isDecoratorNode(node) || $isImageNode(node) || $isMediaNode(node) || $isDiagramNode(node)) {
return true;
}
}
}
/**
- * Insert a new empty node after the selection if the selection contains a single
+ * Insert a new empty node before/after the selection if the selection contains a single
* selected node (like image, media etc...).
*/
-function insertAfterSingleSelectedNode(editor: LexicalEditor, event: KeyboardEvent|null): boolean {
+function insertAdjacentToSingleSelectedNode(editor: LexicalEditor, event: KeyboardEvent|null): boolean {
const selectionNodes = getLastSelection(editor)?.getNodes() || [];
if (isSingleSelectedNode(selectionNodes)) {
const node = selectionNodes[0];
const nearestBlock = $getNearestNodeBlockParent(node) || node;
+ const insertBefore = event?.shiftKey === true;
if (nearestBlock) {
requestAnimationFrame(() => {
editor.update(() => {
const newParagraph = $createParagraphNode();
- nearestBlock.insertAfter(newParagraph);
+ if (insertBefore) {
+ nearestBlock.insertBefore(newParagraph);
+ } else {
+ nearestBlock.insertAfter(newParagraph);
+ }
newParagraph.select();
});
});
return false;
}
+function focusAdjacentOrInsertForSingleSelectNode(editor: LexicalEditor, event: KeyboardEvent|null, after: boolean = true): boolean {
+ const selectionNodes = getLastSelection(editor)?.getNodes() || [];
+ if (!isSingleSelectedNode(selectionNodes)) {
+ return false;
+ }
+
+ event?.preventDefault();
+ const node = selectionNodes[0];
+ editor.update(() => {
+ $selectOrCreateAdjacent(node, after);
+ });
+
+ return true;
+}
+
/**
* Insert a new node after a details node, if inside a details node that's
* the last element, and if the cursor is at the last block within the details node.
}
}
+function $isSingleListItem(nodes: LexicalNode[]): boolean {
+ if (nodes.length !== 1) {
+ return false;
+ }
+
+ const node = nodes[0];
+ return $isListItemNode(node) || $isListItemNode(node.getParent());
+}
+
/**
* Inset the nodes within selection when a range of nodes is selected
* or if a list node is selected.
const change = event?.shiftKey ? -40 : 40;
const selection = $getSelection();
const nodes = selection?.getNodes() || [];
- if (nodes.length > 1 || (nodes.length === 1 && $isListItemNode(nodes[0].getParent()))) {
+ if (nodes.length > 1 || $isSingleListItem(nodes)) {
editor.update(() => {
$setInsetForSelection(editor, change);
});
}, COMMAND_PRIORITY_LOW);
const unregisterEnter = context.editor.registerCommand(KEY_ENTER_COMMAND, (event): boolean => {
- return insertAfterSingleSelectedNode(context.editor, event)
+ return insertAdjacentToSingleSelectedNode(context.editor, event)
|| moveAfterDetailsOnEmptyLine(context.editor, event);
}, COMMAND_PRIORITY_LOW);
return handleInsetOnTab(context.editor, event);
}, COMMAND_PRIORITY_LOW);
+ const unregisterUp = context.editor.registerCommand(KEY_ARROW_UP_COMMAND, (event): boolean => {
+ return focusAdjacentOrInsertForSingleSelectNode(context.editor, event, false);
+ }, COMMAND_PRIORITY_LOW);
+
const unregisterDown = context.editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (event): boolean => {
- return insertAfterDetails(context.editor, event);
+ return insertAfterDetails(context.editor, event)
+ || focusAdjacentOrInsertForSingleSelectNode(context.editor, event, true)
}, COMMAND_PRIORITY_LOW);
return () => {
unregisterDelete();
unregisterEnter();
unregisterTab();
+ unregisterUp();
unregisterDown();
};
}
\ No newline at end of file