diff --git a/packages/pages-components/.storybook/snapshots/__snapshots__/components-lexicalrichtext--primary.png b/packages/pages-components/.storybook/snapshots/__snapshots__/components-lexicalrichtext--primary.png index 455b90a8..9304b7e5 100644 Binary files a/packages/pages-components/.storybook/snapshots/__snapshots__/components-lexicalrichtext--primary.png and b/packages/pages-components/.storybook/snapshots/__snapshots__/components-lexicalrichtext--primary.png differ diff --git a/packages/pages-components/CHANGELOG.md b/packages/pages-components/CHANGELOG.md index 0710cf78..d1787e53 100644 --- a/packages/pages-components/CHANGELOG.md +++ b/packages/pages-components/CHANGELOG.md @@ -2,39 +2,39 @@ ##### Continuous Integration -* support trusted publishing ([#109](https://github.com/yext/js/pull/109)) ([8650b782](https://github.com/yext/js/commit/8650b782671dd76e87f2e39455510026ab597bd8)) +- support trusted publishing ([#109](https://github.com/yext/js/pull/109)) ([8650b782](https://github.com/yext/js/commit/8650b782671dd76e87f2e39455510026ab597bd8)) ##### New Features -* **pages-components:** support cdn parameters and use cdn for aspect images ([#108](https://github.com/yext/js/pull/108)) ([7dde1c51](https://github.com/yext/js/commit/7dde1c5153657ef3e7e1dfdfa35c14e10a8cdab5)) +- **pages-components:** support cdn parameters and use cdn for aspect images ([#108](https://github.com/yext/js/pull/108)) ([7dde1c51](https://github.com/yext/js/commit/7dde1c5153657ef3e7e1dfdfa35c14e10a8cdab5)) ##### Bug Fixes -* store tag correctly for publish step ([#115](https://github.com/yext/js/pull/115)) ([0622c36e](https://github.com/yext/js/commit/0622c36e77f5ce7d73e90efe4d7146a4684241d1)) -* provide correct tag for publish ([#114](https://github.com/yext/js/pull/114)) ([f994b98a](https://github.com/yext/js/commit/f994b98a359f6df578cc0d44fbca408b6d4d4172)) -* pass correct tag to publish step ([#113](https://github.com/yext/js/pull/113)) ([2953455a](https://github.com/yext/js/commit/2953455a83591443427c4cf8635c27386002d040)) -* checkout main branch before publishing ([#112](https://github.com/yext/js/pull/112)) ([e10d7c12](https://github.com/yext/js/commit/e10d7c12417bc95c28f7bdcdefc0f961a92647c6)) -* split on '@' in verifyPublish ([#111](https://github.com/yext/js/pull/111)) ([f924fc6e](https://github.com/yext/js/commit/f924fc6e50a5277ebdd502e4707b950f28eb6743)) -* support LocationMap Mapbox in VLE iframe ([#110](https://github.com/yext/js/pull/110)) ([0c70007d](https://github.com/yext/js/commit/0c70007d6041c9bca7e8bbc26266128068c33a34)) -* block cta links that are not strings ([#107](https://github.com/yext/js/pull/107)) ([3a03462f](https://github.com/yext/js/commit/3a03462f61823e49f236bf90b9e359665983dc08)) +- store tag correctly for publish step ([#115](https://github.com/yext/js/pull/115)) ([0622c36e](https://github.com/yext/js/commit/0622c36e77f5ce7d73e90efe4d7146a4684241d1)) +- provide correct tag for publish ([#114](https://github.com/yext/js/pull/114)) ([f994b98a](https://github.com/yext/js/commit/f994b98a359f6df578cc0d44fbca408b6d4d4172)) +- pass correct tag to publish step ([#113](https://github.com/yext/js/pull/113)) ([2953455a](https://github.com/yext/js/commit/2953455a83591443427c4cf8635c27386002d040)) +- checkout main branch before publishing ([#112](https://github.com/yext/js/pull/112)) ([e10d7c12](https://github.com/yext/js/commit/e10d7c12417bc95c28f7bdcdefc0f961a92647c6)) +- split on '@' in verifyPublish ([#111](https://github.com/yext/js/pull/111)) ([f924fc6e](https://github.com/yext/js/commit/f924fc6e50a5277ebdd502e4707b950f28eb6743)) +- support LocationMap Mapbox in VLE iframe ([#110](https://github.com/yext/js/pull/110)) ([0c70007d](https://github.com/yext/js/commit/0c70007d6041c9bca7e8bbc26266128068c33a34)) +- block cta links that are not strings ([#107](https://github.com/yext/js/pull/107)) ([3a03462f](https://github.com/yext/js/commit/3a03462f61823e49f236bf90b9e359665983dc08)) ##### Other Changes -* pages-components@1.1.13 ([bfc928bf](https://github.com/yext/js/commit/bfc928bff2ffe880e6c19a48522722971a0c4e16)) -* pages-components@1.1.13 ([6287e0be](https://github.com/yext/js/commit/6287e0bea4dc6ac7f6d16b05bed6d81baa88e239)) -* store tag correctly for publish step ([#115](https://github.com/yext/js/pull/115))" ([7bec74d9](https://github.com/yext/js/commit/7bec74d9b4f668fe2d601bb10fbd84def11d6310)) -* pages-components@1.1.13" ([69a9e328](https://github.com/yext/js/commit/69a9e328136e378495e2ea33bc97f68263eb7dc4)) -* pages-components@1.1.13 ([e6e57d1a](https://github.com/yext/js/commit/e6e57d1a56670a811919e78ca40d45b28dac2005)) -* pages-components@1.1.13" ([1d1bf855](https://github.com/yext/js/commit/1d1bf855761263ad96a5993ba8c52bf62eee90dd)) -* pages-components@1.1.13 ([c4420bdd](https://github.com/yext/js/commit/c4420bdd470c0f45dbaaed9837f87e3e39da6511)) -* pages-components@1.1.13" ([0369cda3](https://github.com/yext/js/commit/0369cda377095f78eaa3e7382745b71a00b15dfe)) -* pages-components@1.1.13 ([17a6db63](https://github.com/yext/js/commit/17a6db6306ef02e8e6af4bb30022d6d848d8eec4)) -* pages-components@1.1.13" ([797c5c27](https://github.com/yext/js/commit/797c5c275a8a2a384044fcb871a4a569e049f532)) -* pages-components@1.1.13 ([fedd33ba](https://github.com/yext/js/commit/fedd33ba48f92bab59074550f98e4c095dc984d8)) -* pages-components@1.1.13" ([ab3be7ce](https://github.com/yext/js/commit/ab3be7cef5c5df40074bc201a539aee4d17e2e16)) -* pages-components@1.1.13 ([333203a7](https://github.com/yext/js/commit/333203a78d7b6deedad14f162c1ad299095ab562)) -* pages-components@1.1.13" ([326031ec](https://github.com/yext/js/commit/326031ec7fc863735d011bfea419383b87e6e175)) -* pages-components@1.1.13 ([721b8caf](https://github.com/yext/js/commit/721b8cafed50e61e4ac50bd03755c178a205048b)) +- pages-components@1.1.13 ([bfc928bf](https://github.com/yext/js/commit/bfc928bff2ffe880e6c19a48522722971a0c4e16)) +- pages-components@1.1.13 ([6287e0be](https://github.com/yext/js/commit/6287e0bea4dc6ac7f6d16b05bed6d81baa88e239)) +- store tag correctly for publish step ([#115](https://github.com/yext/js/pull/115))" ([7bec74d9](https://github.com/yext/js/commit/7bec74d9b4f668fe2d601bb10fbd84def11d6310)) +- pages-components@1.1.13" ([69a9e328](https://github.com/yext/js/commit/69a9e328136e378495e2ea33bc97f68263eb7dc4)) +- pages-components@1.1.13 ([e6e57d1a](https://github.com/yext/js/commit/e6e57d1a56670a811919e78ca40d45b28dac2005)) +- pages-components@1.1.13" ([1d1bf855](https://github.com/yext/js/commit/1d1bf855761263ad96a5993ba8c52bf62eee90dd)) +- pages-components@1.1.13 ([c4420bdd](https://github.com/yext/js/commit/c4420bdd470c0f45dbaaed9837f87e3e39da6511)) +- pages-components@1.1.13" ([0369cda3](https://github.com/yext/js/commit/0369cda377095f78eaa3e7382745b71a00b15dfe)) +- pages-components@1.1.13 ([17a6db63](https://github.com/yext/js/commit/17a6db6306ef02e8e6af4bb30022d6d848d8eec4)) +- pages-components@1.1.13" ([797c5c27](https://github.com/yext/js/commit/797c5c275a8a2a384044fcb871a4a569e049f532)) +- pages-components@1.1.13 ([fedd33ba](https://github.com/yext/js/commit/fedd33ba48f92bab59074550f98e4c095dc984d8)) +- pages-components@1.1.13" ([ab3be7ce](https://github.com/yext/js/commit/ab3be7cef5c5df40074bc201a539aee4d17e2e16)) +- pages-components@1.1.13 ([333203a7](https://github.com/yext/js/commit/333203a78d7b6deedad14f162c1ad299095ab562)) +- pages-components@1.1.13" ([326031ec](https://github.com/yext/js/commit/326031ec7fc863735d011bfea419383b87e6e175)) +- pages-components@1.1.13 ([721b8caf](https://github.com/yext/js/commit/721b8cafed50e61e4ac50bd03755c178a205048b)) #### 1.1.12 (2025-08-22) diff --git a/packages/pages-components/THIRD-PARTY-NOTICES b/packages/pages-components/THIRD-PARTY-NOTICES index 0aca514f..da8c9956 100644 --- a/packages/pages-components/THIRD-PARTY-NOTICES +++ b/packages/pages-components/THIRD-PARTY-NOTICES @@ -137,12 +137,12 @@ The following npm packages may be included in this product: - @lexical/code@0.12.6 - @lexical/hashtag@0.12.6 + - @lexical/html@0.12.6 - @lexical/link@0.12.6 - @lexical/list@0.12.6 - @lexical/react@0.12.6 - @lexical/rich-text@0.12.6 - @lexical/table@0.12.6 - - @lexical/utils@0.12.6 - lexical@0.12.6 These packages each contain the following license: diff --git a/packages/pages-components/index.d.ts b/packages/pages-components/index.d.ts index 580cb73c..d31433e2 100644 --- a/packages/pages-components/index.d.ts +++ b/packages/pages-components/index.d.ts @@ -2,11 +2,5 @@ declare module "@lexical/react/LexicalComposer" { export type InitialConfigType = any; export class LexicalComposer extends React.Component {} } -declare module "@lexical/react/LexicalRichTextPlugin"; -declare module "@lexical/react/LexicalContentEditable"; -declare module "@lexical/react/LexicalErrorBoundary"; -declare module "@lexical/react/LexicalListPlugin"; -declare module "@lexical/react/LexicalLinkPlugin"; -declare module "@lexical/react/LexicalTablePlugin"; declare module "@lexical/react/LexicalHorizontalRuleNode"; declare module "@lexical/react/LexicalComposerContext"; diff --git a/packages/pages-components/package.json b/packages/pages-components/package.json index 4cc99bc4..808e0a7f 100644 --- a/packages/pages-components/package.json +++ b/packages/pages-components/package.json @@ -93,19 +93,19 @@ "vitest": "^3.0.5" }, "dependencies": { - "@lexical/code": "^0.12.5", - "@lexical/hashtag": "^0.12.5", - "@lexical/link": "^0.12.5", - "@lexical/list": "^0.12.5", - "@lexical/react": "^0.12.5", - "@lexical/rich-text": "^0.12.5", - "@lexical/table": "^0.12.5", - "@lexical/utils": "^0.12.5", + "@lexical/code": "0.12.6", + "@lexical/hashtag": "0.12.6", + "@lexical/html": "0.12.6", + "@lexical/link": "0.12.6", + "@lexical/list": "0.12.6", + "@lexical/react": "0.12.6", + "@lexical/rich-text": "0.12.6", + "@lexical/table": "0.12.6", "@yext/analytics": "^1.0.0", "browser-or-node": "^2.1.1", "classnames": "^2.3.2", "cross-fetch": "^4.0.0", - "lexical": "^0.12.5", + "lexical": "0.12.6", "mdast-util-from-markdown": "1.2.0", "mdast-util-to-hast": "12.3.0", "micromark-util-chunked": "1.0.0", diff --git a/packages/pages-components/src/components/richText/LexicalRichText.tsx b/packages/pages-components/src/components/richText/LexicalRichText.tsx index 31ccd79a..2179d3fe 100644 --- a/packages/pages-components/src/components/richText/LexicalRichText.tsx +++ b/packages/pages-components/src/components/richText/LexicalRichText.tsx @@ -1,53 +1,54 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { LexicalComposer } from "@lexical/react/LexicalComposer"; -import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin"; -import { ContentEditable } from "@lexical/react/LexicalContentEditable"; -import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary"; -import { ListPlugin } from "@lexical/react/LexicalListPlugin"; -import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin"; -import { TablePlugin } from "@lexical/react/LexicalTablePlugin"; -import { - CodeHighlightPlugin, - ImagePlugin, - ListMaxIndentLevelPlugin, -} from "./plugins/index.js"; import { LexicalRichTextProps } from "./types.js"; import { generateConfig } from "./methods.js"; import styles from "./lexical.module.css"; +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { $generateHtmlFromNodes } from "@lexical/html"; /** + * @deprecated Use the stream's .html version instead with `dangerouslySetInnerHTML`. + * ```jsx + *
+ * ``` + * * Renders a read-only view of a Lexical Rich Text field. Styling for the various * types of Rich Text element can be optionally provided. If not provided, Yext default * styling will be applied. + * + * Note: This component currently only supports rendering on the client side. */ export function LexicalRichText({ serializedAST, nodeClassNames, }: LexicalRichTextProps) { + const [html, setHtml] = useState(""); + return ( - -
-
- - } - ErrorBoundary={LexicalErrorBoundary} - placeholder={
} - /> - - - - - - + <> + + +
+
+
+
-
-
+ + ); } + +function HtmlExportPlugin({ onHTML }: { onHTML: (html: string) => void }) { + const [editor] = useLexicalComposerContext(); + + useEffect(() => { + editor.update(() => { + const html = $generateHtmlFromNodes(editor); + onHTML(html); + }); + }, [editor, onHTML]); + + return null; +} diff --git a/packages/pages-components/src/components/richText/TableCellNode.tsx b/packages/pages-components/src/components/richText/TableCellNode.tsx new file mode 100644 index 00000000..1f108b7d --- /dev/null +++ b/packages/pages-components/src/components/richText/TableCellNode.tsx @@ -0,0 +1,58 @@ +import { TableCellNode, SerializedTableCellNode } from "@lexical/table"; +import { LexicalEditor, DOMExportOutput, $applyNodeReplacement } from "lexical"; + +/** + * This class extends the default Lexical {@link TableCellNode} to remove the + * default styles the Node automatically applies. https://github.com/facebook/lexical/issues/5081 + */ +export class CustomTableCellNode extends TableCellNode { + static getType(): string { + return "tablecell"; + } + + static clone(node: CustomTableCellNode) { + const cellNode = new CustomTableCellNode( + node.__headerState, + node.__colSpan, + node.__width, + node.__key + ); + cellNode.__rowSpan = node.__rowSpan; + cellNode.__backgroundColor = node.__backgroundColor; + return cellNode; + } + + static importJSON(serializedNode: SerializedTableCellNode): TableCellNode { + const colSpan = serializedNode.colSpan || 1; + const rowSpan = serializedNode.rowSpan || 1; + const cellNode = $applyNodeReplacement( + new CustomTableCellNode( + serializedNode.headerState, + colSpan, + serializedNode.width || undefined + ) + ) as TableCellNode; + cellNode.__rowSpan = rowSpan; + cellNode.__backgroundColor = serializedNode.backgroundColor || null; + return cellNode; + } + + exportJSON(): SerializedTableCellNode { + return super.exportJSON(); + } + + exportDOM(editor: LexicalEditor): DOMExportOutput { + const domExported = super.exportDOM(editor); + if (domExported.element) { + const element_ = domExported.element as HTMLElement; + element_.removeAttribute("style"); + element_.style.width = this.getWidth() ? `${this.getWidth()}px` : ""; + return { + ...domExported, + element: element_, + }; + } + + return domExported; + } +} diff --git a/packages/pages-components/src/components/richText/imageNode/ImageNode.tsx b/packages/pages-components/src/components/richText/imageNode/ImageNode.tsx index 01820b4b..3538f53e 100644 --- a/packages/pages-components/src/components/richText/imageNode/ImageNode.tsx +++ b/packages/pages-components/src/components/richText/imageNode/ImageNode.tsx @@ -1,5 +1,11 @@ import React from "react"; -import { EditorConfig, DecoratorNode, NodeKey } from "lexical"; +import { + EditorConfig, + DecoratorNode, + NodeKey, + DOMExportOutput, + LexicalEditor, +} from "lexical"; import LexicalImage from "./LexicalImage.js"; import { SerializedImageNode } from "./types.js"; @@ -94,6 +100,35 @@ export class ImageNode extends DecoratorNode { return span; } + exportDOM(editor: LexicalEditor): DOMExportOutput { + const span = document.createElement("span"); + const className = editor._config.theme.image; + if (className) { + span.className = className; + } + + const img = document.createElement("img"); + img.src = this.__src; // Use 'this' for instance properties + if (this.__altText) { + img.alt = this.__altText; + } + if (this.__width !== "inherit" && this.__width) { + img.width = Number(this.__width); + } + if (this.__height !== "inherit" && this.__height) { + img.height = Number(this.__height); + } + if (this.__maxWidth) { + img.style.maxWidth = `${this.__maxWidth}px`; + } + img.loading = "lazy"; + img.decoding = "async"; + + span.appendChild(img); + + return { element: span }; + } + /** * Since this Node will only be used in a read-only context, we don't need to worry about * updating the DOM when its attributes change. diff --git a/packages/pages-components/src/components/richText/methods.ts b/packages/pages-components/src/components/richText/methods.ts index fb9c882b..58b12358 100644 --- a/packages/pages-components/src/components/richText/methods.ts +++ b/packages/pages-components/src/components/richText/methods.ts @@ -1,5 +1,5 @@ import { InitialConfigType } from "@lexical/react/LexicalComposer"; -import { TableCellNode, TableNode, TableRowNode } from "@lexical/table"; +import { TableNode, TableRowNode } from "@lexical/table"; import { ListItemNode, ListNode } from "@lexical/list"; import { CodeHighlightNode, CodeNode } from "@lexical/code"; import { AutoLinkNode, LinkNode } from "@lexical/link"; @@ -7,8 +7,9 @@ import { HeadingNode, QuoteNode } from "@lexical/rich-text"; import { HashtagNode } from "@lexical/hashtag"; import { EditorThemeClasses } from "lexical"; import DefaultNodeStyling from "./DefaultStyling.js"; -import { ImageNode } from "./imageNode/index.js"; +import { ImageNode } from "./imageNode/ImageNode.js"; import { HorizontalRuleNode } from "@lexical/react/LexicalHorizontalRuleNode"; +import { CustomTableCellNode } from "./TableCellNode.js"; /** * Configuration for the Lexical Editor that powers the {@link LexicalComposer}. There is @@ -36,7 +37,7 @@ export function generateConfig( CodeNode, CodeHighlightNode, TableNode, - TableCellNode, + CustomTableCellNode, TableRowNode, AutoLinkNode, LinkNode, diff --git a/packages/pages-components/src/components/richText/plugins/CodeHighlightPlugin.ts b/packages/pages-components/src/components/richText/plugins/CodeHighlightPlugin.ts deleted file mode 100644 index 55002446..00000000 --- a/packages/pages-components/src/components/richText/plugins/CodeHighlightPlugin.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { useEffect } from "react"; -import { registerCodeHighlighting } from "@lexical/code"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; - -export function CodeHighlightPlugin() { - const [editor] = useLexicalComposerContext(); - - useEffect(() => registerCodeHighlighting(editor), [editor]); - - return null; -} diff --git a/packages/pages-components/src/components/richText/plugins/ImagePlugin.ts b/packages/pages-components/src/components/richText/plugins/ImagePlugin.ts deleted file mode 100644 index f0e7816b..00000000 --- a/packages/pages-components/src/components/richText/plugins/ImagePlugin.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { useEffect } from "react"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; -import { $wrapNodeInElement, mergeRegister } from "@lexical/utils"; -import { - $createRangeSelection, - $getSelection, - $isNodeSelection, - $setSelection, - $createParagraphNode, - $insertNodes, - $isRootOrShadowRoot, - COMMAND_PRIORITY_EDITOR, - COMMAND_PRIORITY_LOW, - COMMAND_PRIORITY_HIGH, - DRAGOVER_COMMAND, - DRAGSTART_COMMAND, - DROP_COMMAND, - createCommand, - LexicalEditor, -} from "lexical"; -import { - $createImageNode, - $isImageNode, - ImageNode, - SerializedImageNode, -} from "../imageNode/index.js"; - -const CAN_USE_DOM = - typeof window !== "undefined" && - typeof window.document !== "undefined" && - typeof window.document.createElement !== "undefined"; -const getDOMSelection = (targetWindow: (Window & typeof globalThis) | null) => - CAN_USE_DOM ? (targetWindow || window).getSelection() : null; -const INSERT_IMAGE_COMMAND = createCommand( - "INSERT_IMAGE_COMMAND" -); - -/** - * Image Plugin used by Lexical Composer - * - * author - Ruihao Zhu (rzhu\@yext.com) - */ -export function ImagePlugin() { - const [editor] = useLexicalComposerContext(); - - useEffect(() => { - if (!editor.hasNodes([ImageNode])) { - throw new Error("ImagePlugin: ImageNode not registered on editor"); - } - - return mergeRegister( - editor.registerCommand( - INSERT_IMAGE_COMMAND, - (payload: any) => { - const imageNode = $createImageNode(payload); - $insertNodes([imageNode]); - if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) { - $wrapNodeInElement(imageNode, $createParagraphNode).selectEnd(); - } - return true; - }, - COMMAND_PRIORITY_EDITOR - ), - editor.registerCommand( - DRAGSTART_COMMAND, - (event: any) => { - return onDragStart(event); - }, - COMMAND_PRIORITY_HIGH - ), - editor.registerCommand( - DRAGOVER_COMMAND, - (event: any) => { - return onDragover(event); - }, - COMMAND_PRIORITY_LOW - ), - editor.registerCommand( - DROP_COMMAND, - (event: any) => { - return onDrop(event, editor); - }, - COMMAND_PRIORITY_HIGH - ) - ); - }, [editor]); - - return null; -} - -function onDragStart(event: DragEvent): boolean { - const TRANSPARENT_IMAGE = - "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; - const img = document.createElement("img"); - img.src = TRANSPARENT_IMAGE; - const node = getImageNodeInSelection(); - if (!node) { - return false; - } - const dataTransfer = event.dataTransfer; - if (!dataTransfer) { - return false; - } - dataTransfer.setData("text/plain", "_"); - dataTransfer.setDragImage(img, 0, 0); - dataTransfer.setData( - "application/x-lexical-drag", - JSON.stringify({ - data: { - altText: node.__altText, - height: node.__height, - key: node.getKey(), - maxWidth: node.__maxWidth, - src: node.__src, - width: node.__width, - }, - type: "image", - }) - ); - return true; -} - -function onDragover(event: DragEvent): boolean { - const node = getImageNodeInSelection(); - if (!node) { - return false; - } - if (!canDropImage(event)) { - event.preventDefault(); - } - return true; -} - -function onDrop(event: DragEvent, editor: LexicalEditor): boolean { - const node = getImageNodeInSelection(); - if (!node) { - return false; - } - const data = getDragImageData(event); - if (!data) { - return false; - } - event.preventDefault(); - if (canDropImage(event)) { - const range = getDragSelection(event); - node.remove(); - const rangeSelection = $createRangeSelection(); - if (range !== null && range !== undefined) { - rangeSelection.applyDOMRange(range); - } - $setSelection(rangeSelection); - editor.dispatchCommand(INSERT_IMAGE_COMMAND, data); - } - return true; -} - -function getImageNodeInSelection(): ImageNode | null { - const selection = $getSelection(); - if (!$isNodeSelection(selection)) { - return null; - } - const nodes = selection.getNodes(); - const node = nodes[0]; - return $isImageNode(node) ? node : null; -} - -function getDragImageData(event: DragEvent) { - const dragData = event.dataTransfer?.getData("application/x-lexical-drag"); - if (!dragData) { - return null; - } - const { type, data } = JSON.parse(dragData); - if (type !== "image") { - return null; - } - return data; -} - -function canDropImage(event: DragEvent): boolean { - const target = event.target; - return !!( - target && - target instanceof HTMLElement && - !target.closest("code, span.yext-default-richtextv2-theme__image") && - target.parentElement - ); -} - -function getDragSelection(event: DragEvent): Range | null { - let range; - const target = event.target; - const targetWindow = - target === null || !(target instanceof Node) - ? null - : target.nodeType === 9 - ? (target as Document).defaultView - : (target as HTMLElement).ownerDocument.defaultView; - const domSelection = getDOMSelection(targetWindow); - if (document.caretRangeFromPoint) { - range = document.caretRangeFromPoint(event.clientX, event.clientY); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - } else if (event.rangeParent && domSelection !== null) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - domSelection.collapse(event.rangeParent, event.rangeOffset || 0); - range = domSelection.getRangeAt(0); - } else { - throw Error(`Cannot get the selection when dragging`); - } - return range; -} diff --git a/packages/pages-components/src/components/richText/plugins/ListMaxIndentLevelPlugin.ts b/packages/pages-components/src/components/richText/plugins/ListMaxIndentLevelPlugin.ts deleted file mode 100644 index 1d55f87f..00000000 --- a/packages/pages-components/src/components/richText/plugins/ListMaxIndentLevelPlugin.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { useEffect } from "react"; -import { $getListDepth, $isListItemNode, $isListNode } from "@lexical/list"; -import { - INDENT_CONTENT_COMMAND, - $getSelection, - $isElementNode, - $isRangeSelection, - RangeSelection, - ElementNode, -} from "lexical"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; - -function getElementNodesInSelection( - selection: RangeSelection -): Set { - const nodesInSelection = selection.getNodes(); - if (nodesInSelection.length === 0) { - return new Set([ - selection.anchor.getNode().getParentOrThrow(), - selection.focus.getNode().getParentOrThrow(), - ]); - } - return new Set( - nodesInSelection.map((n) => ($isElementNode(n) ? n : n.getParentOrThrow())) - ); -} - -const HIGH_PRIORITY = 3; - -function isIndentPermitted(maxDepth: number): boolean { - const selection = $getSelection(); - if (!$isRangeSelection(selection)) { - return false; - } - const elementNodesInSelection = getElementNodesInSelection(selection); - let totalDepth = 0; - for (const elementNode of elementNodesInSelection) { - if ($isListNode(elementNode)) { - totalDepth = Math.max($getListDepth(elementNode) + 1, totalDepth); - } else if ($isListItemNode(elementNode)) { - const parent = elementNode.getParent(); - if (!$isListNode(parent)) { - throw new Error( - "ListMaxIndentLevelPlugin: A ListItemNode must have a ListNode for a parent." - ); - } - totalDepth = Math.max($getListDepth(parent) + 1, totalDepth); - } - } - return totalDepth <= maxDepth; -} - -export function ListMaxIndentLevelPlugin({ maxDepth }: { maxDepth: number }) { - const [editor] = useLexicalComposerContext(); - useEffect(() => { - return editor.registerCommand( - INDENT_CONTENT_COMMAND, - () => !isIndentPermitted(maxDepth || 7), - HIGH_PRIORITY - ); - }, [editor, maxDepth]); - return null; -} diff --git a/packages/pages-components/src/components/richText/plugins/index.ts b/packages/pages-components/src/components/richText/plugins/index.ts deleted file mode 100644 index 90bb61e1..00000000 --- a/packages/pages-components/src/components/richText/plugins/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { CodeHighlightPlugin } from "./CodeHighlightPlugin.js"; -export { ImagePlugin } from "./ImagePlugin.js"; -export { ListMaxIndentLevelPlugin } from "./ListMaxIndentLevelPlugin.js"; diff --git a/packages/pages-components/src/components/richText/sampleData.ts b/packages/pages-components/src/components/richText/sampleData.ts index caa0640e..a7fa6bb1 100644 --- a/packages/pages-components/src/components/richText/sampleData.ts +++ b/packages/pages-components/src/components/richText/sampleData.ts @@ -158,6 +158,7 @@ export const LEXICAL_RICH_TEXT_SERIALIZED_AST = { type: "tablecell", version: 1, headerState: 3, + width: 300, }, { children: [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93ff4caf..2c13e01b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,28 +84,28 @@ importers: packages/pages-components: dependencies: '@lexical/code': - specifier: ^0.12.5 + specifier: 0.12.6 version: 0.12.6(lexical@0.12.6) '@lexical/hashtag': - specifier: ^0.12.5 + specifier: 0.12.6 + version: 0.12.6(lexical@0.12.6) + '@lexical/html': + specifier: 0.12.6 version: 0.12.6(lexical@0.12.6) '@lexical/link': - specifier: ^0.12.5 + specifier: 0.12.6 version: 0.12.6(lexical@0.12.6) '@lexical/list': - specifier: ^0.12.5 + specifier: 0.12.6 version: 0.12.6(lexical@0.12.6) '@lexical/react': - specifier: ^0.12.5 + specifier: 0.12.6 version: 0.12.6(lexical@0.12.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(yjs@13.6.23) '@lexical/rich-text': - specifier: ^0.12.5 + specifier: 0.12.6 version: 0.12.6(@lexical/clipboard@0.12.6(lexical@0.12.6))(@lexical/selection@0.12.6(lexical@0.12.6))(@lexical/utils@0.12.6(lexical@0.12.6))(lexical@0.12.6) '@lexical/table': - specifier: ^0.12.5 - version: 0.12.6(lexical@0.12.6) - '@lexical/utils': - specifier: ^0.12.5 + specifier: 0.12.6 version: 0.12.6(lexical@0.12.6) '@yext/analytics': specifier: ^1.0.0 @@ -120,7 +120,7 @@ importers: specifier: ^4.0.0 version: 4.1.0(encoding@0.1.13) lexical: - specifier: ^0.12.5 + specifier: 0.12.6 version: 0.12.6 mapbox-gl: specifier: ^2.9.2