mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-30 07:32:39 +01:00
Lexical: Split helpers to utils, refactored files
This commit is contained in:
parent
e94ad78ea7
commit
efec752985
@ -4,14 +4,14 @@ import {registerRichText} from '@lexical/rich-text';
|
|||||||
import {mergeRegister} from '@lexical/utils';
|
import {mergeRegister} from '@lexical/utils';
|
||||||
import {getNodesForPageEditor, registerCommonNodeMutationListeners} from './nodes';
|
import {getNodesForPageEditor, registerCommonNodeMutationListeners} from './nodes';
|
||||||
import {buildEditorUI} from "./ui";
|
import {buildEditorUI} from "./ui";
|
||||||
import {getEditorContentAsHtml, setEditorContentFromHtml} from "./actions";
|
import {getEditorContentAsHtml, setEditorContentFromHtml} from "./utils/actions";
|
||||||
import {registerTableResizer} from "./ui/framework/helpers/table-resizer";
|
import {registerTableResizer} from "./ui/framework/helpers/table-resizer";
|
||||||
import {el} from "./helpers";
|
|
||||||
import {EditorUiContext} from "./ui/framework/core";
|
import {EditorUiContext} from "./ui/framework/core";
|
||||||
import {listen as listenToCommonEvents} from "./common-events";
|
import {listen as listenToCommonEvents} from "./services/common-events";
|
||||||
import {handleDropEvents} from "./drop-handling";
|
import {handleDropEvents} from "./services/drop-handling";
|
||||||
import {registerTaskListHandler} from "./ui/framework/helpers/task-list-handler";
|
import {registerTaskListHandler} from "./ui/framework/helpers/task-list-handler";
|
||||||
import {registerTableSelectionHandler} from "./ui/framework/helpers/table-selection-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 {
|
export function createPageEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface {
|
||||||
const config: CreateEditorArgs = {
|
const config: CreateEditorArgs = {
|
||||||
|
@ -8,9 +8,9 @@ import {
|
|||||||
Spread
|
Spread
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
import type {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import {el} from "../helpers";
|
|
||||||
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
|
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
|
||||||
import {CodeEditor} from "../../components";
|
import {CodeEditor} from "../../components";
|
||||||
|
import {el} from "../utils/dom";
|
||||||
|
|
||||||
export type SerializedCodeBlockNode = Spread<{
|
export type SerializedCodeBlockNode = Spread<{
|
||||||
language: string;
|
language: string;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import {$isListNode, ListItemNode, SerializedListItemNode} from "@lexical/list";
|
import {$isListNode, ListItemNode, SerializedListItemNode} from "@lexical/list";
|
||||||
import {EditorConfig} from "lexical/LexicalEditor";
|
import {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import {DOMExportOutput, LexicalEditor, LexicalNode} from "lexical";
|
import {DOMExportOutput, LexicalEditor, LexicalNode} from "lexical";
|
||||||
import {el} from "../helpers";
|
|
||||||
|
import {el} from "../utils/dom";
|
||||||
|
|
||||||
function updateListItemChecked(
|
function updateListItemChecked(
|
||||||
dom: HTMLElement,
|
dom: HTMLElement,
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import {SerializedTableNode, TableNode, TableRowNode} from "@lexical/table";
|
import {SerializedTableNode, TableNode, TableRowNode} from "@lexical/table";
|
||||||
import {DOMConversion, DOMConversionMap, DOMConversionOutput, LexicalEditor, LexicalNode, Spread} from "lexical";
|
import {DOMConversion, DOMConversionMap, DOMConversionOutput, LexicalEditor, LexicalNode, Spread} from "lexical";
|
||||||
import {EditorConfig} from "lexical/LexicalEditor";
|
import {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import {el} from "../helpers";
|
|
||||||
|
import {el} from "../utils/dom";
|
||||||
|
|
||||||
export type SerializedCustomTableNode = Spread<{
|
export type SerializedCustomTableNode = Spread<{
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -7,7 +7,8 @@ import {
|
|||||||
SerializedElementNode,
|
SerializedElementNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
import type {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import {el} from "../helpers";
|
|
||||||
|
import {el} from "../utils/dom";
|
||||||
|
|
||||||
export class DetailsNode extends ElementNode {
|
export class DetailsNode extends ElementNode {
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@ import {
|
|||||||
Spread
|
Spread
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
import type {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import {el} from "../helpers";
|
|
||||||
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
|
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
|
||||||
import * as DrawIO from '../../services/drawio';
|
import * as DrawIO from '../../services/drawio';
|
||||||
import {EditorUiContext} from "../ui/framework/core";
|
import {EditorUiContext} from "../ui/framework/core";
|
||||||
import {HttpError} from "../../services/http";
|
import {HttpError} from "../../services/http";
|
||||||
|
import {el} from "../utils/dom";
|
||||||
|
|
||||||
export type SerializedDiagramNode = Spread<{
|
export type SerializedDiagramNode = Spread<{
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -8,8 +8,8 @@ import {
|
|||||||
Spread
|
Spread
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
import type {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import {el} from "../helpers";
|
|
||||||
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
|
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
|
||||||
|
import {el} from "../utils/dom";
|
||||||
|
|
||||||
export interface ImageNodeOptions {
|
export interface ImageNodeOptions {
|
||||||
alt?: string;
|
alt?: string;
|
||||||
|
@ -7,7 +7,8 @@ import {
|
|||||||
SerializedElementNode, Spread
|
SerializedElementNode, Spread
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
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 MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio';
|
||||||
export type MediaNodeSource = {
|
export type MediaNodeSource = {
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
insertHtmlIntoEditor,
|
insertHtmlIntoEditor,
|
||||||
prependHtmlToEditor,
|
prependHtmlToEditor,
|
||||||
setEditorContentFromHtml
|
setEditorContentFromHtml
|
||||||
} from "./actions";
|
} from "../utils/actions";
|
||||||
|
|
||||||
type EditorEventContent = {
|
type EditorEventContent = {
|
||||||
html: string;
|
html: string;
|
@ -3,12 +3,8 @@ import {
|
|||||||
LexicalEditor,
|
LexicalEditor,
|
||||||
LexicalNode
|
LexicalNode
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import {
|
import {$insertNewBlockNodesAtSelection, $selectSingleNode} from "../utils/selection";
|
||||||
$getNearestBlockNodeForCoords,
|
import {$getNearestBlockNodeForCoords, $htmlToBlockNodes} from "../utils/nodes";
|
||||||
$htmlToBlockNodes,
|
|
||||||
$insertNewBlockNodesAtSelection,
|
|
||||||
$selectSingleNode
|
|
||||||
} from "./helpers";
|
|
||||||
|
|
||||||
function $getNodeFromMouseEvent(event: MouseEvent, editor: LexicalEditor): LexicalNode|null {
|
function $getNodeFromMouseEvent(event: MouseEvent, editor: LexicalEditor): LexicalNode|null {
|
||||||
const x = event.clientX;
|
const x = event.clientX;
|
@ -1,8 +1,8 @@
|
|||||||
import {EditorDecorator} from "../framework/decorator";
|
import {EditorDecorator} from "../framework/decorator";
|
||||||
import {EditorUiContext} from "../framework/core";
|
import {EditorUiContext} from "../framework/core";
|
||||||
import {$openCodeEditorForNode, CodeBlockNode} from "../../nodes/code-block";
|
import {$openCodeEditorForNode, CodeBlockNode} from "../../nodes/code-block";
|
||||||
import {$selectionContainsNode, $selectSingleNode} from "../../helpers";
|
|
||||||
import {BaseSelection} from "lexical";
|
import {BaseSelection} from "lexical";
|
||||||
|
import {$selectionContainsNode, $selectSingleNode} from "../../utils/selection";
|
||||||
|
|
||||||
|
|
||||||
export class CodeBlockDecorator extends EditorDecorator {
|
export class CodeBlockDecorator extends EditorDecorator {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {EditorDecorator} from "../framework/decorator";
|
import {EditorDecorator} from "../framework/decorator";
|
||||||
import {EditorUiContext} from "../framework/core";
|
import {EditorUiContext} from "../framework/core";
|
||||||
import {$selectionContainsNode, $selectSingleNode} from "../../helpers";
|
|
||||||
import {BaseSelection} from "lexical";
|
import {BaseSelection} from "lexical";
|
||||||
import {$openDrawingEditorForNode, DiagramNode} from "../../nodes/diagram";
|
import {$openDrawingEditorForNode, DiagramNode} from "../../nodes/diagram";
|
||||||
|
import {$selectionContainsNode, $selectSingleNode} from "../../utils/selection";
|
||||||
|
|
||||||
|
|
||||||
export class DiagramDecorator extends EditorDecorator {
|
export class DiagramDecorator extends EditorDecorator {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import {EditorDecorator} from "../framework/decorator";
|
import {EditorDecorator} from "../framework/decorator";
|
||||||
import {el, $selectSingleNode} from "../../helpers";
|
|
||||||
import {$createNodeSelection, $setSelection} from "lexical";
|
import {$createNodeSelection, $setSelection} from "lexical";
|
||||||
import {EditorUiContext} from "../framework/core";
|
import {EditorUiContext} from "../framework/core";
|
||||||
import {ImageNode} from "../../nodes/image";
|
import {ImageNode} from "../../nodes/image";
|
||||||
import {MouseDragTracker, MouseDragTrackerDistance} from "../framework/helpers/mouse-drag-tracker";
|
import {MouseDragTracker, MouseDragTrackerDistance} from "../framework/helpers/mouse-drag-tracker";
|
||||||
|
import {$selectSingleNode} from "../../utils/selection";
|
||||||
|
import {el} from "../../utils/dom";
|
||||||
|
|
||||||
|
|
||||||
export class ImageDecorator extends EditorDecorator {
|
export class ImageDecorator extends EditorDecorator {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import {$getSelection, BaseSelection, ElementFormatType} from "lexical";
|
import {$getSelection, BaseSelection, ElementFormatType} from "lexical";
|
||||||
import {$getBlockElementNodesInSelection, $selectionContainsElementFormat} from "../../../helpers";
|
|
||||||
import {EditorButtonDefinition} from "../../framework/buttons";
|
import {EditorButtonDefinition} from "../../framework/buttons";
|
||||||
import alignLeftIcon from "@icons/editor/align-left.svg";
|
import alignLeftIcon from "@icons/editor/align-left.svg";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import alignCenterIcon from "@icons/editor/align-center.svg";
|
import alignCenterIcon from "@icons/editor/align-center.svg";
|
||||||
import alignRightIcon from "@icons/editor/align-right.svg";
|
import alignRightIcon from "@icons/editor/align-right.svg";
|
||||||
import alignJustifyIcon from "@icons/editor/align-justify.svg";
|
import alignJustifyIcon from "@icons/editor/align-justify.svg";
|
||||||
|
import {$getBlockElementNodesInSelection, $selectionContainsElementFormat} from "../../../utils/selection";
|
||||||
|
|
||||||
|
|
||||||
function setAlignmentForSection(alignment: ElementFormatType): void {
|
function setAlignmentForSection(alignment: ElementFormatType): void {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import {$createCalloutNode, $isCalloutNodeOfCategory, CalloutCategory} from "../../../nodes/callout";
|
import {$createCalloutNode, $isCalloutNodeOfCategory, CalloutCategory} from "../../../nodes/callout";
|
||||||
import {EditorButtonDefinition} from "../../framework/buttons";
|
import {EditorButtonDefinition} from "../../framework/buttons";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {$selectionContainsNodeType, $toggleSelectionBlockNodeType} from "../../../helpers";
|
|
||||||
import {$createParagraphNode, $isParagraphNode, BaseSelection, LexicalNode} from "lexical";
|
import {$createParagraphNode, $isParagraphNode, BaseSelection, LexicalNode} from "lexical";
|
||||||
import {
|
import {
|
||||||
$createHeadingNode,
|
$createHeadingNode,
|
||||||
@ -11,6 +10,7 @@ import {
|
|||||||
HeadingNode,
|
HeadingNode,
|
||||||
HeadingTagType
|
HeadingTagType
|
||||||
} from "@lexical/rich-text";
|
} from "@lexical/rich-text";
|
||||||
|
import {$selectionContainsNodeType, $toggleSelectionBlockNodeType} from "../../../utils/selection";
|
||||||
|
|
||||||
function buildCalloutButton(category: CalloutCategory, name: string): EditorButtonDefinition {
|
function buildCalloutButton(category: CalloutCategory, name: string): EditorButtonDefinition {
|
||||||
return {
|
return {
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
} from "lexical";
|
} from "lexical";
|
||||||
import redoIcon from "@icons/editor/redo.svg";
|
import redoIcon from "@icons/editor/redo.svg";
|
||||||
import sourceIcon from "@icons/editor/source-view.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";
|
import fullscreenIcon from "@icons/editor/fullscreen.svg";
|
||||||
|
|
||||||
export const undo: EditorButtonDefinition = {
|
export const undo: EditorButtonDefinition = {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import {$getSelection, $isTextNode, BaseSelection, FORMAT_TEXT_COMMAND, TextFormatType} from "lexical";
|
import {$getSelection, $isTextNode, BaseSelection, FORMAT_TEXT_COMMAND, TextFormatType} from "lexical";
|
||||||
import {EditorBasicButtonDefinition, EditorButtonDefinition} from "../../framework/buttons";
|
import {EditorBasicButtonDefinition, EditorButtonDefinition} from "../../framework/buttons";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {$selectionContainsTextFormat} from "../../../helpers";
|
|
||||||
import boldIcon from "@icons/editor/bold.svg";
|
import boldIcon from "@icons/editor/bold.svg";
|
||||||
import italicIcon from "@icons/editor/italic.svg";
|
import italicIcon from "@icons/editor/italic.svg";
|
||||||
import underlinedIcon from "@icons/editor/underlined.svg";
|
import underlinedIcon from "@icons/editor/underlined.svg";
|
||||||
@ -12,6 +11,7 @@ import superscriptIcon from "@icons/editor/superscript.svg";
|
|||||||
import subscriptIcon from "@icons/editor/subscript.svg";
|
import subscriptIcon from "@icons/editor/subscript.svg";
|
||||||
import codeIcon from "@icons/editor/code.svg";
|
import codeIcon from "@icons/editor/code.svg";
|
||||||
import formatClearIcon from "@icons/editor/format-clear.svg";
|
import formatClearIcon from "@icons/editor/format-clear.svg";
|
||||||
|
import {$selectionContainsTextFormat} from "../../../utils/selection";
|
||||||
|
|
||||||
function buildFormatButton(label: string, format: TextFormatType, icon: string): EditorButtonDefinition {
|
function buildFormatButton(label: string, format: TextFormatType, icon: string): EditorButtonDefinition {
|
||||||
return {
|
return {
|
||||||
|
@ -2,10 +2,10 @@ import {$isListNode, insertList, ListNode, ListType, removeList} from "@lexical/
|
|||||||
import {EditorButtonDefinition} from "../../framework/buttons";
|
import {EditorButtonDefinition} from "../../framework/buttons";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {$getSelection, BaseSelection, LexicalNode} from "lexical";
|
import {$getSelection, BaseSelection, LexicalNode} from "lexical";
|
||||||
import {$selectionContainsNodeType} from "../../../helpers";
|
|
||||||
import listBulletIcon from "@icons/editor/list-bullet.svg";
|
import listBulletIcon from "@icons/editor/list-bullet.svg";
|
||||||
import listNumberedIcon from "@icons/editor/list-numbered.svg";
|
import listNumberedIcon from "@icons/editor/list-numbered.svg";
|
||||||
import listCheckIcon from "@icons/editor/list-check.svg";
|
import listCheckIcon from "@icons/editor/list-check.svg";
|
||||||
|
import {$selectionContainsNodeType} from "../../../utils/selection";
|
||||||
|
|
||||||
|
|
||||||
function buildListButton(label: string, type: ListType, icon: string): EditorButtonDefinition {
|
function buildListButton(label: string, type: ListType, icon: string): EditorButtonDefinition {
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
BaseSelection,
|
BaseSelection,
|
||||||
ElementNode
|
ElementNode
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import {$getNodeFromSelection, $insertNewBlockNodeAtSelection, $selectionContainsNodeType} from "../../../helpers";
|
|
||||||
import {$isLinkNode, LinkNode} from "@lexical/link";
|
import {$isLinkNode, LinkNode} from "@lexical/link";
|
||||||
import unlinkIcon from "@icons/editor/unlink.svg";
|
import unlinkIcon from "@icons/editor/unlink.svg";
|
||||||
import imageIcon from "@icons/editor/image.svg";
|
import imageIcon from "@icons/editor/image.svg";
|
||||||
@ -26,6 +25,11 @@ import detailsIcon from "@icons/editor/details.svg";
|
|||||||
import mediaIcon from "@icons/editor/media.svg";
|
import mediaIcon from "@icons/editor/media.svg";
|
||||||
import {$createDetailsNode, $isDetailsNode} from "../../../nodes/details";
|
import {$createDetailsNode, $isDetailsNode} from "../../../nodes/details";
|
||||||
import {$isMediaNode, MediaNode} from "../../../nodes/media";
|
import {$isMediaNode, MediaNode} from "../../../nodes/media";
|
||||||
|
import {
|
||||||
|
$getNodeFromSelection,
|
||||||
|
$insertNewBlockNodeAtSelection,
|
||||||
|
$selectionContainsNodeType
|
||||||
|
} from "../../../utils/selection";
|
||||||
|
|
||||||
export const link: EditorButtonDefinition = {
|
export const link: EditorButtonDefinition = {
|
||||||
label: 'Insert/edit link',
|
label: 'Insert/edit link',
|
||||||
|
@ -8,10 +8,6 @@ import insertColumnBeforeIcon from "@icons/editor/table-insert-column-before.svg
|
|||||||
import insertRowAboveIcon from "@icons/editor/table-insert-row-above.svg";
|
import insertRowAboveIcon from "@icons/editor/table-insert-row-above.svg";
|
||||||
import insertRowBelowIcon from "@icons/editor/table-insert-row-below.svg";
|
import insertRowBelowIcon from "@icons/editor/table-insert-row-below.svg";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {
|
|
||||||
$getNodeFromSelection, $getParentOfType,
|
|
||||||
$selectionContainsNodeType
|
|
||||||
} from "../../../helpers";
|
|
||||||
import {$getSelection, BaseSelection} from "lexical";
|
import {$getSelection, BaseSelection} from "lexical";
|
||||||
import {$isCustomTableNode} from "../../../nodes/custom-table";
|
import {$isCustomTableNode} from "../../../nodes/custom-table";
|
||||||
import {
|
import {
|
||||||
@ -22,6 +18,8 @@ import {
|
|||||||
$insertTableRow__EXPERIMENTAL, $isTableCellNode,
|
$insertTableRow__EXPERIMENTAL, $isTableCellNode,
|
||||||
$isTableNode, $isTableRowNode, $isTableSelection, $unmergeCell, TableCellNode, TableNode,
|
$isTableNode, $isTableRowNode, $isTableSelection, $unmergeCell, TableCellNode, TableNode,
|
||||||
} from "@lexical/table";
|
} from "@lexical/table";
|
||||||
|
import {$getNodeFromSelection, $selectionContainsNodeType} from "../../../utils/selection";
|
||||||
|
import {$getParentOfType} from "../../../utils/nodes";
|
||||||
|
|
||||||
const neverActive = (): boolean => false;
|
const neverActive = (): boolean => false;
|
||||||
const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isTableCellNode);
|
const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isTableCellNode);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {EditorFormDefinition} from "../../framework/forms";
|
import {EditorFormDefinition} from "../../framework/forms";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {setEditorContentFromHtml} from "../../../actions";
|
import {setEditorContentFromHtml} from "../../../utils/actions";
|
||||||
|
|
||||||
export const source: EditorFormDefinition = {
|
export const source: EditorFormDefinition = {
|
||||||
submitText: 'Save',
|
submitText: 'Save',
|
||||||
|
@ -4,8 +4,8 @@ import {$createTextNode, $getSelection} from "lexical";
|
|||||||
import {$createImageNode} from "../../../nodes/image";
|
import {$createImageNode} from "../../../nodes/image";
|
||||||
import {$createLinkNode} from "@lexical/link";
|
import {$createLinkNode} from "@lexical/link";
|
||||||
import {$createMediaNodeFromHtml, $createMediaNodeFromSrc, $isMediaNode, MediaNode} from "../../../nodes/media";
|
import {$createMediaNodeFromHtml, $createMediaNodeFromSrc, $isMediaNode, MediaNode} from "../../../nodes/media";
|
||||||
import {$getNodeFromSelection} from "../../../helpers";
|
|
||||||
import {$insertNodeToNearestRoot} from "@lexical/utils";
|
import {$insertNodeToNearestRoot} from "@lexical/utils";
|
||||||
|
import {$getNodeFromSelection} from "../../../utils/selection";
|
||||||
|
|
||||||
export const image: EditorFormDefinition = {
|
export const image: EditorFormDefinition = {
|
||||||
submitText: 'Apply',
|
submitText: 'Apply',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {el} from "../../../helpers";
|
|
||||||
import {EditorUiElement} from "../core";
|
import {EditorUiElement} from "../core";
|
||||||
import {$getSelection} from "lexical";
|
import {$getSelection} from "lexical";
|
||||||
import {$patchStyleText} from "@lexical/selection";
|
import {$patchStyleText} from "@lexical/selection";
|
||||||
|
import {el} from "../../../utils/dom";
|
||||||
|
|
||||||
const colorChoices = [
|
const colorChoices = [
|
||||||
'#000000',
|
'#000000',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {el} from "../../../helpers";
|
|
||||||
import {handleDropdown} from "../helpers/dropdowns";
|
import {handleDropdown} from "../helpers/dropdowns";
|
||||||
import {EditorContainerUiElement, EditorUiElement} from "../core";
|
import {EditorContainerUiElement, EditorUiElement} from "../core";
|
||||||
import {EditorBasicButtonDefinition, EditorButton} from "../buttons";
|
import {EditorBasicButtonDefinition, EditorButton} from "../buttons";
|
||||||
|
import {el} from "../../../utils/dom";
|
||||||
|
|
||||||
export type EditorDropdownButtonOptions = {
|
export type EditorDropdownButtonOptions = {
|
||||||
showOnHover?: boolean;
|
showOnHover?: boolean;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {el} from "../../../helpers";
|
|
||||||
import {EditorUiStateUpdate, EditorContainerUiElement} from "../core";
|
import {EditorUiStateUpdate, EditorContainerUiElement} from "../core";
|
||||||
import {EditorButton} from "../buttons";
|
import {EditorButton} from "../buttons";
|
||||||
import {handleDropdown} from "../helpers/dropdowns";
|
import {handleDropdown} from "../helpers/dropdowns";
|
||||||
|
import {el} from "../../../utils/dom";
|
||||||
|
|
||||||
export class EditorFormatMenu extends EditorContainerUiElement {
|
export class EditorFormatMenu extends EditorContainerUiElement {
|
||||||
buildDOM(): HTMLElement {
|
buildDOM(): HTMLElement {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {el} from "../../../helpers";
|
|
||||||
import {EditorButton, EditorButtonDefinition} from "../buttons";
|
import {EditorButton, EditorButtonDefinition} from "../buttons";
|
||||||
|
import {el} from "../../../utils/dom";
|
||||||
|
|
||||||
export class FormatPreviewButton extends EditorButton {
|
export class FormatPreviewButton extends EditorButton {
|
||||||
protected previewSampleElement: HTMLElement;
|
protected previewSampleElement: HTMLElement;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {EditorContainerUiElement, EditorUiElement} from "../core";
|
import {EditorContainerUiElement, EditorUiElement} from "../core";
|
||||||
import {el} from "../../../helpers";
|
|
||||||
import {EditorDropdownButton} from "./dropdown-button";
|
import {EditorDropdownButton} from "./dropdown-button";
|
||||||
import moreHorizontal from "@icons/editor/more-horizontal.svg"
|
import moreHorizontal from "@icons/editor/more-horizontal.svg"
|
||||||
|
import {el} from "../../../utils/dom";
|
||||||
|
|
||||||
|
|
||||||
export class EditorOverflowContainer extends EditorContainerUiElement {
|
export class EditorOverflowContainer extends EditorContainerUiElement {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import {el, $insertNewBlockNodeAtSelection} from "../../../helpers";
|
|
||||||
import {EditorUiElement} from "../core";
|
import {EditorUiElement} from "../core";
|
||||||
import {$createTableNodeWithDimensions} from "@lexical/table";
|
import {$createTableNodeWithDimensions} from "@lexical/table";
|
||||||
import {CustomTableNode} from "../../../nodes/custom-table";
|
import {CustomTableNode} from "../../../nodes/custom-table";
|
||||||
|
import {$insertNewBlockNodeAtSelection} from "../../../utils/selection";
|
||||||
|
import {el} from "../../../utils/dom";
|
||||||
|
|
||||||
|
|
||||||
export class EditorTableCreator extends EditorUiElement {
|
export class EditorTableCreator extends EditorUiElement {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {BaseSelection} from "lexical";
|
import {BaseSelection} from "lexical";
|
||||||
import {EditorUiContext, EditorUiElement, EditorUiStateUpdate} from "./core";
|
import {EditorUiContext, EditorUiElement, EditorUiStateUpdate} from "./core";
|
||||||
import {el} from "../../helpers";
|
|
||||||
|
import {el} from "../../utils/dom";
|
||||||
|
|
||||||
export interface EditorBasicButtonDefinition {
|
export interface EditorBasicButtonDefinition {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {BaseSelection, LexicalEditor} from "lexical";
|
import {BaseSelection, LexicalEditor} from "lexical";
|
||||||
import {EditorUIManager} from "./manager";
|
import {EditorUIManager} from "./manager";
|
||||||
import {el} from "../../helpers";
|
|
||||||
|
import {el} from "../../utils/dom";
|
||||||
|
|
||||||
export type EditorUiStateUpdate = {
|
export type EditorUiStateUpdate = {
|
||||||
editor: LexicalEditor;
|
editor: LexicalEditor;
|
||||||
|
@ -5,8 +5,8 @@ import {
|
|||||||
EditorUiBuilderDefinition,
|
EditorUiBuilderDefinition,
|
||||||
isUiBuilderDefinition
|
isUiBuilderDefinition
|
||||||
} from "./core";
|
} from "./core";
|
||||||
import {el} from "../../helpers";
|
|
||||||
import {uniqueId} from "../../../services/util";
|
import {uniqueId} from "../../../services/util";
|
||||||
|
import {el} from "../../utils/dom";
|
||||||
|
|
||||||
export interface EditorFormFieldDefinition {
|
export interface EditorFormFieldDefinition {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {$getNearestNodeFromDOMNode, LexicalEditor} from "lexical";
|
import {$getNearestNodeFromDOMNode, LexicalEditor} from "lexical";
|
||||||
import {el} from "../../../helpers";
|
|
||||||
import {MouseDragTracker, MouseDragTrackerDistance} from "./mouse-drag-tracker";
|
import {MouseDragTracker, MouseDragTrackerDistance} from "./mouse-drag-tracker";
|
||||||
import {$getTableColumnWidth, $setTableColumnWidth, CustomTableNode} from "../../../nodes/custom-table";
|
import {$getTableColumnWidth, $setTableColumnWidth, CustomTableNode} from "../../../nodes/custom-table";
|
||||||
import {TableRowNode} from "@lexical/table";
|
import {TableRowNode} from "@lexical/table";
|
||||||
|
import {el} from "../../../utils/dom";
|
||||||
|
|
||||||
type MarkerDomRecord = {x: HTMLElement, y: HTMLElement};
|
type MarkerDomRecord = {x: HTMLElement, y: HTMLElement};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {EditorForm, EditorFormDefinition} from "./forms";
|
import {EditorForm, EditorFormDefinition} from "./forms";
|
||||||
import {el} from "../../helpers";
|
|
||||||
import {EditorContainerUiElement} from "./core";
|
import {EditorContainerUiElement} from "./core";
|
||||||
import closeIcon from "@icons/close.svg";
|
import closeIcon from "@icons/close.svg";
|
||||||
|
import {el} from "../../utils/dom";
|
||||||
|
|
||||||
export interface EditorModalDefinition {
|
export interface EditorModalDefinition {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {EditorContainerUiElement, EditorUiElement} from "./core";
|
import {EditorContainerUiElement, EditorUiElement} from "./core";
|
||||||
import {el} from "../../helpers";
|
|
||||||
|
import {el} from "../../utils/dom";
|
||||||
|
|
||||||
export type EditorContextToolbarDefinition = {
|
export type EditorContextToolbarDefinition = {
|
||||||
selector: string;
|
selector: string;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {EditorButton} from "./framework/buttons";
|
import {EditorButton} from "./framework/buttons";
|
||||||
import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiElement} from "./framework/core";
|
import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiElement} from "./framework/core";
|
||||||
import {$selectionContainsNodeType, el} from "../helpers";
|
|
||||||
import {EditorFormatMenu} from "./framework/blocks/format-menu";
|
import {EditorFormatMenu} from "./framework/blocks/format-menu";
|
||||||
import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
|
import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
|
||||||
import {EditorDropdownButton} from "./framework/blocks/dropdown-button";
|
import {EditorDropdownButton} from "./framework/blocks/dropdown-button";
|
||||||
@ -65,6 +64,8 @@ import {
|
|||||||
unlink
|
unlink
|
||||||
} from "./defaults/buttons/objects";
|
} from "./defaults/buttons/objects";
|
||||||
import {$isTableNode} from "@lexical/table";
|
import {$isTableNode} from "@lexical/table";
|
||||||
|
import {$selectionContainsNodeType} from "../utils/selection";
|
||||||
|
import {el} from "../utils/dom";
|
||||||
|
|
||||||
export function getMainEditorFullToolbar(): EditorContainerUiElement {
|
export function getMainEditorFullToolbar(): EditorContainerUiElement {
|
||||||
return new EditorSimpleClassContainer('editor-toolbar-main', [
|
return new EditorSimpleClassContainer('editor-toolbar-main', [
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import {$getRoot, $getSelection, LexicalEditor} from "lexical";
|
import {$getRoot, $getSelection, LexicalEditor} from "lexical";
|
||||||
import {$generateHtmlFromNodes} from "@lexical/html";
|
import {$generateHtmlFromNodes} from "@lexical/html";
|
||||||
import {$htmlToBlockNodes} from "./helpers";
|
import {$htmlToBlockNodes} from "./nodes";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function setEditorContentFromHtml(editor: LexicalEditor, html: string) {
|
export function setEditorContentFromHtml(editor: LexicalEditor, html: string) {
|
||||||
editor.update(() => {
|
editor.update(() => {
|
24
resources/js/wysiwyg/utils/dom.ts
Normal file
24
resources/js/wysiwyg/utils/dom.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
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');
|
||||||
|
}
|
53
resources/js/wysiwyg/utils/nodes.ts
Normal file
53
resources/js/wysiwyg/utils/nodes.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
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;
|
||||||
|
}
|
@ -1,64 +1,28 @@
|
|||||||
import {
|
import {
|
||||||
$createNodeSelection,
|
$createNodeSelection,
|
||||||
$createParagraphNode, $getRoot,
|
$createParagraphNode,
|
||||||
$getSelection, $isElementNode,
|
$getRoot,
|
||||||
$isTextNode, $setSelection,
|
$getSelection,
|
||||||
BaseSelection, ElementFormatType, ElementNode, LexicalEditor,
|
$isElementNode,
|
||||||
LexicalNode, TextFormatType
|
$isTextNode,
|
||||||
|
$setSelection,
|
||||||
|
BaseSelection,
|
||||||
|
ElementFormatType,
|
||||||
|
ElementNode,
|
||||||
|
LexicalNode,
|
||||||
|
TextFormatType
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
|
|
||||||
import {$findMatchingParent, $getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
|
import {$findMatchingParent, $getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
|
||||||
|
import {LexicalElementNodeCreator, LexicalNodeMatcher} from "../nodes";
|
||||||
import {$setBlocksType} from "@lexical/selection";
|
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 {
|
import {$getParentOfType} from "./nodes";
|
||||||
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) {
|
export function $selectionContainsNodeType(selection: BaseSelection | null, matcher: LexicalNodeMatcher): boolean {
|
||||||
if (typeof child === 'string') {
|
|
||||||
el.append(document.createTextNode(child));
|
|
||||||
} else {
|
|
||||||
el.append(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return $getNodeFromSelection(selection, matcher) !== null;
|
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) {
|
if (!selection) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -77,17 +41,7 @@ export function $getNodeFromSelection(selection: BaseSelection|null, matcher: Le
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function $getParentOfType(node: LexicalNode, matcher: LexicalNodeMatcher): LexicalNode|null {
|
export function $selectionContainsTextFormat(selection: BaseSelection | null, format: TextFormatType): boolean {
|
||||||
for (const parent of node.getParents()) {
|
|
||||||
if (matcher(parent)) {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function $selectionContainsTextFormat(selection: BaseSelection|null, format: TextFormatType): boolean {
|
|
||||||
if (!selection) {
|
if (!selection) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -140,7 +94,7 @@ export function $selectSingleNode(node: LexicalNode) {
|
|||||||
$setSelection(nodeSelection);
|
$setSelection(nodeSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function $selectionContainsNode(selection: BaseSelection|null, node: LexicalNode): boolean {
|
export function $selectionContainsNode(selection: BaseSelection | null, node: LexicalNode): boolean {
|
||||||
if (!selection) {
|
if (!selection) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -155,7 +109,7 @@ export function $selectionContainsNode(selection: BaseSelection|null, node: Lexi
|
|||||||
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);
|
const nodes = $getBlockElementNodesInSelection(selection);
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
if (node.getFormatType() === format) {
|
if (node.getFormatType() === format) {
|
||||||
@ -166,7 +120,7 @@ export function $selectionContainsElementFormat(selection: BaseSelection|null, f
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function $getBlockElementNodesInSelection(selection: BaseSelection|null): ElementNode[] {
|
export function $getBlockElementNodesInSelection(selection: BaseSelection | null): ElementNode[] {
|
||||||
if (!selection) {
|
if (!selection) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -175,7 +129,7 @@ export function $getBlockElementNodesInSelection(selection: BaseSelection|null):
|
|||||||
for (const node of selection.getNodes()) {
|
for (const node of selection.getNodes()) {
|
||||||
const blockElement = $findMatchingParent(node, (node) => {
|
const blockElement = $findMatchingParent(node, (node) => {
|
||||||
return $isElementNode(node) && !node.isInline();
|
return $isElementNode(node) && !node.isInline();
|
||||||
}) as ElementNode|null;
|
}) as ElementNode | null;
|
||||||
|
|
||||||
if (blockElement) {
|
if (blockElement) {
|
||||||
blockNodes.set(blockElement.getKey(), blockElement);
|
blockNodes.set(blockElement.getKey(), blockElement);
|
||||||
@ -184,24 +138,3 @@ export function $getBlockElementNodesInSelection(selection: BaseSelection|null):
|
|||||||
|
|
||||||
return Array.from(blockNodes.values());
|
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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user