@cyberyozh/text-editor 0.1.16 → 0.2.0
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/editor.css +1 -0
- package/dist/editor.css.map +1 -1
- package/dist/editor.d.mts +9 -1
- package/dist/editor.d.ts +9 -1
- package/dist/editor.js +172 -34
- package/dist/editor.js.map +1 -1
- package/dist/editor.mjs +179 -41
- package/dist/editor.mjs.map +1 -1
- package/dist/styles/index.css +1 -0
- package/package.json +3 -2
- package/src/editor/dialogs/image-dialog/image-dialog.tsx +9 -1
- package/src/editor/editor.tsx +52 -9
- package/src/editor/handlers/index.ts +4 -0
- package/src/editor/handlers/insertion-handlers.ts +113 -0
- package/src/editor/index.ts +1 -1
- package/src/extensions/editor/figure.ts +14 -15
- package/src/styles/extensions/figure.css +1 -0
- package/src/types/image-handler.ts +5 -0
- package/src/utils/prosemirror-iterator.ts +38 -0
package/dist/editor.js
CHANGED
|
@@ -36,10 +36,10 @@ __export(editor_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(editor_exports);
|
|
37
37
|
|
|
38
38
|
// src/editor/editor.tsx
|
|
39
|
-
var
|
|
39
|
+
var import_react37 = require("react");
|
|
40
40
|
var import_state6 = require("@tiptap/pm/state");
|
|
41
|
-
var
|
|
42
|
-
var
|
|
41
|
+
var import_model2 = require("@tiptap/pm/model");
|
|
42
|
+
var import_react38 = require("@tiptap/react");
|
|
43
43
|
var import_menus = require("@tiptap/react/menus");
|
|
44
44
|
var import_github_dark = require("highlight.js/styles/github-dark.css");
|
|
45
45
|
var import_katex_min = require("katex/dist/katex.min.css");
|
|
@@ -1917,6 +1917,7 @@ var import_jsx_runtime67 = require("react/jsx-runtime");
|
|
|
1917
1917
|
var ImageDialog = ({
|
|
1918
1918
|
open,
|
|
1919
1919
|
onClose,
|
|
1920
|
+
defaultFile,
|
|
1920
1921
|
onImageAdd,
|
|
1921
1922
|
onImageEdit,
|
|
1922
1923
|
uploadHandler,
|
|
@@ -2000,6 +2001,11 @@ var ImageDialog = ({
|
|
|
2000
2001
|
}));
|
|
2001
2002
|
};
|
|
2002
2003
|
};
|
|
2004
|
+
(0, import_react13.useEffect)(() => {
|
|
2005
|
+
if (defaultFile) {
|
|
2006
|
+
handleSelectFile(defaultFile);
|
|
2007
|
+
}
|
|
2008
|
+
}, []);
|
|
2003
2009
|
const handleAreaClick = () => {
|
|
2004
2010
|
inputRef.current?.click();
|
|
2005
2011
|
};
|
|
@@ -6470,7 +6476,8 @@ var Figure2 = BaseFigure.extend({
|
|
|
6470
6476
|
};
|
|
6471
6477
|
const updateImageDimensions = (width, height) => {
|
|
6472
6478
|
imageContainer.style.width = `${width}px`;
|
|
6473
|
-
imageContainer.style.
|
|
6479
|
+
imageContainer.style.aspectRatio = `${width} / ${height}`;
|
|
6480
|
+
imageContainer.style.height = "";
|
|
6474
6481
|
};
|
|
6475
6482
|
const figure = document.createElement("figure");
|
|
6476
6483
|
figure.setAttribute("data-drag-handle", "");
|
|
@@ -6482,7 +6489,7 @@ var Figure2 = BaseFigure.extend({
|
|
|
6482
6489
|
if (attrs.title) img.title = attrs.title;
|
|
6483
6490
|
if (attrs.width && attrs.height) {
|
|
6484
6491
|
imageContainer.style.width = `${attrs.width}px`;
|
|
6485
|
-
imageContainer.style.
|
|
6492
|
+
imageContainer.style.aspectRatio = `${attrs.width} / ${attrs.height}`;
|
|
6486
6493
|
}
|
|
6487
6494
|
img.onload = () => {
|
|
6488
6495
|
naturalAspectRatio = img.naturalHeight / img.naturalWidth;
|
|
@@ -6490,15 +6497,11 @@ var Figure2 = BaseFigure.extend({
|
|
|
6490
6497
|
maxHeight = Math.round(maxWidth * naturalAspectRatio);
|
|
6491
6498
|
}
|
|
6492
6499
|
if (attrs.width && attrs.height && maxWidth && attrs.width > maxWidth) {
|
|
6493
|
-
const width = maxWidth;
|
|
6494
|
-
const height = Math.round(attrs.height / attrs.width * maxWidth);
|
|
6495
|
-
imageContainer.style.width = `${width}px`;
|
|
6496
|
-
imageContainer.style.height = `${height}px`;
|
|
6497
6500
|
const pos = getPos();
|
|
6498
6501
|
if (typeof pos === "number") {
|
|
6499
6502
|
editor.chain().updateAttributes("image", {
|
|
6500
|
-
width:
|
|
6501
|
-
height: Math.round(height)
|
|
6503
|
+
width: maxWidth,
|
|
6504
|
+
height: Math.round(attrs.height / attrs.width * maxWidth)
|
|
6502
6505
|
}).run();
|
|
6503
6506
|
}
|
|
6504
6507
|
}
|
|
@@ -6540,11 +6543,12 @@ var Figure2 = BaseFigure.extend({
|
|
|
6540
6543
|
}
|
|
6541
6544
|
},
|
|
6542
6545
|
onUpdate: (updatedNode) => {
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
}
|
|
6546
|
-
if (
|
|
6547
|
-
imageContainer.style.
|
|
6546
|
+
const newWidth = updatedNode.attrs.width;
|
|
6547
|
+
const newHeight = updatedNode.attrs.height;
|
|
6548
|
+
if (newWidth) imageContainer.style.width = `${newWidth}px`;
|
|
6549
|
+
if (newWidth && newHeight) {
|
|
6550
|
+
imageContainer.style.aspectRatio = `${newWidth} / ${newHeight}`;
|
|
6551
|
+
imageContainer.style.height = "";
|
|
6548
6552
|
}
|
|
6549
6553
|
const existingCaption = figure.querySelector("figcaption");
|
|
6550
6554
|
if (updatedNode.attrs.caption && !existingCaption) {
|
|
@@ -6582,6 +6586,7 @@ var Figure2 = BaseFigure.extend({
|
|
|
6582
6586
|
}
|
|
6583
6587
|
}
|
|
6584
6588
|
});
|
|
6589
|
+
imageContainer.style.height = "";
|
|
6585
6590
|
figure.appendChild(resizableView.dom);
|
|
6586
6591
|
if (attrs.caption) {
|
|
6587
6592
|
const figcaption = document.createElement("figcaption");
|
|
@@ -6775,10 +6780,110 @@ var createHandleKatexAdd = (editor, closeDialogCommand, savedPos) => {
|
|
|
6775
6780
|
};
|
|
6776
6781
|
};
|
|
6777
6782
|
|
|
6783
|
+
// src/editor/handlers/insertion-handlers.ts
|
|
6784
|
+
var import_react35 = require("react");
|
|
6785
|
+
|
|
6786
|
+
// src/utils/prosemirror-iterator.ts
|
|
6787
|
+
var import_model = require("@tiptap/pm/model");
|
|
6788
|
+
function prosemirrorIterator(slice, cb) {
|
|
6789
|
+
const stack = [];
|
|
6790
|
+
if (slice instanceof import_model.Slice) {
|
|
6791
|
+
collectFragmentChildren(slice.content, stack);
|
|
6792
|
+
} else if (slice instanceof import_model.Fragment) {
|
|
6793
|
+
collectFragmentChildren(slice, stack);
|
|
6794
|
+
} else {
|
|
6795
|
+
stack.push(slice);
|
|
6796
|
+
}
|
|
6797
|
+
while (stack.length > 0) {
|
|
6798
|
+
const node = stack.pop();
|
|
6799
|
+
const shouldStop = cb(node);
|
|
6800
|
+
if (shouldStop === true) break;
|
|
6801
|
+
if (node.content.size > 0) {
|
|
6802
|
+
collectFragmentChildren(node.content, stack);
|
|
6803
|
+
}
|
|
6804
|
+
}
|
|
6805
|
+
}
|
|
6806
|
+
function collectFragmentChildren(fragment, stack) {
|
|
6807
|
+
for (let i = fragment.childCount - 1; i >= 0; i--) {
|
|
6808
|
+
stack.push(fragment.child(i));
|
|
6809
|
+
}
|
|
6810
|
+
}
|
|
6811
|
+
|
|
6812
|
+
// src/editor/handlers/insertion-handlers.ts
|
|
6813
|
+
function useInsertionHandlers() {
|
|
6814
|
+
const [insertedImage, setInsertedImage] = (0, import_react35.useState)(null);
|
|
6815
|
+
const { activeDialog, openDialogCommand } = useDialogCommand();
|
|
6816
|
+
(0, import_react35.useEffect)(() => {
|
|
6817
|
+
if (insertedImage && !activeDialog) {
|
|
6818
|
+
setInsertedImage(null);
|
|
6819
|
+
}
|
|
6820
|
+
}, [insertedImage, activeDialog]);
|
|
6821
|
+
function genericInsertionHandler(file, event) {
|
|
6822
|
+
if (!file || !file.type.startsWith("image/")) return;
|
|
6823
|
+
event.preventDefault();
|
|
6824
|
+
event.stopPropagation();
|
|
6825
|
+
setInsertedImage(file);
|
|
6826
|
+
openDialogCommand("image");
|
|
6827
|
+
}
|
|
6828
|
+
function onPaste(_, event) {
|
|
6829
|
+
const file = event.clipboardData?.files?.[0];
|
|
6830
|
+
genericInsertionHandler(file, event);
|
|
6831
|
+
}
|
|
6832
|
+
function onDrop(_, event) {
|
|
6833
|
+
const file = event.dataTransfer?.files?.[0];
|
|
6834
|
+
genericInsertionHandler(file, event);
|
|
6835
|
+
}
|
|
6836
|
+
function onMarkupInsertion(view, event, slice, setDisabled, uploadMultipleByURL) {
|
|
6837
|
+
if (!uploadMultipleByURL) return;
|
|
6838
|
+
let hasMarkupInsertion = false;
|
|
6839
|
+
if ("clipboardData" in event && Array.from(event.clipboardData?.items ?? []).some(
|
|
6840
|
+
(item) => item.type === "text/html"
|
|
6841
|
+
)) {
|
|
6842
|
+
hasMarkupInsertion = true;
|
|
6843
|
+
}
|
|
6844
|
+
if ("dataTransfer" in event && Array.from(event.dataTransfer?.items ?? []).some(
|
|
6845
|
+
(item) => item.type === "text/html"
|
|
6846
|
+
)) {
|
|
6847
|
+
hasMarkupInsertion = true;
|
|
6848
|
+
}
|
|
6849
|
+
if (hasMarkupInsertion) {
|
|
6850
|
+
const imagesToNodes = [];
|
|
6851
|
+
setDisabled(true);
|
|
6852
|
+
prosemirrorIterator(slice, (node) => {
|
|
6853
|
+
if (node.type.name === "image" && !node.attrs.fileId) {
|
|
6854
|
+
try {
|
|
6855
|
+
const url = new URL(node.attrs.src).toString();
|
|
6856
|
+
imagesToNodes.push([url, node]);
|
|
6857
|
+
} catch {
|
|
6858
|
+
}
|
|
6859
|
+
}
|
|
6860
|
+
});
|
|
6861
|
+
uploadMultipleByURL(imagesToNodes.map(([url]) => url)).then((results) => {
|
|
6862
|
+
results.forEach((result, index) => {
|
|
6863
|
+
const node = imagesToNodes[index][1];
|
|
6864
|
+
node.attrs.src = null;
|
|
6865
|
+
node.attrs.fileId = result?.fileId;
|
|
6866
|
+
node.attrs.storageKey = result?.storageKey;
|
|
6867
|
+
});
|
|
6868
|
+
view.dispatch(view.state.tr.replaceSelection(slice));
|
|
6869
|
+
}).finally(() => {
|
|
6870
|
+
setDisabled(false);
|
|
6871
|
+
});
|
|
6872
|
+
return true;
|
|
6873
|
+
}
|
|
6874
|
+
}
|
|
6875
|
+
return {
|
|
6876
|
+
insertedImage,
|
|
6877
|
+
onPaste,
|
|
6878
|
+
onDrop,
|
|
6879
|
+
onMarkupInsertion
|
|
6880
|
+
};
|
|
6881
|
+
}
|
|
6882
|
+
|
|
6778
6883
|
// src/editor/commands/suggestion.tsx
|
|
6779
6884
|
var import_dom = require("@floating-ui/dom");
|
|
6780
6885
|
var import_core19 = require("@tiptap/core");
|
|
6781
|
-
var
|
|
6886
|
+
var import_react36 = require("@tiptap/react");
|
|
6782
6887
|
|
|
6783
6888
|
// src/editor/commands/items.ts
|
|
6784
6889
|
var getCommandItems = (translations2, hasImageHandlers) => [
|
|
@@ -7111,7 +7216,7 @@ var createSuggestions = (translations2, hasImageHandlers) => ({
|
|
|
7111
7216
|
render: () => {
|
|
7112
7217
|
return {
|
|
7113
7218
|
onStart: async (props) => {
|
|
7114
|
-
component = new
|
|
7219
|
+
component = new import_react36.ReactRenderer(CommandsList, {
|
|
7115
7220
|
props,
|
|
7116
7221
|
editor: props.editor
|
|
7117
7222
|
});
|
|
@@ -7534,27 +7639,35 @@ function TiptapContent({
|
|
|
7534
7639
|
lang,
|
|
7535
7640
|
error
|
|
7536
7641
|
}) {
|
|
7537
|
-
const [linkDialogContext, setLinkDialogContext] = (0,
|
|
7538
|
-
const [katexDialogContext, setKatexDialogContext] = (0,
|
|
7642
|
+
const [linkDialogContext, setLinkDialogContext] = (0, import_react37.useState)(null);
|
|
7643
|
+
const [katexDialogContext, setKatexDialogContext] = (0, import_react37.useState)(
|
|
7539
7644
|
null
|
|
7540
7645
|
);
|
|
7541
|
-
const [editFigureContext, setEditFigureContext] = (0,
|
|
7542
|
-
const [savedDialogCursorPos, setSavedDialogCursorPos] = (0,
|
|
7646
|
+
const [editFigureContext, setEditFigureContext] = (0, import_react37.useState)(null);
|
|
7647
|
+
const [savedDialogCursorPos, setSavedDialogCursorPos] = (0, import_react37.useState)(void 0);
|
|
7543
7648
|
const { activeDialog, closeDialogCommand, openDialogCommand } = useDialogCommand();
|
|
7544
7649
|
const translations2 = useTiptapTranslations();
|
|
7650
|
+
const [isDisabledInternal, setIsDisabledInternal] = (0, import_react37.useState)(isDisabled);
|
|
7651
|
+
const disabled = isDisabled || isDisabledInternal;
|
|
7545
7652
|
let safeContent;
|
|
7546
7653
|
try {
|
|
7547
7654
|
safeContent = JSON.parse(content);
|
|
7548
7655
|
} catch {
|
|
7549
7656
|
safeContent = { type: "doc", content: [{ type: "paragraph" }] };
|
|
7550
7657
|
}
|
|
7551
|
-
const
|
|
7658
|
+
const {
|
|
7659
|
+
insertedImage,
|
|
7660
|
+
onPaste,
|
|
7661
|
+
onDrop,
|
|
7662
|
+
onMarkupInsertion
|
|
7663
|
+
} = useInsertionHandlers();
|
|
7664
|
+
const editor = (0, import_react38.useEditor)(
|
|
7552
7665
|
{
|
|
7553
|
-
editable: isEditable && !
|
|
7666
|
+
editable: isEditable && !disabled,
|
|
7554
7667
|
extensions: getEditorExtensions({
|
|
7555
7668
|
translations: translations2,
|
|
7556
7669
|
suggestion: createSuggestions(translations2, !!imageHandlers),
|
|
7557
|
-
isEditable: isEditable && !
|
|
7670
|
+
isEditable: isEditable && !disabled,
|
|
7558
7671
|
getImageById: imageHandlers?.getImage,
|
|
7559
7672
|
onKatexClick: (latex, pos) => {
|
|
7560
7673
|
setKatexDialogContext({ latex, pos });
|
|
@@ -7568,14 +7681,38 @@ function TiptapContent({
|
|
|
7568
7681
|
content: safeContent,
|
|
7569
7682
|
immediatelyRender: false,
|
|
7570
7683
|
editorProps: {
|
|
7571
|
-
|
|
7684
|
+
handleDOMEvents: {
|
|
7685
|
+
paste: onPaste,
|
|
7686
|
+
drop: onDrop
|
|
7687
|
+
},
|
|
7688
|
+
handleDrop: (view, event, slice) => {
|
|
7689
|
+
if (view.dragging) return false;
|
|
7690
|
+
const toReturn = onMarkupInsertion(
|
|
7691
|
+
view,
|
|
7692
|
+
event,
|
|
7693
|
+
slice,
|
|
7694
|
+
setIsDisabledInternal,
|
|
7695
|
+
imageHandlers?.uploadMultipleByURL
|
|
7696
|
+
);
|
|
7697
|
+
if (typeof toReturn === "boolean") return toReturn;
|
|
7698
|
+
return false;
|
|
7699
|
+
},
|
|
7700
|
+
handlePaste: (view, event, slice) => {
|
|
7701
|
+
const toReturn = onMarkupInsertion(
|
|
7702
|
+
view,
|
|
7703
|
+
event,
|
|
7704
|
+
slice,
|
|
7705
|
+
setIsDisabledInternal,
|
|
7706
|
+
imageHandlers?.uploadMultipleByURL
|
|
7707
|
+
);
|
|
7708
|
+
if (typeof toReturn === "boolean") return toReturn;
|
|
7572
7709
|
try {
|
|
7573
7710
|
const text = event.clipboardData?.getData("text/plain");
|
|
7574
7711
|
if (!text) return false;
|
|
7575
7712
|
const json = JSON.parse(text);
|
|
7576
7713
|
if (json.type === "doc" && json.content) {
|
|
7577
7714
|
const node = view.state.schema.nodeFromJSON(json);
|
|
7578
|
-
const newSlice = new
|
|
7715
|
+
const newSlice = new import_model2.Slice(node.content, 0, 0);
|
|
7579
7716
|
view.dispatch(view.state.tr.replaceSelection(newSlice));
|
|
7580
7717
|
return true;
|
|
7581
7718
|
}
|
|
@@ -7590,7 +7727,7 @@ function TiptapContent({
|
|
|
7590
7727
|
},
|
|
7591
7728
|
[lang]
|
|
7592
7729
|
);
|
|
7593
|
-
(0,
|
|
7730
|
+
(0, import_react37.useEffect)(() => {
|
|
7594
7731
|
if (activeDialog && editor && !editor.isDestroyed) {
|
|
7595
7732
|
const pos = editor.state.selection.from;
|
|
7596
7733
|
setSavedDialogCursorPos(pos);
|
|
@@ -7617,7 +7754,7 @@ function TiptapContent({
|
|
|
7617
7754
|
const katexHandler = createHandleKatexAdd(editor, handleCloseDialog, savedDialogCursorPos);
|
|
7618
7755
|
katexHandler(latex, katexDialogContext?.pos);
|
|
7619
7756
|
};
|
|
7620
|
-
(0,
|
|
7757
|
+
(0, import_react37.useEffect)(() => {
|
|
7621
7758
|
if (!editor || editor.isDestroyed) return;
|
|
7622
7759
|
editor.commands.setContent(safeContent, {
|
|
7623
7760
|
emitUpdate: false
|
|
@@ -7638,8 +7775,8 @@ function TiptapContent({
|
|
|
7638
7775
|
if (!editor) return null;
|
|
7639
7776
|
return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(import_jsx_runtime92.Fragment, { children: [
|
|
7640
7777
|
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(DragHandle, { editor }),
|
|
7641
|
-
isEditable && !
|
|
7642
|
-
isEditable && !
|
|
7778
|
+
isEditable && !disabled && /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(TableMenu, { editor }),
|
|
7779
|
+
isEditable && !disabled && /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
|
|
7643
7780
|
import_menus.BubbleMenu,
|
|
7644
7781
|
{
|
|
7645
7782
|
shouldShow: shouldBubbleMenuShow,
|
|
@@ -7657,10 +7794,10 @@ function TiptapContent({
|
|
|
7657
7794
|
/* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(
|
|
7658
7795
|
"div",
|
|
7659
7796
|
{
|
|
7660
|
-
className: `tiptap-content-wrapper ${!!error ? "has-error" : ""} ${
|
|
7797
|
+
className: `tiptap-content-wrapper ${!!error ? "has-error" : ""} ${disabled ? "is-disabled" : ""}`,
|
|
7661
7798
|
children: [
|
|
7662
|
-
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
|
|
7663
|
-
isEditable && !
|
|
7799
|
+
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(import_react38.EditorContent, { editor }),
|
|
7800
|
+
isEditable && !disabled && /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(CharacterCount, { editor })
|
|
7664
7801
|
]
|
|
7665
7802
|
}
|
|
7666
7803
|
),
|
|
@@ -7671,6 +7808,7 @@ function TiptapContent({
|
|
|
7671
7808
|
{
|
|
7672
7809
|
open: true,
|
|
7673
7810
|
onClose: handleCloseDialog,
|
|
7811
|
+
defaultFile: insertedImage,
|
|
7674
7812
|
onImageAdd: handleImageAdd,
|
|
7675
7813
|
onImageEdit: handleImageEdit,
|
|
7676
7814
|
uploadHandler: imageHandlers?.upload,
|