@cyberyozh/text-editor 0.5.0 → 0.5.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyberyozh/text-editor",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Rich text editor library based on Tiptap for CyberYozh",
5
5
  "author": "CyberYozh",
6
6
  "license": "MIT",
@@ -0,0 +1,37 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { shouldHandleInsertedFile } from "./markup-insertion";
3
+
4
+ function fakeEvent() {
5
+ return {
6
+ preventDefault: vi.fn(),
7
+ stopPropagation: vi.fn(),
8
+ } as unknown as Event & {
9
+ preventDefault: ReturnType<typeof vi.fn>;
10
+ stopPropagation: ReturnType<typeof vi.fn>;
11
+ };
12
+ }
13
+
14
+ describe("shouldHandleInsertedFile", () => {
15
+ it("leaves the event untouched when clipboard has no files (plain text / html paste)", () => {
16
+ const event = fakeEvent();
17
+ expect(shouldHandleInsertedFile(undefined, event)).toBe(false);
18
+ expect(event.preventDefault).not.toHaveBeenCalled();
19
+ expect(event.stopPropagation).not.toHaveBeenCalled();
20
+ });
21
+
22
+ it("blocks the event for a non-image file and does not handle it", () => {
23
+ const event = fakeEvent();
24
+ const file = new File(["data"], "doc.pdf", { type: "application/pdf" });
25
+ expect(shouldHandleInsertedFile(file, event)).toBe(false);
26
+ expect(event.preventDefault).toHaveBeenCalledOnce();
27
+ expect(event.stopPropagation).toHaveBeenCalledOnce();
28
+ });
29
+
30
+ it("blocks the event for an image file and handles it", () => {
31
+ const event = fakeEvent();
32
+ const file = new File(["data"], "pic.png", { type: "image/png" });
33
+ expect(shouldHandleInsertedFile(file, event)).toBe(true);
34
+ expect(event.preventDefault).toHaveBeenCalledOnce();
35
+ expect(event.stopPropagation).toHaveBeenCalledOnce();
36
+ });
37
+ });
@@ -3,7 +3,12 @@ import { useDialogCommand } from "../context";
3
3
  import { EditorView } from "@tiptap/pm/view";
4
4
  import { Slice } from "@tiptap/pm/model";
5
5
  import { ImageHandlers } from "@/types";
6
- import { applyUploadResultsToNodes, collectRehostableImages, removeImagesFromSlice } from "./markup-insertion";
6
+ import {
7
+ applyUploadResultsToNodes,
8
+ collectRehostableImages,
9
+ removeImagesFromSlice,
10
+ shouldHandleInsertedFile,
11
+ } from "./markup-insertion";
7
12
 
8
13
  export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
9
14
  const [insertedImage, setInsertedImage] = useState<File | null>(null);
@@ -17,10 +22,7 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
17
22
  }, [insertedImage, activeDialog]);
18
23
 
19
24
  function genericInsertionHandler(file: File | undefined, event: Event) {
20
- event.preventDefault();
21
- event.stopPropagation();
22
-
23
- if (!file || !file.type.startsWith("image/")) return;
25
+ if (!file || !shouldHandleInsertedFile(file, event)) return;
24
26
  if (!imageHandlers?.upload) return;
25
27
 
26
28
  setInsertedImage(file);
@@ -48,7 +50,7 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
48
50
  if (
49
51
  "clipboardData" in event &&
50
52
  Array.from(event.clipboardData?.items ?? []).some(
51
- (item) => item.type === "text/html"
53
+ (item) => item.type === "text/html",
52
54
  )
53
55
  ) {
54
56
  hasMarkupInsertion = true;
@@ -57,7 +59,7 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
57
59
  if (
58
60
  "dataTransfer" in event &&
59
61
  Array.from(event.dataTransfer?.items ?? []).some(
60
- (item) => item.type === "text/html"
62
+ (item) => item.type === "text/html",
61
63
  )
62
64
  ) {
63
65
  hasMarkupInsertion = true;
@@ -69,7 +71,8 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
69
71
 
70
72
  const imagesToNodes = collectRehostableImages(slice);
71
73
 
72
- imageHandlers.uploadMultipleByURL(imagesToNodes.map(([url]) => url))
74
+ imageHandlers
75
+ .uploadMultipleByURL(imagesToNodes.map(([url]) => url))
73
76
  .then((results) => {
74
77
  applyUploadResultsToNodes(imagesToNodes, results);
75
78
  view.dispatch(view.state.tr.replaceSelection(slice));
@@ -78,7 +81,9 @@ export function useInsertionHandlers(imageHandlers?: ImageHandlers) {
78
81
  setDisabled(false);
79
82
  });
80
83
  } else {
81
- view.dispatch(view.state.tr.replaceSelection(removeImagesFromSlice(slice)));
84
+ view.dispatch(
85
+ view.state.tr.replaceSelection(removeImagesFromSlice(slice)),
86
+ );
82
87
  }
83
88
 
84
89
  return true;
@@ -70,3 +70,12 @@ export function applyUploadResultsToNodes(
70
70
  node.attrs.storageKey = result.storageKey;
71
71
  });
72
72
  }
73
+
74
+ export function shouldHandleInsertedFile(file: File | undefined, event: Event): boolean {
75
+ if (!file) return false;
76
+
77
+ event.preventDefault();
78
+ event.stopPropagation();
79
+
80
+ return file.type.startsWith("image/");
81
+ }