mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-06-01 22:39:13 +08:00
Lexical: Added list support, started todo
This commit is contained in:
@ -1,13 +1,13 @@
|
||||
import {
|
||||
$createNodeSelection,
|
||||
$createParagraphNode, $getRoot,
|
||||
$getSelection,
|
||||
$getSelection, $isElementNode,
|
||||
$isTextNode, $setSelection,
|
||||
BaseSelection,
|
||||
BaseSelection, ElementFormatType, ElementNode,
|
||||
LexicalEditor, LexicalNode, TextFormatType
|
||||
} from "lexical";
|
||||
import {getNodesForPageEditor, LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
|
||||
import {$getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
|
||||
import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
|
||||
import {$findMatchingParent, $getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
|
||||
import {$setBlocksType} from "@lexical/selection";
|
||||
|
||||
export function el(tag: string, attrs: Record<string, string|null> = {}, children: (string|HTMLElement)[] = []): HTMLElement {
|
||||
@ -114,4 +114,34 @@ export function selectionContainsNode(selection: BaseSelection|null, node: Lexic
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function selectionContainsElementFormat(selection: BaseSelection|null, format: ElementFormatType): boolean {
|
||||
const nodes = getBlockElementNodesInSelection(selection);
|
||||
for (const node of nodes) {
|
||||
if (node.getFormatType() === format) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getBlockElementNodesInSelection(selection: BaseSelection|null): ElementNode[] {
|
||||
if (!selection) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const blockNodes: Map<string, ElementNode> = new Map();
|
||||
for (const node of selection.getNodes()) {
|
||||
const blockElement = $findMatchingParent(node, (node) => {
|
||||
return $isElementNode(node) && !node.isInline();
|
||||
}) as ElementNode|null;
|
||||
|
||||
if (blockElement) {
|
||||
blockNodes.set(blockElement.getKey(), blockElement);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(blockNodes.values());
|
||||
}
|
27
resources/js/wysiwyg/todo.md
Normal file
27
resources/js/wysiwyg/todo.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Lexical based editor todo
|
||||
|
||||
## Main Todo
|
||||
|
||||
- Alignments: Use existing classes for blocks
|
||||
- Alignments: Handle inline block content (image, video)
|
||||
- Add Type: Video/media/embed
|
||||
- Add Type: Drawings
|
||||
- Handle toolbars on scroll
|
||||
- Table features
|
||||
- Image paste upload
|
||||
- Keyboard shortcuts support
|
||||
- Global/shared editor events support
|
||||
- Draft/change management (connect with page editor component)
|
||||
- Add ID support to all block types
|
||||
- Template drag & drop / insert
|
||||
- Video attachment drop / insert
|
||||
- Task list render/import from existing format
|
||||
- Link popup menu for cross-content reference
|
||||
- Link heading-based ID reference menu
|
||||
- Image gallery integration for insert
|
||||
- Image gallery integration for form
|
||||
|
||||
## Bugs
|
||||
|
||||
- Image resizing currently bugged, maybe change to ghost resizer in decorator instead of updating core node.
|
||||
- Table resize bars often floating around in wrong place, and shows on hover or interrupts mouse actions.
|
@ -1,15 +1,28 @@
|
||||
import {EditorBasicButtonDefinition, EditorButton, EditorButtonDefinition} from "../framework/buttons";
|
||||
import {
|
||||
$createNodeSelection,
|
||||
$createParagraphNode, $createTextNode, $getRoot, $getSelection,
|
||||
$isParagraphNode, $isTextNode, $setSelection,
|
||||
BaseSelection, CAN_REDO_COMMAND, CAN_UNDO_COMMAND, COMMAND_PRIORITY_LOW, ElementNode, FORMAT_TEXT_COMMAND,
|
||||
$createParagraphNode,
|
||||
$createTextNode,
|
||||
$getRoot,
|
||||
$getSelection,
|
||||
$isParagraphNode,
|
||||
$isTextNode,
|
||||
$setSelection,
|
||||
BaseSelection,
|
||||
CAN_REDO_COMMAND,
|
||||
CAN_UNDO_COMMAND,
|
||||
COMMAND_PRIORITY_LOW,
|
||||
ElementFormatType,
|
||||
ElementNode,
|
||||
FORMAT_TEXT_COMMAND,
|
||||
LexicalNode,
|
||||
REDO_COMMAND, TextFormatType,
|
||||
REDO_COMMAND,
|
||||
TextFormatType,
|
||||
UNDO_COMMAND
|
||||
} from "lexical";
|
||||
import {
|
||||
getNodeFromSelection, insertNewBlockNodeAtSelection,
|
||||
getBlockElementNodesInSelection,
|
||||
getNodeFromSelection, insertNewBlockNodeAtSelection, selectionContainsElementFormat,
|
||||
selectionContainsNodeType,
|
||||
selectionContainsTextFormat,
|
||||
toggleSelectionBlockNodeType
|
||||
@ -29,31 +42,35 @@ import {$isImageNode, ImageNode} from "../../nodes/image";
|
||||
import {$createDetailsNode, $isDetailsNode} from "../../nodes/details";
|
||||
import {getEditorContentAsHtml} from "../../actions";
|
||||
import {$isListNode, insertList, ListNode, ListType, removeList} from "@lexical/list";
|
||||
import undoIcon from "@icons/editor/undo.svg"
|
||||
import redoIcon from "@icons/editor/redo.svg"
|
||||
import boldIcon from "@icons/editor/bold.svg"
|
||||
import italicIcon from "@icons/editor/italic.svg"
|
||||
import underlinedIcon from "@icons/editor/underlined.svg"
|
||||
import undoIcon from "@icons/editor/undo.svg";
|
||||
import redoIcon from "@icons/editor/redo.svg";
|
||||
import boldIcon from "@icons/editor/bold.svg";
|
||||
import italicIcon from "@icons/editor/italic.svg";
|
||||
import underlinedIcon from "@icons/editor/underlined.svg";
|
||||
import textColorIcon from "@icons/editor/text-color.svg";
|
||||
import highlightIcon from "@icons/editor/highlighter.svg";
|
||||
import strikethroughIcon from "@icons/editor/strikethrough.svg"
|
||||
import superscriptIcon from "@icons/editor/superscript.svg"
|
||||
import subscriptIcon from "@icons/editor/subscript.svg"
|
||||
import codeIcon from "@icons/editor/code.svg"
|
||||
import formatClearIcon from "@icons/editor/format-clear.svg"
|
||||
import listBulletIcon from "@icons/editor/list-bullet.svg"
|
||||
import listNumberedIcon from "@icons/editor/list-numbered.svg"
|
||||
import listCheckIcon from "@icons/editor/list-check.svg"
|
||||
import linkIcon from "@icons/editor/link.svg"
|
||||
import unlinkIcon from "@icons/editor/unlink.svg"
|
||||
import tableIcon from "@icons/editor/table.svg"
|
||||
import imageIcon from "@icons/editor/image.svg"
|
||||
import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg"
|
||||
import codeBlockIcon from "@icons/editor/code-block.svg"
|
||||
import detailsIcon from "@icons/editor/details.svg"
|
||||
import sourceIcon from "@icons/editor/source-view.svg"
|
||||
import fullscreenIcon from "@icons/editor/fullscreen.svg"
|
||||
import editIcon from "@icons/edit.svg"
|
||||
import strikethroughIcon from "@icons/editor/strikethrough.svg";
|
||||
import superscriptIcon from "@icons/editor/superscript.svg";
|
||||
import subscriptIcon from "@icons/editor/subscript.svg";
|
||||
import codeIcon from "@icons/editor/code.svg";
|
||||
import formatClearIcon from "@icons/editor/format-clear.svg";
|
||||
import alignLeftIcon from "@icons/editor/align-left.svg";
|
||||
import alignCenterIcon from "@icons/editor/align-center.svg";
|
||||
import alignRightIcon from "@icons/editor/align-right.svg";
|
||||
import alignJustifyIcon from "@icons/editor/align-justify.svg";
|
||||
import listBulletIcon from "@icons/editor/list-bullet.svg";
|
||||
import listNumberedIcon from "@icons/editor/list-numbered.svg";
|
||||
import listCheckIcon from "@icons/editor/list-check.svg";
|
||||
import linkIcon from "@icons/editor/link.svg";
|
||||
import unlinkIcon from "@icons/editor/unlink.svg";
|
||||
import tableIcon from "@icons/editor/table.svg";
|
||||
import imageIcon from "@icons/editor/image.svg";
|
||||
import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg";
|
||||
import codeBlockIcon from "@icons/editor/code-block.svg";
|
||||
import detailsIcon from "@icons/editor/details.svg";
|
||||
import sourceIcon from "@icons/editor/source-view.svg";
|
||||
import fullscreenIcon from "@icons/editor/fullscreen.svg";
|
||||
import editIcon from "@icons/edit.svg";
|
||||
import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "../../nodes/horizontal-rule";
|
||||
import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../../nodes/code-block";
|
||||
|
||||
@ -203,6 +220,59 @@ export const clearFormating: EditorButtonDefinition = {
|
||||
}
|
||||
};
|
||||
|
||||
function setAlignmentForSection(alignment: ElementFormatType): void {
|
||||
const selection = $getSelection();
|
||||
const elements = getBlockElementNodesInSelection(selection);
|
||||
for (const node of elements) {
|
||||
node.setFormat(alignment);
|
||||
}
|
||||
}
|
||||
|
||||
export const alignLeft: EditorButtonDefinition = {
|
||||
label: 'Align left',
|
||||
icon: alignLeftIcon,
|
||||
action(context: EditorUiContext) {
|
||||
context.editor.update(() => setAlignmentForSection('left'));
|
||||
},
|
||||
isActive(selection: BaseSelection|null) {
|
||||
return selectionContainsElementFormat(selection, 'left');
|
||||
}
|
||||
};
|
||||
|
||||
export const alignCenter: EditorButtonDefinition = {
|
||||
label: 'Align center',
|
||||
icon: alignCenterIcon,
|
||||
action(context: EditorUiContext) {
|
||||
context.editor.update(() => setAlignmentForSection('center'));
|
||||
},
|
||||
isActive(selection: BaseSelection|null) {
|
||||
return selectionContainsElementFormat(selection, 'center');
|
||||
}
|
||||
};
|
||||
|
||||
export const alignRight: EditorButtonDefinition = {
|
||||
label: 'Align right',
|
||||
icon: alignRightIcon,
|
||||
action(context: EditorUiContext) {
|
||||
context.editor.update(() => setAlignmentForSection('right'));
|
||||
},
|
||||
isActive(selection: BaseSelection|null) {
|
||||
return selectionContainsElementFormat(selection, 'right');
|
||||
}
|
||||
};
|
||||
|
||||
export const alignJustify: EditorButtonDefinition = {
|
||||
label: 'Align justify',
|
||||
icon: alignJustifyIcon,
|
||||
action(context: EditorUiContext) {
|
||||
context.editor.update(() => setAlignmentForSection('justify'));
|
||||
},
|
||||
isActive(selection: BaseSelection|null) {
|
||||
return selectionContainsElementFormat(selection, 'justify');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function buildListButton(label: string, type: ListType, icon: string): EditorButtonDefinition {
|
||||
return {
|
||||
label,
|
||||
|
@ -1,5 +1,8 @@
|
||||
import {EditorButton} from "./framework/buttons";
|
||||
import {
|
||||
alignCenter, alignJustify,
|
||||
alignLeft,
|
||||
alignRight,
|
||||
blockquote, bold, bulletList, clearFormating, code, codeBlock,
|
||||
dangerCallout, details, editCodeBlock, fullscreen,
|
||||
h2, h3, h4, h5, highlightColor, horizontalRule, image,
|
||||
@ -62,6 +65,14 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement {
|
||||
new EditorButton(clearFormating),
|
||||
]),
|
||||
|
||||
// Alignment
|
||||
new EditorOverflowContainer(4, [
|
||||
new EditorButton(alignLeft),
|
||||
new EditorButton(alignCenter),
|
||||
new EditorButton(alignRight),
|
||||
new EditorButton(alignJustify),
|
||||
]),
|
||||
|
||||
// Lists
|
||||
new EditorOverflowContainer(3, [
|
||||
new EditorButton(bulletList),
|
||||
|
Reference in New Issue
Block a user