@tarviks/lexical-rich-editor 1.3.10 → 1.3.12
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 +127 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +128 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
5
5
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
6
6
|
import { useLexicalEditable } from '@lexical/react/useLexicalEditable';
|
|
7
7
|
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
|
|
8
|
-
import { createCommand, DecoratorNode, COMMAND_PRIORITY_HIGH, TextNode, ElementNode, SELECTION_CHANGE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, $getSelection, $isRangeSelection, $findMatchingParent, COMMAND_PRIORITY_LOW, KEY_DOWN_COMMAND, $getNodeByKey, $createRangeSelection, $setSelection, $createParagraphNode, $insertNodes, $isRootOrShadowRoot, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, DRAGSTART_COMMAND, DRAGOVER_COMMAND, DROP_COMMAND, $getRoot, $createTextNode, $
|
|
8
|
+
import { createCommand, $isTextNode, DecoratorNode, COMMAND_PRIORITY_HIGH, TextNode, ElementNode, SELECTION_CHANGE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, $getSelection, $isRangeSelection, $findMatchingParent, COMMAND_PRIORITY_LOW, KEY_DOWN_COMMAND, $getNodeByKey, $createRangeSelection, $setSelection, $createParagraphNode, $insertNodes, $isRootOrShadowRoot, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, DRAGSTART_COMMAND, DRAGOVER_COMMAND, DROP_COMMAND, $getRoot, $createTextNode, $isElementNode, FORMAT_ELEMENT_COMMAND, CLICK_COMMAND, $isNodeSelection, $applyNodeReplacement, FORMAT_TEXT_COMMAND, $isDecoratorNode, $getNearestNodeFromDOMNode, COMMAND_PRIORITY_CRITICAL, REDO_COMMAND, UNDO_COMMAND, getDOMSelection, KEY_ESCAPE_COMMAND, isHTMLElement, getDOMSelectionFromTarget, $isLineBreakNode, SKIP_SELECTION_FOCUS_TAG, createEditor, KEY_ENTER_COMMAND } from 'lexical';
|
|
9
9
|
import { mergeStyleSets, Stack, css, useTheme, Callout, TextField } from '@fluentui/react';
|
|
10
10
|
import { makeStyles, FluentProvider, webLightTheme, Menu, MenuTrigger, MenuPopover, MenuList, MenuGroup, MenuGroupHeader, MenuItem, MenuDivider, Dropdown, Option, Button, ToolbarDivider, MenuItemRadio, Field, Input, MessageBar, MessageBarBody, Popover, PopoverSurface, PopoverTrigger } from '@fluentui/react-components';
|
|
11
11
|
import { CodeHighlightNode, CodeNode, $isCodeHighlightNode } from '@lexical/code';
|
|
@@ -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
|
|
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
|
-
|
|
1130
|
-
|
|
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
|
}
|
|
@@ -2172,6 +2209,48 @@ function $createYouTubeNode(videoID, width = DEFAULT_WIDTH, height = DEFAULT_HEI
|
|
|
2172
2209
|
function $isYouTubeNode(node) {
|
|
2173
2210
|
return node instanceof YouTubeNode;
|
|
2174
2211
|
}
|
|
2212
|
+
var PRESERVED_STYLE_PROPS = [
|
|
2213
|
+
["fontFamily", "font-family"],
|
|
2214
|
+
["fontSize", "font-size"],
|
|
2215
|
+
["color", "color"],
|
|
2216
|
+
["backgroundColor", "background-color"]
|
|
2217
|
+
];
|
|
2218
|
+
var $convertSpanElementWithStyle = (domNode) => {
|
|
2219
|
+
const style = domNode.style;
|
|
2220
|
+
const cssParts = [];
|
|
2221
|
+
for (const [domProp, cssProp] of PRESERVED_STYLE_PROPS) {
|
|
2222
|
+
const value = style[domProp];
|
|
2223
|
+
if (value) cssParts.push(`${cssProp}: ${value}`);
|
|
2224
|
+
}
|
|
2225
|
+
const cssText = cssParts.length > 0 ? `${cssParts.join("; ")};` : "";
|
|
2226
|
+
const fontWeight = style.fontWeight;
|
|
2227
|
+
const textDecoration = style.textDecoration.split(" ");
|
|
2228
|
+
const hasBoldFontWeight = fontWeight === "700" || fontWeight === "bold";
|
|
2229
|
+
const hasLinethroughTextDecoration = textDecoration.includes("line-through");
|
|
2230
|
+
const hasItalicFontStyle = style.fontStyle === "italic";
|
|
2231
|
+
const hasUnderlineTextDecoration = textDecoration.includes("underline");
|
|
2232
|
+
const verticalAlign = style.verticalAlign;
|
|
2233
|
+
return {
|
|
2234
|
+
node: null,
|
|
2235
|
+
forChild: (lexicalNode) => {
|
|
2236
|
+
if (!$isTextNode(lexicalNode)) return lexicalNode;
|
|
2237
|
+
if (cssText) lexicalNode.setStyle(cssText);
|
|
2238
|
+
if (hasBoldFontWeight && !lexicalNode.hasFormat("bold")) lexicalNode.toggleFormat("bold");
|
|
2239
|
+
if (hasLinethroughTextDecoration && !lexicalNode.hasFormat("strikethrough")) lexicalNode.toggleFormat("strikethrough");
|
|
2240
|
+
if (hasItalicFontStyle && !lexicalNode.hasFormat("italic")) lexicalNode.toggleFormat("italic");
|
|
2241
|
+
if (hasUnderlineTextDecoration && !lexicalNode.hasFormat("underline")) lexicalNode.toggleFormat("underline");
|
|
2242
|
+
if (verticalAlign === "sub" && !lexicalNode.hasFormat("subscript")) lexicalNode.toggleFormat("subscript");
|
|
2243
|
+
if (verticalAlign === "super" && !lexicalNode.hasFormat("superscript")) lexicalNode.toggleFormat("superscript");
|
|
2244
|
+
return lexicalNode;
|
|
2245
|
+
}
|
|
2246
|
+
};
|
|
2247
|
+
};
|
|
2248
|
+
var preserveTextStyleImportMap = {
|
|
2249
|
+
span: () => ({
|
|
2250
|
+
conversion: $convertSpanElementWithStyle,
|
|
2251
|
+
priority: 0
|
|
2252
|
+
})
|
|
2253
|
+
};
|
|
2175
2254
|
var normalize = (s) => (s ?? "").replace(/\s+/g, " ").trim();
|
|
2176
2255
|
function readContextWindow(maxChars = 300, cursorIndex) {
|
|
2177
2256
|
const full = normalize($getRoot().getTextContent());
|
|
@@ -2945,6 +3024,8 @@ var ALLOWED_ATTRS = /* @__PURE__ */ new Set([
|
|
|
2945
3024
|
"data-lex-block",
|
|
2946
3025
|
"data-kind",
|
|
2947
3026
|
"data-lexical-decorator",
|
|
3027
|
+
"data-lexical-inline-image",
|
|
3028
|
+
"data-position",
|
|
2948
3029
|
// Misc
|
|
2949
3030
|
"title",
|
|
2950
3031
|
"allowfullscreen"
|
|
@@ -5099,11 +5180,37 @@ function SpellCheckPlugin({
|
|
|
5099
5180
|
}
|
|
5100
5181
|
) : null;
|
|
5101
5182
|
}
|
|
5183
|
+
function getScrollClipAncestor(el) {
|
|
5184
|
+
let node = el.parentElement;
|
|
5185
|
+
while (node) {
|
|
5186
|
+
const style = getComputedStyle(node);
|
|
5187
|
+
if ((style.overflowY === "auto" || style.overflowY === "scroll") && node.scrollHeight > node.clientHeight) {
|
|
5188
|
+
return node;
|
|
5189
|
+
}
|
|
5190
|
+
node = node.parentElement;
|
|
5191
|
+
}
|
|
5192
|
+
return null;
|
|
5193
|
+
}
|
|
5194
|
+
var BUTTON_VERTICAL_FOOTPRINT = 34;
|
|
5195
|
+
function isRectVisible(rect, cellDom) {
|
|
5196
|
+
if (rect.width === 0 && rect.height === 0) return false;
|
|
5197
|
+
const clipAncestor = getScrollClipAncestor(cellDom);
|
|
5198
|
+
const containerRect = clipAncestor?.getBoundingClientRect();
|
|
5199
|
+
const visibleTop = containerRect ? Math.max(0, containerRect.top) : 0;
|
|
5200
|
+
const visibleBottom = containerRect ? Math.min(window.innerHeight, containerRect.bottom) : window.innerHeight;
|
|
5201
|
+
const visibleLeft = containerRect ? Math.max(0, containerRect.left) : 0;
|
|
5202
|
+
const visibleRight = containerRect ? Math.min(window.innerWidth, containerRect.right) : window.innerWidth;
|
|
5203
|
+
if (rect.top < visibleTop) return false;
|
|
5204
|
+
if (rect.top + BUTTON_VERTICAL_FOOTPRINT > visibleBottom) return false;
|
|
5205
|
+
if (rect.left >= visibleRight || rect.right <= visibleLeft) return false;
|
|
5206
|
+
return true;
|
|
5207
|
+
}
|
|
5102
5208
|
function TableActionMenuPlugin({ disabled = false }) {
|
|
5103
5209
|
const [editor] = useLexicalComposerContext();
|
|
5104
5210
|
const portalContainer = useFloatingPortalContainer(editor);
|
|
5105
5211
|
const [isInTable, setIsInTable] = React9.useState(false);
|
|
5106
5212
|
const [anchorRect, setAnchorRect] = React9.useState(null);
|
|
5213
|
+
const [isAnchorVisible, setIsAnchorVisible] = React9.useState(false);
|
|
5107
5214
|
const [contentRight, setContentRight] = React9.useState(null);
|
|
5108
5215
|
const [open, setOpen] = React9.useState(false);
|
|
5109
5216
|
const openRef = React9.useRef(false);
|
|
@@ -5129,8 +5236,10 @@ function TableActionMenuPlugin({ disabled = false }) {
|
|
|
5129
5236
|
if (tableNode) {
|
|
5130
5237
|
const dom = editor.getElementByKey(tableNode.getKey());
|
|
5131
5238
|
if (dom) {
|
|
5239
|
+
const rect = dom.getBoundingClientRect();
|
|
5132
5240
|
setIsInTable(true);
|
|
5133
|
-
setAnchorRect(
|
|
5241
|
+
setAnchorRect(rect);
|
|
5242
|
+
setIsAnchorVisible(isRectVisible(rect, dom));
|
|
5134
5243
|
setContentRight(null);
|
|
5135
5244
|
return;
|
|
5136
5245
|
}
|
|
@@ -5139,6 +5248,7 @@ function TableActionMenuPlugin({ disabled = false }) {
|
|
|
5139
5248
|
if (!$isRangeSelection(selection)) {
|
|
5140
5249
|
setIsInTable(false);
|
|
5141
5250
|
setAnchorRect(null);
|
|
5251
|
+
setIsAnchorVisible(false);
|
|
5142
5252
|
setContentRight(null);
|
|
5143
5253
|
return;
|
|
5144
5254
|
}
|
|
@@ -5147,6 +5257,7 @@ function TableActionMenuPlugin({ disabled = false }) {
|
|
|
5147
5257
|
if (!cellNode || !$isTableCellNode(cellNode)) {
|
|
5148
5258
|
setIsInTable(false);
|
|
5149
5259
|
setAnchorRect(null);
|
|
5260
|
+
setIsAnchorVisible(false);
|
|
5150
5261
|
setContentRight(null);
|
|
5151
5262
|
return;
|
|
5152
5263
|
}
|
|
@@ -5154,11 +5265,14 @@ function TableActionMenuPlugin({ disabled = false }) {
|
|
|
5154
5265
|
if (!cellDom) {
|
|
5155
5266
|
setIsInTable(false);
|
|
5156
5267
|
setAnchorRect(null);
|
|
5268
|
+
setIsAnchorVisible(false);
|
|
5157
5269
|
setContentRight(null);
|
|
5158
5270
|
return;
|
|
5159
5271
|
}
|
|
5272
|
+
const cellRect = cellDom.getBoundingClientRect();
|
|
5160
5273
|
setIsInTable(true);
|
|
5161
|
-
setAnchorRect(
|
|
5274
|
+
setAnchorRect(cellRect);
|
|
5275
|
+
setIsAnchorVisible(isRectVisible(cellRect, cellDom));
|
|
5162
5276
|
setContentRight(measureContentRight(cellDom));
|
|
5163
5277
|
if ($isRangeSelection(selection)) {
|
|
5164
5278
|
savedAnchorRef.current = {
|
|
@@ -5237,7 +5351,7 @@ function TableActionMenuPlugin({ disabled = false }) {
|
|
|
5237
5351
|
COMMAND_PRIORITY_HIGH
|
|
5238
5352
|
);
|
|
5239
5353
|
}, [editor, disabled]);
|
|
5240
|
-
const canShow = isInTable && !!anchorRect && !disabled;
|
|
5354
|
+
const canShow = isInTable && !!anchorRect && isAnchorVisible && !disabled;
|
|
5241
5355
|
const handleStyle = React9.useMemo(() => {
|
|
5242
5356
|
if (!anchorRect) return void 0;
|
|
5243
5357
|
const top = Math.max(8, anchorRect.top + 6);
|
|
@@ -8196,7 +8310,10 @@ var ContentEditorComponent = forwardRef(
|
|
|
8196
8310
|
AutocompleteNode,
|
|
8197
8311
|
SpellErrorNode,
|
|
8198
8312
|
HtmlBlockNode
|
|
8199
|
-
]
|
|
8313
|
+
],
|
|
8314
|
+
html: {
|
|
8315
|
+
import: preserveTextStyleImportMap
|
|
8316
|
+
}
|
|
8200
8317
|
};
|
|
8201
8318
|
props.onBeforeInitialize?.(config);
|
|
8202
8319
|
return config;
|