Skip to content

Commit 46613f7

Browse files
committed
Lexical: Added backspace handling for details
Allows more reliable removal of details block on backspace at first child position with the details block.
1 parent 519acaf commit 46613f7

File tree

6 files changed

+48
-11
lines changed

6 files changed

+48
-11
lines changed

resources/js/wysiwyg/index.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,6 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
9191
window.debugEditorState = () => {
9292
return editor.getEditorState().toJSON();
9393
};
94-
context.manager.onSelectionChange((selection) => {
95-
console.log(selection, context.editor.getEditorState());
96-
});
9794

9895
registerCommonNodeMutationListeners(context);
9996

resources/js/wysiwyg/lexical/core/nodes/LexicalElementNode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ export class ElementNode extends LexicalNode {
307307
let anchorOffset = _anchorOffset;
308308
let focusOffset = _focusOffset;
309309
const childrenCount = this.getChildrenSize();
310-
if (!this.canBeEmpty()) {
310+
if (!this.canBeEmpty() && !this.shouldSelectDirectly()) {
311311
if (_anchorOffset === 0 && _focusOffset === 0) {
312312
const firstChild = this.getFirstChild();
313313
if ($isTextNode(firstChild) || $isElementNode(firstChild)) {

resources/js/wysiwyg/lexical/rich-text/LexicalDetailsNode.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ export class DetailsNode extends ElementNode {
178178
return true;
179179
}
180180

181+
canBeEmpty(): boolean {
182+
return false;
183+
}
184+
181185
}
182186

183187
export function $createDetailsNode() {

resources/js/wysiwyg/services/keyboard-handling.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {$setInsetForSelection} from "../utils/lists";
1818
import {$isListItemNode} from "@lexical/list";
1919
import {$isDetailsNode, DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
2020
import {$isDiagramNode} from "../utils/diagrams";
21+
import {$unwrapDetailsNode} from "../utils/details";
2122

2223
function isSingleSelectedNode(nodes: LexicalNode[]): boolean {
2324
if (nodes.length === 1) {
@@ -172,6 +173,35 @@ function getDetailsScenario(editor: LexicalEditor): {
172173
}
173174
}
174175

176+
function unwrapDetailsNode(context: EditorUiContext, event: KeyboardEvent): boolean {
177+
const selection = $getSelection();
178+
const nodes = selection?.getNodes() || [];
179+
180+
if (nodes.length !== 1) {
181+
return false;
182+
}
183+
184+
const selectedNearestBlock = $getNearestNodeBlockParent(nodes[0]);
185+
if (!selectedNearestBlock) {
186+
return false;
187+
}
188+
189+
const selectedParentBlock = selectedNearestBlock.getParent();
190+
const selectRange = selection?.getStartEndPoints();
191+
192+
if (selectRange && $isDetailsNode(selectedParentBlock) && selectRange[0].offset === 0 && selectedNearestBlock.getIndexWithinParent() === 0) {
193+
event.preventDefault();
194+
context.editor.update(() => {
195+
$unwrapDetailsNode(selectedParentBlock);
196+
selectedNearestBlock.selectStart();
197+
context.manager.triggerLayoutUpdate();
198+
});
199+
return true;
200+
}
201+
202+
return false;
203+
}
204+
175205
function $isSingleListItem(nodes: LexicalNode[]): boolean {
176206
if (nodes.length !== 1) {
177207
return false;
@@ -201,9 +231,9 @@ function handleInsetOnTab(editor: LexicalEditor, event: KeyboardEvent|null): boo
201231
}
202232

203233
export function registerKeyboardHandling(context: EditorUiContext): () => void {
204-
const unregisterBackspace = context.editor.registerCommand(KEY_BACKSPACE_COMMAND, (): boolean => {
234+
const unregisterBackspace = context.editor.registerCommand(KEY_BACKSPACE_COMMAND, (event): boolean => {
205235
deleteSingleSelectedNode(context.editor);
206-
return false;
236+
return unwrapDetailsNode(context, event);
207237
}, COMMAND_PRIORITY_LOW);
208238

209239
const unregisterDelete = context.editor.registerCommand(KEY_DELETE_COMMAND, (): boolean => {

resources/js/wysiwyg/ui/defaults/buttons/objects.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert}
3434
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
3535
import {$showDetailsForm, $showImageForm, $showLinkForm, $showMediaForm} from "../forms/objects";
3636
import {formatCodeBlock} from "../../../utils/formats";
37+
import {$unwrapDetailsNode} from "../../../utils/details";
3738

3839
export const link: EditorButtonDefinition = {
3940
label: 'Insert/edit link',
@@ -251,11 +252,7 @@ export const detailsUnwrap: EditorButtonDefinition = {
251252
context.editor.update(() => {
252253
const details = $getNodeFromSelection($getSelection(), $isDetailsNode);
253254
if ($isDetailsNode(details)) {
254-
const children = details.getChildren();
255-
for (const child of children) {
256-
details.insertBefore(child);
257-
}
258-
details.remove();
255+
$unwrapDetailsNode(details);
259256
context.manager.triggerLayoutUpdate();
260257
}
261258
})
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
2+
3+
export function $unwrapDetailsNode(node: DetailsNode) {
4+
const children = node.getChildren();
5+
for (const child of children) {
6+
node.insertBefore(child);
7+
}
8+
node.remove();
9+
}

0 commit comments

Comments
 (0)