@tarviks/lexical-rich-editor 1.3.10 → 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.js CHANGED
@@ -906,6 +906,7 @@ var init_InlineImageComponent = __esm({
906
906
  "data-position": position,
907
907
  style: {
908
908
  display: "block",
909
+ maxWidth: "100%",
909
910
  height,
910
911
  width
911
912
  },
@@ -1095,14 +1096,17 @@ var init_InlineImageComponent = __esm({
1095
1096
  InlineImageComponent_default = InlineImageComponent;
1096
1097
  }
1097
1098
  });
1098
- var InlineImageComponent2, $convertInlineImageElement, InlineImageNode, $createInlineImageNode, $isInlineImageNode;
1099
+ var InlineImageComponent2, INLINE_IMAGE_MARKER_ATTR, $convertInlineImageElement, InlineImageNode, $createInlineImageNode, $isInlineImageNode;
1099
1100
  var init_InlineImageNode = __esm({
1100
1101
  "src/Nodes/InlineImageNode.tsx"() {
1101
1102
  InlineImageComponent2 = React9__namespace.lazy(() => Promise.resolve().then(() => (init_InlineImageComponent(), InlineImageComponent_exports)));
1103
+ INLINE_IMAGE_MARKER_ATTR = "data-lexical-inline-image";
1102
1104
  $convertInlineImageElement = (domNode) => {
1103
1105
  if (lexical.isHTMLElement(domNode) && domNode.nodeName === "IMG") {
1104
1106
  const { alt: altText, src, width, height } = domNode;
1105
- const node = $createInlineImageNode({ altText, height, src, width });
1107
+ const positionAttr = domNode.getAttribute("data-position");
1108
+ const position = positionAttr === "left" || positionAttr === "right" || positionAttr === "full" ? positionAttr : void 0;
1109
+ const node = $createInlineImageNode({ altText, height, position, src, width });
1106
1110
  return { node };
1107
1111
  }
1108
1112
  return null;
@@ -1146,10 +1150,15 @@ var init_InlineImageNode = __esm({
1146
1150
  }
1147
1151
  static importDOM() {
1148
1152
  return {
1149
- img: (node) => ({
1150
- conversion: $convertInlineImageElement,
1151
- priority: 0
1152
- })
1153
+ img: (node) => {
1154
+ if (lexical.isHTMLElement(node) && node.hasAttribute(INLINE_IMAGE_MARKER_ATTR)) {
1155
+ return {
1156
+ conversion: $convertInlineImageElement,
1157
+ priority: 3
1158
+ };
1159
+ }
1160
+ return null;
1161
+ }
1153
1162
  };
1154
1163
  }
1155
1164
  constructor(src, altText, position, width, height, showCaption, caption, key) {
@@ -1168,6 +1177,11 @@ var init_InlineImageNode = __esm({
1168
1177
  element.setAttribute("alt", this.__altText);
1169
1178
  element.setAttribute("width", this.__width.toString());
1170
1179
  element.setAttribute("height", this.__height.toString());
1180
+ element.setAttribute(INLINE_IMAGE_MARKER_ATTR, "true");
1181
+ if (this.__position) {
1182
+ element.setAttribute("data-position", this.__position);
1183
+ _InlineImageNode.applyPositionStyle(element, this.__position);
1184
+ }
1171
1185
  return { element };
1172
1186
  }
1173
1187
  exportJSON() {
@@ -1225,12 +1239,34 @@ var init_InlineImageNode = __esm({
1225
1239
  }
1226
1240
  }
1227
1241
  // View
1242
+ // The position-left/right/full behavior is also defined in the package's
1243
+ // CSS file as `.inline-editor-image.position-*` rules, but that file is a
1244
+ // separate import (`@tarviks/lexical-rich-editor/dist/index.css`) a
1245
+ // consuming app can forget to include. Applying it as inline styles here
1246
+ // too means positioning still works even when that stylesheet isn't
1247
+ // loaded, instead of silently no-op'ing back to normal block flow.
1248
+ static applyPositionStyle(span, position) {
1249
+ span.style.removeProperty("float");
1250
+ span.style.removeProperty("display");
1251
+ span.style.removeProperty("margin");
1252
+ if (position === "left") {
1253
+ span.style.float = "left";
1254
+ span.style.margin = "4px 8px 4px 0";
1255
+ } else if (position === "right") {
1256
+ span.style.float = "right";
1257
+ span.style.margin = "4px 0 4px 8px";
1258
+ } else if (position === "full") {
1259
+ span.style.display = "block";
1260
+ span.style.margin = "8px 0";
1261
+ }
1262
+ }
1228
1263
  createDOM(config) {
1229
1264
  const span = document.createElement("span");
1230
1265
  const className = `${config.theme.inlineImage} position-${this.__position}`;
1231
1266
  if (className !== void 0) {
1232
1267
  span.className = className;
1233
1268
  }
1269
+ _InlineImageNode.applyPositionStyle(span, this.__position);
1234
1270
  return span;
1235
1271
  }
1236
1272
  updateDOM(prevNode, dom, config) {
@@ -1240,6 +1276,7 @@ var init_InlineImageNode = __esm({
1240
1276
  if (className !== void 0) {
1241
1277
  dom.className = className;
1242
1278
  }
1279
+ _InlineImageNode.applyPositionStyle(dom, position);
1243
1280
  }
1244
1281
  return false;
1245
1282
  }
@@ -2966,6 +3003,8 @@ var ALLOWED_ATTRS = /* @__PURE__ */ new Set([
2966
3003
  "data-lex-block",
2967
3004
  "data-kind",
2968
3005
  "data-lexical-decorator",
3006
+ "data-lexical-inline-image",
3007
+ "data-position",
2969
3008
  // Misc
2970
3009
  "title",
2971
3010
  "allowfullscreen"
@@ -5120,11 +5159,35 @@ function SpellCheckPlugin({
5120
5159
  }
5121
5160
  ) : null;
5122
5161
  }
5162
+ function getScrollClipAncestor(el) {
5163
+ let node = el.parentElement;
5164
+ while (node) {
5165
+ const style = getComputedStyle(node);
5166
+ if ((style.overflowY === "auto" || style.overflowY === "scroll") && node.scrollHeight > node.clientHeight) {
5167
+ return node;
5168
+ }
5169
+ node = node.parentElement;
5170
+ }
5171
+ return null;
5172
+ }
5173
+ function isRectVisible(rect, cellDom) {
5174
+ 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
+ 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
+ }
5183
+ return true;
5184
+ }
5123
5185
  function TableActionMenuPlugin({ disabled = false }) {
5124
5186
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
5125
5187
  const portalContainer = useFloatingPortalContainer(editor);
5126
5188
  const [isInTable, setIsInTable] = React9__namespace.useState(false);
5127
5189
  const [anchorRect, setAnchorRect] = React9__namespace.useState(null);
5190
+ const [isAnchorVisible, setIsAnchorVisible] = React9__namespace.useState(false);
5128
5191
  const [contentRight, setContentRight] = React9__namespace.useState(null);
5129
5192
  const [open, setOpen] = React9__namespace.useState(false);
5130
5193
  const openRef = React9__namespace.useRef(false);
@@ -5150,8 +5213,10 @@ function TableActionMenuPlugin({ disabled = false }) {
5150
5213
  if (tableNode) {
5151
5214
  const dom = editor.getElementByKey(tableNode.getKey());
5152
5215
  if (dom) {
5216
+ const rect = dom.getBoundingClientRect();
5153
5217
  setIsInTable(true);
5154
- setAnchorRect(dom.getBoundingClientRect());
5218
+ setAnchorRect(rect);
5219
+ setIsAnchorVisible(isRectVisible(rect, dom));
5155
5220
  setContentRight(null);
5156
5221
  return;
5157
5222
  }
@@ -5160,6 +5225,7 @@ function TableActionMenuPlugin({ disabled = false }) {
5160
5225
  if (!lexical.$isRangeSelection(selection)) {
5161
5226
  setIsInTable(false);
5162
5227
  setAnchorRect(null);
5228
+ setIsAnchorVisible(false);
5163
5229
  setContentRight(null);
5164
5230
  return;
5165
5231
  }
@@ -5168,6 +5234,7 @@ function TableActionMenuPlugin({ disabled = false }) {
5168
5234
  if (!cellNode || !table.$isTableCellNode(cellNode)) {
5169
5235
  setIsInTable(false);
5170
5236
  setAnchorRect(null);
5237
+ setIsAnchorVisible(false);
5171
5238
  setContentRight(null);
5172
5239
  return;
5173
5240
  }
@@ -5175,11 +5242,14 @@ function TableActionMenuPlugin({ disabled = false }) {
5175
5242
  if (!cellDom) {
5176
5243
  setIsInTable(false);
5177
5244
  setAnchorRect(null);
5245
+ setIsAnchorVisible(false);
5178
5246
  setContentRight(null);
5179
5247
  return;
5180
5248
  }
5249
+ const cellRect = cellDom.getBoundingClientRect();
5181
5250
  setIsInTable(true);
5182
- setAnchorRect(cellDom.getBoundingClientRect());
5251
+ setAnchorRect(cellRect);
5252
+ setIsAnchorVisible(isRectVisible(cellRect, cellDom));
5183
5253
  setContentRight(measureContentRight(cellDom));
5184
5254
  if (lexical.$isRangeSelection(selection)) {
5185
5255
  savedAnchorRef.current = {
@@ -5258,7 +5328,7 @@ function TableActionMenuPlugin({ disabled = false }) {
5258
5328
  lexical.COMMAND_PRIORITY_HIGH
5259
5329
  );
5260
5330
  }, [editor, disabled]);
5261
- const canShow = isInTable && !!anchorRect && !disabled;
5331
+ const canShow = isInTable && !!anchorRect && isAnchorVisible && !disabled;
5262
5332
  const handleStyle = React9__namespace.useMemo(() => {
5263
5333
  if (!anchorRect) return void 0;
5264
5334
  const top = Math.max(8, anchorRect.top + 6);