1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-09-09 06:29:32 +03:00

Lexical: Added backspace handling for details

Allows more reliable removal of details block on backspace at first
child position with the details block.
This commit is contained in:
Dan Brown
2025-08-27 14:09:38 +01:00
parent 519acaf324
commit 46613f76f6
6 changed files with 48 additions and 11 deletions

View File

@@ -91,9 +91,6 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
window.debugEditorState = () => {
return editor.getEditorState().toJSON();
};
context.manager.onSelectionChange((selection) => {
console.log(selection, context.editor.getEditorState());
});
registerCommonNodeMutationListeners(context);

View File

@@ -307,7 +307,7 @@ export class ElementNode extends LexicalNode {
let anchorOffset = _anchorOffset;
let focusOffset = _focusOffset;
const childrenCount = this.getChildrenSize();
if (!this.canBeEmpty()) {
if (!this.canBeEmpty() && !this.shouldSelectDirectly()) {
if (_anchorOffset === 0 && _focusOffset === 0) {
const firstChild = this.getFirstChild();
if ($isTextNode(firstChild) || $isElementNode(firstChild)) {

View File

@@ -178,6 +178,10 @@ export class DetailsNode extends ElementNode {
return true;
}
canBeEmpty(): boolean {
return false;
}
}
export function $createDetailsNode() {

View File

@@ -18,6 +18,7 @@ import {$setInsetForSelection} from "../utils/lists";
import {$isListItemNode} from "@lexical/list";
import {$isDetailsNode, DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
import {$isDiagramNode} from "../utils/diagrams";
import {$unwrapDetailsNode} from "../utils/details";
function isSingleSelectedNode(nodes: LexicalNode[]): boolean {
if (nodes.length === 1) {
@@ -172,6 +173,35 @@ function getDetailsScenario(editor: LexicalEditor): {
}
}
function unwrapDetailsNode(context: EditorUiContext, event: KeyboardEvent): boolean {
const selection = $getSelection();
const nodes = selection?.getNodes() || [];
if (nodes.length !== 1) {
return false;
}
const selectedNearestBlock = $getNearestNodeBlockParent(nodes[0]);
if (!selectedNearestBlock) {
return false;
}
const selectedParentBlock = selectedNearestBlock.getParent();
const selectRange = selection?.getStartEndPoints();
if (selectRange && $isDetailsNode(selectedParentBlock) && selectRange[0].offset === 0 && selectedNearestBlock.getIndexWithinParent() === 0) {
event.preventDefault();
context.editor.update(() => {
$unwrapDetailsNode(selectedParentBlock);
selectedNearestBlock.selectStart();
context.manager.triggerLayoutUpdate();
});
return true;
}
return false;
}
function $isSingleListItem(nodes: LexicalNode[]): boolean {
if (nodes.length !== 1) {
return false;
@@ -201,9 +231,9 @@ function handleInsetOnTab(editor: LexicalEditor, event: KeyboardEvent|null): boo
}
export function registerKeyboardHandling(context: EditorUiContext): () => void {
const unregisterBackspace = context.editor.registerCommand(KEY_BACKSPACE_COMMAND, (): boolean => {
const unregisterBackspace = context.editor.registerCommand(KEY_BACKSPACE_COMMAND, (event): boolean => {
deleteSingleSelectedNode(context.editor);
return false;
return unwrapDetailsNode(context, event);
}, COMMAND_PRIORITY_LOW);
const unregisterDelete = context.editor.registerCommand(KEY_DELETE_COMMAND, (): boolean => {

View File

@@ -34,6 +34,7 @@ import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert}
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
import {$showDetailsForm, $showImageForm, $showLinkForm, $showMediaForm} from "../forms/objects";
import {formatCodeBlock} from "../../../utils/formats";
import {$unwrapDetailsNode} from "../../../utils/details";
export const link: EditorButtonDefinition = {
label: 'Insert/edit link',
@@ -251,11 +252,7 @@ export const detailsUnwrap: EditorButtonDefinition = {
context.editor.update(() => {
const details = $getNodeFromSelection($getSelection(), $isDetailsNode);
if ($isDetailsNode(details)) {
const children = details.getChildren();
for (const child of children) {
details.insertBefore(child);
}
details.remove();
$unwrapDetailsNode(details);
context.manager.triggerLayoutUpdate();
}
})

View File

@@ -0,0 +1,9 @@
import {DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
export function $unwrapDetailsNode(node: DetailsNode) {
const children = node.getChildren();
for (const child of children) {
node.insertBefore(child);
}
node.remove();
}