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

How do I upload images? #83

Closed
TheStarkor opened this issue Jan 5, 2021 · 13 comments
Closed

How do I upload images? #83

TheStarkor opened this issue Jan 5, 2021 · 13 comments

Comments

@TheStarkor
Copy link

I have an upload server (https://.../upload/img) that returns an image's URL and I found the commands.image.

I want to modify the commands that can upload an image(how to..?) and return ![](res.url)(I can do it with execute)

Can I add components like the figure?

image

@jaywcjlove
Copy link
Member

@TheStarkor Definition tool reference: #85

import React from "react";
import ReactDOM from "react-dom";
import MDEditor, { commands, ICommand, TextState, TextApi } from '@uiw/react-md-editor';

const title3: ICommand = {
  name: 'title3',
  keyCommand: 'title3',
  buttonProps: { 'aria-label': 'Insert title3' },
  icon: (
    <svg width="12" height="12" viewBox="0 0 520 520">
      <path fill="currentColor" d="M15.7083333,468 C7.03242448,468 0,462.030833 0,454.666667 L0,421.333333 C0,413.969167 7.03242448,408 15.7083333,408 L361.291667,408 C369.967576,408 377,413.969167 377,421.333333 L377,454.666667 C377,462.030833 369.967576,468 361.291667,468 L15.7083333,468 Z M21.6666667,366 C9.69989583,366 0,359.831861 0,352.222222 L0,317.777778 C0,310.168139 9.69989583,304 21.6666667,304 L498.333333,304 C510.300104,304 520,310.168139 520,317.777778 L520,352.222222 C520,359.831861 510.300104,366 498.333333,366 L21.6666667,366 Z M136.835938,64 L136.835937,126 L107.25,126 L107.25,251 L40.75,251 L40.75,126 L-5.68434189e-14,126 L-5.68434189e-14,64 L136.835938,64 Z M212,64 L212,251 L161.648438,251 L161.648438,64 L212,64 Z M378,64 L378,126 L343.25,126 L343.25,251 L281.75,251 L281.75,126 L238,126 L238,64 L378,64 Z M449.047619,189.550781 L520,189.550781 L520,251 L405,251 L405,64 L449.047619,64 L449.047619,189.550781 Z" />
    </svg>
  ),
  execute: (state: TextState, api: TextApi) => {
    let modifyText = `### ${state.selectedText}\n`;
    if (!state.selectedText) {
      modifyText = `### `;
    }
    api.replaceSelection(modifyText);
  },
};

export default function App() {
  const [value, setValue] = React.useState("**Hello world!!!**");
  return (
    <div className="container">
      <MDEditor
        value="Hello Markdown!"
        commands={[
          commands.bold, commands.hr, commands.italic, commands.divider, commands.codeEdit, commands.codeLive, commands.codePreview, commands.divider,
          commands.fullscreen, 
          // Custom Toolbars
          title3,
        ]}
      />
    </div>
  );
}

@TheStarkor
Copy link
Author

TheStarkor commented Jan 7, 2021

Thank you for answering @jaywcjlove.

What I need is

const image = {
  name: 'image',
  keyCommand: 'image',
  buttonProps: { 'aria-label': 'Insert image' },
  icon: (
    <svg width="12" height="12" viewBox="0 0 520 520">
      <path fill="currentColor" d="M15.7083333,468 C7.03242448,468 0,462.030833 0,454.666667 L0,421.333333 C0,413.969167 7.03242448,408 15.7083333,408 L361.291667,408 C369.967576,408 377,413.969167 377,421.333333 L377,454.666667 C377,462.030833 369.967576,468 361.291667,468 L15.7083333,468 Z M21.6666667,366 C9.69989583,366 0,359.831861 0,352.222222 L0,317.777778 C0,310.168139 9.69989583,304 21.6666667,304 L498.333333,304 C510.300104,304 520,310.168139 520,317.777778 L520,352.222222 C520,359.831861 510.300104,366 498.333333,366 L21.6666667,366 Z M136.835938,64 L136.835937,126 L107.25,126 L107.25,251 L40.75,251 L40.75,126 L-5.68434189e-14,126 L-5.68434189e-14,64 L136.835938,64 Z M212,64 L212,251 L161.648438,251 L161.648438,64 L212,64 Z M378,64 L378,126 L343.25,126 L343.25,251 L281.75,251 L281.75,126 L238,126 L238,64 L378,64 Z M449.047619,189.550781 L520,189.550781 L520,251 L405,251 L405,64 L449.047619,64 L449.047619,189.550781 Z" />
    </svg>
  ),
  execute: async (state, api) => {
    // OPEN my component. 

    // example
    // const [image, setImaeg] = React.useState('');
    // <ImageUploader value={image} onChange={setImage} />

    let image_url = await upload_image(image);

    let modifyText = `![](${image_url})\n`;
    api.replaceSelection(modifyText);
  },
};
  1. I click my custom button. (like title3)
  2. Show my component that can upload an image like the figure.
  3. Get image buffer and upload.
  4. Modify text.

Can I open a component below the button(commands)?

@jaywcjlove
Copy link
Member

@TheStarkor I need to expand this

image

Do you need such an effect?

@TheStarkor
Copy link
Author

Yes! That's what I want.

@jaywcjlove
Copy link
Member

import React from 'react';
import MDEditor, { commands, ICommand } from '../';
const ExampleCustomToolbar = () => {
const title: ICommand<string> = {
name: 'title3',
keyCommand: 'title3',
buttonProps: null,
icon: (
<span style={{ padding: '0 5px' }}>Custom Toolbar</span>
),
};
return (
<MDEditor
value="Hello Markdown!"
commands={[
title,
commands.group([commands.title1, commands.title2, commands.title3, commands.title4, commands.title5, commands.title6], {
name: 'title',
groupName: 'title',
buttonProps: { 'aria-label': 'Insert title'}
}),
// commands.bold, commands.hr, commands.italic, commands.divider,
commands.group([], {
name: 'update',
groupName: 'update',
icon: (
<svg viewBox="0 0 1024 1024" width="12" height="12">
<path fill="currentColor" d="M716.8 921.6a51.2 51.2 0 1 1 0 102.4H307.2a51.2 51.2 0 1 1 0-102.4h409.6zM475.8016 382.1568a51.2 51.2 0 0 1 72.3968 0l144.8448 144.8448a51.2 51.2 0 0 1-72.448 72.3968L563.2 541.952V768a51.2 51.2 0 0 1-45.2096 50.8416L512 819.2a51.2 51.2 0 0 1-51.2-51.2v-226.048l-57.3952 57.4464a51.2 51.2 0 0 1-67.584 4.2496l-4.864-4.2496a51.2 51.2 0 0 1 0-72.3968zM512 0c138.6496 0 253.4912 102.144 277.1456 236.288l10.752 0.3072C924.928 242.688 1024 348.0576 1024 476.5696 1024 608.9728 918.8352 716.8 788.48 716.8a51.2 51.2 0 1 1 0-102.4l8.3968-0.256C866.2016 609.6384 921.6 550.0416 921.6 476.5696c0-76.4416-59.904-137.8816-133.12-137.8816h-97.28v-51.2C691.2 184.9856 610.6624 102.4 512 102.4S332.8 184.9856 332.8 287.488v51.2H235.52c-73.216 0-133.12 61.44-133.12 137.8816C102.4 552.96 162.304 614.4 235.52 614.4l5.9904 0.3584A51.2 51.2 0 0 1 235.52 716.8C105.1648 716.8 0 608.9728 0 476.5696c0-132.1984 104.8064-239.872 234.8544-240.2816C258.5088 102.144 373.3504 0 512 0z" />
</svg>
),
children: (handle) => {
return (
<div style={{ width: 120, padding: 10 }}>
<div>My Custom Toolbar</div>
<button type="button" onClick={() => console.log('> execute: >>>>>', handle.getState!())}>State</button>
<button type="button" onClick={() => handle.close()}>Close</button>
<button type="button" onClick={() => handle.execute()}>Execute</button>
</div>
);
},
execute: (state: commands.TextState, api: commands.TextApi) => {
console.log('> execute: >>>>>', state)
},
buttonProps: { 'aria-label': 'Insert title'}
}),
// commands.divider,
// commands.codeEdit, commands.codeLive, commands.codePreview, commands.divider,
// commands.fullscreen,
]}
/>
)
}
export default ExampleCustomToolbar;

Upgrade + @uiw/react-md-editor@2.1.0

@jaywcjlove
Copy link
Member

@TheStarkor Example: https://codesandbox.io/s/dazzling-resonance-m08m7

@R3D-Z3R0
Copy link

Hello @jaywcjlove the example is not working and we wanted to implement a small drag & drop it
2021-07-22 17_11_24-React Typescript - CodeSandbox
into our markdown

@jmvalino
Copy link

hi @jaywcjlove can you give more example on how to achieve this functionality ? Thanks

@killerbytes
Copy link

How to implement with a custom component eg: Modal?

@dino3616
Copy link

Hi, I have tried to implement a way to upload an image by Drag&Drop directly into the editor or by Copy&Paste and display it in the preview.
It may not be a very pretty method, but I hope it will be useful to you.
Here's my EXAMPLE!

import MDEditor from '@uiw/react-md-editor';
import { useState } from 'react';
import onImagePasted from './utils/onImagePasted';
import '@uiw/react-md-editor/markdown-editor.css';
import '@uiw/react-markdown-preview/markdown.css';

const MdEditor = () => {
  const [markdown, setMarkdown] = useState<string | undefined>();

  return (
    <div data-color-mode="light">
      <MDEditor
        value={markdown}
        onChange={(value) => {
          setMarkdown(value);
        }}
        onPaste={async (event) => {
          await onImagePasted(event.clipboardData, setMarkdown);
        }}
        onDrop={async (event) => {
          await onImagePasted(event.dataTransfer, setMarkdown);
        }}
        height={440}
        textareaProps={{
          placeholder: 'Fill in your markdown for the coolest of the cool.',
        }}
        hideToolbar
      />
    </div>
  );
};

export default MdEditor;
import type { SetStateAction } from 'react';
import { fileUpload } from '../../../../../libs/firebase/storage';
import insertToTextArea from './insertToTextArea';

const onImagePasted = async (dataTransfer: DataTransfer, setMarkdown: (value: SetStateAction<string | undefined>) => void) => {
  const files: File[] = [];
  for (let index = 0; index < dataTransfer.items.length; index += 1) {
    const file = dataTransfer.files.item(index);

    if (file) {
      files.push(file);
    }
  }

  await Promise.all(
    files.map(async (file) => {
      const url = await fileUpload(file);
      const insertedMarkdown = insertToTextArea(`![](${url})`);
      if (!insertedMarkdown) {
        return;
      }
      setMarkdown(insertedMarkdown);
    }),
  );
};

export default onImagePasted;
const insertToTextArea = (intsertString: string) => {
  const textarea = document.querySelector('textarea');
  if (!textarea) {
    return null;
  }

  let sentence = textarea.value;
  const len = sentence.length;
  const pos = textarea.selectionStart;
  const end = textarea.selectionEnd;

  const front = sentence.slice(0, pos);
  const back = sentence.slice(pos, len);

  sentence = front + intsertString + back;

  textarea.value = sentence;
  textarea.selectionEnd = end + intsertString.length;

  return sentence;
};

export default insertToTextArea;

@dibasdauliya
Copy link

@TheStarkor Example: https://codesandbox.io/s/dazzling-resonance-m08m7

getting TypeError: handle.getState is not a function error

@nguyenvietdung31
Copy link

Thank you for answering @jaywcjlove.

What I need is

const image = {
  name: 'image',
  keyCommand: 'image',
  buttonProps: { 'aria-label': 'Insert image' },
  icon: (
    <svg width="12" height="12" viewBox="0 0 520 520">
      <path fill="currentColor" d="M15.7083333,468 C7.03242448,468 0,462.030833 0,454.666667 L0,421.333333 C0,413.969167 7.03242448,408 15.7083333,408 L361.291667,408 C369.967576,408 377,413.969167 377,421.333333 L377,454.666667 C377,462.030833 369.967576,468 361.291667,468 L15.7083333,468 Z M21.6666667,366 C9.69989583,366 0,359.831861 0,352.222222 L0,317.777778 C0,310.168139 9.69989583,304 21.6666667,304 L498.333333,304 C510.300104,304 520,310.168139 520,317.777778 L520,352.222222 C520,359.831861 510.300104,366 498.333333,366 L21.6666667,366 Z M136.835938,64 L136.835937,126 L107.25,126 L107.25,251 L40.75,251 L40.75,126 L-5.68434189e-14,126 L-5.68434189e-14,64 L136.835938,64 Z M212,64 L212,251 L161.648438,251 L161.648438,64 L212,64 Z M378,64 L378,126 L343.25,126 L343.25,251 L281.75,251 L281.75,126 L238,126 L238,64 L378,64 Z M449.047619,189.550781 L520,189.550781 L520,251 L405,251 L405,64 L449.047619,64 L449.047619,189.550781 Z" />
    </svg>
  ),
  execute: async (state, api) => {
    // OPEN my component. 

    // example
    // const [image, setImaeg] = React.useState('');
    // <ImageUploader value={image} onChange={setImage} />

    let image_url = await upload_image(image);

    let modifyText = `![](${image_url})\n`;
    api.replaceSelection(modifyText);
  },
};
  1. I click my custom button. (like title3)
  2. Show my component that can upload an image like the figure.
  3. Get image buffer and upload.
  4. Modify text.

Can I open a component below the button(commands)?

can you share the code that open your component (e.g a modal) ?

@BarrySong97
Copy link

Hi, I have tried to implement a way to upload an image by Drag&Drop directly into the editor or by Copy&Paste and display it in the preview. It may not be a very pretty method, but I hope it will be useful to you. Here's my EXAMPLE!

import MDEditor from '@uiw/react-md-editor';
import { useState } from 'react';
import onImagePasted from './utils/onImagePasted';
import '@uiw/react-md-editor/markdown-editor.css';
import '@uiw/react-markdown-preview/markdown.css';

const MdEditor = () => {
  const [markdown, setMarkdown] = useState<string | undefined>();

  return (
    <div data-color-mode="light">
      <MDEditor
        value={markdown}
        onChange={(value) => {
          setMarkdown(value);
        }}
        onPaste={async (event) => {
          await onImagePasted(event.clipboardData, setMarkdown);
        }}
        onDrop={async (event) => {
          await onImagePasted(event.dataTransfer, setMarkdown);
        }}
        height={440}
        textareaProps={{
          placeholder: 'Fill in your markdown for the coolest of the cool.',
        }}
        hideToolbar
      />
    </div>
  );
};

export default MdEditor;
import type { SetStateAction } from 'react';
import { fileUpload } from '../../../../../libs/firebase/storage';
import insertToTextArea from './insertToTextArea';

const onImagePasted = async (dataTransfer: DataTransfer, setMarkdown: (value: SetStateAction<string | undefined>) => void) => {
  const files: File[] = [];
  for (let index = 0; index < dataTransfer.items.length; index += 1) {
    const file = dataTransfer.files.item(index);

    if (file) {
      files.push(file);
    }
  }

  await Promise.all(
    files.map(async (file) => {
      const url = await fileUpload(file);
      const insertedMarkdown = insertToTextArea(`![](${url})`);
      if (!insertedMarkdown) {
        return;
      }
      setMarkdown(insertedMarkdown);
    }),
  );
};

export default onImagePasted;
const insertToTextArea = (intsertString: string) => {
  const textarea = document.querySelector('textarea');
  if (!textarea) {
    return null;
  }

  let sentence = textarea.value;
  const len = sentence.length;
  const pos = textarea.selectionStart;
  const end = textarea.selectionEnd;

  const front = sentence.slice(0, pos);
  const back = sentence.slice(pos, len);

  sentence = front + intsertString + back;

  textarea.value = sentence;
  textarea.selectionEnd = end + intsertString.length;

  return sentence;
};

export default insertToTextArea;

great job, my friend

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

9 participants