@webiny/lexical-editor 6.5.0-beta.7 → 6.5.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Editor/RichTextEditor.js +0 -3
- package/components/Editor/RichTextEditor.js.map +1 -1
- package/context/RichTextEditorContext.js +1 -4
- package/context/RichTextEditorContext.js.map +1 -1
- package/package.json +15 -15
- package/plugins/FloatingLinkEditorPlugin/useFloatingLinkEditor.js +15 -0
- package/plugins/FloatingLinkEditorPlugin/useFloatingLinkEditor.js.map +1 -1
|
@@ -57,9 +57,6 @@ const BaseRichTextEditor = ({ onChange, toolbar, staticToolbar, nodes, placehold
|
|
|
57
57
|
theme: props.theme,
|
|
58
58
|
toolbarActionPlugins: props.toolbarActionPlugins
|
|
59
59
|
}, staticToolbar && !disabled ? staticToolbar : null, /*#__PURE__*/ react.createElement("div", {
|
|
60
|
-
"data-role": "overlays",
|
|
61
|
-
className: "relative"
|
|
62
|
-
}), /*#__PURE__*/ react.createElement("div", {
|
|
63
60
|
className: "editor-shell",
|
|
64
61
|
ref: scrollRef,
|
|
65
62
|
style: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components/Editor/RichTextEditor.js","sources":["../../../src/components/Editor/RichTextEditor.tsx"],"sourcesContent":["import React, { Fragment, useId, useRef, useState } from \"react\";\nimport type { Klass, LexicalNode } from \"lexical\";\nimport { LexicalComposer } from \"@lexical/react/LexicalComposer\";\nimport { AutoFocusPlugin } from \"@lexical/react/LexicalAutoFocusPlugin\";\nimport { ClearEditorPlugin } from \"@lexical/react/LexicalClearEditorPlugin\";\nimport { RichTextPlugin } from \"@lexical/react/LexicalRichTextPlugin\";\nimport { LexicalErrorBoundary } from \"@lexical/react/LexicalErrorBoundary\";\nimport { makeDecoratable } from \"@webiny/react-composition\";\nimport { HistoryPlugin } from \"@lexical/react/LexicalHistoryPlugin\";\nimport { ContentEditable } from \"@lexical/react/LexicalContentEditable\";\nimport type { EditorTheme } from \"@webiny/lexical-theme\";\nimport { allNodes } from \"@webiny/lexical-nodes\";\nimport { RichTextEditorProvider } from \"~/context/RichTextEditorContext.js\";\nimport { BlurEventPlugin } from \"~/plugins/BlurEventPlugin/BlurEventPlugin.js\";\nimport type { LexicalValue, ToolbarActionPlugin } from \"~/types.js\";\nimport { EditorPlaceholder } from \"~/components/Editor/EditorPlaceholder.js\";\nimport { SharedHistoryContext, useSharedHistoryContext } from \"~/context/SharedHistoryContext.js\";\nimport {\n LexicalEditorWithConfig,\n useLexicalEditorConfig\n} from \"~/components/LexicalEditorConfig/LexicalEditorConfig.js\";\nimport { StateHandlingPlugin } from \"~/plugins/StateHandlingPlugin.js\";\n\nexport type InitialEditorConfig = React.ComponentProps<typeof LexicalComposer>[\"initialConfig\"] & {\n editorId: string;\n};\n\nexport interface RichTextEditorProps {\n children?: React.ReactNode | React.ReactNode[];\n classes?: string;\n disabled?: boolean;\n contentEditableStyles?: React.CSSProperties;\n focus?: boolean;\n height?: number | string;\n nodes?: Klass<LexicalNode>[];\n onBlur?: (editorState: LexicalValue) => void;\n onChange?: (value: LexicalValue) => void;\n placeholder?: string;\n placeholderStyles?: React.CSSProperties;\n staticToolbar?: React.ReactNode;\n styles?: React.CSSProperties;\n tag?: string;\n theme: EditorTheme;\n toolbarActionPlugins?: ToolbarActionPlugin[];\n toolbar?: React.ReactNode;\n value: LexicalValue | null | undefined;\n configRef?: React.MutableRefObject<InitialEditorConfig | undefined>;\n width?: number | string;\n}\n\nconst BaseRichTextEditor = ({\n onChange,\n toolbar,\n staticToolbar,\n nodes,\n placeholder,\n children,\n onBlur,\n focus,\n styles,\n width,\n disabled = false,\n height,\n contentEditableStyles,\n placeholderStyles,\n ...props\n}: RichTextEditorProps) => {\n const editorTheme = useRef(props.theme);\n const config = useLexicalEditorConfig();\n const { historyState } = useSharedHistoryContext();\n // Apply the default paragraph's typography class so the placeholder matches the real\n // text's size/line-height/font (the muted color is kept via placeholderStyles).\n const defaultParagraphClassName = props.theme?.typography?.paragraphs?.[0]?.className;\n const scrollRef = useRef(null);\n\n const [floatingAnchorElem, setFloatingAnchorElem] = useState<HTMLElement | undefined>(\n undefined\n );\n const onRef = (_floatingAnchorElem: HTMLDivElement) => {\n if (_floatingAnchorElem !== null) {\n setFloatingAnchorElem(_floatingAnchorElem);\n }\n };\n\n const sizeStyle = {\n height: height || \"\",\n width: width || \"\"\n };\n\n const configNodes = config.nodes.map(node => node.node);\n const configPlugins = config.plugins.map(plugin => (\n <Fragment key={plugin.name}>{plugin.element}</Fragment>\n ));\n\n const initialConfig = {\n editorId: useId(),\n editable: !disabled,\n namespace: \"webiny\",\n onError: () => {\n // Ignore errors. We don't want to break the app because of errors caused by config/value updates.\n // These are usually resolved in the next component render cycle.\n },\n nodes: [...allNodes, ...configNodes, ...(nodes || [])],\n theme: {\n ...editorTheme.current.tokens,\n // I'm not aware of a better way to pass custom data to nodes.\n // For now, we're using Lexical's theme to pass colors and typography.\n $colors: editorTheme.current.colors,\n $typography: editorTheme.current.typography,\n $cacheKey: JSON.stringify(editorTheme.current)\n }\n };\n\n if (props.configRef) {\n props.configRef.current = initialConfig;\n }\n\n return (\n /**\n * Once the LexicalComposer is mounted, it caches the `initialConfig` internally, and all future\n * updates to the config will be ignored. This is a problem because we pull in Nodes from our config,\n * and initially, there can be multiple re-renders, while the config object is settled.\n *\n * To bypass this issue, we generate a naive `key` based on the number of Nodes.\n */\n <SharedHistoryContext>\n <LexicalComposer initialConfig={initialConfig} key={initialConfig.nodes.length}>\n <RichTextEditorProvider\n theme={props.theme}\n toolbarActionPlugins={props.toolbarActionPlugins}\n >\n {staticToolbar && !disabled ? staticToolbar : null}\n <div data-role={\"overlays\"} className={\"relative\"}></div>\n <div\n /* This className is necessary for targeting of editor container from CSS files. */\n className={\"editor-shell\"}\n ref={scrollRef}\n style={{\n ...styles,\n ...sizeStyle,\n overflow: \"auto\",\n position: \"relative\"\n }}\n >\n {/* State plugins. */}\n <StateHandlingPlugin\n value={props.value}\n onChange={disabled ? undefined : onChange}\n />\n <ClearEditorPlugin />\n <HistoryPlugin externalHistoryState={historyState} />\n {/* Event plugins. */}\n {onBlur && <BlurEventPlugin onBlur={onBlur} />}\n {focus && <AutoFocusPlugin />}\n {/* External plugins and components. */}\n {configPlugins}\n {children}\n <RichTextPlugin\n contentEditable={\n <div className=\"editor-scroller\" style={{ ...sizeStyle }}>\n <div className=\"editor\" ref={onRef}>\n <ContentEditable\n disabled={disabled}\n style={{ outline: 0, ...contentEditableStyles }}\n />\n </div>\n </div>\n }\n placeholder={null}\n ErrorBoundary={LexicalErrorBoundary}\n />\n <EditorPlaceholder\n text={placeholder || \"Enter text...\"}\n styles={placeholderStyles}\n fallbackClassName={defaultParagraphClassName}\n />\n {/* Toolbar. */}\n {disabled ? null : floatingAnchorElem && toolbar}\n </div>\n </RichTextEditorProvider>\n </LexicalComposer>\n </SharedHistoryContext>\n );\n};\n\n/**\n * @description Main editor container\n */\nexport const RichTextEditor = makeDecoratable(\"RichTextEditor\", (props: RichTextEditorProps) => {\n return (\n <LexicalEditorWithConfig>\n <BaseRichTextEditor {...props} />\n </LexicalEditorWithConfig>\n );\n});\n\nexport namespace RichTextEditor {\n export type InitialConfig = InitialEditorConfig;\n}\n"],"names":["BaseRichTextEditor","onChange","toolbar","staticToolbar","nodes","placeholder","children","onBlur","focus","styles","width","disabled","height","contentEditableStyles","placeholderStyles","props","editorTheme","useRef","config","useLexicalEditorConfig","historyState","useSharedHistoryContext","defaultParagraphClassName","scrollRef","floatingAnchorElem","setFloatingAnchorElem","useState","undefined","onRef","_floatingAnchorElem","sizeStyle","configNodes","node","configPlugins","plugin","Fragment","initialConfig","useId","allNodes","JSON","SharedHistoryContext","LexicalComposer","RichTextEditorProvider","StateHandlingPlugin","ClearEditorPlugin","HistoryPlugin","BlurEventPlugin","AutoFocusPlugin","RichTextPlugin","ContentEditable","LexicalErrorBoundary","EditorPlaceholder","RichTextEditor","makeDecoratable","LexicalEditorWithConfig"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"components/Editor/RichTextEditor.js","sources":["../../../src/components/Editor/RichTextEditor.tsx"],"sourcesContent":["import React, { Fragment, useId, useRef, useState } from \"react\";\nimport type { Klass, LexicalNode } from \"lexical\";\nimport { LexicalComposer } from \"@lexical/react/LexicalComposer\";\nimport { AutoFocusPlugin } from \"@lexical/react/LexicalAutoFocusPlugin\";\nimport { ClearEditorPlugin } from \"@lexical/react/LexicalClearEditorPlugin\";\nimport { RichTextPlugin } from \"@lexical/react/LexicalRichTextPlugin\";\nimport { LexicalErrorBoundary } from \"@lexical/react/LexicalErrorBoundary\";\nimport { makeDecoratable } from \"@webiny/react-composition\";\nimport { HistoryPlugin } from \"@lexical/react/LexicalHistoryPlugin\";\nimport { ContentEditable } from \"@lexical/react/LexicalContentEditable\";\nimport type { EditorTheme } from \"@webiny/lexical-theme\";\nimport { allNodes } from \"@webiny/lexical-nodes\";\nimport { RichTextEditorProvider } from \"~/context/RichTextEditorContext.js\";\nimport { BlurEventPlugin } from \"~/plugins/BlurEventPlugin/BlurEventPlugin.js\";\nimport type { LexicalValue, ToolbarActionPlugin } from \"~/types.js\";\nimport { EditorPlaceholder } from \"~/components/Editor/EditorPlaceholder.js\";\nimport { SharedHistoryContext, useSharedHistoryContext } from \"~/context/SharedHistoryContext.js\";\nimport {\n LexicalEditorWithConfig,\n useLexicalEditorConfig\n} from \"~/components/LexicalEditorConfig/LexicalEditorConfig.js\";\nimport { StateHandlingPlugin } from \"~/plugins/StateHandlingPlugin.js\";\n\nexport type InitialEditorConfig = React.ComponentProps<typeof LexicalComposer>[\"initialConfig\"] & {\n editorId: string;\n};\n\nexport interface RichTextEditorProps {\n children?: React.ReactNode | React.ReactNode[];\n classes?: string;\n disabled?: boolean;\n contentEditableStyles?: React.CSSProperties;\n focus?: boolean;\n height?: number | string;\n nodes?: Klass<LexicalNode>[];\n onBlur?: (editorState: LexicalValue) => void;\n onChange?: (value: LexicalValue) => void;\n placeholder?: string;\n placeholderStyles?: React.CSSProperties;\n staticToolbar?: React.ReactNode;\n styles?: React.CSSProperties;\n tag?: string;\n theme: EditorTheme;\n toolbarActionPlugins?: ToolbarActionPlugin[];\n toolbar?: React.ReactNode;\n value: LexicalValue | null | undefined;\n configRef?: React.MutableRefObject<InitialEditorConfig | undefined>;\n width?: number | string;\n}\n\nconst BaseRichTextEditor = ({\n onChange,\n toolbar,\n staticToolbar,\n nodes,\n placeholder,\n children,\n onBlur,\n focus,\n styles,\n width,\n disabled = false,\n height,\n contentEditableStyles,\n placeholderStyles,\n ...props\n}: RichTextEditorProps) => {\n const editorTheme = useRef(props.theme);\n const config = useLexicalEditorConfig();\n const { historyState } = useSharedHistoryContext();\n // Apply the default paragraph's typography class so the placeholder matches the real\n // text's size/line-height/font (the muted color is kept via placeholderStyles).\n const defaultParagraphClassName = props.theme?.typography?.paragraphs?.[0]?.className;\n const scrollRef = useRef(null);\n\n const [floatingAnchorElem, setFloatingAnchorElem] = useState<HTMLElement | undefined>(\n undefined\n );\n const onRef = (_floatingAnchorElem: HTMLDivElement) => {\n if (_floatingAnchorElem !== null) {\n setFloatingAnchorElem(_floatingAnchorElem);\n }\n };\n\n const sizeStyle = {\n height: height || \"\",\n width: width || \"\"\n };\n\n const configNodes = config.nodes.map(node => node.node);\n const configPlugins = config.plugins.map(plugin => (\n <Fragment key={plugin.name}>{plugin.element}</Fragment>\n ));\n\n const initialConfig = {\n editorId: useId(),\n editable: !disabled,\n namespace: \"webiny\",\n onError: () => {\n // Ignore errors. We don't want to break the app because of errors caused by config/value updates.\n // These are usually resolved in the next component render cycle.\n },\n nodes: [...allNodes, ...configNodes, ...(nodes || [])],\n theme: {\n ...editorTheme.current.tokens,\n // I'm not aware of a better way to pass custom data to nodes.\n // For now, we're using Lexical's theme to pass colors and typography.\n $colors: editorTheme.current.colors,\n $typography: editorTheme.current.typography,\n $cacheKey: JSON.stringify(editorTheme.current)\n }\n };\n\n if (props.configRef) {\n props.configRef.current = initialConfig;\n }\n\n return (\n /**\n * Once the LexicalComposer is mounted, it caches the `initialConfig` internally, and all future\n * updates to the config will be ignored. This is a problem because we pull in Nodes from our config,\n * and initially, there can be multiple re-renders, while the config object is settled.\n *\n * To bypass this issue, we generate a naive `key` based on the number of Nodes.\n */\n <SharedHistoryContext>\n <LexicalComposer initialConfig={initialConfig} key={initialConfig.nodes.length}>\n <RichTextEditorProvider\n theme={props.theme}\n toolbarActionPlugins={props.toolbarActionPlugins}\n >\n {staticToolbar && !disabled ? staticToolbar : null}\n <div\n /* This className is necessary for targeting of editor container from CSS files. */\n className={\"editor-shell\"}\n ref={scrollRef}\n style={{\n ...styles,\n ...sizeStyle,\n overflow: \"auto\",\n position: \"relative\"\n }}\n >\n {/* State plugins. */}\n <StateHandlingPlugin\n value={props.value}\n onChange={disabled ? undefined : onChange}\n />\n <ClearEditorPlugin />\n <HistoryPlugin externalHistoryState={historyState} />\n {/* Event plugins. */}\n {onBlur && <BlurEventPlugin onBlur={onBlur} />}\n {focus && <AutoFocusPlugin />}\n {/* External plugins and components. */}\n {configPlugins}\n {children}\n <RichTextPlugin\n contentEditable={\n <div className=\"editor-scroller\" style={{ ...sizeStyle }}>\n <div className=\"editor\" ref={onRef}>\n <ContentEditable\n disabled={disabled}\n style={{ outline: 0, ...contentEditableStyles }}\n />\n </div>\n </div>\n }\n placeholder={null}\n ErrorBoundary={LexicalErrorBoundary}\n />\n <EditorPlaceholder\n text={placeholder || \"Enter text...\"}\n styles={placeholderStyles}\n fallbackClassName={defaultParagraphClassName}\n />\n {/* Toolbar. */}\n {disabled ? null : floatingAnchorElem && toolbar}\n </div>\n </RichTextEditorProvider>\n </LexicalComposer>\n </SharedHistoryContext>\n );\n};\n\n/**\n * @description Main editor container\n */\nexport const RichTextEditor = makeDecoratable(\"RichTextEditor\", (props: RichTextEditorProps) => {\n return (\n <LexicalEditorWithConfig>\n <BaseRichTextEditor {...props} />\n </LexicalEditorWithConfig>\n );\n});\n\nexport namespace RichTextEditor {\n export type InitialConfig = InitialEditorConfig;\n}\n"],"names":["BaseRichTextEditor","onChange","toolbar","staticToolbar","nodes","placeholder","children","onBlur","focus","styles","width","disabled","height","contentEditableStyles","placeholderStyles","props","editorTheme","useRef","config","useLexicalEditorConfig","historyState","useSharedHistoryContext","defaultParagraphClassName","scrollRef","floatingAnchorElem","setFloatingAnchorElem","useState","undefined","onRef","_floatingAnchorElem","sizeStyle","configNodes","node","configPlugins","plugin","Fragment","initialConfig","useId","allNodes","JSON","SharedHistoryContext","LexicalComposer","RichTextEditorProvider","StateHandlingPlugin","ClearEditorPlugin","HistoryPlugin","BlurEventPlugin","AutoFocusPlugin","RichTextPlugin","ContentEditable","LexicalErrorBoundary","EditorPlaceholder","RichTextEditor","makeDecoratable","LexicalEditorWithConfig"],"mappings":"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"}
|
|
@@ -9,10 +9,7 @@ const RichTextEditorProvider = ({ theme, toolbarActionPlugins = [], children })=
|
|
|
9
9
|
if (!rootElement) return document.body;
|
|
10
10
|
const dialogContent = rootElement.closest("[role='dialog']");
|
|
11
11
|
if (dialogContent) return dialogContent;
|
|
12
|
-
|
|
13
|
-
if (!shell) return document.body;
|
|
14
|
-
const overlays = shell.previousElementSibling;
|
|
15
|
-
return overlays ?? document.body;
|
|
12
|
+
return document.body;
|
|
16
13
|
}, [
|
|
17
14
|
editor
|
|
18
15
|
]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context/RichTextEditorContext.js","sources":["../../src/context/RichTextEditorContext.tsx"],"sourcesContent":["import React, { createContext, useCallback, useMemo } from \"react\";\nimport type { LexicalEditor } from \"lexical\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { type EditorTheme, Theme } from \"@webiny/lexical-theme\";\nimport type { ToolbarActionPlugin } from \"~/types.js\";\n\nexport interface RichTextEditorContext {\n editor: LexicalEditor;\n getOverlaysElement: () => HTMLElement;\n toolbarActionPlugins: ToolbarActionPlugin[];\n theme: Theme;\n}\n\nexport const RichTextEditorContext = createContext<RichTextEditorContext | undefined>(undefined);\n\ninterface RichTextEditorProviderProps {\n theme: EditorTheme;\n toolbarActionPlugins?: ToolbarActionPlugin[];\n children?: React.ReactNode | React.ReactNode[];\n}\n\nexport const RichTextEditorProvider = ({\n theme,\n toolbarActionPlugins = [],\n children\n}: RichTextEditorProviderProps) => {\n const [editor] = useLexicalComposerContext();\n\n const getOverlaysElement = useCallback(() => {\n const rootElement = editor.getRootElement();\n if (!rootElement) {\n return document.body;\n }\n\n const dialogContent = rootElement.closest(\"[role='dialog']\");\n if (dialogContent) {\n return dialogContent as HTMLElement;\n }\n\n
|
|
1
|
+
{"version":3,"file":"context/RichTextEditorContext.js","sources":["../../src/context/RichTextEditorContext.tsx"],"sourcesContent":["import React, { createContext, useCallback, useMemo } from \"react\";\nimport type { LexicalEditor } from \"lexical\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { type EditorTheme, Theme } from \"@webiny/lexical-theme\";\nimport type { ToolbarActionPlugin } from \"~/types.js\";\n\nexport interface RichTextEditorContext {\n editor: LexicalEditor;\n getOverlaysElement: () => HTMLElement;\n toolbarActionPlugins: ToolbarActionPlugin[];\n theme: Theme;\n}\n\nexport const RichTextEditorContext = createContext<RichTextEditorContext | undefined>(undefined);\n\ninterface RichTextEditorProviderProps {\n theme: EditorTheme;\n toolbarActionPlugins?: ToolbarActionPlugin[];\n children?: React.ReactNode | React.ReactNode[];\n}\n\nexport const RichTextEditorProvider = ({\n theme,\n toolbarActionPlugins = [],\n children\n}: RichTextEditorProviderProps) => {\n const [editor] = useLexicalComposerContext();\n\n const getOverlaysElement = useCallback(() => {\n const rootElement = editor.getRootElement();\n if (!rootElement) {\n return document.body;\n }\n\n // Inside a dialog, overlays must stay within the dialog, or its focus trap and\n // pointer-events lock make them unusable.\n const dialogContent = rootElement.closest(\"[role='dialog']\");\n if (dialogContent) {\n return dialogContent as HTMLElement;\n }\n\n // Anywhere else, render into the body, so ancestors with `overflow: hidden`\n // (e.g. accordions around nested object fields) don't clip the overlays.\n return document.body;\n }, [editor]);\n\n const internalTheme = useMemo(\n () =>\n new Theme({\n colors: theme.colors,\n typography: theme.typography,\n fontSizes: theme.fontSizes,\n tokens: theme.tokens,\n allowCustomColors: theme.allowCustomColors\n }),\n [theme]\n );\n\n return (\n <RichTextEditorContext.Provider\n value={{\n editor,\n getOverlaysElement,\n theme: internalTheme,\n toolbarActionPlugins\n }}\n >\n {children}\n </RichTextEditorContext.Provider>\n );\n};\n"],"names":["RichTextEditorContext","createContext","undefined","RichTextEditorProvider","theme","toolbarActionPlugins","children","editor","useLexicalComposerContext","getOverlaysElement","useCallback","rootElement","document","dialogContent","internalTheme","useMemo","Theme"],"mappings":";;;AAaO,MAAMA,wBAAwB,WAAHA,GAAGC,cAAiDC;AAQ/E,MAAMC,yBAAyB,CAAC,EACnCC,KAAK,EACLC,uBAAuB,EAAE,EACzBC,QAAQ,EACkB;IAC1B,MAAM,CAACC,OAAO,GAAGC;IAEjB,MAAMC,qBAAqBC,YAAY;QACnC,MAAMC,cAAcJ,OAAO,cAAc;QACzC,IAAI,CAACI,aACD,OAAOC,SAAS,IAAI;QAKxB,MAAMC,gBAAgBF,YAAY,OAAO,CAAC;QAC1C,IAAIE,eACA,OAAOA;QAKX,OAAOD,SAAS,IAAI;IACxB,GAAG;QAACL;KAAO;IAEX,MAAMO,gBAAgBC,QAClB,IACI,IAAIC,MAAM;YACN,QAAQZ,MAAM,MAAM;YACpB,YAAYA,MAAM,UAAU;YAC5B,WAAWA,MAAM,SAAS;YAC1B,QAAQA,MAAM,MAAM;YACpB,mBAAmBA,MAAM,iBAAiB;QAC9C,IACJ;QAACA;KAAM;IAGX,OAAO,WAAP,GACI,oBAACJ,sBAAsB,QAAQ;QAC3B,OAAO;YACHO;YACAE;YACA,OAAOK;YACPT;QACJ;OAECC;AAGb"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/lexical-editor",
|
|
3
|
-
"version": "6.5.0-beta.
|
|
3
|
+
"version": "6.5.0-beta.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -12,27 +12,27 @@
|
|
|
12
12
|
},
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@lexical/code-prism": "0.
|
|
16
|
-
"@lexical/history": "0.
|
|
17
|
-
"@lexical/react": "0.
|
|
18
|
-
"@lexical/rich-text": "0.
|
|
19
|
-
"@lexical/selection": "0.
|
|
20
|
-
"@lexical/text": "0.
|
|
21
|
-
"@lexical/utils": "0.
|
|
22
|
-
"@webiny/icons": "6.5.0-beta.
|
|
23
|
-
"@webiny/lexical-nodes": "6.5.0-beta.
|
|
24
|
-
"@webiny/lexical-theme": "6.5.0-beta.
|
|
25
|
-
"@webiny/react-composition": "6.5.0-beta.
|
|
26
|
-
"@webiny/react-properties": "6.5.0-beta.
|
|
15
|
+
"@lexical/code-prism": "0.52.0",
|
|
16
|
+
"@lexical/history": "0.52.0",
|
|
17
|
+
"@lexical/react": "0.52.0",
|
|
18
|
+
"@lexical/rich-text": "0.52.0",
|
|
19
|
+
"@lexical/selection": "0.52.0",
|
|
20
|
+
"@lexical/text": "0.52.0",
|
|
21
|
+
"@lexical/utils": "0.52.0",
|
|
22
|
+
"@webiny/icons": "6.5.0-beta.8",
|
|
23
|
+
"@webiny/lexical-nodes": "6.5.0-beta.8",
|
|
24
|
+
"@webiny/lexical-theme": "6.5.0-beta.8",
|
|
25
|
+
"@webiny/react-composition": "6.5.0-beta.8",
|
|
26
|
+
"@webiny/react-properties": "6.5.0-beta.8",
|
|
27
27
|
"clsx": "2.1.1",
|
|
28
|
-
"lexical": "0.
|
|
28
|
+
"lexical": "0.52.0",
|
|
29
29
|
"lodash": "4.18.1",
|
|
30
30
|
"radix-ui": "1.6.7",
|
|
31
31
|
"react": "18.3.1",
|
|
32
32
|
"react-dom": "18.3.1"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@webiny/build-tools": "6.5.0-beta.
|
|
35
|
+
"@webiny/build-tools": "6.5.0-beta.8"
|
|
36
36
|
},
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public"
|
|
@@ -35,6 +35,7 @@ function useFloatingLinkEditor(editor) {
|
|
|
35
35
|
const [linkData, setLinkData] = useState(emptyLinkData);
|
|
36
36
|
const [lastSelection, setLastSelection] = useState(null);
|
|
37
37
|
const suppressedSelectionKeyRef = useRef(null);
|
|
38
|
+
const rangeRef = useRef(null);
|
|
38
39
|
const updateLinkEditor = useCallback(()=>{
|
|
39
40
|
const selection = $getSelection();
|
|
40
41
|
const selectionKey = getSelectionKey(selection);
|
|
@@ -42,6 +43,7 @@ function useFloatingLinkEditor(editor) {
|
|
|
42
43
|
if (selectionKey === suppressedSelectionKeyRef.current) {
|
|
43
44
|
const editorElem = editorRef.current;
|
|
44
45
|
if (editorElem) setFloatingElemPosition(null, editorElem);
|
|
46
|
+
rangeRef.current = null;
|
|
45
47
|
setLastSelection(null);
|
|
46
48
|
setLinkData(emptyLinkData);
|
|
47
49
|
return true;
|
|
@@ -56,10 +58,12 @@ function useFloatingLinkEditor(editor) {
|
|
|
56
58
|
const rootElement = editor.getRootElement();
|
|
57
59
|
if (null !== selection && null !== nativeSelection && null !== rootElement && rootElement.contains(nativeSelection.anchorNode)) {
|
|
58
60
|
const range = nativeSelection.getRangeAt(0);
|
|
61
|
+
rangeRef.current = range;
|
|
59
62
|
setFloatingElemPosition(range, editorElem);
|
|
60
63
|
setLastSelection(selection);
|
|
61
64
|
} else if (!activeElement || "link-input" !== activeElement.className) {
|
|
62
65
|
if (null !== rootElement) setFloatingElemPosition(null, editorElem);
|
|
66
|
+
rangeRef.current = null;
|
|
63
67
|
setLastSelection(null);
|
|
64
68
|
setLinkData(emptyLinkData);
|
|
65
69
|
}
|
|
@@ -101,6 +105,17 @@ function useFloatingLinkEditor(editor) {
|
|
|
101
105
|
editor,
|
|
102
106
|
updateLinkEditor
|
|
103
107
|
]);
|
|
108
|
+
useEffect(()=>{
|
|
109
|
+
const reposition = ()=>{
|
|
110
|
+
if (rangeRef.current && editorRef.current) setFloatingElemPosition(rangeRef.current, editorRef.current);
|
|
111
|
+
};
|
|
112
|
+
window.addEventListener("scroll", reposition, true);
|
|
113
|
+
window.addEventListener("resize", reposition);
|
|
114
|
+
return ()=>{
|
|
115
|
+
window.removeEventListener("scroll", reposition, true);
|
|
116
|
+
window.removeEventListener("resize", reposition);
|
|
117
|
+
};
|
|
118
|
+
}, []);
|
|
104
119
|
useEffect(()=>{
|
|
105
120
|
editor.read(()=>{
|
|
106
121
|
updateLinkEditor();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins/FloatingLinkEditorPlugin/useFloatingLinkEditor.js","sources":["../../../src/plugins/FloatingLinkEditorPlugin/useFloatingLinkEditor.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n SELECTION_CHANGE_COMMAND,\n type BaseSelection,\n type LexicalEditor,\n COMMAND_PRIORITY_LOW,\n $getSelection,\n $isRangeSelection,\n mergeRegister\n} from \"lexical\";\nimport { $isLinkNode, TOGGLE_LINK_COMMAND } from \"@webiny/lexical-nodes\";\nimport { getSelectedNode } from \"~/utils/getSelectedNode.js\";\nimport { setFloatingElemPosition } from \"~/utils/setFloatingElemPosition.js\";\nimport { sanitizeUrl } from \"~/utils/sanitizeUrl.js\";\nimport { LinkData } from \"./types.js\";\n\nconst emptyLinkData: LinkData = { url: \"\", target: null, alt: null };\n\nfunction getSelectionKey(selection: BaseSelection | null): string | null {\n if ($isRangeSelection(selection)) {\n return `${selection.anchor.key}:${selection.anchor.offset}-${selection.focus.key}:${selection.focus.offset}`;\n }\n return null;\n}\n\nfunction getLinkDataFromSelection(): LinkData {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return emptyLinkData;\n }\n\n const node = getSelectedNode(selection);\n const parent = node.getParent();\n\n if ($isLinkNode(parent)) {\n return {\n url: parent.getURL(),\n target: parent.getTarget(),\n alt: parent.getAlt()\n };\n }\n\n if ($isLinkNode(node)) {\n return {\n url: node.getURL(),\n target: node.getTarget(),\n alt: node.getAlt()\n };\n }\n\n return emptyLinkData;\n}\n\nexport function useFloatingLinkEditor(editor: LexicalEditor) {\n const editorRef = useRef<HTMLDivElement | null>(null);\n const [linkData, setLinkData] = useState<LinkData>(emptyLinkData);\n const [lastSelection, setLastSelection] = useState<BaseSelection | null>(null);\n const suppressedSelectionKeyRef = useRef<string | null>(null);\n\n const updateLinkEditor = useCallback(() => {\n const selection = $getSelection();\n const selectionKey = getSelectionKey(selection);\n\n // If we're still on the same selection that was suppressed, hide the popover.\n if (suppressedSelectionKeyRef.current !== null) {\n if (selectionKey === suppressedSelectionKeyRef.current) {\n const editorElem = editorRef.current;\n if (editorElem) {\n setFloatingElemPosition(null, editorElem);\n }\n setLastSelection(null);\n setLinkData(emptyLinkData);\n return true;\n }\n // New selection — clear suppression.\n suppressedSelectionKeyRef.current = null;\n }\n\n setLinkData(getLinkDataFromSelection());\n\n const editorElem = editorRef.current;\n const nativeSelection = window.getSelection();\n const activeElement = document.activeElement;\n\n if (editorElem === null) {\n return;\n }\n\n const rootElement = editor.getRootElement();\n\n if (\n selection !== null &&\n nativeSelection !== null &&\n rootElement !== null &&\n rootElement.contains(nativeSelection.anchorNode)\n ) {\n const range = nativeSelection.getRangeAt(0);\n setFloatingElemPosition(range, editorElem);\n setLastSelection(selection);\n } else if (!activeElement || activeElement.className !== \"link-input\") {\n if (rootElement !== null) {\n setFloatingElemPosition(null, editorElem);\n }\n setLastSelection(null);\n setLinkData(emptyLinkData);\n }\n\n return true;\n }, [editor]);\n\n const removeLink = useCallback(() => {\n editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);\n }, [editor]);\n\n const applyChanges = useCallback(\n (linkData: LinkData) => {\n const confirmedLinkData = {\n url: sanitizeUrl(linkData.url),\n target: linkData.target,\n alt: linkData.alt\n };\n\n if (lastSelection !== null) {\n editor.read(() => {\n const selection = $getSelection();\n suppressedSelectionKeyRef.current = getSelectionKey(selection);\n });\n\n editor.dispatchCommand(TOGGLE_LINK_COMMAND, confirmedLinkData);\n }\n\n setLastSelection(null);\n },\n [editor, lastSelection]\n );\n\n useEffect(() => {\n return mergeRegister(\n editor.registerUpdateListener(({ editorState }) => {\n editorState.read(() => {\n updateLinkEditor();\n });\n }),\n\n editor.registerCommand(\n SELECTION_CHANGE_COMMAND,\n () => {\n updateLinkEditor();\n return false;\n },\n COMMAND_PRIORITY_LOW\n )\n );\n }, [editor, updateLinkEditor]);\n\n useEffect(() => {\n editor.read(() => {\n updateLinkEditor();\n });\n }, [editor, updateLinkEditor]);\n\n return { editorRef, linkData, applyChanges, removeLink };\n}\n"],"names":["emptyLinkData","getSelectionKey","selection","$isRangeSelection","getLinkDataFromSelection","$getSelection","node","getSelectedNode","parent","$isLinkNode","useFloatingLinkEditor","editor","editorRef","useRef","linkData","setLinkData","useState","lastSelection","setLastSelection","suppressedSelectionKeyRef","updateLinkEditor","useCallback","selectionKey","editorElem","setFloatingElemPosition","nativeSelection","window","activeElement","document","rootElement","range","removeLink","TOGGLE_LINK_COMMAND","applyChanges","confirmedLinkData","sanitizeUrl","useEffect","mergeRegister","editorState","SELECTION_CHANGE_COMMAND","COMMAND_PRIORITY_LOW"],"mappings":";;;;;;AAgBA,MAAMA,gBAA0B;IAAE,KAAK;IAAI,QAAQ;IAAM,KAAK;AAAK;AAEnE,SAASC,gBAAgBC,SAA+B;IACpD,IAAIC,kBAAkBD,YAClB,OAAO,GAAGA,UAAU,MAAM,CAAC,GAAG,CAAC,CAAC,EAAEA,UAAU,MAAM,CAAC,MAAM,CAAC,CAAC,EAAEA,UAAU,KAAK,CAAC,GAAG,CAAC,CAAC,EAAEA,UAAU,KAAK,CAAC,MAAM,EAAE;IAEhH,OAAO;AACX;AAEA,SAASE;IACL,MAAMF,YAAYG;IAClB,IAAI,CAACF,kBAAkBD,YACnB,OAAOF;IAGX,MAAMM,OAAOC,gBAAgBL;IAC7B,MAAMM,SAASF,KAAK,SAAS;IAE7B,IAAIG,YAAYD,SACZ,OAAO;QACH,KAAKA,OAAO,MAAM;QAClB,QAAQA,OAAO,SAAS;QACxB,KAAKA,OAAO,MAAM;IACtB;IAGJ,IAAIC,YAAYH,OACZ,OAAO;QACH,KAAKA,KAAK,MAAM;QAChB,QAAQA,KAAK,SAAS;QACtB,KAAKA,KAAK,MAAM;IACpB;IAGJ,OAAON;AACX;AAEO,SAASU,sBAAsBC,MAAqB;IACvD,MAAMC,YAAYC,OAA8B;IAChD,MAAM,CAACC,UAAUC,YAAY,GAAGC,SAAmBhB;IACnD,MAAM,CAACiB,eAAeC,iBAAiB,GAAGF,SAA+B;IACzE,MAAMG,4BAA4BN,OAAsB;
|
|
1
|
+
{"version":3,"file":"plugins/FloatingLinkEditorPlugin/useFloatingLinkEditor.js","sources":["../../../src/plugins/FloatingLinkEditorPlugin/useFloatingLinkEditor.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n SELECTION_CHANGE_COMMAND,\n type BaseSelection,\n type LexicalEditor,\n COMMAND_PRIORITY_LOW,\n $getSelection,\n $isRangeSelection,\n mergeRegister\n} from \"lexical\";\nimport { $isLinkNode, TOGGLE_LINK_COMMAND } from \"@webiny/lexical-nodes\";\nimport { getSelectedNode } from \"~/utils/getSelectedNode.js\";\nimport { setFloatingElemPosition } from \"~/utils/setFloatingElemPosition.js\";\nimport { sanitizeUrl } from \"~/utils/sanitizeUrl.js\";\nimport { LinkData } from \"./types.js\";\n\nconst emptyLinkData: LinkData = { url: \"\", target: null, alt: null };\n\nfunction getSelectionKey(selection: BaseSelection | null): string | null {\n if ($isRangeSelection(selection)) {\n return `${selection.anchor.key}:${selection.anchor.offset}-${selection.focus.key}:${selection.focus.offset}`;\n }\n return null;\n}\n\nfunction getLinkDataFromSelection(): LinkData {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n return emptyLinkData;\n }\n\n const node = getSelectedNode(selection);\n const parent = node.getParent();\n\n if ($isLinkNode(parent)) {\n return {\n url: parent.getURL(),\n target: parent.getTarget(),\n alt: parent.getAlt()\n };\n }\n\n if ($isLinkNode(node)) {\n return {\n url: node.getURL(),\n target: node.getTarget(),\n alt: node.getAlt()\n };\n }\n\n return emptyLinkData;\n}\n\nexport function useFloatingLinkEditor(editor: LexicalEditor) {\n const editorRef = useRef<HTMLDivElement | null>(null);\n const [linkData, setLinkData] = useState<LinkData>(emptyLinkData);\n const [lastSelection, setLastSelection] = useState<BaseSelection | null>(null);\n const suppressedSelectionKeyRef = useRef<string | null>(null);\n // The popover is rendered outside the editor, so it doesn't move when the page scrolls.\n // We keep the last range around to reposition the popover on scroll.\n const rangeRef = useRef<Range | null>(null);\n\n const updateLinkEditor = useCallback(() => {\n const selection = $getSelection();\n const selectionKey = getSelectionKey(selection);\n\n // If we're still on the same selection that was suppressed, hide the popover.\n if (suppressedSelectionKeyRef.current !== null) {\n if (selectionKey === suppressedSelectionKeyRef.current) {\n const editorElem = editorRef.current;\n if (editorElem) {\n setFloatingElemPosition(null, editorElem);\n }\n rangeRef.current = null;\n setLastSelection(null);\n setLinkData(emptyLinkData);\n return true;\n }\n // New selection — clear suppression.\n suppressedSelectionKeyRef.current = null;\n }\n\n setLinkData(getLinkDataFromSelection());\n\n const editorElem = editorRef.current;\n const nativeSelection = window.getSelection();\n const activeElement = document.activeElement;\n\n if (editorElem === null) {\n return;\n }\n\n const rootElement = editor.getRootElement();\n\n if (\n selection !== null &&\n nativeSelection !== null &&\n rootElement !== null &&\n rootElement.contains(nativeSelection.anchorNode)\n ) {\n const range = nativeSelection.getRangeAt(0);\n rangeRef.current = range;\n setFloatingElemPosition(range, editorElem);\n setLastSelection(selection);\n } else if (!activeElement || activeElement.className !== \"link-input\") {\n if (rootElement !== null) {\n setFloatingElemPosition(null, editorElem);\n }\n rangeRef.current = null;\n setLastSelection(null);\n setLinkData(emptyLinkData);\n }\n\n return true;\n }, [editor]);\n\n const removeLink = useCallback(() => {\n editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);\n }, [editor]);\n\n const applyChanges = useCallback(\n (linkData: LinkData) => {\n const confirmedLinkData = {\n url: sanitizeUrl(linkData.url),\n target: linkData.target,\n alt: linkData.alt\n };\n\n if (lastSelection !== null) {\n editor.read(() => {\n const selection = $getSelection();\n suppressedSelectionKeyRef.current = getSelectionKey(selection);\n });\n\n editor.dispatchCommand(TOGGLE_LINK_COMMAND, confirmedLinkData);\n }\n\n setLastSelection(null);\n },\n [editor, lastSelection]\n );\n\n useEffect(() => {\n return mergeRegister(\n editor.registerUpdateListener(({ editorState }) => {\n editorState.read(() => {\n updateLinkEditor();\n });\n }),\n\n editor.registerCommand(\n SELECTION_CHANGE_COMMAND,\n () => {\n updateLinkEditor();\n return false;\n },\n COMMAND_PRIORITY_LOW\n )\n );\n }, [editor, updateLinkEditor]);\n\n useEffect(() => {\n const reposition = () => {\n if (rangeRef.current && editorRef.current) {\n setFloatingElemPosition(rangeRef.current, editorRef.current);\n }\n };\n\n // Capture scroll events from any scrollable ancestor, not only the window.\n window.addEventListener(\"scroll\", reposition, true);\n window.addEventListener(\"resize\", reposition);\n\n return () => {\n window.removeEventListener(\"scroll\", reposition, true);\n window.removeEventListener(\"resize\", reposition);\n };\n }, []);\n\n useEffect(() => {\n editor.read(() => {\n updateLinkEditor();\n });\n }, [editor, updateLinkEditor]);\n\n return { editorRef, linkData, applyChanges, removeLink };\n}\n"],"names":["emptyLinkData","getSelectionKey","selection","$isRangeSelection","getLinkDataFromSelection","$getSelection","node","getSelectedNode","parent","$isLinkNode","useFloatingLinkEditor","editor","editorRef","useRef","linkData","setLinkData","useState","lastSelection","setLastSelection","suppressedSelectionKeyRef","rangeRef","updateLinkEditor","useCallback","selectionKey","editorElem","setFloatingElemPosition","nativeSelection","window","activeElement","document","rootElement","range","removeLink","TOGGLE_LINK_COMMAND","applyChanges","confirmedLinkData","sanitizeUrl","useEffect","mergeRegister","editorState","SELECTION_CHANGE_COMMAND","COMMAND_PRIORITY_LOW","reposition"],"mappings":"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"}
|