import {mergeRegister} from '@lexical/utils';
import {getNodesForPageEditor, registerCommonNodeMutationListeners} from './nodes';
import {buildEditorUI} from "./ui";
-import {getEditorContentAsHtml, setEditorContentFromHtml} from "./actions";
+import {getEditorContentAsHtml, setEditorContentFromHtml} from "./utils/actions";
import {registerTableResizer} from "./ui/framework/helpers/table-resizer";
-import {el} from "./helpers";
import {EditorUiContext} from "./ui/framework/core";
-import {listen as listenToCommonEvents} from "./common-events";
-import {handleDropEvents} from "./drop-handling";
+import {listen as listenToCommonEvents} from "./services/common-events";
+import {handleDropEvents} from "./services/drop-handling";
import {registerTaskListHandler} from "./ui/framework/helpers/task-list-handler";
import {registerTableSelectionHandler} from "./ui/framework/helpers/table-selection-handler";
+import {el} from "./utils/dom";
export function createPageEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface {
const config: CreateEditorArgs = {
Spread
} from "lexical";
import type {EditorConfig} from "lexical/LexicalEditor";
-import {el} from "../helpers";
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
import {CodeEditor} from "../../components";
+import {el} from "../utils/dom";
export type SerializedCodeBlockNode = Spread<{
language: string;
import {$isListNode, ListItemNode, SerializedListItemNode} from "@lexical/list";
import {EditorConfig} from "lexical/LexicalEditor";
import {DOMExportOutput, LexicalEditor, LexicalNode} from "lexical";
-import {el} from "../helpers";
+
+import {el} from "../utils/dom";
function updateListItemChecked(
dom: HTMLElement,
import {SerializedTableNode, TableNode, TableRowNode} from "@lexical/table";
import {DOMConversion, DOMConversionMap, DOMConversionOutput, LexicalEditor, LexicalNode, Spread} from "lexical";
import {EditorConfig} from "lexical/LexicalEditor";
-import {el} from "../helpers";
+
+import {el} from "../utils/dom";
export type SerializedCustomTableNode = Spread<{
id: string;
SerializedElementNode,
} from 'lexical';
import type {EditorConfig} from "lexical/LexicalEditor";
-import {el} from "../helpers";
+
+import {el} from "../utils/dom";
export class DetailsNode extends ElementNode {
Spread
} from "lexical";
import type {EditorConfig} from "lexical/LexicalEditor";
-import {el} from "../helpers";
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
import * as DrawIO from '../../services/drawio';
import {EditorUiContext} from "../ui/framework/core";
import {HttpError} from "../../services/http";
+import {el} from "../utils/dom";
export type SerializedDiagramNode = Spread<{
id: string;
Spread
} from "lexical";
import type {EditorConfig} from "lexical/LexicalEditor";
-import {el} from "../helpers";
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
+import {el} from "../utils/dom";
export interface ImageNodeOptions {
alt?: string;
SerializedElementNode, Spread
} from 'lexical';
import type {EditorConfig} from "lexical/LexicalEditor";
-import {el} from "../helpers";
+
+import {el} from "../utils/dom";
export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio';
export type MediaNodeSource = {
insertHtmlIntoEditor,
prependHtmlToEditor,
setEditorContentFromHtml
-} from "./actions";
+} from "../utils/actions";
type EditorEventContent = {
html: string;
LexicalEditor,
LexicalNode
} from "lexical";
-import {
- $getNearestBlockNodeForCoords,
- $htmlToBlockNodes,
- $insertNewBlockNodesAtSelection,
- $selectSingleNode
-} from "./helpers";
+import {$insertNewBlockNodesAtSelection, $selectSingleNode} from "../utils/selection";
+import {$getNearestBlockNodeForCoords, $htmlToBlockNodes} from "../utils/nodes";
function $getNodeFromMouseEvent(event: MouseEvent, editor: LexicalEditor): LexicalNode|null {
const x = event.clientX;
import {EditorDecorator} from "../framework/decorator";
import {EditorUiContext} from "../framework/core";
import {$openCodeEditorForNode, CodeBlockNode} from "../../nodes/code-block";
-import {$selectionContainsNode, $selectSingleNode} from "../../helpers";
import {BaseSelection} from "lexical";
+import {$selectionContainsNode, $selectSingleNode} from "../../utils/selection";
export class CodeBlockDecorator extends EditorDecorator {
import {EditorDecorator} from "../framework/decorator";
import {EditorUiContext} from "../framework/core";
-import {$selectionContainsNode, $selectSingleNode} from "../../helpers";
import {BaseSelection} from "lexical";
import {$openDrawingEditorForNode, DiagramNode} from "../../nodes/diagram";
+import {$selectionContainsNode, $selectSingleNode} from "../../utils/selection";
export class DiagramDecorator extends EditorDecorator {
import {EditorDecorator} from "../framework/decorator";
-import {el, $selectSingleNode} from "../../helpers";
import {$createNodeSelection, $setSelection} from "lexical";
import {EditorUiContext} from "../framework/core";
import {ImageNode} from "../../nodes/image";
import {MouseDragTracker, MouseDragTrackerDistance} from "../framework/helpers/mouse-drag-tracker";
+import {$selectSingleNode} from "../../utils/selection";
+import {el} from "../../utils/dom";
export class ImageDecorator extends EditorDecorator {
import {$getSelection, BaseSelection, ElementFormatType} from "lexical";
-import {$getBlockElementNodesInSelection, $selectionContainsElementFormat} from "../../../helpers";
import {EditorButtonDefinition} from "../../framework/buttons";
import alignLeftIcon from "@icons/editor/align-left.svg";
import {EditorUiContext} from "../../framework/core";
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 {$getBlockElementNodesInSelection, $selectionContainsElementFormat} from "../../../utils/selection";
function setAlignmentForSection(alignment: ElementFormatType): void {
import {$createCalloutNode, $isCalloutNodeOfCategory, CalloutCategory} from "../../../nodes/callout";
import {EditorButtonDefinition} from "../../framework/buttons";
import {EditorUiContext} from "../../framework/core";
-import {$selectionContainsNodeType, $toggleSelectionBlockNodeType} from "../../../helpers";
import {$createParagraphNode, $isParagraphNode, BaseSelection, LexicalNode} from "lexical";
import {
$createHeadingNode,
HeadingNode,
HeadingTagType
} from "@lexical/rich-text";
+import {$selectionContainsNodeType, $toggleSelectionBlockNodeType} from "../../../utils/selection";
function buildCalloutButton(category: CalloutCategory, name: string): EditorButtonDefinition {
return {
} from "lexical";
import redoIcon from "@icons/editor/redo.svg";
import sourceIcon from "@icons/editor/source-view.svg";
-import {getEditorContentAsHtml} from "../../../actions";
+import {getEditorContentAsHtml} from "../../../utils/actions";
import fullscreenIcon from "@icons/editor/fullscreen.svg";
export const undo: EditorButtonDefinition = {
import {$getSelection, $isTextNode, BaseSelection, FORMAT_TEXT_COMMAND, TextFormatType} from "lexical";
import {EditorBasicButtonDefinition, EditorButtonDefinition} from "../../framework/buttons";
import {EditorUiContext} from "../../framework/core";
-import {$selectionContainsTextFormat} from "../../../helpers";
import boldIcon from "@icons/editor/bold.svg";
import italicIcon from "@icons/editor/italic.svg";
import underlinedIcon from "@icons/editor/underlined.svg";
import subscriptIcon from "@icons/editor/subscript.svg";
import codeIcon from "@icons/editor/code.svg";
import formatClearIcon from "@icons/editor/format-clear.svg";
+import {$selectionContainsTextFormat} from "../../../utils/selection";
function buildFormatButton(label: string, format: TextFormatType, icon: string): EditorButtonDefinition {
return {
import {EditorButtonDefinition} from "../../framework/buttons";
import {EditorUiContext} from "../../framework/core";
import {$getSelection, BaseSelection, LexicalNode} from "lexical";
-import {$selectionContainsNodeType} from "../../../helpers";
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 {$selectionContainsNodeType} from "../../../utils/selection";
function buildListButton(label: string, type: ListType, icon: string): EditorButtonDefinition {
BaseSelection,
ElementNode
} from "lexical";
-import {$getNodeFromSelection, $insertNewBlockNodeAtSelection, $selectionContainsNodeType} from "../../../helpers";
import {$isLinkNode, LinkNode} from "@lexical/link";
import unlinkIcon from "@icons/editor/unlink.svg";
import imageIcon from "@icons/editor/image.svg";
import mediaIcon from "@icons/editor/media.svg";
import {$createDetailsNode, $isDetailsNode} from "../../../nodes/details";
import {$isMediaNode, MediaNode} from "../../../nodes/media";
+import {
+ $getNodeFromSelection,
+ $insertNewBlockNodeAtSelection,
+ $selectionContainsNodeType
+} from "../../../utils/selection";
export const link: EditorButtonDefinition = {
label: 'Insert/edit link',
import insertRowAboveIcon from "@icons/editor/table-insert-row-above.svg";
import insertRowBelowIcon from "@icons/editor/table-insert-row-below.svg";
import {EditorUiContext} from "../../framework/core";
-import {
- $getNodeFromSelection, $getParentOfType,
- $selectionContainsNodeType
-} from "../../../helpers";
import {$getSelection, BaseSelection} from "lexical";
import {$isCustomTableNode} from "../../../nodes/custom-table";
import {
$insertTableRow__EXPERIMENTAL, $isTableCellNode,
$isTableNode, $isTableRowNode, $isTableSelection, $unmergeCell, TableCellNode, TableNode,
} from "@lexical/table";
+import {$getNodeFromSelection, $selectionContainsNodeType} from "../../../utils/selection";
+import {$getParentOfType} from "../../../utils/nodes";
const neverActive = (): boolean => false;
const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isTableCellNode);
import {EditorFormDefinition} from "../../framework/forms";
import {EditorUiContext} from "../../framework/core";
-import {setEditorContentFromHtml} from "../../../actions";
+import {setEditorContentFromHtml} from "../../../utils/actions";
export const source: EditorFormDefinition = {
submitText: 'Save',
import {$createImageNode} from "../../../nodes/image";
import {$createLinkNode} from "@lexical/link";
import {$createMediaNodeFromHtml, $createMediaNodeFromSrc, $isMediaNode, MediaNode} from "../../../nodes/media";
-import {$getNodeFromSelection} from "../../../helpers";
import {$insertNodeToNearestRoot} from "@lexical/utils";
+import {$getNodeFromSelection} from "../../../utils/selection";
export const image: EditorFormDefinition = {
submitText: 'Apply',
-import {el} from "../../../helpers";
import {EditorUiElement} from "../core";
import {$getSelection} from "lexical";
import {$patchStyleText} from "@lexical/selection";
+import {el} from "../../../utils/dom";
const colorChoices = [
'#000000',
-import {el} from "../../../helpers";
import {handleDropdown} from "../helpers/dropdowns";
import {EditorContainerUiElement, EditorUiElement} from "../core";
import {EditorBasicButtonDefinition, EditorButton} from "../buttons";
+import {el} from "../../../utils/dom";
export type EditorDropdownButtonOptions = {
showOnHover?: boolean;
-import {el} from "../../../helpers";
import {EditorUiStateUpdate, EditorContainerUiElement} from "../core";
import {EditorButton} from "../buttons";
import {handleDropdown} from "../helpers/dropdowns";
+import {el} from "../../../utils/dom";
export class EditorFormatMenu extends EditorContainerUiElement {
buildDOM(): HTMLElement {
-import {el} from "../../../helpers";
import {EditorButton, EditorButtonDefinition} from "../buttons";
+import {el} from "../../../utils/dom";
export class FormatPreviewButton extends EditorButton {
protected previewSampleElement: HTMLElement;
import {EditorContainerUiElement, EditorUiElement} from "../core";
-import {el} from "../../../helpers";
import {EditorDropdownButton} from "./dropdown-button";
import moreHorizontal from "@icons/editor/more-horizontal.svg"
+import {el} from "../../../utils/dom";
export class EditorOverflowContainer extends EditorContainerUiElement {
-import {el, $insertNewBlockNodeAtSelection} from "../../../helpers";
import {EditorUiElement} from "../core";
import {$createTableNodeWithDimensions} from "@lexical/table";
import {CustomTableNode} from "../../../nodes/custom-table";
+import {$insertNewBlockNodeAtSelection} from "../../../utils/selection";
+import {el} from "../../../utils/dom";
export class EditorTableCreator extends EditorUiElement {
import {BaseSelection} from "lexical";
import {EditorUiContext, EditorUiElement, EditorUiStateUpdate} from "./core";
-import {el} from "../../helpers";
+
+import {el} from "../../utils/dom";
export interface EditorBasicButtonDefinition {
label: string;
import {BaseSelection, LexicalEditor} from "lexical";
import {EditorUIManager} from "./manager";
-import {el} from "../../helpers";
+
+import {el} from "../../utils/dom";
export type EditorUiStateUpdate = {
editor: LexicalEditor;
EditorUiBuilderDefinition,
isUiBuilderDefinition
} from "./core";
-import {el} from "../../helpers";
import {uniqueId} from "../../../services/util";
+import {el} from "../../utils/dom";
export interface EditorFormFieldDefinition {
label: string;
import {$getNearestNodeFromDOMNode, LexicalEditor} from "lexical";
-import {el} from "../../../helpers";
import {MouseDragTracker, MouseDragTrackerDistance} from "./mouse-drag-tracker";
import {$getTableColumnWidth, $setTableColumnWidth, CustomTableNode} from "../../../nodes/custom-table";
import {TableRowNode} from "@lexical/table";
+import {el} from "../../../utils/dom";
type MarkerDomRecord = {x: HTMLElement, y: HTMLElement};
import {EditorForm, EditorFormDefinition} from "./forms";
-import {el} from "../../helpers";
import {EditorContainerUiElement} from "./core";
import closeIcon from "@icons/close.svg";
+import {el} from "../../utils/dom";
export interface EditorModalDefinition {
title: string;
import {EditorContainerUiElement, EditorUiElement} from "./core";
-import {el} from "../../helpers";
+
+import {el} from "../../utils/dom";
export type EditorContextToolbarDefinition = {
selector: string;
import {EditorButton} from "./framework/buttons";
import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiElement} from "./framework/core";
-import {$selectionContainsNodeType, el} from "../helpers";
import {EditorFormatMenu} from "./framework/blocks/format-menu";
import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
import {EditorDropdownButton} from "./framework/blocks/dropdown-button";
unlink
} from "./defaults/buttons/objects";
import {$isTableNode} from "@lexical/table";
+import {$selectionContainsNodeType} from "../utils/selection";
+import {el} from "../utils/dom";
export function getMainEditorFullToolbar(): EditorContainerUiElement {
return new EditorSimpleClassContainer('editor-toolbar-main', [
import {$getRoot, $getSelection, LexicalEditor} from "lexical";
import {$generateHtmlFromNodes} from "@lexical/html";
-import {$htmlToBlockNodes} from "./helpers";
-
-
+import {$htmlToBlockNodes} from "./nodes";
export function setEditorContentFromHtml(editor: LexicalEditor, html: string) {
editor.update(() => {
--- /dev/null
+export function el(tag: string, attrs: Record<string, string | null> = {}, children: (string | HTMLElement)[] = []): HTMLElement {
+ const el = document.createElement(tag);
+ const attrKeys = Object.keys(attrs);
+ for (const attr of attrKeys) {
+ if (attrs[attr] !== null) {
+ el.setAttribute(attr, attrs[attr] as string);
+ }
+ }
+
+ for (const child of children) {
+ if (typeof child === 'string') {
+ el.append(document.createTextNode(child));
+ } else {
+ el.append(child);
+ }
+ }
+
+ return el;
+}
+
+export function htmlToDom(html: string): Document {
+ const parser = new DOMParser();
+ return parser.parseFromString(html, 'text/html');
+}
\ No newline at end of file
--- /dev/null
+import {$getRoot, $isTextNode, LexicalEditor, LexicalNode} from "lexical";
+import {LexicalNodeMatcher} from "../nodes";
+import {$createCustomParagraphNode} from "../nodes/custom-paragraph";
+import {$generateNodesFromDOM} from "@lexical/html";
+import {htmlToDom} from "./dom";
+
+function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
+ return nodes.map(node => {
+ if ($isTextNode(node)) {
+ const paragraph = $createCustomParagraphNode();
+ paragraph.append(node);
+ return paragraph;
+ }
+ return node;
+ });
+}
+
+export function $htmlToBlockNodes(editor: LexicalEditor, html: string): LexicalNode[] {
+ const dom = htmlToDom(html);
+ const nodes = $generateNodesFromDOM(editor, dom);
+ return wrapTextNodes(nodes);
+}
+
+export function $getParentOfType(node: LexicalNode, matcher: LexicalNodeMatcher): LexicalNode | null {
+ for (const parent of node.getParents()) {
+ if (matcher(parent)) {
+ return parent;
+ }
+ }
+
+ return null;
+}
+
+/**
+ * Get the nearest root/block level node for the given position.
+ */
+export function $getNearestBlockNodeForCoords(editor: LexicalEditor, x: number, y: number): LexicalNode | null {
+ // TODO - Take into account x for floated blocks?
+ const rootNodes = $getRoot().getChildren();
+ for (const node of rootNodes) {
+ const nodeDom = editor.getElementByKey(node.__key);
+ if (!nodeDom) {
+ continue;
+ }
+
+ const bounds = nodeDom.getBoundingClientRect();
+ if (y <= bounds.bottom) {
+ return node;
+ }
+ }
+
+ return null;
+}
\ No newline at end of file
import {
$createNodeSelection,
- $createParagraphNode, $getRoot,
- $getSelection, $isElementNode,
- $isTextNode, $setSelection,
- BaseSelection, ElementFormatType, ElementNode, LexicalEditor,
- LexicalNode, TextFormatType
+ $createParagraphNode,
+ $getRoot,
+ $getSelection,
+ $isElementNode,
+ $isTextNode,
+ $setSelection,
+ BaseSelection,
+ ElementFormatType,
+ ElementNode,
+ LexicalNode,
+ TextFormatType
} from "lexical";
-import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
import {$findMatchingParent, $getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
+import {LexicalElementNodeCreator, LexicalNodeMatcher} from "../nodes";
import {$setBlocksType} from "@lexical/selection";
-import {$createCustomParagraphNode} from "./nodes/custom-paragraph";
-import {$generateNodesFromDOM} from "@lexical/html";
-
-export function el(tag: string, attrs: Record<string, string|null> = {}, children: (string|HTMLElement)[] = []): HTMLElement {
- const el = document.createElement(tag);
- const attrKeys = Object.keys(attrs);
- for (const attr of attrKeys) {
- if (attrs[attr] !== null) {
- el.setAttribute(attr, attrs[attr] as string);
- }
- }
-
- for (const child of children) {
- if (typeof child === 'string') {
- el.append(document.createTextNode(child));
- } else {
- el.append(child);
- }
- }
- return el;
-}
+import {$getParentOfType} from "./nodes";
-function htmlToDom(html: string): Document {
- const parser = new DOMParser();
- return parser.parseFromString(html, 'text/html');
-}
-
-function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
- return nodes.map(node => {
- if ($isTextNode(node)) {
- const paragraph = $createCustomParagraphNode();
- paragraph.append(node);
- return paragraph;
- }
- return node;
- });
-}
-
-export function $htmlToBlockNodes(editor: LexicalEditor, html: string): LexicalNode[] {
- const dom = htmlToDom(html);
- const nodes = $generateNodesFromDOM(editor, dom);
- return wrapTextNodes(nodes);
-}
-
-export function $selectionContainsNodeType(selection: BaseSelection|null, matcher: LexicalNodeMatcher): boolean {
+export function $selectionContainsNodeType(selection: BaseSelection | null, matcher: LexicalNodeMatcher): boolean {
return $getNodeFromSelection(selection, matcher) !== null;
}
-export function $getNodeFromSelection(selection: BaseSelection|null, matcher: LexicalNodeMatcher): LexicalNode|null {
+export function $getNodeFromSelection(selection: BaseSelection | null, matcher: LexicalNodeMatcher): LexicalNode | null {
if (!selection) {
return null;
}
return null;
}
-export function $getParentOfType(node: LexicalNode, matcher: LexicalNodeMatcher): LexicalNode|null {
- for (const parent of node.getParents()) {
- if (matcher(parent)) {
- return parent;
- }
- }
-
- return null;
-}
-
-export function $selectionContainsTextFormat(selection: BaseSelection|null, format: TextFormatType): boolean {
+export function $selectionContainsTextFormat(selection: BaseSelection | null, format: TextFormatType): boolean {
if (!selection) {
return false;
}
$setSelection(nodeSelection);
}
-export function $selectionContainsNode(selection: BaseSelection|null, node: LexicalNode): boolean {
+export function $selectionContainsNode(selection: BaseSelection | null, node: LexicalNode): boolean {
if (!selection) {
return false;
}
return false;
}
-export function $selectionContainsElementFormat(selection: BaseSelection|null, format: ElementFormatType): boolean {
+export function $selectionContainsElementFormat(selection: BaseSelection | null, format: ElementFormatType): boolean {
const nodes = $getBlockElementNodesInSelection(selection);
for (const node of nodes) {
if (node.getFormatType() === format) {
return false;
}
-export function $getBlockElementNodesInSelection(selection: BaseSelection|null): ElementNode[] {
+export function $getBlockElementNodesInSelection(selection: BaseSelection | null): ElementNode[] {
if (!selection) {
return [];
}
for (const node of selection.getNodes()) {
const blockElement = $findMatchingParent(node, (node) => {
return $isElementNode(node) && !node.isInline();
- }) as ElementNode|null;
+ }) as ElementNode | null;
if (blockElement) {
blockNodes.set(blockElement.getKey(), blockElement);
}
return Array.from(blockNodes.values());
-}
-
-/**
- * Get the nearest root/block level node for the given position.
- */
-export function $getNearestBlockNodeForCoords(editor: LexicalEditor, x: number, y: number): LexicalNode|null {
- // TODO - Take into account x for floated blocks?
- const rootNodes = $getRoot().getChildren();
- for (const node of rootNodes) {
- const nodeDom = editor.getElementByKey(node.__key);
- if (!nodeDom) {
- continue;
- }
-
- const bounds = nodeDom.getBoundingClientRect();
- if (y <= bounds.bottom) {
- return node;
- }
- }
-
- return null;
}
\ No newline at end of file