@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.d.mts CHANGED
@@ -11,11 +11,15 @@ interface ImageUploadResult {
11
11
  interface ImageUploadHandler {
12
12
  (file: File): Promise<ImageUploadResult>;
13
13
  }
14
+ interface ImageUploadMultipleByURLHandler {
15
+ (files: string[]): Promise<(ImageUploadResult | null)[]>;
16
+ }
14
17
  interface ImageGetHandler {
15
18
  (fileId: string): string | null;
16
19
  }
17
20
  interface ImageHandlers {
18
21
  upload: ImageUploadHandler;
22
+ uploadMultipleByURL?: ImageUploadMultipleByURLHandler;
19
23
  getImage?: ImageGetHandler;
20
24
  }
21
25
 
@@ -47,8 +51,12 @@ interface TiptapProps {
47
51
  lang?: SupportedLanguage;
48
52
  error?: string;
49
53
  }
54
+ interface KatexDialogContextProps {
55
+ latex?: string;
56
+ pos?: number;
57
+ }
50
58
  declare const Tiptap: (props: TiptapProps) => react_jsx_runtime.JSX.Element;
51
59
 
52
60
  declare function htmlToTiptapContent(content: string | JSONContent$1): JSONContent$1;
53
61
 
54
- export { Tiptap, type SupportedLanguage as TiptapSupportedLanguages, htmlToTiptapContent };
62
+ export { type KatexDialogContextProps, Tiptap, type TiptapProps, type SupportedLanguage as TiptapSupportedLanguages, htmlToTiptapContent };
package/dist/editor.d.ts CHANGED
@@ -11,11 +11,15 @@ interface ImageUploadResult {
11
11
  interface ImageUploadHandler {
12
12
  (file: File): Promise<ImageUploadResult>;
13
13
  }
14
+ interface ImageUploadMultipleByURLHandler {
15
+ (files: string[]): Promise<(ImageUploadResult | null)[]>;
16
+ }
14
17
  interface ImageGetHandler {
15
18
  (fileId: string): string | null;
16
19
  }
17
20
  interface ImageHandlers {
18
21
  upload: ImageUploadHandler;
22
+ uploadMultipleByURL?: ImageUploadMultipleByURLHandler;
19
23
  getImage?: ImageGetHandler;
20
24
  }
21
25
 
@@ -47,8 +51,12 @@ interface TiptapProps {
47
51
  lang?: SupportedLanguage;
48
52
  error?: string;
49
53
  }
54
+ interface KatexDialogContextProps {
55
+ latex?: string;
56
+ pos?: number;
57
+ }
50
58
  declare const Tiptap: (props: TiptapProps) => react_jsx_runtime.JSX.Element;
51
59
 
52
60
  declare function htmlToTiptapContent(content: string | JSONContent$1): JSONContent$1;
53
61
 
54
- export { Tiptap, type SupportedLanguage as TiptapSupportedLanguages, htmlToTiptapContent };
62
+ export { type KatexDialogContextProps, Tiptap, type TiptapProps, type SupportedLanguage as TiptapSupportedLanguages, htmlToTiptapContent };
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 import_react36 = require("react");
39
+ var import_react37 = require("react");
40
40
  var import_state6 = require("@tiptap/pm/state");
41
- var import_model = require("@tiptap/pm/model");
42
- var import_react37 = require("@tiptap/react");
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
  };
@@ -6774,10 +6780,110 @@ var createHandleKatexAdd = (editor, closeDialogCommand, savedPos) => {
6774
6780
  };
6775
6781
  };
6776
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
+
6777
6883
  // src/editor/commands/suggestion.tsx
6778
6884
  var import_dom = require("@floating-ui/dom");
6779
6885
  var import_core19 = require("@tiptap/core");
6780
- var import_react35 = require("@tiptap/react");
6886
+ var import_react36 = require("@tiptap/react");
6781
6887
 
6782
6888
  // src/editor/commands/items.ts
6783
6889
  var getCommandItems = (translations2, hasImageHandlers) => [
@@ -7110,7 +7216,7 @@ var createSuggestions = (translations2, hasImageHandlers) => ({
7110
7216
  render: () => {
7111
7217
  return {
7112
7218
  onStart: async (props) => {
7113
- component = new import_react35.ReactRenderer(CommandsList, {
7219
+ component = new import_react36.ReactRenderer(CommandsList, {
7114
7220
  props,
7115
7221
  editor: props.editor
7116
7222
  });
@@ -7533,27 +7639,35 @@ function TiptapContent({
7533
7639
  lang,
7534
7640
  error
7535
7641
  }) {
7536
- const [linkDialogContext, setLinkDialogContext] = (0, import_react36.useState)(null);
7537
- const [katexDialogContext, setKatexDialogContext] = (0, import_react36.useState)(
7642
+ const [linkDialogContext, setLinkDialogContext] = (0, import_react37.useState)(null);
7643
+ const [katexDialogContext, setKatexDialogContext] = (0, import_react37.useState)(
7538
7644
  null
7539
7645
  );
7540
- const [editFigureContext, setEditFigureContext] = (0, import_react36.useState)(null);
7541
- const [savedDialogCursorPos, setSavedDialogCursorPos] = (0, import_react36.useState)(void 0);
7646
+ const [editFigureContext, setEditFigureContext] = (0, import_react37.useState)(null);
7647
+ const [savedDialogCursorPos, setSavedDialogCursorPos] = (0, import_react37.useState)(void 0);
7542
7648
  const { activeDialog, closeDialogCommand, openDialogCommand } = useDialogCommand();
7543
7649
  const translations2 = useTiptapTranslations();
7650
+ const [isDisabledInternal, setIsDisabledInternal] = (0, import_react37.useState)(isDisabled);
7651
+ const disabled = isDisabled || isDisabledInternal;
7544
7652
  let safeContent;
7545
7653
  try {
7546
7654
  safeContent = JSON.parse(content);
7547
7655
  } catch {
7548
7656
  safeContent = { type: "doc", content: [{ type: "paragraph" }] };
7549
7657
  }
7550
- const editor = (0, import_react37.useEditor)(
7658
+ const {
7659
+ insertedImage,
7660
+ onPaste,
7661
+ onDrop,
7662
+ onMarkupInsertion
7663
+ } = useInsertionHandlers();
7664
+ const editor = (0, import_react38.useEditor)(
7551
7665
  {
7552
- editable: isEditable && !isDisabled,
7666
+ editable: isEditable && !disabled,
7553
7667
  extensions: getEditorExtensions({
7554
7668
  translations: translations2,
7555
7669
  suggestion: createSuggestions(translations2, !!imageHandlers),
7556
- isEditable: isEditable && !isDisabled,
7670
+ isEditable: isEditable && !disabled,
7557
7671
  getImageById: imageHandlers?.getImage,
7558
7672
  onKatexClick: (latex, pos) => {
7559
7673
  setKatexDialogContext({ latex, pos });
@@ -7567,14 +7681,38 @@ function TiptapContent({
7567
7681
  content: safeContent,
7568
7682
  immediatelyRender: false,
7569
7683
  editorProps: {
7570
- handlePaste: (view, event) => {
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;
7571
7709
  try {
7572
7710
  const text = event.clipboardData?.getData("text/plain");
7573
7711
  if (!text) return false;
7574
7712
  const json = JSON.parse(text);
7575
7713
  if (json.type === "doc" && json.content) {
7576
7714
  const node = view.state.schema.nodeFromJSON(json);
7577
- const newSlice = new import_model.Slice(node.content, 0, 0);
7715
+ const newSlice = new import_model2.Slice(node.content, 0, 0);
7578
7716
  view.dispatch(view.state.tr.replaceSelection(newSlice));
7579
7717
  return true;
7580
7718
  }
@@ -7589,7 +7727,7 @@ function TiptapContent({
7589
7727
  },
7590
7728
  [lang]
7591
7729
  );
7592
- (0, import_react36.useEffect)(() => {
7730
+ (0, import_react37.useEffect)(() => {
7593
7731
  if (activeDialog && editor && !editor.isDestroyed) {
7594
7732
  const pos = editor.state.selection.from;
7595
7733
  setSavedDialogCursorPos(pos);
@@ -7616,7 +7754,7 @@ function TiptapContent({
7616
7754
  const katexHandler = createHandleKatexAdd(editor, handleCloseDialog, savedDialogCursorPos);
7617
7755
  katexHandler(latex, katexDialogContext?.pos);
7618
7756
  };
7619
- (0, import_react36.useEffect)(() => {
7757
+ (0, import_react37.useEffect)(() => {
7620
7758
  if (!editor || editor.isDestroyed) return;
7621
7759
  editor.commands.setContent(safeContent, {
7622
7760
  emitUpdate: false
@@ -7637,8 +7775,8 @@ function TiptapContent({
7637
7775
  if (!editor) return null;
7638
7776
  return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(import_jsx_runtime92.Fragment, { children: [
7639
7777
  /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(DragHandle, { editor }),
7640
- isEditable && !isDisabled && /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(TableMenu, { editor }),
7641
- isEditable && !isDisabled && /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
7778
+ isEditable && !disabled && /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(TableMenu, { editor }),
7779
+ isEditable && !disabled && /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
7642
7780
  import_menus.BubbleMenu,
7643
7781
  {
7644
7782
  shouldShow: shouldBubbleMenuShow,
@@ -7656,10 +7794,10 @@ function TiptapContent({
7656
7794
  /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(
7657
7795
  "div",
7658
7796
  {
7659
- className: `tiptap-content-wrapper ${!!error ? "has-error" : ""} ${isDisabled ? "is-disabled" : ""}`,
7797
+ className: `tiptap-content-wrapper ${!!error ? "has-error" : ""} ${disabled ? "is-disabled" : ""}`,
7660
7798
  children: [
7661
- /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(import_react37.EditorContent, { editor }),
7662
- isEditable && !isDisabled && /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(CharacterCount, { editor })
7799
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(import_react38.EditorContent, { editor }),
7800
+ isEditable && !disabled && /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(CharacterCount, { editor })
7663
7801
  ]
7664
7802
  }
7665
7803
  ),
@@ -7670,6 +7808,7 @@ function TiptapContent({
7670
7808
  {
7671
7809
  open: true,
7672
7810
  onClose: handleCloseDialog,
7811
+ defaultFile: insertedImage,
7673
7812
  onImageAdd: handleImageAdd,
7674
7813
  onImageEdit: handleImageEdit,
7675
7814
  uploadHandler: imageHandlers?.upload,