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:
@@ -91,9 +91,6 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
|
|||||||
window.debugEditorState = () => {
|
window.debugEditorState = () => {
|
||||||
return editor.getEditorState().toJSON();
|
return editor.getEditorState().toJSON();
|
||||||
};
|
};
|
||||||
context.manager.onSelectionChange((selection) => {
|
|
||||||
console.log(selection, context.editor.getEditorState());
|
|
||||||
});
|
|
||||||
|
|
||||||
registerCommonNodeMutationListeners(context);
|
registerCommonNodeMutationListeners(context);
|
||||||
|
|
||||||
|
@@ -307,7 +307,7 @@ export class ElementNode extends LexicalNode {
|
|||||||
let anchorOffset = _anchorOffset;
|
let anchorOffset = _anchorOffset;
|
||||||
let focusOffset = _focusOffset;
|
let focusOffset = _focusOffset;
|
||||||
const childrenCount = this.getChildrenSize();
|
const childrenCount = this.getChildrenSize();
|
||||||
if (!this.canBeEmpty()) {
|
if (!this.canBeEmpty() && !this.shouldSelectDirectly()) {
|
||||||
if (_anchorOffset === 0 && _focusOffset === 0) {
|
if (_anchorOffset === 0 && _focusOffset === 0) {
|
||||||
const firstChild = this.getFirstChild();
|
const firstChild = this.getFirstChild();
|
||||||
if ($isTextNode(firstChild) || $isElementNode(firstChild)) {
|
if ($isTextNode(firstChild) || $isElementNode(firstChild)) {
|
||||||
|
@@ -178,6 +178,10 @@ export class DetailsNode extends ElementNode {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canBeEmpty(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function $createDetailsNode() {
|
export function $createDetailsNode() {
|
||||||
|
@@ -18,6 +18,7 @@ import {$setInsetForSelection} from "../utils/lists";
|
|||||||
import {$isListItemNode} from "@lexical/list";
|
import {$isListItemNode} from "@lexical/list";
|
||||||
import {$isDetailsNode, DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
|
import {$isDetailsNode, DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
|
||||||
import {$isDiagramNode} from "../utils/diagrams";
|
import {$isDiagramNode} from "../utils/diagrams";
|
||||||
|
import {$unwrapDetailsNode} from "../utils/details";
|
||||||
|
|
||||||
function isSingleSelectedNode(nodes: LexicalNode[]): boolean {
|
function isSingleSelectedNode(nodes: LexicalNode[]): boolean {
|
||||||
if (nodes.length === 1) {
|
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 {
|
function $isSingleListItem(nodes: LexicalNode[]): boolean {
|
||||||
if (nodes.length !== 1) {
|
if (nodes.length !== 1) {
|
||||||
return false;
|
return false;
|
||||||
@@ -201,9 +231,9 @@ function handleInsetOnTab(editor: LexicalEditor, event: KeyboardEvent|null): boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function registerKeyboardHandling(context: EditorUiContext): () => void {
|
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);
|
deleteSingleSelectedNode(context.editor);
|
||||||
return false;
|
return unwrapDetailsNode(context, event);
|
||||||
}, COMMAND_PRIORITY_LOW);
|
}, COMMAND_PRIORITY_LOW);
|
||||||
|
|
||||||
const unregisterDelete = context.editor.registerCommand(KEY_DELETE_COMMAND, (): boolean => {
|
const unregisterDelete = context.editor.registerCommand(KEY_DELETE_COMMAND, (): boolean => {
|
||||||
|
@@ -34,6 +34,7 @@ import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert}
|
|||||||
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
|
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
|
||||||
import {$showDetailsForm, $showImageForm, $showLinkForm, $showMediaForm} from "../forms/objects";
|
import {$showDetailsForm, $showImageForm, $showLinkForm, $showMediaForm} from "../forms/objects";
|
||||||
import {formatCodeBlock} from "../../../utils/formats";
|
import {formatCodeBlock} from "../../../utils/formats";
|
||||||
|
import {$unwrapDetailsNode} from "../../../utils/details";
|
||||||
|
|
||||||
export const link: EditorButtonDefinition = {
|
export const link: EditorButtonDefinition = {
|
||||||
label: 'Insert/edit link',
|
label: 'Insert/edit link',
|
||||||
@@ -251,11 +252,7 @@ export const detailsUnwrap: EditorButtonDefinition = {
|
|||||||
context.editor.update(() => {
|
context.editor.update(() => {
|
||||||
const details = $getNodeFromSelection($getSelection(), $isDetailsNode);
|
const details = $getNodeFromSelection($getSelection(), $isDetailsNode);
|
||||||
if ($isDetailsNode(details)) {
|
if ($isDetailsNode(details)) {
|
||||||
const children = details.getChildren();
|
$unwrapDetailsNode(details);
|
||||||
for (const child of children) {
|
|
||||||
details.insertBefore(child);
|
|
||||||
}
|
|
||||||
details.remove();
|
|
||||||
context.manager.triggerLayoutUpdate();
|
context.manager.triggerLayoutUpdate();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
9
resources/js/wysiwyg/utils/details.ts
Normal file
9
resources/js/wysiwyg/utils/details.ts
Normal 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();
|
||||||
|
}
|
Reference in New Issue
Block a user