@tarviks/lexical-rich-editor 1.3.11 → 1.3.13

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/dist/index.js CHANGED
@@ -2230,6 +2230,48 @@ function $createYouTubeNode(videoID, width = DEFAULT_WIDTH, height = DEFAULT_HEI
2230
2230
  function $isYouTubeNode(node) {
2231
2231
  return node instanceof YouTubeNode;
2232
2232
  }
2233
+ var PRESERVED_STYLE_PROPS = [
2234
+ ["fontFamily", "font-family"],
2235
+ ["fontSize", "font-size"],
2236
+ ["color", "color"],
2237
+ ["backgroundColor", "background-color"]
2238
+ ];
2239
+ var $convertSpanElementWithStyle = (domNode) => {
2240
+ const style = domNode.style;
2241
+ const cssParts = [];
2242
+ for (const [domProp, cssProp] of PRESERVED_STYLE_PROPS) {
2243
+ const value = style[domProp];
2244
+ if (value) cssParts.push(`${cssProp}: ${value}`);
2245
+ }
2246
+ const cssText = cssParts.length > 0 ? `${cssParts.join("; ")};` : "";
2247
+ const fontWeight = style.fontWeight;
2248
+ const textDecoration = style.textDecoration.split(" ");
2249
+ const hasBoldFontWeight = fontWeight === "700" || fontWeight === "bold";
2250
+ const hasLinethroughTextDecoration = textDecoration.includes("line-through");
2251
+ const hasItalicFontStyle = style.fontStyle === "italic";
2252
+ const hasUnderlineTextDecoration = textDecoration.includes("underline");
2253
+ const verticalAlign = style.verticalAlign;
2254
+ return {
2255
+ node: null,
2256
+ forChild: (lexicalNode) => {
2257
+ if (!lexical.$isTextNode(lexicalNode)) return lexicalNode;
2258
+ if (cssText) lexicalNode.setStyle(cssText);
2259
+ if (hasBoldFontWeight && !lexicalNode.hasFormat("bold")) lexicalNode.toggleFormat("bold");
2260
+ if (hasLinethroughTextDecoration && !lexicalNode.hasFormat("strikethrough")) lexicalNode.toggleFormat("strikethrough");
2261
+ if (hasItalicFontStyle && !lexicalNode.hasFormat("italic")) lexicalNode.toggleFormat("italic");
2262
+ if (hasUnderlineTextDecoration && !lexicalNode.hasFormat("underline")) lexicalNode.toggleFormat("underline");
2263
+ if (verticalAlign === "sub" && !lexicalNode.hasFormat("subscript")) lexicalNode.toggleFormat("subscript");
2264
+ if (verticalAlign === "super" && !lexicalNode.hasFormat("superscript")) lexicalNode.toggleFormat("superscript");
2265
+ return lexicalNode;
2266
+ }
2267
+ };
2268
+ };
2269
+ var preserveTextStyleImportMap = {
2270
+ span: () => ({
2271
+ conversion: $convertSpanElementWithStyle,
2272
+ priority: 0
2273
+ })
2274
+ };
2233
2275
  var normalize = (s) => (s ?? "").replace(/\s+/g, " ").trim();
2234
2276
  function readContextWindow(maxChars = 300, cursorIndex) {
2235
2277
  const full = normalize(lexical.$getRoot().getTextContent());
@@ -3177,12 +3219,16 @@ function splitHeadingsAtBrSequences(html) {
3177
3219
  });
3178
3220
  return doc.body.innerHTML;
3179
3221
  }
3180
- var CustomOnChangePlugin = ({ value, onChange }) => {
3222
+ var CustomOnChangePlugin = ({ value, onChange, isReadOnly }) => {
3181
3223
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
3182
3224
  const initializedRef = React9.useRef(false);
3225
+ const lastImportedValueRef = React9.useRef(null);
3183
3226
  React9.useEffect(() => {
3184
- if (!value || initializedRef.current) return;
3227
+ if (!value) return;
3228
+ if (!isReadOnly && initializedRef.current) return;
3229
+ if (value === lastImportedValueRef.current) return;
3185
3230
  initializedRef.current = true;
3231
+ lastImportedValueRef.current = value;
3186
3232
  editor.update(() => {
3187
3233
  const root = lexical.$getRoot();
3188
3234
  root.clear();
@@ -3193,14 +3239,17 @@ var CustomOnChangePlugin = ({ value, onChange }) => {
3193
3239
  const nodes = html.$generateNodesFromDOM(editor, dom);
3194
3240
  root.append(...nodes);
3195
3241
  });
3196
- }, [editor, value]);
3242
+ }, [editor, value, isReadOnly]);
3197
3243
  return /* @__PURE__ */ jsxRuntime.jsx(
3198
3244
  LexicalOnChangePlugin.OnChangePlugin,
3199
3245
  {
3246
+ ignoreSelectionChange: true,
3200
3247
  onChange: (editorState) => {
3201
3248
  editorState.read(() => {
3202
3249
  const raw = html.$generateHtmlFromNodes(editor);
3203
- onChange(postProcessOutput(splitHeadingsAtBrSequences(raw)));
3250
+ const html$1 = postProcessOutput(splitHeadingsAtBrSequences(raw));
3251
+ lastImportedValueRef.current = html$1;
3252
+ onChange(html$1);
3204
3253
  });
3205
3254
  }
3206
3255
  }
@@ -5170,16 +5219,18 @@ function getScrollClipAncestor(el) {
5170
5219
  }
5171
5220
  return null;
5172
5221
  }
5222
+ var BUTTON_VERTICAL_FOOTPRINT = 34;
5173
5223
  function isRectVisible(rect, cellDom) {
5174
5224
  if (rect.width === 0 && rect.height === 0) return false;
5175
- if (rect.bottom <= 0 || rect.top >= window.innerHeight) return false;
5176
- if (rect.right <= 0 || rect.left >= window.innerWidth) return false;
5177
5225
  const clipAncestor = getScrollClipAncestor(cellDom);
5178
- if (clipAncestor) {
5179
- const containerRect = clipAncestor.getBoundingClientRect();
5180
- if (rect.bottom <= containerRect.top || rect.top >= containerRect.bottom) return false;
5181
- if (rect.right <= containerRect.left || rect.left >= containerRect.right) return false;
5182
- }
5226
+ const containerRect = clipAncestor?.getBoundingClientRect();
5227
+ const visibleTop = containerRect ? Math.max(0, containerRect.top) : 0;
5228
+ const visibleBottom = containerRect ? Math.min(window.innerHeight, containerRect.bottom) : window.innerHeight;
5229
+ const visibleLeft = containerRect ? Math.max(0, containerRect.left) : 0;
5230
+ const visibleRight = containerRect ? Math.min(window.innerWidth, containerRect.right) : window.innerWidth;
5231
+ if (rect.top < visibleTop) return false;
5232
+ if (rect.top + BUTTON_VERTICAL_FOOTPRINT > visibleBottom) return false;
5233
+ if (rect.left >= visibleRight || rect.right <= visibleLeft) return false;
5183
5234
  return true;
5184
5235
  }
5185
5236
  function TableActionMenuPlugin({ disabled = false }) {
@@ -5331,9 +5382,25 @@ function TableActionMenuPlugin({ disabled = false }) {
5331
5382
  const canShow = isInTable && !!anchorRect && isAnchorVisible && !disabled;
5332
5383
  const handleStyle = React9__namespace.useMemo(() => {
5333
5384
  if (!anchorRect) return void 0;
5334
- const top = Math.max(8, anchorRect.top + 6);
5335
- const clampedCellRight = Math.min(anchorRect.right, window.innerWidth - 8);
5336
- const left = contentRight !== null ? Math.max(anchorRect.left + 4, Math.min(contentRight + 4, clampedCellRight - 32)) : Math.max(8, anchorRect.left + 8);
5385
+ const BUTTON_SIZE = 28;
5386
+ const PAD = 8;
5387
+ const editorRect = editor.getRootElement()?.getBoundingClientRect();
5388
+ const clipRect = editorRect ? {
5389
+ top: Math.max(editorRect.top, 0),
5390
+ left: Math.max(editorRect.left, 0),
5391
+ right: Math.min(editorRect.right, window.innerWidth),
5392
+ bottom: Math.min(editorRect.bottom, window.innerHeight)
5393
+ } : { top: 0, left: 0, right: window.innerWidth, bottom: window.innerHeight };
5394
+ const isCellVisible = anchorRect.bottom > clipRect.top && anchorRect.top < clipRect.bottom && anchorRect.right > clipRect.left && anchorRect.left < clipRect.right;
5395
+ if (!isCellVisible) return void 0;
5396
+ const minTop = editorRect ? editorRect.top + PAD : PAD;
5397
+ const maxTop = editorRect ? Math.max(minTop, editorRect.bottom - BUTTON_SIZE - PAD) : window.innerHeight - BUTTON_SIZE - PAD;
5398
+ const minLeft = editorRect ? editorRect.left + PAD : PAD;
5399
+ const maxLeft = editorRect ? Math.max(minLeft, editorRect.right - BUTTON_SIZE - PAD) : window.innerWidth - BUTTON_SIZE - PAD;
5400
+ const top = Math.min(Math.max(anchorRect.top + 6, minTop), maxTop);
5401
+ const clampedCellRight = Math.min(anchorRect.right, maxLeft + BUTTON_SIZE);
5402
+ const rawLeft = contentRight !== null ? Math.max(anchorRect.left + 4, Math.min(contentRight + 4, clampedCellRight - 32)) : anchorRect.left + 8;
5403
+ const left = Math.min(Math.max(rawLeft, minLeft), maxLeft);
5337
5404
  return {
5338
5405
  position: "fixed",
5339
5406
  top,
@@ -5343,7 +5410,7 @@ function TableActionMenuPlugin({ disabled = false }) {
5343
5410
  // button renders behind the modal chrome in a full-page/Panel view.
5344
5411
  zIndex: 10000010
5345
5412
  };
5346
- }, [anchorRect, contentRight]);
5413
+ }, [anchorRect, contentRight, editor]);
5347
5414
  const dangerStyle = {
5348
5415
  color: "var(--colorPaletteRedForeground1)"
5349
5416
  };
@@ -8287,7 +8354,10 @@ var ContentEditorComponent = React9.forwardRef(
8287
8354
  AutocompleteNode,
8288
8355
  SpellErrorNode,
8289
8356
  HtmlBlockNode
8290
- ]
8357
+ ],
8358
+ html: {
8359
+ import: preserveTextStyleImportMap
8360
+ }
8291
8361
  };
8292
8362
  props.onBeforeInitialize?.(config);
8293
8363
  return config;
@@ -8615,7 +8685,14 @@ var ContentEditorComponent = React9.forwardRef(
8615
8685
  }
8616
8686
  ),
8617
8687
  !isReadOnly && props.showFloatingToolbar && /* @__PURE__ */ jsxRuntime.jsx(CharacterStylesPopupPlugin, {}),
8618
- /* @__PURE__ */ jsxRuntime.jsx(CustomOnChangePlugin, { value: props.value, onChange: props.onChange }),
8688
+ /* @__PURE__ */ jsxRuntime.jsx(
8689
+ CustomOnChangePlugin,
8690
+ {
8691
+ value: props.value,
8692
+ onChange: props.onChange,
8693
+ isReadOnly
8694
+ }
8695
+ ),
8619
8696
  (props.wordLimit !== void 0 || props.required || props.minWords !== void 0 || props.maxWords !== void 0 || props.minChars !== void 0 || props.maxChars !== void 0 || props.noImages || props.maxImages !== void 0 || props.noLinks || props.maxLinks !== void 0 || props.noTables) && /* @__PURE__ */ jsxRuntime.jsx(ContentMetricsPlugin, { onMetricsChange: handleMetrics }),
8620
8697
  /* @__PURE__ */ jsxRuntime.jsx(
8621
8698
  RefApiPlugin,