mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-10-12 02:01:37 +00:00
Lexical: Fixed ability to cut/copy/drag+drop details nodes
Required updating the selection management to ensure they are selected upon mousedown, for when a drag operation starts. Also added draggable management, which we have to avoid having there when open unless a drag is started, otherwise contents are not editable in chromium browsers.
This commit is contained in:
@@ -1282,7 +1282,7 @@ export class RangeSelection implements BaseSelection {
|
||||
if (
|
||||
insertedParagraph &&
|
||||
$isElementNode(lastInsertedBlock) &&
|
||||
(insertedParagraph.canMergeWhenEmpty() || INTERNAL_$isBlock(lastToInsert))
|
||||
(insertedParagraph.canMergeWhenEmpty() || INTERNAL_$isBlock(lastToInsert) || ($isElementNode(lastToInsert) && lastToInsert.isShadowRoot()))
|
||||
) {
|
||||
lastInsertedBlock.append(...insertedParagraph.getChildren());
|
||||
insertedParagraph.remove();
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
LexicalEditor,
|
||||
LexicalNode,
|
||||
SerializedElementNode, Spread,
|
||||
EditorConfig, DOMExportOutput,
|
||||
EditorConfig, DOMExportOutput, $getSelection,
|
||||
} from 'lexical';
|
||||
|
||||
import {extractDirectionFromElement} from "lexical/nodes/common";
|
||||
@@ -82,6 +82,7 @@ export class DetailsNode extends ElementNode {
|
||||
el.setAttribute('open', 'true');
|
||||
el.removeAttribute('contenteditable');
|
||||
} else {
|
||||
el.setAttribute('draggable', 'true');
|
||||
el.setAttribute('contenteditable', 'false');
|
||||
}
|
||||
|
||||
@@ -94,6 +95,20 @@ export class DetailsNode extends ElementNode {
|
||||
this.select();
|
||||
});
|
||||
});
|
||||
summary.addEventListener('pointerdown', event => {
|
||||
_editor.update(() => {
|
||||
this.select();
|
||||
});
|
||||
|
||||
// We juggle the draggable so that the details element can be dragged when started from the summary,
|
||||
// but details element otherwise remains undraggable when open to allow editing of its contents
|
||||
el.draggable = true;
|
||||
const pointerUpCallback = () => {
|
||||
document.removeEventListener('pointerup', pointerUpCallback);
|
||||
el.draggable = false;
|
||||
};
|
||||
document.addEventListener('pointerup', pointerUpCallback);
|
||||
});
|
||||
|
||||
summary.addEventListener('dblclick', event => {
|
||||
event.preventDefault();
|
||||
@@ -116,8 +131,10 @@ export class DetailsNode extends ElementNode {
|
||||
dom.toggleAttribute('open', this.__open);
|
||||
if (this.__open) {
|
||||
dom.removeAttribute('contenteditable');
|
||||
dom.removeAttribute('draggable');
|
||||
} else {
|
||||
dom.setAttribute('contenteditable', 'false');
|
||||
dom.setAttribute('draggable', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +185,7 @@ export class DetailsNode extends ElementNode {
|
||||
|
||||
element.removeAttribute('open');
|
||||
element.removeAttribute('contenteditable');
|
||||
element.removeAttribute('draggable');
|
||||
|
||||
return {element};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
} from "lexical";
|
||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
||||
import {CommonBlockAlignment, extractAlignmentFromElement} from "lexical/nodes/common";
|
||||
import {$selectSingleNode} from "../../utils/selection";
|
||||
import {SerializedElementNode} from "lexical/nodes/LexicalElementNode";
|
||||
|
||||
export interface ImageNodeOptions {
|
||||
|
||||
@@ -56,12 +56,12 @@ function insertHtmlToEditor(editor: LexicalEditor, html: string, isFromInternal:
|
||||
if (isFromInternal) {
|
||||
const selected = $getSelection();
|
||||
if ($isRangeSelection(selected)) {
|
||||
selected.removeText();
|
||||
|
||||
// Handle single selected node scenarios
|
||||
const singleSelectedNode = $getSingleSelectableNode(selected.getNodes());
|
||||
if (singleSelectedNode) {
|
||||
singleSelectedNode.remove();
|
||||
} else {
|
||||
selected.removeText();
|
||||
}
|
||||
|
||||
// Clear out empty blocks
|
||||
|
||||
@@ -18,6 +18,7 @@ import {$isImageNode} from "@lexical/rich-text/LexicalImageNode";
|
||||
import {$isMediaNode} from "@lexical/rich-text/LexicalMediaNode";
|
||||
import {$isDiagramNode} from "./diagrams";
|
||||
import {$isLinkedImageNode} from "./images";
|
||||
import {$isDetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
|
||||
|
||||
function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
|
||||
return nodes.map(node => {
|
||||
@@ -176,6 +177,10 @@ export function $isSingleSelectableNode(nodes: LexicalNode[]): boolean {
|
||||
if ($isDecoratorNode(node) || $isImageNode(node) || $isMediaNode(node) || $isDiagramNode(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($isDetailsNode(node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user