Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

THIS IS NOT AN ISSUE: HACK to Paste Image directly from clipboard #619

Open
dhenry123 opened this issue Feb 3, 2024 · 3 comments
Open

Comments

@dhenry123
Copy link

First of all, thank you very much for your excellent work.

Prepare your server

2 routes :

  • Updload image /upload/new (formdata with the attribut "image" is used in this present case)
    • server store image
    • response (json): {url: "[url to retrive image calling getImage], alt: "image name alternative"}
  • get image /getImage/:imagename (for example)

UI

The process is as follows:

  • copy the image to the clipboard
  • focus on the markdown editor
  • paste
  • the manager tries to identify the context; if it is an image, it sends the image to the server
  • the server returns the url from which the image can be accessed from the server
  • the handler finishes by adding the context of the markdow : ![altname](url) image to the clipboard and the contents of the clipboard are pasted into the markdown editor
  • the markdown editor is refreshed and renders the image by calling the provided url
  • +++

With React, build your component

[...]

// Image upload to server
const imageUploadHandler = async (
    image: File
  ): Promise<{ alt: string; url: string } | null> => {
    if (image && image.size === 0) return null;
    const formData = new FormData();
    formData.append("image", image);
    //formData.append("what else you want", "your value");
    const response = await fetch('/upload/new', {
          method: 'POST',
          body: formData
        })
    return  (await response.json()) as { alt: string, url: string }
};

  const handlePaste = async (event: React.ClipboardEvent<HTMLDivElement>) => {
    // Access the clipboard data using event.clipboardData
    const clipboardData = event.clipboardData;
   // only if clipboard payload is file
    if (clipboardData.files.length === 1) {
      const myfile = clipboardData.files[0] as File;
     // you could perform some test: image size, type mime ....
      const url = await imageUploadHandler(myfile);
      event.preventDefault();
      if (url) {
       // change clipboard payload,
       // document execCommand is obsolete, you could replace with navigator.clipboard API but some browser
      // accept write() method only if the connexion is secure (https)...
        document.execCommand(
          "insertText",
          false,
          `![${url.alt}](${url.url})\n`
        );
      } else {
        document.execCommand(
          "insertText",
          false,
          "ERROR Image has not been stored on server"
        );
      }
    }
  };
 return (
    <div className={`MarkDownEditor`}>
      <MDEditor
       [...]
       // because the MDEditor interface is an extends of textarea props
       // you can use all the methods provided by this interface
      // e.g. add a method for dragging and dropping a file...
        onPaste={handlePaste}
       [...]
      />
    </div>
  );
[...]
@BenchBadr
Copy link

BenchBadr commented Feb 21, 2024

This works too

 useEffect(() => {
      const handlePaste = async (event) => {
        const clipboardData = event.clipboardData || window.clipboardData;
        if (clipboardData && clipboardData.items) {
          for (const item of clipboardData.items) {
            if (item.type.indexOf('image') !== -1) {
              const blob = item.getAsFile();
              const reader = new FileReader();
              reader.onload = () => {
                const base64Data = reader.result.split(',')[1];
                // console.log('Base64 Image Data:', base64Data);
                uploadImageToAPI(base64Data, event)
              };
  
              reader.readAsDataURL(blob);
            }
          }
        } else {
          // console.log('Text Data:', clipboardData.getData('text'));
        }
      };
  
      const textInput = document.querySelector('.w-md-editor-text-input');
      if (textInput) {
        textInput.addEventListener('paste', handlePaste);
      }
  
      return () => {
        if (textInput) {
          textInput.removeEventListener('paste', handlePaste);
        }
      };
    }, []);

I also added a little loader in the toolbar :

const upload = {
    name: "upload",
    keyCommand: "upload",
    buttonProps: { "aria-label": "Insert help" },
    icon: (
          <div className="material-icons-outlined" style={{fontSize:'12px',animation:'spin 2s linear infinite',display:['none','block'][uploading]}}>cached</div>
    ),
  };

@AizenSosike
Copy link

thanks you so much, It helps me a lot

@vprakia
Copy link

vprakia commented Jul 11, 2024

Can't thank you enough sir!
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants