text-editor-studio-ts
Version:
A powerful mobile-responsive rich text editor built with Lexical and React
277 lines (276 loc) • 7.36 kB
JavaScript
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
import { useState, useEffect, useRef, useCallback, useMemo } from "react";
import { o, a, u, E as ExcalidrawModal, b as $isExcalidrawNode } from "./index-BwW17RmP.js";
import { mergeRegister } from "@lexical/utils";
import { $getNodeByKey, CLICK_COMMAND, COMMAND_PRIORITY_LOW, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND } from "lexical";
import { exportToSvg } from "@excalidraw/excalidraw";
import { I as ImageResizer } from "./image-resizer-CFedlLqf.js";
const removeStyleFromSvg_HACK = (svg) => {
const styleTag = svg?.firstElementChild?.firstElementChild;
const viewBox = svg.getAttribute("viewBox");
if (viewBox != null) {
const viewBoxDimensions = viewBox.split(" ");
svg.setAttribute("width", viewBoxDimensions[2]);
svg.setAttribute("height", viewBoxDimensions[3]);
}
if (styleTag && styleTag.tagName === "style") {
styleTag.remove();
}
};
function ExcalidrawImage({
elements,
files,
imageContainerRef,
appState,
rootClassName = null,
width = "inherit",
height = "inherit"
}) {
const [Svg, setSvg] = useState(null);
useEffect(() => {
const setContent = async () => {
const svg = await exportToSvg({
appState,
elements,
files
});
removeStyleFromSvg_HACK(svg);
svg.setAttribute("width", "100%");
svg.setAttribute("height", "100%");
svg.setAttribute("display", "block");
setSvg(svg);
};
setContent();
}, [elements, files, appState]);
const containerStyle = {};
if (width !== "inherit") {
containerStyle.width = `${width}px`;
}
if (height !== "inherit") {
containerStyle.height = `${height}px`;
}
return /* @__PURE__ */ jsx(
"div",
{
ref: (node) => {
if (node) {
if (imageContainerRef) {
imageContainerRef.current = node;
}
}
},
className: rootClassName ?? "",
style: containerStyle,
dangerouslySetInnerHTML: { __html: Svg?.outerHTML ?? "" }
}
);
}
function ExcalidrawComponent({
nodeKey,
data,
width,
height
}) {
const [editor] = o();
const isEditable = a();
const [isModalOpen, setModalOpen] = useState(
data === "[]" && editor.isEditable()
);
const imageContainerRef = useRef(null);
const buttonRef = useRef(null);
const captionButtonRef = useRef(null);
const [isSelected, setSelected, clearSelection] = u(nodeKey);
const [isResizing, setIsResizing] = useState(false);
const $onDelete = useCallback(
(event) => {
if (isSelected) {
event.preventDefault();
editor.update(() => {
const node = $getNodeByKey(nodeKey);
if (node) {
node.remove();
}
});
}
return false;
},
[editor, isSelected, nodeKey]
);
useEffect(() => {
if (!isEditable) {
if (isSelected) {
clearSelection();
}
return;
}
return mergeRegister(
editor.registerCommand(
CLICK_COMMAND,
(event) => {
const buttonElem = buttonRef.current;
const eventTarget = event.target;
if (isResizing) {
return true;
}
if (buttonElem !== null && buttonElem.contains(eventTarget)) {
if (!event.shiftKey) {
clearSelection();
}
setSelected(!isSelected);
if (event.detail > 1) {
setModalOpen(true);
}
return true;
}
return false;
},
COMMAND_PRIORITY_LOW
),
editor.registerCommand(
KEY_DELETE_COMMAND,
$onDelete,
COMMAND_PRIORITY_LOW
),
editor.registerCommand(
KEY_BACKSPACE_COMMAND,
$onDelete,
COMMAND_PRIORITY_LOW
)
);
}, [
clearSelection,
editor,
isSelected,
isResizing,
$onDelete,
setSelected,
isEditable
]);
const deleteNode = useCallback(() => {
setModalOpen(false);
return editor.update(() => {
const node = $getNodeByKey(nodeKey);
if (node) {
node.remove();
}
});
}, [editor, nodeKey]);
const setData = (els, aps, fls) => {
return editor.update(() => {
const node = $getNodeByKey(nodeKey);
if ($isExcalidrawNode(node)) {
if (els && els.length > 0 || Object.keys(fls).length > 0) {
node.setData(
JSON.stringify({
appState: aps,
elements: els,
files: fls
})
);
} else {
node.remove();
}
}
});
};
const onResizeStart = () => {
setIsResizing(true);
};
const onResizeEnd = (nextWidth, nextHeight) => {
setTimeout(() => {
setIsResizing(false);
}, 200);
editor.update(() => {
const node = $getNodeByKey(nodeKey);
if ($isExcalidrawNode(node)) {
node.setWidth(nextWidth);
node.setHeight(nextHeight);
}
});
};
const openModal = useCallback(() => {
setModalOpen(true);
}, []);
const {
elements = [],
files = {},
appState = {}
} = useMemo(() => JSON.parse(data), [data]);
const closeModal = useCallback(() => {
setModalOpen(false);
if (elements.length === 0) {
editor.update(() => {
const node = $getNodeByKey(nodeKey);
if (node) {
node.remove();
}
});
}
}, [editor, nodeKey, elements.length]);
return /* @__PURE__ */ jsxs(Fragment, { children: [
isEditable && isModalOpen && /* @__PURE__ */ jsx(
ExcalidrawModal,
{
initialElements: elements,
initialFiles: files,
initialAppState: appState,
isShown: isModalOpen,
onDelete: deleteNode,
onClose: closeModal,
onSave: (els, aps, fls) => {
setData(els, aps, fls);
setModalOpen(false);
},
closeOnClickOutside: false
}
),
elements.length > 0 && /* @__PURE__ */ jsxs(
"button",
{
ref: buttonRef,
className: `m-0 border-0 bg-transparent p-0 ${isSelected ? "user-select-none ring-2 ring-primary ring-offset-2" : ""}`,
children: [
/* @__PURE__ */ jsx(
ExcalidrawImage,
{
imageContainerRef,
className: "image",
elements,
files,
appState,
width,
height
}
),
isSelected && isEditable && /* @__PURE__ */ jsx(
"div",
{
className: "image-edit-button",
role: "button",
tabIndex: 0,
onMouseDown: (event) => event.preventDefault(),
onClick: openModal
}
),
(isSelected || isResizing) && isEditable && /* @__PURE__ */ jsx(
ImageResizer,
{
buttonRef: captionButtonRef,
showCaption: true,
setShowCaption: () => null,
imageRef: imageContainerRef,
editor,
onResizeStart,
onResizeEnd,
captionsEnabled: true
}
)
]
}
)
] });
}
export {
ExcalidrawComponent as default
};
//# sourceMappingURL=excalidraw-component-DrhkyxVZ.js.map