@tarviks/lexical-rich-editor 1.3.9 → 1.3.11

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.mjs CHANGED
@@ -885,6 +885,7 @@ var init_InlineImageComponent = __esm({
885
885
  "data-position": position,
886
886
  style: {
887
887
  display: "block",
888
+ maxWidth: "100%",
888
889
  height,
889
890
  width
890
891
  },
@@ -1074,14 +1075,17 @@ var init_InlineImageComponent = __esm({
1074
1075
  InlineImageComponent_default = InlineImageComponent;
1075
1076
  }
1076
1077
  });
1077
- var InlineImageComponent2, $convertInlineImageElement, InlineImageNode, $createInlineImageNode, $isInlineImageNode;
1078
+ var InlineImageComponent2, INLINE_IMAGE_MARKER_ATTR, $convertInlineImageElement, InlineImageNode, $createInlineImageNode, $isInlineImageNode;
1078
1079
  var init_InlineImageNode = __esm({
1079
1080
  "src/Nodes/InlineImageNode.tsx"() {
1080
1081
  InlineImageComponent2 = React9.lazy(() => Promise.resolve().then(() => (init_InlineImageComponent(), InlineImageComponent_exports)));
1082
+ INLINE_IMAGE_MARKER_ATTR = "data-lexical-inline-image";
1081
1083
  $convertInlineImageElement = (domNode) => {
1082
1084
  if (isHTMLElement(domNode) && domNode.nodeName === "IMG") {
1083
1085
  const { alt: altText, src, width, height } = domNode;
1084
- const node = $createInlineImageNode({ altText, height, src, width });
1086
+ const positionAttr = domNode.getAttribute("data-position");
1087
+ const position = positionAttr === "left" || positionAttr === "right" || positionAttr === "full" ? positionAttr : void 0;
1088
+ const node = $createInlineImageNode({ altText, height, position, src, width });
1085
1089
  return { node };
1086
1090
  }
1087
1091
  return null;
@@ -1125,10 +1129,15 @@ var init_InlineImageNode = __esm({
1125
1129
  }
1126
1130
  static importDOM() {
1127
1131
  return {
1128
- img: (node) => ({
1129
- conversion: $convertInlineImageElement,
1130
- priority: 0
1131
- })
1132
+ img: (node) => {
1133
+ if (isHTMLElement(node) && node.hasAttribute(INLINE_IMAGE_MARKER_ATTR)) {
1134
+ return {
1135
+ conversion: $convertInlineImageElement,
1136
+ priority: 3
1137
+ };
1138
+ }
1139
+ return null;
1140
+ }
1132
1141
  };
1133
1142
  }
1134
1143
  constructor(src, altText, position, width, height, showCaption, caption, key) {
@@ -1147,6 +1156,11 @@ var init_InlineImageNode = __esm({
1147
1156
  element.setAttribute("alt", this.__altText);
1148
1157
  element.setAttribute("width", this.__width.toString());
1149
1158
  element.setAttribute("height", this.__height.toString());
1159
+ element.setAttribute(INLINE_IMAGE_MARKER_ATTR, "true");
1160
+ if (this.__position) {
1161
+ element.setAttribute("data-position", this.__position);
1162
+ _InlineImageNode.applyPositionStyle(element, this.__position);
1163
+ }
1150
1164
  return { element };
1151
1165
  }
1152
1166
  exportJSON() {
@@ -1204,12 +1218,34 @@ var init_InlineImageNode = __esm({
1204
1218
  }
1205
1219
  }
1206
1220
  // View
1221
+ // The position-left/right/full behavior is also defined in the package's
1222
+ // CSS file as `.inline-editor-image.position-*` rules, but that file is a
1223
+ // separate import (`@tarviks/lexical-rich-editor/dist/index.css`) a
1224
+ // consuming app can forget to include. Applying it as inline styles here
1225
+ // too means positioning still works even when that stylesheet isn't
1226
+ // loaded, instead of silently no-op'ing back to normal block flow.
1227
+ static applyPositionStyle(span, position) {
1228
+ span.style.removeProperty("float");
1229
+ span.style.removeProperty("display");
1230
+ span.style.removeProperty("margin");
1231
+ if (position === "left") {
1232
+ span.style.float = "left";
1233
+ span.style.margin = "4px 8px 4px 0";
1234
+ } else if (position === "right") {
1235
+ span.style.float = "right";
1236
+ span.style.margin = "4px 0 4px 8px";
1237
+ } else if (position === "full") {
1238
+ span.style.display = "block";
1239
+ span.style.margin = "8px 0";
1240
+ }
1241
+ }
1207
1242
  createDOM(config) {
1208
1243
  const span = document.createElement("span");
1209
1244
  const className = `${config.theme.inlineImage} position-${this.__position}`;
1210
1245
  if (className !== void 0) {
1211
1246
  span.className = className;
1212
1247
  }
1248
+ _InlineImageNode.applyPositionStyle(span, this.__position);
1213
1249
  return span;
1214
1250
  }
1215
1251
  updateDOM(prevNode, dom, config) {
@@ -1219,6 +1255,7 @@ var init_InlineImageNode = __esm({
1219
1255
  if (className !== void 0) {
1220
1256
  dom.className = className;
1221
1257
  }
1258
+ _InlineImageNode.applyPositionStyle(dom, position);
1222
1259
  }
1223
1260
  return false;
1224
1261
  }
@@ -2945,6 +2982,8 @@ var ALLOWED_ATTRS = /* @__PURE__ */ new Set([
2945
2982
  "data-lex-block",
2946
2983
  "data-kind",
2947
2984
  "data-lexical-decorator",
2985
+ "data-lexical-inline-image",
2986
+ "data-position",
2948
2987
  // Misc
2949
2988
  "title",
2950
2989
  "allowfullscreen"
@@ -5099,11 +5138,35 @@ function SpellCheckPlugin({
5099
5138
  }
5100
5139
  ) : null;
5101
5140
  }
5141
+ function getScrollClipAncestor(el) {
5142
+ let node = el.parentElement;
5143
+ while (node) {
5144
+ const style = getComputedStyle(node);
5145
+ if ((style.overflowY === "auto" || style.overflowY === "scroll") && node.scrollHeight > node.clientHeight) {
5146
+ return node;
5147
+ }
5148
+ node = node.parentElement;
5149
+ }
5150
+ return null;
5151
+ }
5152
+ function isRectVisible(rect, cellDom) {
5153
+ if (rect.width === 0 && rect.height === 0) return false;
5154
+ if (rect.bottom <= 0 || rect.top >= window.innerHeight) return false;
5155
+ if (rect.right <= 0 || rect.left >= window.innerWidth) return false;
5156
+ const clipAncestor = getScrollClipAncestor(cellDom);
5157
+ if (clipAncestor) {
5158
+ const containerRect = clipAncestor.getBoundingClientRect();
5159
+ if (rect.bottom <= containerRect.top || rect.top >= containerRect.bottom) return false;
5160
+ if (rect.right <= containerRect.left || rect.left >= containerRect.right) return false;
5161
+ }
5162
+ return true;
5163
+ }
5102
5164
  function TableActionMenuPlugin({ disabled = false }) {
5103
5165
  const [editor] = useLexicalComposerContext();
5104
5166
  const portalContainer = useFloatingPortalContainer(editor);
5105
5167
  const [isInTable, setIsInTable] = React9.useState(false);
5106
5168
  const [anchorRect, setAnchorRect] = React9.useState(null);
5169
+ const [isAnchorVisible, setIsAnchorVisible] = React9.useState(false);
5107
5170
  const [contentRight, setContentRight] = React9.useState(null);
5108
5171
  const [open, setOpen] = React9.useState(false);
5109
5172
  const openRef = React9.useRef(false);
@@ -5129,8 +5192,10 @@ function TableActionMenuPlugin({ disabled = false }) {
5129
5192
  if (tableNode) {
5130
5193
  const dom = editor.getElementByKey(tableNode.getKey());
5131
5194
  if (dom) {
5195
+ const rect = dom.getBoundingClientRect();
5132
5196
  setIsInTable(true);
5133
- setAnchorRect(dom.getBoundingClientRect());
5197
+ setAnchorRect(rect);
5198
+ setIsAnchorVisible(isRectVisible(rect, dom));
5134
5199
  setContentRight(null);
5135
5200
  return;
5136
5201
  }
@@ -5139,6 +5204,7 @@ function TableActionMenuPlugin({ disabled = false }) {
5139
5204
  if (!$isRangeSelection(selection)) {
5140
5205
  setIsInTable(false);
5141
5206
  setAnchorRect(null);
5207
+ setIsAnchorVisible(false);
5142
5208
  setContentRight(null);
5143
5209
  return;
5144
5210
  }
@@ -5147,6 +5213,7 @@ function TableActionMenuPlugin({ disabled = false }) {
5147
5213
  if (!cellNode || !$isTableCellNode(cellNode)) {
5148
5214
  setIsInTable(false);
5149
5215
  setAnchorRect(null);
5216
+ setIsAnchorVisible(false);
5150
5217
  setContentRight(null);
5151
5218
  return;
5152
5219
  }
@@ -5154,11 +5221,14 @@ function TableActionMenuPlugin({ disabled = false }) {
5154
5221
  if (!cellDom) {
5155
5222
  setIsInTable(false);
5156
5223
  setAnchorRect(null);
5224
+ setIsAnchorVisible(false);
5157
5225
  setContentRight(null);
5158
5226
  return;
5159
5227
  }
5228
+ const cellRect = cellDom.getBoundingClientRect();
5160
5229
  setIsInTable(true);
5161
- setAnchorRect(cellDom.getBoundingClientRect());
5230
+ setAnchorRect(cellRect);
5231
+ setIsAnchorVisible(isRectVisible(cellRect, cellDom));
5162
5232
  setContentRight(measureContentRight(cellDom));
5163
5233
  if ($isRangeSelection(selection)) {
5164
5234
  savedAnchorRef.current = {
@@ -5184,6 +5254,18 @@ function TableActionMenuPlugin({ disabled = false }) {
5184
5254
  })
5185
5255
  );
5186
5256
  }, [editor, updateFromSelection]);
5257
+ React9.useEffect(() => {
5258
+ const root = editor.getRootElement();
5259
+ const reposition = () => updateFromSelection();
5260
+ window.addEventListener("resize", reposition);
5261
+ window.addEventListener("scroll", reposition, true);
5262
+ root?.addEventListener("scroll", reposition, { passive: true });
5263
+ return () => {
5264
+ window.removeEventListener("resize", reposition);
5265
+ window.removeEventListener("scroll", reposition, true);
5266
+ root?.removeEventListener("scroll", reposition);
5267
+ };
5268
+ }, [editor, updateFromSelection]);
5187
5269
  React9.useEffect(() => {
5188
5270
  return editor.registerCommand(
5189
5271
  KEY_DOWN_COMMAND,
@@ -5225,7 +5307,7 @@ function TableActionMenuPlugin({ disabled = false }) {
5225
5307
  COMMAND_PRIORITY_HIGH
5226
5308
  );
5227
5309
  }, [editor, disabled]);
5228
- const canShow = isInTable && !!anchorRect && !disabled;
5310
+ const canShow = isInTable && !!anchorRect && isAnchorVisible && !disabled;
5229
5311
  const handleStyle = React9.useMemo(() => {
5230
5312
  if (!anchorRect) return void 0;
5231
5313
  const top = Math.max(8, anchorRect.top + 6);