@cyberyozh/text-editor 0.1.17 → 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.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/editor/editor.tsx
2
- import { useState as useState12, useEffect as useEffect9 } from "react";
2
+ import { useState as useState13, useEffect as useEffect11 } from "react";
3
3
  import { NodeSelection } from "@tiptap/pm/state";
4
- import { Slice } from "@tiptap/pm/model";
4
+ import { Slice as Slice2 } from "@tiptap/pm/model";
5
5
  import { useEditor, EditorContent } from "@tiptap/react";
6
6
  import { BubbleMenu } from "@tiptap/react/menus";
7
7
  import "highlight.js/styles/github-dark.css";
@@ -1764,7 +1764,7 @@ var TableMenu = ({ editor }) => {
1764
1764
  };
1765
1765
 
1766
1766
  // src/editor/dialogs/image-dialog/image-dialog.tsx
1767
- import { useRef as useRef6, useState as useState6 } from "react";
1767
+ import { useEffect as useEffect5, useRef as useRef6, useState as useState6 } from "react";
1768
1768
 
1769
1769
  // src/editor/components/button/button.tsx
1770
1770
  import { jsx as jsx64 } from "react/jsx-runtime";
@@ -1880,6 +1880,7 @@ import { Fragment as Fragment2, jsx as jsx67, jsxs as jsxs18 } from "react/jsx-r
1880
1880
  var ImageDialog = ({
1881
1881
  open,
1882
1882
  onClose,
1883
+ defaultFile,
1883
1884
  onImageAdd,
1884
1885
  onImageEdit,
1885
1886
  uploadHandler,
@@ -1963,6 +1964,11 @@ var ImageDialog = ({
1963
1964
  }));
1964
1965
  };
1965
1966
  };
1967
+ useEffect5(() => {
1968
+ if (defaultFile) {
1969
+ handleSelectFile(defaultFile);
1970
+ }
1971
+ }, []);
1966
1972
  const handleAreaClick = () => {
1967
1973
  inputRef.current?.click();
1968
1974
  };
@@ -2154,7 +2160,7 @@ var ImageDialog = ({
2154
2160
  };
2155
2161
 
2156
2162
  // src/editor/dialogs/add-katex-dialog/add-katex-dialog.tsx
2157
- import { useEffect as useEffect5, useRef as useRef7, useState as useState7 } from "react";
2163
+ import { useEffect as useEffect6, useRef as useRef7, useState as useState7 } from "react";
2158
2164
  import { jsx as jsx68, jsxs as jsxs19 } from "react/jsx-runtime";
2159
2165
  var AddKatexDialog = ({
2160
2166
  open,
@@ -2165,7 +2171,7 @@ var AddKatexDialog = ({
2165
2171
  const translations2 = useTiptapTranslations();
2166
2172
  const [content, setContent] = useState7(initialContent);
2167
2173
  const contentInputRef = useRef7(null);
2168
- useEffect5(() => {
2174
+ useEffect6(() => {
2169
2175
  if (open) {
2170
2176
  setContent(initialContent);
2171
2177
  setTimeout(() => {
@@ -2264,7 +2270,7 @@ var AddKatexDialog = ({
2264
2270
  };
2265
2271
 
2266
2272
  // src/editor/dialogs/add-link-dialog/add-link-dialog.tsx
2267
- import { useEffect as useEffect6, useRef as useRef8, useState as useState8 } from "react";
2273
+ import { useEffect as useEffect7, useRef as useRef8, useState as useState8 } from "react";
2268
2274
  import { jsx as jsx69, jsxs as jsxs20 } from "react/jsx-runtime";
2269
2275
  var AddLinkDialog = ({
2270
2276
  open,
@@ -2288,7 +2294,7 @@ var AddLinkDialog = ({
2288
2294
  setAddNoopener(initialLinkRel);
2289
2295
  setError("");
2290
2296
  };
2291
- useEffect6(() => {
2297
+ useEffect7(() => {
2292
2298
  if (open) {
2293
2299
  clearDialogState();
2294
2300
  setTimeout(() => {
@@ -2455,7 +2461,7 @@ var AddLinkDialog = ({
2455
2461
  };
2456
2462
 
2457
2463
  // src/editor/dialogs/add-video-dialog/add-video-dialog.tsx
2458
- import { useEffect as useEffect7, useRef as useRef9, useState as useState9 } from "react";
2464
+ import { useEffect as useEffect8, useRef as useRef9, useState as useState9 } from "react";
2459
2465
 
2460
2466
  // src/utils/detect-video-hosting.ts
2461
2467
  var VIDEO_PATTERNS = {
@@ -2542,7 +2548,7 @@ var AddVideoDialog = ({ open, onClose, onVideoAdd }) => {
2542
2548
  const [error, setError] = useState9("");
2543
2549
  const [isValidating, setIsValidating] = useState9(false);
2544
2550
  const urlInputRef = useRef9(null);
2545
- useEffect7(() => {
2551
+ useEffect8(() => {
2546
2552
  if (open) {
2547
2553
  setUrl("");
2548
2554
  setError("");
@@ -5636,13 +5642,13 @@ var Video = ({ src, children }) => {
5636
5642
  };
5637
5643
 
5638
5644
  // src/editor/controls/emoji-picker.tsx
5639
- import { useEffect as useEffect8, useRef as useRef10 } from "react";
5645
+ import { useEffect as useEffect9, useRef as useRef10 } from "react";
5640
5646
  import EmojiPicker from "emoji-picker-react";
5641
5647
  import { jsx as jsx83 } from "react/jsx-runtime";
5642
5648
  var EmojiPickerComponent = ({ isOpen, onClose, onEmojiSelect }) => {
5643
5649
  const pickerRef = useRef10(null);
5644
5650
  const translations2 = useTiptapTranslations();
5645
- useEffect8(() => {
5651
+ useEffect9(() => {
5646
5652
  const handleClickOutside = (event) => {
5647
5653
  if (pickerRef.current && !pickerRef.current.contains(event.target)) {
5648
5654
  onClose();
@@ -6737,6 +6743,106 @@ var createHandleKatexAdd = (editor, closeDialogCommand, savedPos) => {
6737
6743
  };
6738
6744
  };
6739
6745
 
6746
+ // src/editor/handlers/insertion-handlers.ts
6747
+ import { useEffect as useEffect10, useState as useState12 } from "react";
6748
+
6749
+ // src/utils/prosemirror-iterator.ts
6750
+ import { Fragment as Fragment3, Slice } from "@tiptap/pm/model";
6751
+ function prosemirrorIterator(slice, cb) {
6752
+ const stack = [];
6753
+ if (slice instanceof Slice) {
6754
+ collectFragmentChildren(slice.content, stack);
6755
+ } else if (slice instanceof Fragment3) {
6756
+ collectFragmentChildren(slice, stack);
6757
+ } else {
6758
+ stack.push(slice);
6759
+ }
6760
+ while (stack.length > 0) {
6761
+ const node = stack.pop();
6762
+ const shouldStop = cb(node);
6763
+ if (shouldStop === true) break;
6764
+ if (node.content.size > 0) {
6765
+ collectFragmentChildren(node.content, stack);
6766
+ }
6767
+ }
6768
+ }
6769
+ function collectFragmentChildren(fragment, stack) {
6770
+ for (let i = fragment.childCount - 1; i >= 0; i--) {
6771
+ stack.push(fragment.child(i));
6772
+ }
6773
+ }
6774
+
6775
+ // src/editor/handlers/insertion-handlers.ts
6776
+ function useInsertionHandlers() {
6777
+ const [insertedImage, setInsertedImage] = useState12(null);
6778
+ const { activeDialog, openDialogCommand } = useDialogCommand();
6779
+ useEffect10(() => {
6780
+ if (insertedImage && !activeDialog) {
6781
+ setInsertedImage(null);
6782
+ }
6783
+ }, [insertedImage, activeDialog]);
6784
+ function genericInsertionHandler(file, event) {
6785
+ if (!file || !file.type.startsWith("image/")) return;
6786
+ event.preventDefault();
6787
+ event.stopPropagation();
6788
+ setInsertedImage(file);
6789
+ openDialogCommand("image");
6790
+ }
6791
+ function onPaste(_, event) {
6792
+ const file = event.clipboardData?.files?.[0];
6793
+ genericInsertionHandler(file, event);
6794
+ }
6795
+ function onDrop(_, event) {
6796
+ const file = event.dataTransfer?.files?.[0];
6797
+ genericInsertionHandler(file, event);
6798
+ }
6799
+ function onMarkupInsertion(view, event, slice, setDisabled, uploadMultipleByURL) {
6800
+ if (!uploadMultipleByURL) return;
6801
+ let hasMarkupInsertion = false;
6802
+ if ("clipboardData" in event && Array.from(event.clipboardData?.items ?? []).some(
6803
+ (item) => item.type === "text/html"
6804
+ )) {
6805
+ hasMarkupInsertion = true;
6806
+ }
6807
+ if ("dataTransfer" in event && Array.from(event.dataTransfer?.items ?? []).some(
6808
+ (item) => item.type === "text/html"
6809
+ )) {
6810
+ hasMarkupInsertion = true;
6811
+ }
6812
+ if (hasMarkupInsertion) {
6813
+ const imagesToNodes = [];
6814
+ setDisabled(true);
6815
+ prosemirrorIterator(slice, (node) => {
6816
+ if (node.type.name === "image" && !node.attrs.fileId) {
6817
+ try {
6818
+ const url = new URL(node.attrs.src).toString();
6819
+ imagesToNodes.push([url, node]);
6820
+ } catch {
6821
+ }
6822
+ }
6823
+ });
6824
+ uploadMultipleByURL(imagesToNodes.map(([url]) => url)).then((results) => {
6825
+ results.forEach((result, index) => {
6826
+ const node = imagesToNodes[index][1];
6827
+ node.attrs.src = null;
6828
+ node.attrs.fileId = result?.fileId;
6829
+ node.attrs.storageKey = result?.storageKey;
6830
+ });
6831
+ view.dispatch(view.state.tr.replaceSelection(slice));
6832
+ }).finally(() => {
6833
+ setDisabled(false);
6834
+ });
6835
+ return true;
6836
+ }
6837
+ }
6838
+ return {
6839
+ insertedImage,
6840
+ onPaste,
6841
+ onDrop,
6842
+ onMarkupInsertion
6843
+ };
6844
+ }
6845
+
6740
6846
  // src/editor/commands/suggestion.tsx
6741
6847
  import { computePosition, flip, shift } from "@floating-ui/dom";
6742
6848
  import { posToDOMRect } from "@tiptap/core";
@@ -7486,7 +7592,7 @@ function getTranslations(lang) {
7486
7592
  }
7487
7593
 
7488
7594
  // src/editor/editor.tsx
7489
- import { Fragment as Fragment3, jsx as jsx92, jsxs as jsxs35 } from "react/jsx-runtime";
7595
+ import { Fragment as Fragment4, jsx as jsx92, jsxs as jsxs35 } from "react/jsx-runtime";
7490
7596
  function TiptapContent({
7491
7597
  content,
7492
7598
  handleChange,
@@ -7496,27 +7602,35 @@ function TiptapContent({
7496
7602
  lang,
7497
7603
  error
7498
7604
  }) {
7499
- const [linkDialogContext, setLinkDialogContext] = useState12(null);
7500
- const [katexDialogContext, setKatexDialogContext] = useState12(
7605
+ const [linkDialogContext, setLinkDialogContext] = useState13(null);
7606
+ const [katexDialogContext, setKatexDialogContext] = useState13(
7501
7607
  null
7502
7608
  );
7503
- const [editFigureContext, setEditFigureContext] = useState12(null);
7504
- const [savedDialogCursorPos, setSavedDialogCursorPos] = useState12(void 0);
7609
+ const [editFigureContext, setEditFigureContext] = useState13(null);
7610
+ const [savedDialogCursorPos, setSavedDialogCursorPos] = useState13(void 0);
7505
7611
  const { activeDialog, closeDialogCommand, openDialogCommand } = useDialogCommand();
7506
7612
  const translations2 = useTiptapTranslations();
7613
+ const [isDisabledInternal, setIsDisabledInternal] = useState13(isDisabled);
7614
+ const disabled = isDisabled || isDisabledInternal;
7507
7615
  let safeContent;
7508
7616
  try {
7509
7617
  safeContent = JSON.parse(content);
7510
7618
  } catch {
7511
7619
  safeContent = { type: "doc", content: [{ type: "paragraph" }] };
7512
7620
  }
7621
+ const {
7622
+ insertedImage,
7623
+ onPaste,
7624
+ onDrop,
7625
+ onMarkupInsertion
7626
+ } = useInsertionHandlers();
7513
7627
  const editor = useEditor(
7514
7628
  {
7515
- editable: isEditable && !isDisabled,
7629
+ editable: isEditable && !disabled,
7516
7630
  extensions: getEditorExtensions({
7517
7631
  translations: translations2,
7518
7632
  suggestion: createSuggestions(translations2, !!imageHandlers),
7519
- isEditable: isEditable && !isDisabled,
7633
+ isEditable: isEditable && !disabled,
7520
7634
  getImageById: imageHandlers?.getImage,
7521
7635
  onKatexClick: (latex, pos) => {
7522
7636
  setKatexDialogContext({ latex, pos });
@@ -7530,14 +7644,38 @@ function TiptapContent({
7530
7644
  content: safeContent,
7531
7645
  immediatelyRender: false,
7532
7646
  editorProps: {
7533
- handlePaste: (view, event) => {
7647
+ handleDOMEvents: {
7648
+ paste: onPaste,
7649
+ drop: onDrop
7650
+ },
7651
+ handleDrop: (view, event, slice) => {
7652
+ if (view.dragging) return false;
7653
+ const toReturn = onMarkupInsertion(
7654
+ view,
7655
+ event,
7656
+ slice,
7657
+ setIsDisabledInternal,
7658
+ imageHandlers?.uploadMultipleByURL
7659
+ );
7660
+ if (typeof toReturn === "boolean") return toReturn;
7661
+ return false;
7662
+ },
7663
+ handlePaste: (view, event, slice) => {
7664
+ const toReturn = onMarkupInsertion(
7665
+ view,
7666
+ event,
7667
+ slice,
7668
+ setIsDisabledInternal,
7669
+ imageHandlers?.uploadMultipleByURL
7670
+ );
7671
+ if (typeof toReturn === "boolean") return toReturn;
7534
7672
  try {
7535
7673
  const text = event.clipboardData?.getData("text/plain");
7536
7674
  if (!text) return false;
7537
7675
  const json = JSON.parse(text);
7538
7676
  if (json.type === "doc" && json.content) {
7539
7677
  const node = view.state.schema.nodeFromJSON(json);
7540
- const newSlice = new Slice(node.content, 0, 0);
7678
+ const newSlice = new Slice2(node.content, 0, 0);
7541
7679
  view.dispatch(view.state.tr.replaceSelection(newSlice));
7542
7680
  return true;
7543
7681
  }
@@ -7552,7 +7690,7 @@ function TiptapContent({
7552
7690
  },
7553
7691
  [lang]
7554
7692
  );
7555
- useEffect9(() => {
7693
+ useEffect11(() => {
7556
7694
  if (activeDialog && editor && !editor.isDestroyed) {
7557
7695
  const pos = editor.state.selection.from;
7558
7696
  setSavedDialogCursorPos(pos);
@@ -7579,7 +7717,7 @@ function TiptapContent({
7579
7717
  const katexHandler = createHandleKatexAdd(editor, handleCloseDialog, savedDialogCursorPos);
7580
7718
  katexHandler(latex, katexDialogContext?.pos);
7581
7719
  };
7582
- useEffect9(() => {
7720
+ useEffect11(() => {
7583
7721
  if (!editor || editor.isDestroyed) return;
7584
7722
  editor.commands.setContent(safeContent, {
7585
7723
  emitUpdate: false
@@ -7598,10 +7736,10 @@ function TiptapContent({
7598
7736
  return true;
7599
7737
  };
7600
7738
  if (!editor) return null;
7601
- return /* @__PURE__ */ jsxs35(Fragment3, { children: [
7739
+ return /* @__PURE__ */ jsxs35(Fragment4, { children: [
7602
7740
  /* @__PURE__ */ jsx92(DragHandle, { editor }),
7603
- isEditable && !isDisabled && /* @__PURE__ */ jsx92(TableMenu, { editor }),
7604
- isEditable && !isDisabled && /* @__PURE__ */ jsx92(
7741
+ isEditable && !disabled && /* @__PURE__ */ jsx92(TableMenu, { editor }),
7742
+ isEditable && !disabled && /* @__PURE__ */ jsx92(
7605
7743
  BubbleMenu,
7606
7744
  {
7607
7745
  shouldShow: shouldBubbleMenuShow,
@@ -7619,20 +7757,21 @@ function TiptapContent({
7619
7757
  /* @__PURE__ */ jsxs35(
7620
7758
  "div",
7621
7759
  {
7622
- className: `tiptap-content-wrapper ${!!error ? "has-error" : ""} ${isDisabled ? "is-disabled" : ""}`,
7760
+ className: `tiptap-content-wrapper ${!!error ? "has-error" : ""} ${disabled ? "is-disabled" : ""}`,
7623
7761
  children: [
7624
7762
  /* @__PURE__ */ jsx92(EditorContent, { editor }),
7625
- isEditable && !isDisabled && /* @__PURE__ */ jsx92(CharacterCount, { editor })
7763
+ isEditable && !disabled && /* @__PURE__ */ jsx92(CharacterCount, { editor })
7626
7764
  ]
7627
7765
  }
7628
7766
  ),
7629
7767
  !!error && /* @__PURE__ */ jsx92("p", { className: "tiptap-error-message", children: error }),
7630
- isEditable && /* @__PURE__ */ jsxs35(Fragment3, { children: [
7768
+ isEditable && /* @__PURE__ */ jsxs35(Fragment4, { children: [
7631
7769
  activeDialog === "image" && /* @__PURE__ */ jsx92(
7632
7770
  ImageDialog,
7633
7771
  {
7634
7772
  open: true,
7635
7773
  onClose: handleCloseDialog,
7774
+ defaultFile: insertedImage,
7636
7775
  onImageAdd: handleImageAdd,
7637
7776
  onImageEdit: handleImageEdit,
7638
7777
  uploadHandler: imageHandlers?.upload,